# 실패의 연대기 (Failure Journal)

> Windows VM의 디스크 컨트롤러를 **SATA → VirtIO-SCSI**로 마이그레이션하는 과정에서 만난 모든 함정의 기록.
> 진짜 원인 — `vioscsi START_TYPE=0 (BOOT_START)` 누락 — 을 찾기까지 수 시간을 소비했다.
> 같은 길을 가는 사람이 이 글을 읽고 30분에 끝낼 수 있길.

작성일: 2026-05-19  
작업 환경: Proxmox VE 8.1.3 / Windows 10 Pro 19045 / virtio-win-0.1.285

---

## 출발점

목표:
- SMB에 보관된 `vm100.vhd` (120GiB virt, 106GiB sparse) Windows 디스크 이미지를 Proxmox VM에 활용
- 가능하면 SATA 에뮬레이션 대신 **VirtIO-SCSI 컨트롤러**로 운영해서 성능/효율 확보 (멀티큐, iothread, discard/TRIM)

도구:
- `qm importdisk` (vhd → raw zvol 변환)
- ZFS pool `pve-zfs` (vol storage)
- `pnputil` (Windows 드라이버 등록)
- `qm sendkey`, QEMU monitor screendump (원격 시각 확인)

---

## Step 1 — vhd → raw zvol 변환

```bash
qm importdisk <VMID> /mnt/smb/vm100.vhd pve-zfs --format raw
```

결과: `pve-zfs:vm-<VMID>-disk-1` 생성 (REFER 73.4GB sparse). config에 `unused0:`로 자동 등록.

**관찰**: 변환 후 zvol 크기가 120GiB + ~1MB 패딩. fdisk가 `GPT PMBR size mismatch` 경고. 이때는 대수롭지 않게 넘어감.

---

## Step 2 — sata1로 attach + 부팅 시도

```bash
qm set <VMID> --sata1 pve-zfs:vm-<VMID>-disk-1
qm set <VMID> --boot 'order=sata1;sata0;ide0'
qm start <VMID>
```

**결과**: Windows 10 데스크톱 정상 진입! 사용자도 다 보임. 데이터 모두 살아있음.

**우리가 잘못 알았던 것**: "vhd 변환이 잘 됐구나" — 사실은 **sata0 (기존에 갖고 있던 vm-101-disk-0 zvol)에서 부팅된 것**이었음. `Get-Disk`로 확인 안 했으니 모름.

---

## Step 3 — 안정화: efidisk 추가, VirtIO 드라이버 등록

부팅이 잘 되니 마스터화 준비. 두 가지 작업 진행.

### 3-1. efidisk0 추가 (OVMF NVRAM 영구화)

```bash
qm set <VMID> --efidisk0 pve-zfs:1,efitype=4m,pre-enrolled-keys=0
```

이전엔 `WARN: no efidisk configured` 떠서 매 부팅마다 OVMF가 NVRAM 새로 만들고 fallback path로 부팅했음. 영구화하면 부팅 엔트리 저장됨.

### 3-2. VirtIO 드라이버 사전 설치

virtio-win.iso를 attach하고 임시 SCSI 디스크도 같이 띄워 게스트가 새 PCI 디바이스 인식하게 만든 다음:

```cmd
pnputil /add-driver F:\vioscsi\w10\amd64\vioscsi.inf /install
pnputil /add-driver F:\viostor\w10\amd64\viostor.inf /install
pnputil /add-driver F:\NetKVM\w10\amd64\netkvm.inf /install
```

`pnputil`이 INF 파일을 driver store에 등록 + `/install` 옵션이 매칭되는 PnP 디바이스에 즉시 설치. `Get-PnpDevice`에서 "Red Hat VirtIO SCSI pass-through controller" Status=OK 보임. `Get-Service vioscsi` → RUNNING.

**우리가 잘못 알았던 것**: "드라이버 등록 완료, 이제 부팅 디스크 인터페이스 바꿔도 BSOD 안 나겠지" — **틀렸다**.

---

## Step 4 — 인터페이스 교체: sata1 → scsi0

깨끗한 마스터를 위해 sata1 detach, scsi0로 같은 zvol attach:

```bash
qm set <VMID> --scsi0 pve-zfs:vm-<VMID>-disk-1,iothread=1,discard=on,ssd=1
qm set <VMID> --delete sata1
qm set <VMID> --boot 'order=scsi0'
qm start <VMID>
```

