커널 모듈
gpu-unlock-ada는 NVIDIA 순정 드라이버가 GPU를 잡기 전에 initramfs에서 GPU 내부 보안 프로세서(SEC2)를 돌려 SM 속도 선택 레지스터(SS0/SS1)를 "전속"으로 바꾸고, PCIe FLR로 GPU를 한 번 리셋한 뒤 순정 드라이버에 넘기는 리눅스 커널 모듈입니다. 드라이버를 패치하지도, 파일시스템을 바꾸지도 않습니다. 커널 명령줄에 gpu_unlock.targets=가 있을 때만 동작합니다.
이 저장소는 Zagitta/gpu-unlock(리눅스 모듈·펌웨어 생성·부팅 패키징)을 바탕으로, TOTORONG/AD103_win_unlocker(Hugging Face)가 공개한 AD102 페이로드와 디버그 부스터 분석을 통합해 RTX 4090(AD102)까지 넓힌 포크입니다. 이후 RTX 4070 Ti SUPER(AD103)와 RTX 4060 Ti 16GB(AD106)를 "카드 한 장에서 확인된 실험 대상"으로 추가했고, Windows UEFI 실험 포트(Latence.app 기여)가 한 폴더에 따로 들어 있습니다.
차(GPU)에는 엔진 출력은 그대로인데 특정 작업(FP32 누적 텐서 연산)에서만 반 속도로 묶는 제한 장치(SM 속도 선택값)가 있습니다. 보통은 운전자(드라이버)가 시동을 걸 때 이미 잠겨 있습니다.
이 모듈은 운전자가 타기 전에 정비소 문(initramfs)을 열고, 허락 장치(PLM 권한 레지스터)를 풀어 제한 값을 바꾼 뒤, 차를 한 번 껐다 켜서(FLR) 운전자에게 넘깁니다. 운전자는 아무것도 모른 채 풀린 차를 몹니다.
| 카드 · 모드 | 풀기 전 | 풀린 뒤 | 비고 |
|---|---|---|---|
| RTX 4090 FP16→FP32 | 165.6 TF/s | 331.6 TF/s | 명령어 발행률 기준 |
| RTX 4090 BF16→FP32 | 165.5 TF/s | 330.6 TF/s | INT8·dp4a·FP32는 그대로 |
| RTX 4070 Ti SUPER FP16→FP32 | 95.0 TF/s | 189.9 TF/s | 같은 240 W 전력 제한 |
| 4070 Ti SUPER TF32→FP32 | 47.49 TF/s | 94.51 TF/s | 1.99배 |
| 4070 Ti SUPER FP8 CUTLASS GEMM | 59.326 TF/s | 59.495 TF/s | 0.3% 차이, 이득 없음 |
"2배"는 레지스터에 올려 둔 값으로 명령어만 돌리는 발행률 측정이고, 실제 cuBLAS 행렬곱에서는 RTX 4090의 FP16이 12~34%, BF16이 4096³ 크기에서 거의 변화 없음으로 나왔습니다. README가 스스로 "전체 응용 가속이 아니다"라고 못박은 대목입니다.
2026-10-25 GitHub 기준 스타 약 1,241개, 포크 144개입니다. 저장소는 2026-10-23에 만들어져 이틀 만에 이 수치에 도달했고, 커밋은 19개, 릴리스는 v1.1.0·v1.1.1 둘입니다. 이 수치는 관심도일 뿐 도입 효과를 뜻하지 않습니다.
docs/evidence/)가 함께 올라와 있습니다.SHA256SUMS, 같은 카드에서 stock/unlocked를 맞붙인 "matched" 실험 디렉터리까지 남깁니다.firmware/NOTICE.md가 밝힙니다.| 구분 | 무엇을 푸나 | 이 저장소와의 관계 |
|---|---|---|
| CMP 40HX/170HX 계열 | 채굴 전용 카드의 제한 | V67 체인과 일부 상수가 파생 원본(cmp90hx-unlock) |
| Zagitta/gpu-unlock | Ampere(GA10x) 텐서 제한 | 리눅스 모듈·패키징의 직접 부모 |
| TOTORONG AD103_win_unlocker | Ada 페이로드·디버그 부스터 분석 | AD102 페이로드의 출처 |
| 이 저장소 | Ada(AD102/103/106) 카드별 옵트인 | 통합 + 카드 한 장 단위 검증 + 안전 게이트 |
이 도구는 펌웨어가 의도적으로 막아 둔 영역을 우회합니다. 보증·약관(NVIDIA 드라이버 라이선스)·주변 안정성에 영향을 줄 수 있고, 저장소도 "카드 한 장에서 확인했다"는 표현을 반복합니다. 이 문서는 구조와 근거를 설명할 뿐 개별 카드의 안전을 보증하지 않습니다.
초판 커밋 4e7fefc의 추적 파일은 338개였습니다. 다음 개수·줄 수는 그 시점의 구조 관측값이며 최신 개수는 아닙니다. 확장자별로는 헤더 112개(상당수가 Windows 포트에 들어 있는 EDK2 UEFI 헤더와 호환 계층), C 43개, 마크다운 27개, Python 28개, JSON 33개, CUDA(.cu) 4개, PowerShell 4개입니다. 리눅스 모듈 자체는 kernel/gpu_unlock/의 .c 6개·.h 9개로 합계 약 3,170줄이고, 그중 unlock_610.c가 1,781줄입니다.
| 층 | 기술 | 근거 파일 |
|---|---|---|
| 커널 모듈 | C, Kbuild, DKMS, pci_dev, pcie_flr, 비동기 도메인(ASYNC_DOMAIN_EXCLUSIVE) | kernel/gpu_unlock/main.c, Kbuild, dkms.conf |
| 펌웨어 가공 | Python 3, zstd, SHA-256 핀(고정) 검증 | firmware/generate_v67.py, extract_boot_assets.py, fetch_artifacts.py |
| 빌드·배포 | Make, Arch PKGBUILD, Debian(debian/), RPM spec | Makefile, packaging/ |
| 부팅 통합 | mkinitcpio·initramfs-tools·dracut 드롭인 | packaging/boot/*.conf |
| 호스트 테스트 | C 목(mock) 하니스 + Python unittest | tests/ (18개 항목, 목 헤더 디렉터리 포함) |
| 검증 도구 | CUDA(mma_bench.cu), cuBLAS, NVIDIA RM 제어 호출용 ctypes | diagnostics/bench/, tools/read_sm_rates.py |
| Windows 실험 | UEFI 앱(C), EDK2 헤더, PowerShell 빌드 | windows/ad104-msi/ |
firmware/artifacts-610.43.03.json이 NVIDIA 오픈 커널 모듈 소스 아카이브(URL·SHA-256 포함), nvidia-utils 패키지, CMP90 참조 패치 파일들을 해시와 함께 적어 둡니다. fetch_artifacts.py는 HTTPS만 허용하고, 리다이렉트도 HTTPS여야 하며, 1 GiB를 넘으면 중단하고, 해시가 다르면 거부합니다. kernel/gpu_unlock/firmware_pins_610.h는 같은 해시를 커널 안에 박아서, 모듈이 펌웨어를 읽은 뒤 SHA-256을 다시 확인한 다음에야 카드를 건드립니다.
이 순서는 gpu_unlock_610_unlock()(unlock_610.c 끝부분)에 unlock_transaction_* 함수들이 차례로 호출되는 모양 그대로입니다. 중간 하나가 실패하면 뒤 단계는 건너뛰고 되돌리기(unwind)가 돌며, 실패한 카드는 gpu_unlock: unlock failed (errno)를 남깁니다.
main.c의 gpu_unlock_init()은 다음 규칙을 순서대로 적용합니다.
if (!targets || !*targets) return 0; // 명령줄 옵트인이 없으면 종료
all = !strcmp(targets, "all");
if (!all) report_unmatched(); // 오타난 PCI 주소를 로그로 알림
while ((pdev = next_supported(pdev))) {
// exact로 표시된 실험 ID는 "all"이나 부분일치 금지,
// 각 카드의 정확한 PCI 주소가 필요 (쉼표 목록 가능)
if (!all && !listed(pci_name(pdev))) continue;
async_schedule_domain(unlock_one, pci_dev_get(pdev), &unlock_domain);
}
async_synchronize_full_domain(&unlock_domain); // nvidia 가 뜨기 전에 전부 끝내기
supported()는 PCI 장치 ID로 대상 카드를 거릅니다. RTX 4090(2684)과 GA10x 목록 외에는 experimental_ids[]의 ID·enabled 플래그·exact 조건을 확인합니다. AD103·AD104·AD106·AD107, AD103M과 일부 AD102 변형이 별도 플래그로 추가됐습니다. RTX 4090 D(2685)는 exact=false인 예외이며 다른 실험 ID는 정확한 PCI 주소도 필요합니다. unlock_one()은 드라이버가 이미 바인딩된 카드면 -EBUSY로 건너뜁니다.
NVIDIA GPU는 부팅 때 VBIOS의 FWSEC가 보호 영역(WPR2)을 만들고, 서명된 Booter가 SEC2에서 GSP를 올립니다. 이 모듈은 같은 과정을 Linux 커널 모듈이 직접 수행하되, 서명 영역에 V67 페이로드(64,000바이트, GU_V67_SIZE 0xfa00)를 끼워 SEC2가 그것을 실행하게 합니다. AD102 페이로드는 4개의 쓰기 체인(WPR_CFG, FBPA, WPR, FEAT_OVR_PLM)을 만들고, 마지막에 mailbox0 표식 0x67b00001을 요청합니다. ad102_permissions[]가 이 네 가지 레지스터 주소와 기대값을 표로 들고 있습니다.
static const struct { const char *name; u32 offset, expected; bool required; }
ad102_permissions[] = {
{ "WPR_CFG", 0x001fa7cc, 0xfffff0ff, true },
{ "FBPA", 0x009a0148, 0xffffffff, false }, // 관찰용, 필수 아님
{ "WPR", 0x001fa7c4, 0xffffffff, true },
{ "FEAT", GU_FEAT_PLM_OFFSET, 0xffffffff, true },
};
그 뒤 write_selector()가 SS0 = 0x88888888, SS1 = 0x8(정의: GU_SS0_UNLOCK, GU_SS1_UNLOCK)을 쓰고 읽어서 값이 맞는지 확인합니다. AD102에서는 페이로드 표식과 권한 변화가 확인되기 전에 선택값을 쓰지 않습니다(payload_verified 검사, 아니면 -EPROTO).
unlock_transaction_reset()은 선택값이 맞는지 한 번 더 읽고, 버스 마스터(BME)를 끈 상태로 pcie_flr()을 호출합니다. 이어서 PCI 설정·BAR 값이 이전과 같은지, ROM GFW 정지(halt)가 확인되는지, 보호 영역 WPR2가 꺼져 있는지(hi >> 4가 0), SS0/SS1이 FLR 뒤에도 남아 있는지를 확인합니다. WPR2가 남아 있으면 순정 드라이버가 거부하기 때문에 -EBUSY로 중단합니다. 한 번이라도 모순이 나오면 실패이고, 펌웨어가 쓸 수도 있는 DMA 버퍼는 해제하지 않고 남겨 둡니다(README: "power off before trying again").
| 패턴 | 코드에서 보이는 모습 |
|---|---|
| 옵트인 + 단계적 허용 | 명령줄 targets, 카드별 experimental 플래그, 4070 Ti SUPER·4060 Ti는 all 금지 |
| 입력 고정 + 사용 시점 재검증 | 빌드 때 SHA-256 핀, 모듈 로드 때 firmware_pins로 다시 확인 |
| 트랜잭션 + 되돌리기 | 단계 호출 11회와 unlock_transaction_unwind(), 결과 매크로 GU_RESULT가 줄 번호를 남김 |
| 증거 중심 개발 | 로그에 읽은 값·기대값을 매번 출력, 실측은 docs/evidence/에 해시와 함께 보관 |
이 코드의 진짜 볼거리는 "레지스터 한두 개를 바꾼다"가 아니라, 실패 가능한 하드웨어 작업을 읽은 값·기대값·되돌리기로 감싼 방어 설계입니다.
README.md와 QUICKSTART.md — 무엇을 하는지와 어디까지 검증됐는지.kernel/gpu_unlock/main.c — 176줄이라 한 번에 읽힙니다.unlock_610.c의 gpu_unlock_610_unlock()부터 거꾸로 단계 함수 읽기.firmware/README.md와 generate_v67.py — 페이로드가 어떤 바이트로 이루어지는지.docs/ada-2705-report.md와 evidence/ — 주장이 어떤 로그로 뒷받침되는지.| 주제 | 이 저장소에서 보이는 것 | 실습 아이디어 |
|---|---|---|
| 리눅스 PCI 드라이버 기초 | pci_get_device 순회, pci_dev_lock, pcie_flr, pci_restore_state | main.c의 흐름을 따라 그리기 |
| GPU 부팅 체인 | VBIOS → FWSEC → Booter(SEC2) → GSP-RM, WPR2 보호 영역 | docs/How Ampere was unlocked.md의 순서도 읽기 |
| 공급망 방어 | URL+SHA-256 핀, HTTPS 강제, 다운로드 크기 한도, 커널 안 재검증 | fetch_artifacts.py를 읽고 위협 목록 만들기 |
| 하드웨어 상태 검증 습관 | "썼다"가 아니라 "읽어서 같다"로 판정, 로그에 기대값 병기 | write_selector() 패턴을 다른 코드에 적용 |
| 목(mock)으로 커널 코드 테스트 | tests/unlock_shim/linux/*.h가 커널 헤더를 흉내 냄 | make test 후 목 헤더 추가해 보기 |
| 과학적 주장 쓰기 | "카드 1장, 1회" 같은 한계 명시, 발행률 vs 실제 GEMM 구분 | README의 한계 문장만 모아 목록 만들기 |
레지스터 이름과 숫자가 낯설어도 괜찮습니다. 먼저 "읽는다 → 기대값과 비교한다 → 다르면 중단한다" 구조를 찾고, 그다음에 값의 의미를 문서에서 맞춰 보세요. 코드의 절반이 이 반복입니다.
| 대상 | PCI ID | 상태 |
|---|---|---|
| RTX 4090 (AD102) | 10de:2684 | 일반 옵트인. 독립 실행 1회가 더 있음(커널 7.2.11, 드라이버 615.78.08) |
| RTX 4090 D | 10de:2685 | rtx4090d_experimental=1 필요 |
| RTX 4070 Ti SUPER (AD103) | 10de:2705 | 실험적, 카드 1장에서 확인, 정확한 PCI 주소 + ad103_experimental=1 |
| RTX 4060 Ti 16GB (AD106) | 10de:2805 | 실험적, 카드 1장·콜드 부팅 1회, ad106_experimental=1, 같은 카드 stock 처리량 비교는 아직 없음 |
| GA102/GA104/GA106 등 Ampere | supported_ids[] 목록 | 상속 목록. README는 "모든 레거시 보드를 재검증하지 않았다"고 적음 |
전문가용 RTX A 시리즈와 데이터센터 GPU는 허용하지 않습니다(이미 전속이기 때문). 공유 펌웨어를 쓴다고 해서 목록 밖 카드가 지원되는 것은 아닙니다.
| PCI ID | 구분 | 소스의 옵트인 이름 |
|---|---|---|
| 2689 | AD102형 4070 Ti SUPER | rtx4070tis_ad102_experimental |
| 2702·2705·2709 | AD103 4080 SUPER·4070 Ti SUPER·4070 | ad103_experimental |
| 2704 | RTX 4080 | rtx4080_experimental |
| 2757 | RTX 4090 Laptop | ad103m_experimental |
| 2782·2783·2786 | AD104 4070 Ti·4070 SUPER·4070 | ad104_experimental |
| 2803·2805 | AD106 4060 Ti 변형 | ad106_experimental |
| 2882·28a0·28e0·28e1 | AD107 데스크톱·노트북 변형 | ad107_experimental |
표의 ID는 모두 exact=true이며 플래그만 켜거나 targets=all로 고를 수 없습니다. main.c가 장치 허용 여부를 결정하고 unlock_610.c의 ad102_family()는 허용된 ID를 같은 고정 펌웨어 경로로 보내는 역할만 합니다. 같은 펌웨어를 쓴다는 사실이 목록 밖 GPU의 지원을 뜻하지 않습니다. GA107 3050 6GB(2584)도 상속 목록에 추가됐지만 저장소가 기록한 검증은 카드 한 장입니다.
노트북은 특히 상태 레지스터가 풀렸다는 것과 실제 처리량 증가를 나눠 봐야 합니다. README의 4090 Laptop(2757)은 Clevo에서 처리량 변화가 없고 Lenovo에서 두 차례 콜드 부팅의 약 2배가 보고됐습니다. 수정된 48GB 4090의 양자화 MoE 서빙도 8K 프리필 0.983배로 이득이 없었습니다. 이 수치는 저장소 기여자의 관측이며 이번 해설에서 독립 측정하지 않았습니다. 기존 AD103 CUTLASS FP8 한 커널의 0.3% 차이만으로 FP8 전체에 이득이 없다고 일반화해서도 안 됩니다.
벤치 러너의 power_limit()은 노트북의 N/A 전력 제한을 None으로 표현하고 기대값과 실제값이 모두 N/A일 때만 통과시킵니다. 전력 제한 검사를 끈 것이 아닙니다. 지원 확대 뒤에도 카드·전력·커널·드라이버와 실측 종류를 함께 기록해야 합니다.
zstd. 커널 설정은 CONFIG_PCI, CONFIG_FW_LOADER(dkms.conf), 그리고 SHA-256 라이브러리 CONFIG_CRYPTO_LIB_SHA256(README).makepkg), Debian/Ubuntu(dpkg-buildpackage), Fedora/RHEL(rpmbuild·DNF 필요). initramfs 생성기는 mkinitcpio·initramfs-tools·dracut 지원.$HOME/.cache/gpu-unlock에 둡니다(CACHE=로 변경).nvcc.README는 첫 시도에서 완전히 전원을 끄고 다시 켜라고 권합니다. 카드의 펌웨어 상태가 새것이어야 하기 때문이며, WPR2가 남아 있으면 모듈이 시도를 거부합니다. 이미 드라이버가 붙은 카드에는 소용이 없고, 실패 메시지에 DMA 버퍼를 남긴다고 나오면 다시 시도하기 전에 전원을 끄라고 안내합니다.
순정 부트 항목을 반드시 남겨 두고, 디스플레이 카드면 FLR 직전에 펌웨어 프레임버퍼를 떼어 내기 때문에 nvidia-drm이 넘겨받을 때까지 화면이 비는 것이 정상입니다(README: 디스플레이 인계와 멀티 GPU는 RTX 4090 검증 범위가 아님).
고객·임대 워크로드가 있는 서버에서는 하지 마세요. 증거 문서에도 "고객 워크로드가 없을 때만 벤치를 돌려라"가 적혀 있습니다.
windows/ad104-msi/는 MSI RTX 4070 Ti(AD104) + MSI Z490 시스템 한 대의 캡처 프로필에 묶인 UEFI 앱 참조 구현입니다. Windows 포트 자체는 별도 경로이며, 최신 Linux AD104 지원은 위의 실험 ID 게이트로 판단합니다. 이 포트에는 배포용 EFI 바이너리나 자동 설치기도 없으며, 소스 빌드에는 ESP GUID 등 로컬 프로필 헤더를 직접 만들어야 합니다. 다른 컴퓨터에서 가드를 풀어 쓰지 말라고 README가 명시합니다.
git clone https://github.com/JustAResearcher/gpu-unlock-ada.git
cd gpu-unlock-ada
lspci -Dnn | grep -i nvidia # PCI 주소와 10de:XXXX 확인
python3 tools/print_boot_option.py # 후보 옵션을 출력만 함(호스트 변경 없음)
print_boot_option.py는 lspci -Dnn 결과에서 10de:2684/2685/2705/2805만 찾아 줄을 출력합니다. 출력한 옵션을 적용하는 것은 이 실습의 범위가 아닙니다. 지원 대상이 없다는 메시지가 나오면 읽기 실습만 계속하면 됩니다.
make test # 목 PCI/펌웨어로 트랜잭션을 흉내 냄. GPU 접근 없음
# 생성 자산이 없으면 전체 트랜잭션 테스트는 "건너뜀"으로 표시됨
README와 docs/testing.md에 따르면 테스트는 tests/의 Python과 C 하니스를 함께 돕니다. 어떤 테스트가 skip인지 출력에서 확인하고, make test-full이 왜 입력이 없으면 실패하게 만들어졌는지 Makefile 주석과 비교해 보세요(NVIDIA 입력을 내려받는 make assets가 필요하므로 용량·약관을 먼저 확인).
cd docs/evidence/ada2705
shasum -a 256 -c SHA256SUMS # macOS. 리눅스는 sha256sum -c
head -40 tensor-modes-comparison.json
grep -n "TF" stock-mma.txt unlocked-mma.txt | head
해시가 모두 OK인지 확인한 뒤, README의 "95.0 → 189.9 TF/s"가 stock-mma.txt와 unlocked-mma.txt의 어느 줄에서 나오고 JSON 비교 파일에는 어떤 모양으로 요약되는지 찾아 보세요. 증거 폴더가 "측정 가능한 주장"을 어떻게 구조화하는지 배웁니다.
gpu_unlock_610_unlock()에서 시작해 각 unlock_transaction_* 함수가 무엇을 읽고 쓰는지 한 줄씩 표로 만들고, 실패 시 어떤 단계가 unwind에서 되돌려지는지 표시하세요. 이어서 ad102_permissions[] 네 항목 중 왜 FBPA만 required=false인지 docs/ada-2705-report.md의 "관찰용 필드" 설명과 연결해 보세요.
QUICKSTART에는 PCI 주소 확인 → 패키지 설치 → 부트 옵션 추가 → 완전 전원 차단 후 부팅 → journalctl·tools/read_sm_rates.py 확인 순서가 적혀 있습니다. 이 절차는 GPU 레지스터를 쓰고 보증·약관에 영향을 줄 수 있어서 이 문서가 권하는 실습이 아닙니다. 해 본다면 개인 소유의 별도 장비에서, 순정 부트 항목을 남기고, 증거 문서처럼 stock 측정을 먼저 확보한 뒤 진행하세요.
| 주차 | 주제 | 할 일 |
|---|---|---|
| 1주 | 리눅스 모듈·PCI 기초 | 빈 모듈 만들어 보기, lspci -vv 해석, main.c 읽기 |
| 2주 | GPU 부팅 체인 | docs/How Ampere was unlocked.md, NVIDIA 오픈 커널 모듈(GSP·FWSEC·Booter) 소스 훑기 |
| 3주 | 빌드·공급망·패키징 | fetch_artifacts.py, PKGBUILD/debian/rules/RPM spec 비교 |
| 4주 | 측정 설계 | mma_bench.cu, validate_gemm.cu로 발행률과 GEMM 차이를 설명해 보기 |
더 파고들고 싶다면 envytools·Nouveau의 GSP 부팅 코드, NVIDIA open-gpu-kernel-modules의 kgsp·ksec2 계열을 함께 읽는 것이 자연스러운 다음 단계입니다. 이 저장소의 주석이 그 함수 이름(kgspIsWpr2Up_TU102, ksec2ResetHw_TU102 등)을 직접 인용합니다.
| 용어 | 뜻 |
|---|---|
| SM | GPU의 연산 묶음 단위(Streaming Multiprocessor). 속도 선택값은 SM 단위 필드로 읽힘. |
| SS0 / SS1 | 이 모듈이 쓰는 속도 선택 레지스터(0x0082381c, 0x00823820). 값은 0x88888888, 0x8. |
| PLM | 레지스터 권한 마스크. 쓰기 가능 여부를 정함. |
| FWSEC / WPR2 | VBIOS의 보안 펌웨어 / 그것이 만드는 보호 메모리 영역. 순정 드라이버는 WPR2가 이미 켜져 있으면 거부함. |
| SEC2 / Booter | GPU 내부 보안 프로세서 / 거기서 도는 서명된 부트 이미지. |
| GSP / GSP-RM | GPU 안의 관리 프로세서 / 그 위에서 도는 리소스 매니저 펌웨어. |
| V67 페이로드 | 64,000바이트로 만든 서명 자리용 이미지. GA102용과 AD102 4-PLM용이 별도. |
| FLR | PCIe Function Level Reset. 장치 하나를 재시작. |
| BME | PCI 버스 마스터 사용 비트. 이 모듈은 끈 채로 FLR을 하고 검증함. |
| DKMS | 커널이 바뀔 때마다 모듈을 자동 재빌드하는 체계. |
| initramfs | 부팅 초기 임시 루트. 모듈은 여기서 nvidia보다 먼저 로드됨. |
| 발행률(issue rate) | 명령어를 SM이 내보내는 속도. 응용 성능과 다름. |
| 콜드 부팅 | 전원을 완전히 끊은 뒤의 부팅. 카드 펌웨어 상태가 새로 시작됨. |
4e7fefc)