01 · QUESTION
무엇을 확인할 것인가
guest register, stage-2 translation, virtual interrupt와 host task state의 owner가 vCPU run 전후에 어떻게 바뀌는가?
userspace VMM이 KVM_RUN을 호출하면 KVM core가 vCPU를 load하고 pending request/interrupt를 처리한 뒤 architecture enter_guest를 실행한다. hardware exit는 reason을 decode해 kernel에서 처리하거나 exit structure로 userspace에 반환한다.
host userspace, KVM ioctl context, guest execution, hypervisor exception과 host interrupt context를 나눈다. preemption과 migration은 CPU-local virtualization register owner를 바꾸므로 vcpu_load/put 경계를 지켜야 한다.
02 · CONTRACT
공통 계약과 architecture 구현
| architecture | 핵심 mechanism | 실패 형태 | 확인할 상태 |
|---|---|---|---|
| arm64 | EL2 VHE/nVHE와 stage-2 VTTBR_EL2 | VMID wrap flush 누락, hyp mapping lifetime 오류 또는 virtual timer offset 불일치는 guest memory leak, hyp fault와 시간 점프를 만든다. | HCR_EL2, VTTBR_EL2/VMID, ESR_EL2, FAR/HPFAR, guest PC/PSTATE, GIC LR와 kvm_request bitmap을 본다. |
| x86-64 | VMX VMCS 또는 SVM VMCB와 EPT/NPT | invalid VMCS field는 VM-entry failure를 만들고 stale EPT/VPID는 guest가 이전 mapping을 계속 사용하게 한다. | exit_reason/qualification, guest linear/physical address, EPTP/VPID, interrupt-window flag, VMCS instruction error와 requests를 본다. |
| RISC-V | H-extension VS/VU state와 HGATP two-stage translation | VMID generation, guest timer delta 또는 virtual instruction emulation 오류가 stale mapping, guest hang과 잘못된 time을 만든다. | HSTATUS, HGATP/VMID, HTVAL/HTINST, SCAUSE/SEPC, VS CSR, virtual interrupt bits와 request bitmap을 확인한다. |
03 · DIAGRAMS
세 그림으로 먼저 읽기
arm64
- mechanism
- EL2 VHE/nVHE와 stage-2 VTTBR_EL2
- state
- KVM은 guest EL1 register, timer, GIC virtualization state를 vCPU context에 보관한다. VTTBR_EL2가 VMID와 stage-2 root를 선택하고 HCR_EL2가 trap policy를 정한다. nVHE는 별도 hyp address space와 stack을 사용한다.
- checkpoint
- HCR_EL2, VTTBR_EL2/VMID, ESR_EL2, FAR/HPFAR, guest PC/PSTATE, GIC LR와 kvm_request bitmap을 본다.
x86-64
- mechanism
- VMX VMCS 또는 SVM VMCB와 EPT/NPT
- state
- VMCS/VMCB에 guest/host control register, segment, RIP/RSP와 intercept policy를 둔다. EPTP/NCR3가 second-level translation root를 고르고 posted interrupt/APICv가 일부 interrupt를 exit 없이 주입한다.
- checkpoint
- exit_reason/qualification, guest linear/physical address, EPTP/VPID, interrupt-window flag, VMCS instruction error와 requests를 본다.
RISC-V
- mechanism
- H-extension VS/VU state와 HGATP two-stage translation
- state
- HS-mode KVM이 VS-level CSR, guest GPR/FP/vector와 AIA interrupt state를 vCPU에 저장한다. HGATP가 VMID와 guest-stage root를 선택하며 HFENCE.GVMA/VVMA가 guest translation cache를 무효화한다.
- checkpoint
- HSTATUS, HGATP/VMID, HTVAL/HTINST, SCAUSE/SEPC, VS CSR, virtual interrupt bits와 request bitmap을 확인한다.
04 · SOURCE
Linux 6.18.37 원본 코드와 줄별 설명
소스 위치를 고정된 숫자로 복사하지 않고 Linux v6.18.37 tree에서 함수 선언을 다시 찾아 발췌했습니다. 아래 코드와 각 줄의 설명은 1:1로 대응합니다.
arm64 · Linux 6.18.37
EL2 VHE/nVHE와 stage-2 VTTBR_EL2
KVM은 guest EL1 register, timer, GIC virtualization state를 vCPU context에 보관한다. VTTBR_EL2가 VMID와 stage-2 root를 선택하고 HCR_EL2가 trap policy를 정한다. nVHE는 별도 hyp address space와 stack을 사용한다.
원본 코드: arch/arm64/kvm/arm.c:1157-1251
1157 * @vcpu: The VCPU pointer
1158 *
1159 * This function is called through the VCPU_RUN ioctl called from user space. It
1160 * will execute VM code in a loop until the time slice for the process is used
1161 * or some emulation is needed from user space in which case the function will
1162 * return with return value 0 and with the kvm_run structure filled in with the
1163 * required data for the requested emulation.
1164 */
1165int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
1166{
1167 struct kvm_run *run = vcpu->run;
1168 int ret;
1169
1170 if (run->exit_reason == KVM_EXIT_MMIO) {
1171 ret = kvm_handle_mmio_return(vcpu);
1172 if (ret <= 0)
1173 return ret;
1174 }
1175
1176 vcpu_load(vcpu);
1177
1178 if (!vcpu->wants_to_run) {
1179 ret = -EINTR;
1180 goto out;
1181 }
1182
1183 kvm_sigset_activate(vcpu);
1184
1185 ret = 1;
1186 run->exit_reason = KVM_EXIT_UNKNOWN;
1187 run->flags = 0;
1188 while (ret > 0) {
1189 /*
1190 * Check conditions before entering the guest
1191 */
1192 ret = kvm_xfer_to_guest_mode_handle_work(vcpu);
1193 if (!ret)
1194 ret = 1;
1195
1196 if (ret > 0)
1197 ret = check_vcpu_requests(vcpu);
1198
1199 /*
1200 * Preparing the interrupts to be injected also
1201 * involves poking the GIC, which must be done in a
1202 * non-preemptible context.
1203 */
1204 preempt_disable();
1205
1206 kvm_nested_flush_hwstate(vcpu);
1207
1208 if (kvm_vcpu_has_pmu(vcpu))
1209 kvm_pmu_flush_hwstate(vcpu);
1210
1211 local_irq_disable();
1212
1213 kvm_vgic_flush_hwstate(vcpu);
1214
1215 kvm_pmu_update_vcpu_events(vcpu);
1216
1217 /*
1218 * Ensure we set mode to IN_GUEST_MODE after we disable
1219 * interrupts and before the final VCPU requests check.
1220 * See the comment in kvm_vcpu_exiting_guest_mode() and
1221 * Documentation/virt/kvm/vcpu-requests.rst
1222 */
1223 smp_store_mb(vcpu->mode, IN_GUEST_MODE);
1224
1225 if (ret <= 0 || kvm_vcpu_exit_request(vcpu, &ret)) {
1226 vcpu->mode = OUTSIDE_GUEST_MODE;
1227 isb(); /* Ensure work in x_flush_hwstate is committed */
1228 if (kvm_vcpu_has_pmu(vcpu))
1229 kvm_pmu_sync_hwstate(vcpu);
1230 if (unlikely(!irqchip_in_kernel(vcpu->kvm)))
1231 kvm_timer_sync_user(vcpu);
1232 kvm_vgic_sync_hwstate(vcpu);
1233 local_irq_enable();
1234 preempt_enable();
1235 continue;
1236 }
1237
1238 kvm_arch_vcpu_ctxflush_fp(vcpu);
1239
1240 /**************************************************************
1241 * Enter the guest
1242 */
1243 trace_kvm_entry(*vcpu_pc(vcpu));
1244 guest_timing_enter_irqoff();
1245
1246 ret = kvm_arm_vcpu_enter_exit(vcpu);
1247
1248 vcpu->mode = OUTSIDE_GUEST_MODE;
1249 vcpu->stat.exits++;
1250 /*
1251 * Back from guest라인 바이 라인 주석
빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 95개 줄에 각각 설명을 붙였습니다.
* @vcpu: The VCPU pointerLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* This function is called through the VCPU_RUN ioctl called from user space. ItLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* will execute VM code in a loop until the time slice for the process is usedLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* or some emulation is needed from user space in which case the function willLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* return with return value 0 and with the kvm_run structure filled in with theLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* required data for the requested emulation.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
struct kvm_run *run = vcpu->run;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
int ret;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (run->exit_reason == KVM_EXIT_MMIO) {이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
ret = kvm_handle_mmio_return(vcpu);저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
if (ret <= 0)저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
return ret;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
vcpu_load(vcpu);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (!vcpu->wants_to_run) {이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
ret = -EINTR;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
goto out;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
kvm_sigset_activate(vcpu);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
ret = 1;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
run->exit_reason = KVM_EXIT_UNKNOWN;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
run->flags = 0;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
while (ret > 0) {저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Check conditions before entering the guestLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
ret = kvm_xfer_to_guest_mode_handle_work(vcpu);저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
if (!ret)저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
ret = 1;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (ret > 0)저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
ret = check_vcpu_requests(vcpu);저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Preparing the interrupts to be injected alsoLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* involves poking the GIC, which must be done in aLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* non-preemptible context.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
preempt_disable();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
kvm_nested_flush_hwstate(vcpu);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (kvm_vcpu_has_pmu(vcpu))이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
kvm_pmu_flush_hwstate(vcpu);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
local_irq_disable();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
kvm_vgic_flush_hwstate(vcpu);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
kvm_pmu_update_vcpu_events(vcpu);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Ensure we set mode to IN_GUEST_MODE after we disableLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* interrupts and before the final VCPU requests check.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* See the comment in kvm_vcpu_exiting_guest_mode() andLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Documentation/virt/kvm/vcpu-requests.rstLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
smp_store_mb(vcpu->mode, IN_GUEST_MODE);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (ret <= 0 || kvm_vcpu_exit_request(vcpu, &ret)) {저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
vcpu->mode = OUTSIDE_GUEST_MODE;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
isb(); /* Ensure work in x_flush_hwstate is committed */memory, translation 또는 instruction-fetch 관찰 순서를 확정하는 architecture 명령이다. 앞에서 publish한 상태와 뒤에서 재사용하는 상태의 경계를 이 줄에 둔다.
if (kvm_vcpu_has_pmu(vcpu))이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
kvm_pmu_sync_hwstate(vcpu);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
if (unlikely(!irqchip_in_kernel(vcpu->kvm)))이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
kvm_timer_sync_user(vcpu);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
kvm_vgic_sync_hwstate(vcpu);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
local_irq_enable();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
preempt_enable();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
continue;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
kvm_arch_vcpu_ctxflush_fp(vcpu);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/**************************************************************Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Enter the guestLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
trace_kvm_entry(*vcpu_pc(vcpu));helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
guest_timing_enter_irqoff();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
ret = kvm_arm_vcpu_enter_exit(vcpu);EL2 entry를 통해 guest를 실행하고 다음 trap/interrupt에서 host로 돌아온다.
(blank)빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
vcpu->mode = OUTSIDE_GUEST_MODE;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
vcpu->stat.exits++;이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Back from guestLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
x86-64 · Linux 6.18.37
VMX VMCS 또는 SVM VMCB와 EPT/NPT
VMCS/VMCB에 guest/host control register, segment, RIP/RSP와 intercept policy를 둔다. EPTP/NCR3가 second-level translation root를 고르고 posted interrupt/APICv가 일부 interrupt를 exit 없이 주입한다.
원본 코드: arch/x86/kvm/x86.c:11924-12024
11924 */
11925 if (vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED &&
11926 !kvm_apic_init_sipi_allowed(vcpu))
11927 return -EINVAL;
11928
11929 return kvm_x86_call(vcpu_pre_run)(vcpu);
11930}
11931
11932int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
11933{
11934 struct kvm_queued_exception *ex = &vcpu->arch.exception;
11935 struct kvm_run *kvm_run = vcpu->run;
11936 u64 sync_valid_fields;
11937 int r;
11938
11939 r = kvm_mmu_post_init_vm(vcpu->kvm);
11940 if (r)
11941 return r;
11942
11943 vcpu_load(vcpu);
11944 kvm_sigset_activate(vcpu);
11945 kvm_run->flags = 0;
11946 kvm_load_guest_fpu(vcpu);
11947
11948 kvm_vcpu_srcu_read_lock(vcpu);
11949 if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)) {
11950 if (!vcpu->wants_to_run) {
11951 r = -EINTR;
11952 goto out;
11953 }
11954
11955 /*
11956 * Don't bother switching APIC timer emulation from the
11957 * hypervisor timer to the software timer, the only way for the
11958 * APIC timer to be active is if userspace stuffed vCPU state,
11959 * i.e. put the vCPU into a nonsensical state. Only an INIT
11960 * will transition the vCPU out of UNINITIALIZED (without more
11961 * state stuffing from userspace), which will reset the local
11962 * APIC and thus cancel the timer or drop the IRQ (if the timer
11963 * already expired).
11964 */
11965 kvm_vcpu_srcu_read_unlock(vcpu);
11966 kvm_vcpu_block(vcpu);
11967 kvm_vcpu_srcu_read_lock(vcpu);
11968
11969 if (kvm_apic_accept_events(vcpu) < 0) {
11970 r = 0;
11971 goto out;
11972 }
11973 r = -EAGAIN;
11974 if (signal_pending(current)) {
11975 r = -EINTR;
11976 kvm_run->exit_reason = KVM_EXIT_INTR;
11977 ++vcpu->stat.signal_exits;
11978 }
11979 goto out;
11980 }
11981
11982 sync_valid_fields = kvm_sync_valid_fields(vcpu->kvm);
11983 if ((kvm_run->kvm_valid_regs & ~sync_valid_fields) ||
11984 (kvm_run->kvm_dirty_regs & ~sync_valid_fields)) {
11985 r = -EINVAL;
11986 goto out;
11987 }
11988
11989 if (kvm_run->kvm_dirty_regs) {
11990 r = sync_regs(vcpu);
11991 if (r != 0)
11992 goto out;
11993 }
11994
11995 /* re-sync apic's tpr */
11996 if (!lapic_in_kernel(vcpu)) {
11997 if (kvm_set_cr8(vcpu, kvm_run->cr8) != 0) {
11998 r = -EINVAL;
11999 goto out;
12000 }
12001 }
12002
12003 /*
12004 * If userspace set a pending exception and L2 is active, convert it to
12005 * a pending VM-Exit if L1 wants to intercept the exception.
12006 */
12007 if (vcpu->arch.exception_from_userspace && is_guest_mode(vcpu) &&
12008 kvm_x86_ops.nested_ops->is_exception_vmexit(vcpu, ex->vector,
12009 ex->error_code)) {
12010 kvm_queue_exception_vmexit(vcpu, ex->vector,
12011 ex->has_error_code, ex->error_code,
12012 ex->has_payload, ex->payload);
12013 ex->injected = false;
12014 ex->pending = false;
12015 }
12016 vcpu->arch.exception_from_userspace = false;
12017
12018 if (unlikely(vcpu->arch.complete_userspace_io)) {
12019 int (*cui)(struct kvm_vcpu *) = vcpu->arch.complete_userspace_io;
12020 vcpu->arch.complete_userspace_io = NULL;
12021 r = cui(vcpu);
12022 if (r <= 0)
12023 goto out;
12024 } else {라인 바이 라인 주석
빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 101개 줄에 각각 설명을 붙였습니다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED &&이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
!kvm_apic_init_sipi_allowed(vcpu))helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
return -EINVAL;이 함수가 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
return kvm_x86_call(vcpu_pre_run)(vcpu);이 함수가 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
struct kvm_queued_exception *ex = &vcpu->arch.exception;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
struct kvm_run *kvm_run = vcpu->run;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
u64 sync_valid_fields;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
int r;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
r = kvm_mmu_post_init_vm(vcpu->kvm);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
if (r)이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
return r;이 함수가 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
vcpu_load(vcpu);vCPU를 현재 physical CPU에 load해 VMCS/VMCB와 per-CPU owner를 준비한다.
kvm_sigset_activate(vcpu);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
kvm_run->flags = 0;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
kvm_load_guest_fpu(vcpu);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
kvm_vcpu_srcu_read_lock(vcpu);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
if (!vcpu->wants_to_run) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
r = -EINTR;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
goto out;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Don't bother switching APIC timer emulation from theLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* hypervisor timer to the software timer, the only way for theLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* APIC timer to be active is if userspace stuffed vCPU state,Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* i.e. put the vCPU into a nonsensical state. Only an INITLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* will transition the vCPU out of UNINITIALIZED (without moreLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* state stuffing from userspace), which will reset the localLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* APIC and thus cancel the timer or drop the IRQ (if the timerLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* already expired).Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
kvm_vcpu_srcu_read_unlock(vcpu);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
kvm_vcpu_block(vcpu);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
kvm_vcpu_srcu_read_lock(vcpu);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (kvm_apic_accept_events(vcpu) < 0) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
r = 0;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
goto out;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
r = -EAGAIN;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
if (signal_pending(current)) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
r = -EINTR;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
kvm_run->exit_reason = KVM_EXIT_INTR;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
++vcpu->stat.signal_exits;이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
goto out;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
sync_valid_fields = kvm_sync_valid_fields(vcpu->kvm);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
if ((kvm_run->kvm_valid_regs & ~sync_valid_fields) ||이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
(kvm_run->kvm_dirty_regs & ~sync_valid_fields)) {이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
r = -EINVAL;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
goto out;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (kvm_run->kvm_dirty_regs) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
r = sync_regs(vcpu);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
if (r != 0)이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
goto out;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* re-sync apic's tpr */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (!lapic_in_kernel(vcpu)) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
if (kvm_set_cr8(vcpu, kvm_run->cr8) != 0) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
r = -EINVAL;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
goto out;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* If userspace set a pending exception and L2 is active, convert it toLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* a pending VM-Exit if L1 wants to intercept the exception.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (vcpu->arch.exception_from_userspace && is_guest_mode(vcpu) &&이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
kvm_x86_ops.nested_ops->is_exception_vmexit(vcpu, ex->vector,이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
ex->error_code)) {이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
kvm_queue_exception_vmexit(vcpu, ex->vector,이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
ex->has_error_code, ex->error_code,이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
ex->has_payload, ex->payload);이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
ex->injected = false;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
ex->pending = false;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
vcpu->arch.exception_from_userspace = false;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
(blank)빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (unlikely(vcpu->arch.complete_userspace_io)) {이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
int (*cui)(struct kvm_vcpu *) = vcpu->arch.complete_userspace_io;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
vcpu->arch.complete_userspace_io = NULL;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
r = cui(vcpu);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
if (r <= 0)이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
goto out;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
} else {이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
RISC-V · Linux 6.18.37
H-extension VS/VU state와 HGATP two-stage translation
HS-mode KVM이 VS-level CSR, guest GPR/FP/vector와 AIA interrupt state를 vCPU에 저장한다. HGATP가 VMID와 guest-stage root를 선택하며 HFENCE.GVMA/VVMA가 guest translation cache를 무효화한다.
원본 코드: arch/riscv/kvm/vcpu.c:856-950
856 trap->scause = csr_read(CSR_SCAUSE);
857 trap->stval = csr_read(CSR_STVAL);
858
859 vcpu->arch.last_exit_cpu = vcpu->cpu;
860 guest_state_exit_irqoff();
861 kvm_riscv_vcpu_swap_in_host_state(vcpu);
862}
863
864int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
865{
866 int ret;
867 struct kvm_cpu_trap trap;
868 struct kvm_run *run = vcpu->run;
869
870 if (!vcpu->arch.ran_atleast_once)
871 kvm_riscv_vcpu_setup_config(vcpu);
872
873 /* Mark this VCPU ran at least once */
874 vcpu->arch.ran_atleast_once = true;
875
876 kvm_vcpu_srcu_read_lock(vcpu);
877
878 switch (run->exit_reason) {
879 case KVM_EXIT_MMIO:
880 /* Process MMIO value returned from user-space */
881 ret = kvm_riscv_vcpu_mmio_return(vcpu, vcpu->run);
882 break;
883 case KVM_EXIT_RISCV_SBI:
884 /* Process SBI value returned from user-space */
885 ret = kvm_riscv_vcpu_sbi_return(vcpu, vcpu->run);
886 break;
887 case KVM_EXIT_RISCV_CSR:
888 /* Process CSR value returned from user-space */
889 ret = kvm_riscv_vcpu_csr_return(vcpu, vcpu->run);
890 break;
891 default:
892 ret = 0;
893 break;
894 }
895 if (ret) {
896 kvm_vcpu_srcu_read_unlock(vcpu);
897 return ret;
898 }
899
900 if (!vcpu->wants_to_run) {
901 kvm_vcpu_srcu_read_unlock(vcpu);
902 return -EINTR;
903 }
904
905 vcpu_load(vcpu);
906
907 kvm_sigset_activate(vcpu);
908
909 ret = 1;
910 run->exit_reason = KVM_EXIT_UNKNOWN;
911 while (ret > 0) {
912 /* Check conditions before entering the guest */
913 ret = kvm_xfer_to_guest_mode_handle_work(vcpu);
914 if (ret)
915 continue;
916 ret = 1;
917
918 kvm_riscv_gstage_vmid_update(vcpu);
919
920 ret = kvm_riscv_check_vcpu_requests(vcpu);
921 if (ret <= 0)
922 continue;
923
924 preempt_disable();
925
926 /* Update AIA HW state before entering guest */
927 ret = kvm_riscv_vcpu_aia_update(vcpu);
928 if (ret <= 0) {
929 preempt_enable();
930 continue;
931 }
932
933 local_irq_disable();
934
935 /*
936 * Ensure we set mode to IN_GUEST_MODE after we disable
937 * interrupts and before the final VCPU requests check.
938 * See the comment in kvm_vcpu_exiting_guest_mode() and
939 * Documentation/virt/kvm/vcpu-requests.rst
940 */
941 vcpu->mode = IN_GUEST_MODE;
942
943 kvm_vcpu_srcu_read_unlock(vcpu);
944 smp_mb__after_srcu_read_unlock();
945
946 /*
947 * We might have got VCPU interrupts updated asynchronously
948 * so update it in HW.
949 */
950 kvm_riscv_vcpu_flush_interrupts(vcpu);라인 바이 라인 주석
빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 95개 줄에 각각 설명을 붙였습니다.
trap->scause = csr_read(CSR_SCAUSE);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
trap->stval = csr_read(CSR_STVAL);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
vcpu->arch.last_exit_cpu = vcpu->cpu;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
guest_state_exit_irqoff();helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
kvm_riscv_vcpu_swap_in_host_state(vcpu);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)이 함수의 진입 계약이 시작된다. RISC-V에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
int ret;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
struct kvm_cpu_trap trap;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
struct kvm_run *run = vcpu->run;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (!vcpu->arch.ran_atleast_once)이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
kvm_riscv_vcpu_setup_config(vcpu);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* Mark this VCPU ran at least once */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
vcpu->arch.ran_atleast_once = true;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
kvm_vcpu_srcu_read_lock(vcpu);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
switch (run->exit_reason) {hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
case KVM_EXIT_MMIO:hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
/* Process MMIO value returned from user-space */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
ret = kvm_riscv_vcpu_mmio_return(vcpu, vcpu->run);저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
case KVM_EXIT_RISCV_SBI:hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
/* Process SBI value returned from user-space */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
ret = kvm_riscv_vcpu_sbi_return(vcpu, vcpu->run);저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
case KVM_EXIT_RISCV_CSR:hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.
/* Process CSR value returned from user-space */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
ret = kvm_riscv_vcpu_csr_return(vcpu, vcpu->run);저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
default:분기 label이다. 이 위치로 들어오는 모든 선행 경로가 같은 register, stack, lock과 interrupt 상태를 만족하는지 비교한다.
ret = 0;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
break;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
if (ret) {저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
kvm_vcpu_srcu_read_unlock(vcpu);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
return ret;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (!vcpu->wants_to_run) {이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
kvm_vcpu_srcu_read_unlock(vcpu);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
return -EINTR;이 함수가 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
vcpu_load(vcpu);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
kvm_sigset_activate(vcpu);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
ret = 1;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
run->exit_reason = KVM_EXIT_UNKNOWN;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
while (ret > 0) {저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
/* Check conditions before entering the guest */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
ret = kvm_xfer_to_guest_mode_handle_work(vcpu);저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
if (ret)저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
continue;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
ret = 1;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
kvm_riscv_gstage_vmid_update(vcpu);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
ret = kvm_riscv_check_vcpu_requests(vcpu);저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
if (ret <= 0)저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
continue;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
preempt_disable();helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* Update AIA HW state before entering guest */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
ret = kvm_riscv_vcpu_aia_update(vcpu);저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
if (ret <= 0) {저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
preempt_enable();helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
continue;정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
local_irq_disable();helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Ensure we set mode to IN_GUEST_MODE after we disableLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* interrupts and before the final VCPU requests check.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* See the comment in kvm_vcpu_exiting_guest_mode() andLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Documentation/virt/kvm/vcpu-requests.rstLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
vcpu->mode = IN_GUEST_MODE;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
kvm_vcpu_srcu_read_unlock(vcpu);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
smp_mb__after_srcu_read_unlock();helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* We might have got VCPU interrupts updated asynchronouslyLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* so update it in HW.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
kvm_riscv_vcpu_flush_interrupts(vcpu);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
05 · WORKED EXAMPLE
숫자로 검산하기
guest page fault와 stage-2 fault 구분
guest VA 0x4000 접근이 guest page table에서는 GPA 0x120000으로 변환되지만 해당 GPA memslot이 제거됐다고 가정한다.
- stage 1guest page walker는 GVA 0x4000을 GPA 0x120000으로 정상 변환한다.
- stage 2EL2/EPT/HGATP walker가 GPA에 대응하는 host mapping을 찾지 못해 virtualization exit를 만든다.
- decodearm64 HPFAR, x86 EPT qualification, RISC-V HTVAL이 guest physical fault 정보를 제공한다.
- resolutionKVM MMU가 새 memslot mapping을 만들 수 없으면 userspace에 memory fault/MMIO exit를 반환한다.
결론guest의 일반 page fault와 host가 관리하는 stage-2 fault는 faulting address 공간과 처리 주체가 다르다.
06 · DEEP DIVE
경계별 상세 분석
공통 kernel core와 architecture hook의 경계
userspace VMM이 KVM_RUN을 호출하면 KVM core가 vCPU를 load하고 pending request/interrupt를 처리한 뒤 architecture enter_guest를 실행한다. hardware exit는 reason을 decode해 kernel에서 처리하거나 exit structure로 userspace에 반환한다.
host userspace, KVM ioctl context, guest execution, hypervisor exception과 host interrupt context를 나눈다. preemption과 migration은 CPU-local virtualization register owner를 바꾸므로 vcpu_load/put 경계를 지켜야 한다.
arm64: EL2 VHE/nVHE와 stage-2 VTTBR_EL2
KVM은 guest EL1 register, timer, GIC virtualization state를 vCPU context에 보관한다. VTTBR_EL2가 VMID와 stage-2 root를 선택하고 HCR_EL2가 trap policy를 정한다. nVHE는 별도 hyp address space와 stack을 사용한다.
host/guest sysreg save, stage-2 TLBI, GIC LR injection과 barrier가 guest entry 전 완료되어야 한다. 디버깅할 때는 HCR_EL2, VTTBR_EL2/VMID, ESR_EL2, FAR/HPFAR, guest PC/PSTATE, GIC LR와 kvm_request bitmap을 본다.
x86-64: VMX VMCS 또는 SVM VMCB와 EPT/NPT
VMCS/VMCB에 guest/host control register, segment, RIP/RSP와 intercept policy를 둔다. EPTP/NCR3가 second-level translation root를 고르고 posted interrupt/APICv가 일부 interrupt를 exit 없이 주입한다.
VM-entry control validation, host FPU/debug/speculation state 교체와 INVEPT/INVVPID ordering을 지켜야 한다. 디버깅할 때는 exit_reason/qualification, guest linear/physical address, EPTP/VPID, interrupt-window flag, VMCS instruction error와 requests를 본다.
RISC-V: H-extension VS/VU state와 HGATP two-stage translation
HS-mode KVM이 VS-level CSR, guest GPR/FP/vector와 AIA interrupt state를 vCPU에 저장한다. HGATP가 VMID와 guest-stage root를 선택하며 HFENCE.GVMA/VVMA가 guest translation cache를 무효화한다.
VS CSR save/restore, HGATP write, HFENCE와 virtual interrupt pending publication이 guest entry 전에 정합해야 한다. 디버깅할 때는 HSTATUS, HGATP/VMID, HTVAL/HTINST, SCAUSE/SEPC, VS CSR, virtual interrupt bits와 request bitmap을 확인한다.
객체 수명과 소유권을 먼저 고정한다
VM, vCPU, memory slot와 stage-2 mapping은 in-flight guest access와 SRCU reader가 끝날 때까지 유지된다. memslot 변경은 MMU invalidation sequence와 remote vCPU kick을 동반한다.
주소나 register 값이 맞는지만 확인하면 stale state를 놓친다. producer, publication, consumer와 폐기 지점을 같은 표에 기록한다.
latency upper bound는 hardware instruction 하나가 아니다
entry/exit fixed cost, stage-2 miss, virtual interrupt injection, emulation, host scheduling과 userspace round trip이 더해진다. worst case는 preempted vCPU가 lock 또는 virtual IPI completion을 쥔 상황이다.
평균값 외에 interrupt-off 구간, remote CPU 응답, firmware 호출과 retry 횟수를 분리해야 최악 지연의 원인을 찾을 수 있다.
07 · FAILURE
실패를 어떤 증거로 나눌 것인가
| 분류 | 관찰되는 결과 | 첫 확인값 |
|---|---|---|
| arm64 | VMID wrap flush 누락, hyp mapping lifetime 오류 또는 virtual timer offset 불일치는 guest memory leak, hyp fault와 시간 점프를 만든다. | HCR_EL2, VTTBR_EL2/VMID, ESR_EL2, FAR/HPFAR, guest PC/PSTATE, GIC LR와 kvm_request bitmap을 본다. |
| x86-64 | invalid VMCS field는 VM-entry failure를 만들고 stale EPT/VPID는 guest가 이전 mapping을 계속 사용하게 한다. | exit_reason/qualification, guest linear/physical address, EPTP/VPID, interrupt-window flag, VMCS instruction error와 requests를 본다. |
| RISC-V | VMID generation, guest timer delta 또는 virtual instruction emulation 오류가 stale mapping, guest hang과 잘못된 time을 만든다. | HSTATUS, HGATP/VMID, HTVAL/HTINST, SCAUSE/SEPC, VS CSR, virtual interrupt bits와 request bitmap을 확인한다. |
08 · LAB
재현과 계측 절차
- 같은 guest workload에서 hardware exit reason과 userspace exit reason을 따로 count해 virtualization overhead를 분해한다.
- memslot을 변경하며 remote vCPU kick, stage-2 invalidation과 guest 재진입 순서를 trace한다.
- 동일한 workload에서 세 architecture의 tracepoint 이름, CPU 번호, PC, stack pointer와 address-space identifier를 같은 열로 기록한다.
- 소스만 읽고 끝내지 않고 최종
vmlinux의objdump -dr,readelf -SW결과로 선택된 alternative와 section 배치를 확인한다.
09 · REFERENCES
원문 좌표
- arm64arch/arm64/kvm/arm.c:1157-1251
- x86-64arch/x86/kvm/x86.c:11924-12024
- RISC-Varch/riscv/kvm/vcpu.c:856-950
Linux kernel source: GPL-2.0-only. 이 글의 코드 발췌는 Linux v6.18.37 원문을 기준으로 하며, 분석 문장은 해당 코드의 실행 조건과 상태 경계를 설명합니다.