**결과**: **BSOD 0xc0000225** (Recovery / "필요한 장치가 연결되어 있지 않거나 장치에 액세스할 수 없습니다") — `\Windows\system32\config\system` 위치 못 찾음.

이때 처음으로 뭔가 잘못됐다고 느낌. 그러나 원인을 GPT signature 충돌로 오해.

---

## Step 5 — 오해의 시작: "GPT signature 충돌이 원인이다"

`Get-Disk`로 보니 두 디스크 GUID 완전 동일:

```
Disk 0: SATA, GUID {4bca652d-2f35-4bd7-8175-8951593eb8c1}, 120GB
Disk 1: SAS,  GUID {4bca652d-2f35-4bd7-8175-8951593eb8c1}, 120GB (1MB padding 더 큼)
```

호스트에서 `fdisk -l /dev/zvol/...`로 검증 — 두 zvol의 Disk identifier 동일, 모든 partition UUID 동일.

**가설 (틀린 것)**: Windows가 동일 GPT signature 두 디스크 보면 한쪽을 자동 Offline 처리 → sata0 detach 후 scsi0 단독으로는 BCD device path 매칭 실패.

이 가설로 BCD 수정 시도들이 시작됨.

---

## Step 6 — BCD 수정 시도 1: device/osdevice → "boot" 키워드

```cmd
bcdedit /export C:\bcd-backup-20260519.bcd
bcdedit /set {bootmgr} device boot       # 거부됨 (정상)
bcdedit /set {default} device boot       # 성공
bcdedit /set {default} osdevice boot     # 성공
```

`boot` 키워드 의미: "이 BCD가 로드된 partition을 동적으로 추적해서 사용". 매혹적이지만 **잘못된 적용**.

**검증**: shutdown → sata0 detach → 부팅 → **여전히 BSOD 0xc0000225**.

**왜 안 먹혔나**: `osdevice = boot`는 사실 잘못된 의미. winload는 "boot device = system partition (EFI 100MB FAT32)"으로 해석해서 거기서 `\Windows\system32\winload.efi` 찾으려고 함. EFI partition에 \Windows 없으니 fail.

---

## Step 7 — BCD 수정 시도 2: bcdboot으로 새 BCD 생성

scsi0의 EFI partition을 마운트해서 BCD 재구성:

```cmd
diskpart 스크립트로 Disk 1 Partition 2에 S: 할당
bcdboot C:\Windows /s S: /f UEFI /v
```

bcdboot이 정상 동작:
```
BFSVC: Creating Recovery directory.
BFSVC: Creating OsLoader object.
BFSVC: Setting {default} to {bfc003a5-...}
부팅 파일을 만들었습니다.
```

새 BCD enum:
```
{default}.device   = partition=\Device\Harddisk0\Partition4
{default}.osdevice = partition=\Device\Harddisk0\Partition4
```

**검증**: shutdown → sata0 detach → 부팅 → **다른 BSOD 0xc0000017** (system registry file missing).

이제 한 단계 더 진행됨: OVMF→boot manager→winload까지 OK, OS partition 도달, 거기서 SYSTEM hive 못 읽음.

---

## Step 8 — NTFS 무결성 검사: ntfsfix

호스트에서 NTFS 마운트:

```bash
ntfs-3g /dev/zvol/pve-zfs/vm-<VMID>-disk-1-part4 /mnt/winc -o ro
ls /mnt/winc/Windows/System32/config/SYSTEM   # 24.5MB 존재
hexdump -C -n 16 /mnt/winc/Windows/System32/config/SYSTEM
# 00000000  72 65 67 66 ...   ← "regf" 시그니처 OK
umount /mnt/winc

ntfsfix /dev/zvol/pve-zfs/vm-<VMID>-disk-1-part4
# Processing of $MFT and $MFTMirr completed successfully.
# Checking the alternate boot sector... OK
# NTFS partition was processed successfully.
```

**검증**: 부팅 시도 → **BSOD 0xc0000017 그대로**.

SYSTEM hive 파일은 존재 + 헤더 정상 + NTFS metadata clean. 그러나 부팅 실패.

---

## Step 9 — NVRAM 초기화 시도

가설: OVMF NVRAM에 옛 부트 엔트리(detach된 디스크 가리키는 것)가 캐시되어 fallback 실패.

```bash
qm set <VMID> --delete efidisk0
qm set <VMID> --efidisk0 pve-zfs:1,efitype=4m,pre-enrolled-keys=0
qm start <VMID>
```

**결과**: 새 NVRAM 만들고 부팅 → **BSOD 0xc0000017 그대로**.

---

## Step 10 — WinRE Startup Repair 시도

BSOD 화면에 "F1 to enter Recovery Environment" 안내가 있음. `qm sendkey <VMID> f1`로 시도.

**결과**: WinRE 진입 안 됨. vhd 백업본에 WinRE WIM이 비활성/누락 상태. 막다른 길.

---

## Step 11 — SSD 마이그레이션식 클론 시도

> 사용자 제안: "보통은 디스크 두 개 만들고 1:1 복사하는 도구로 옮기잖아, SSD 마이그레이션처럼"

ZFS send/recv로 vhd 변환본을 새 zvol에 클론:

```bash
zfs snapshot pve-zfs/vm-<VMID>-disk-1@clone-master
time (zfs send pve-zfs/vm-<VMID>-disk-1@clone-master | zfs recv pve-zfs/vm-<VMID>-disk-4)
# real    10m50.003s
```

config 교체: 기존 vhd 변환본 detach + 클론을 scsi0로 attach → boot order=scsi0 단독.

**결과**: **BSOD 0xc0000017 — 클론도 동일 증상**.

이로써 가설이 확정적으로 깨짐: GPT 충돌이 단독 원인이라면 단일 디스크 상태에서 해결됐어야 함. 그런데 안 됨.

---

## Step 12 — 결정적 진단: sata0 단독 부팅 검증

```bash
qm set <VMID> --delete scsi0
qm set <VMID> --sata0 pve-zfs:vm-<VMID>-disk-0,size=120G
qm set <VMID> --boot 'order=sata0'
qm start <VMID>
```

**결과**: ✅ **정상 부팅** (Windows 10 잠금화면, 0bin 사용자).

이게 결정적. **sata0 단독은 부팅 가능 + vhd 변환본 단독은 부팅 불가**. 두 zvol 데이터가 GPT signature는 같지만 어딘가 다르다 — 또는 컨트롤러 차이 자체가 원인.

게스트 상태 확인:
```
Get-Disk         → Disk 0 SATA Online
Get-Partition    → DiskNumber=0 IsBoot=True
Get-Service vioscsi → RUNNING
sc qc vioscsi    → START_TYPE: 3 DEMAND_START   ← 🔥 발견!
```

**진짜 원인 발견**: vioscsi 서비스가 등록은 되어 있지만 **boot critical(=0)이 아니라 수동 시작(=3)**. winload가 부팅 시점에 vioscsi.sys를 로드하지 않음 → VirtIO-SCSI 디스크에서 부팅 시 디스크 접근 못 함 → BSOD.

`pnputil /add-driver vioscsi.inf /install`은 드라이버 파일과 PnP 매칭만 등록. **서비스의 Start 값을 자동으로 boot critical로 승격하지 않는다**.

---

## Step 13 — 진짜 해결: vioscsi boot critical 승격

```cmd
sc config vioscsi start= boot
```

검증:
```
sc qc vioscsi   → START_TYPE: 0 BOOT_START
reg query HKLM\SYSTEM\CurrentControlSet\Services\vioscsi /v Start
                → Start REG_DWORD 0x0
```

---

## Step 14 — Windows 정상 종료 + ZFS 클론 + 마스터화

```bash
# Windows ACPI shutdown
qm shutdown <VMID> --timeout 180

# 검증된 sata0 디스크를 새 zvol로 클론
zfs snapshot pve-zfs/vm-<VMID>-disk-0@master-final
time (zfs send pve-zfs/vm-<VMID>-disk-0@master-final | zfs recv pve-zfs/vm-<VMID>-disk-5)
# real    28m43.568s

# config 교체
qm set <VMID> --delete sata0
qm set <VMID> --scsi0 pve-zfs:vm-<VMID>-disk-5,iothread=1,discard=on,ssd=1
qm set <VMID> --scsihw virtio-scsi-single
qm set <VMID> --boot 'order=scsi0'
qm start <VMID>
```

---

## Step 15 — 검증: 진짜 마스터 완성

```
Get-Disk         → Disk 0  QEMU QEMU HARDDISK  BusType=SAS  120GB  Online
Get-Partition    → DiskNumber=0 IsBoot=True
sc query vioscsi → RUNNING
sc qc vioscsi    → START_TYPE: 0 BOOT_START
```

**단일 디스크 + virtio-scsi-single + iothread + discard + boot critical** = 마스터 완성. Windows 10 잠금화면 정상 진입.

---

## 교훈 (lessons learned)

### 진짜 원인은 vioscsi의 START_TYPE

다른 모든 가설(GPT signature 충돌, BCD 손상, NVRAM 캐시, vhd 변환 미세 손상)은 부분적으로 그럴듯했지만 **단독 원인이 아니었다**. 진짜 원인은:

```
vioscsi Service Start=3 (DEMAND_START)
            ↓
winload가 부팅 시점에 vioscsi.sys 안 로드
            ↓
virtio-scsi 디스크에서 부팅 시 디스크 IO 핸들러 부재
            ↓
INACCESSIBLE_BOOT_DEVICE → BSOD 0xc0000017 또는 0xc0000225
```

### pnputil의 한계

- `pnputil /add-driver vioscsi.inf /install`
- 드라이버 파일과 PnP 디바이스 매칭은 잘 한다
- 그러나 **서비스의 boot priority는 건드리지 않는다**
- 그래서 매뉴얼하게 `sc config vioscsi start= boot` 또는 레지스트리 직접 수정 필요

### "vhd → raw 변환본"은 부팅 보증 안 된다

- `qm importdisk`로 vhd → raw 변환 시 사이즈에 패딩 추가, GPT backup 위치 미세 차이
- 검증된 sata 원본을 ZFS 클론하는 게 더 안전
- vhd는 "백업본"이지 "라이브 부팅 가능 디스크"가 아닐 수 있음

### 동일 GPT signature 디스크 두 개를 동시 attach하지 말 것

- Windows가 한쪽을 자동 Offline 처리하지만 NT namespace 매핑/BCD path 종속성으로 detach 시 BSOD
- 마이그레이션 작업은 단계별로 단독 attach 검증 권장

### bcdedit `osdevice = boot`은 함정

- "boot manager가 로드된 partition을 동적 추적"이라는 매혹적인 표현
- 그러나 winload는 그걸 "system partition (EFI)"으로 해석 → \Windows 못 찾음 → BSOD
- 잘못된 BCD 수정 패턴. **사용하지 말 것**.

### bcdboot은 깔끔하지만 만능 아님

- BCD store 새로 생성 + 자기 자신 가리키도록 설정
- 그러나 partition UUID는 정확히 같은 zvol이라야 매칭됨
- 동일 GPT signature 디스크가 같이 있을 때는 효과 제한적

---

## 부록: 알아두면 좋은 진단 명령

```cmd
:: 서비스 시작 유형
sc qc vioscsi | findstr START_TYPE
reg query HKLM\SYSTEM\CurrentControlSet\Services\vioscsi /v Start

:: VirtIO 디바이스 인식
powershell "Get-PnpDevice | Where Manufacturer -like '*Red Hat*' | Format-Table -AutoSize"

:: 부팅 디스크
powershell "Get-Disk | Format-Table -AutoSize Number,FriendlyName,BusType,Size"
powershell "Get-Partition | Where IsBoot -eq \$true | Format-List"

:: BCD 진단
bcdedit /enum {bootmgr}
bcdedit /enum {default}

:: driverstore의 OEM INF 등록 상태
pnputil /enum-drivers | findstr /i /c:vioscsi /c:viostor /c:netkvm
```

```bash
# 호스트 (PVE)에서 게스트 디스크 NTFS 검증
mount -t ntfs-3g -o ro /dev/zvol/<pool>/vm-<VMID>-disk-N-part4 /mnt/winc
ls /mnt/winc/Windows/System32/config/SYSTEM
hexdump -C -n 16 /mnt/winc/Windows/System32/config/SYSTEM   # "regf" 시그니처
umount /mnt/winc
ntfsfix /dev/zvol/<pool>/vm-<VMID>-disk-N-part4

# 두 zvol 비교
fdisk -l /dev/zvol/<pool>/vm-<VMID>-disk-N
```
