← Architecture 비교DUJINLABS.COM

Linux 6.18.37 LTS · Architecture comparison 18/21

KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension

KVM_RUN ioctl이 vCPU state를 hardware virtualization context에 넣고 guest를 실행한 뒤 exit reason을 host 또는 userspace가 처리하는 과정을 비교합니다.

비교 대상
arm64 / x86-64 / RISC-V
실제 원본
3 files · 291 annotated lines
기준 tag
Linux v6.18.37
분석 축
state · ordering · lifetime · latency

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 경계를 지켜야 한다.

지연 시간 관점entry/exit fixed cost, stage-2 miss, virtual interrupt injection, emulation, host scheduling과 userspace round trip이 더해진다. worst case는 preempted vCPU가 lock 또는 virtual IPI completion을 쥔 상황이다.

02 · CONTRACT

공통 계약과 architecture 구현

architecture핵심 mechanism실패 형태확인할 상태
arm64EL2 VHE/nVHE와 stage-2 VTTBR_EL2VMID 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-64VMX VMCS 또는 SVM VMCB와 EPT/NPTinvalid 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-VH-extension VS/VU state와 HGATP two-stage translationVMID 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

세 그림으로 먼저 읽기

그림 1. 같은 목적, 서로 다른 mechanism각 ISA에서 실제로 추적할 state와 checkpoint를 한 줄에 맞췄습니다.

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을 확인한다.
그림 2. 공통 kernel과 architecture hook의 소유권공통 정책이 hardware state를 직접 소유하지 않는 경계를 표시합니다.
Linux common contractuserspace VMM이 KVM_RUN을 호출하면 KVM core가 vCPU를 load하고 pending request/interrupt를 처리한 뒤 architecture enter_guest를 실행한다. hardware exit는 reason을 decode해 kernel에서 처리하거나 exit structure로 userspace에 반환한다.
arm64EL2 VHE/nVHE와 stage-2 VTTBR_EL2host/guest sysreg save, stage-2 TLBI, GIC LR injection과 barrier가 guest entry 전 완료되어야 한다.
x86-64VMX VMCS 또는 SVM VMCB와 EPT/NPTVM-entry control validation, host FPU/debug/speculation state 교체와 INVEPT/INVVPID ordering을 지켜야 한다.
RISC-VH-extension VS/VU state와 HGATP two-stage translationVS CSR save/restore, HGATP write, HFENCE와 virtual interrupt pending publication이 guest entry 전에 정합해야 한다.
lifetime boundaryVM, vCPU, memory slot와 stage-2 mapping은 in-flight guest access와 SRCU reader가 끝날 때까지 유지된다. memslot 변경은 MMU invalidation sequence와 remote vCPU kick을 동반한다.
그림 3. publication과 관찰 순서state를 준비한 뒤 architecture ordering을 거쳐 관찰 가능한 checkpoint가 됩니다.
arm64state 준비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-64state 준비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-Vstate 준비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을 확인한다.

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개 줄에 각각 설명을 붙였습니다.

L1157 * @vcpu: The VCPU pointer

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1158 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1159 * This function is called through the VCPU_RUN ioctl called from user space. It

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1160 * will execute VM code in a loop until the time slice for the process is used

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1161 * or some emulation is needed from user space in which case the function will

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1162 * return with return value 0 and with the kvm_run structure filled in with the

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1163 * required data for the requested emulation.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1164 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1165int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)

이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L1166{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L1167 struct kvm_run *run = vcpu->run;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L1168 int ret;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1169(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1170 if (run->exit_reason == KVM_EXIT_MMIO) {

이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L1171 ret = kvm_handle_mmio_return(vcpu);

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1172 if (ret <= 0)

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1173 return ret;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1174 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L1175(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1176 vcpu_load(vcpu);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1177(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1178 if (!vcpu->wants_to_run) {

이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L1179 ret = -EINTR;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1180 goto out;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L1181 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L1182(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1183 kvm_sigset_activate(vcpu);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1184(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1185 ret = 1;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1186 run->exit_reason = KVM_EXIT_UNKNOWN;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L1187 run->flags = 0;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L1188 while (ret > 0) {

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1189 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1190 * Check conditions before entering the guest

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1191 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1192 ret = kvm_xfer_to_guest_mode_handle_work(vcpu);

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1193 if (!ret)

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1194 ret = 1;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1195(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1196 if (ret > 0)

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1197 ret = check_vcpu_requests(vcpu);

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1198(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1199 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1200 * Preparing the interrupts to be injected also

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1201 * involves poking the GIC, which must be done in a

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1202 * non-preemptible context.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1203 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1204 preempt_disable();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1205(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1206 kvm_nested_flush_hwstate(vcpu);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1207(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1208 if (kvm_vcpu_has_pmu(vcpu))

이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L1209 kvm_pmu_flush_hwstate(vcpu);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1210(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1211 local_irq_disable();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1212(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1213 kvm_vgic_flush_hwstate(vcpu);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1214(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1215 kvm_pmu_update_vcpu_events(vcpu);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1216(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1217 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1218 * Ensure we set mode to IN_GUEST_MODE after we disable

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1219 * interrupts and before the final VCPU requests check.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1220 * See the comment in kvm_vcpu_exiting_guest_mode() and

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1221 * Documentation/virt/kvm/vcpu-requests.rst

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1222 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1223 smp_store_mb(vcpu->mode, IN_GUEST_MODE);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1224(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1225 if (ret <= 0 || kvm_vcpu_exit_request(vcpu, &ret)) {

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L1226 vcpu->mode = OUTSIDE_GUEST_MODE;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L1227 isb(); /* Ensure work in x_flush_hwstate is committed */

memory, translation 또는 instruction-fetch 관찰 순서를 확정하는 architecture 명령이다. 앞에서 publish한 상태와 뒤에서 재사용하는 상태의 경계를 이 줄에 둔다.

L1228 if (kvm_vcpu_has_pmu(vcpu))

이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L1229 kvm_pmu_sync_hwstate(vcpu);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1230 if (unlikely(!irqchip_in_kernel(vcpu->kvm)))

이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L1231 kvm_timer_sync_user(vcpu);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1232 kvm_vgic_sync_hwstate(vcpu);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1233 local_irq_enable();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1234 preempt_enable();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1235 continue;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L1236 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L1237(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1238 kvm_arch_vcpu_ctxflush_fp(vcpu);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1239(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1240 /**************************************************************

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1241 * Enter the guest

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1242 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1243 trace_kvm_entry(*vcpu_pc(vcpu));

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1244 guest_timing_enter_irqoff();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L1245(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1246 ret = kvm_arm_vcpu_enter_exit(vcpu);

EL2 entry를 통해 guest를 실행하고 다음 trap/interrupt에서 host로 돌아온다.

L1247(blank)

빈 줄은 arm64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L1248 vcpu->mode = OUTSIDE_GUEST_MODE;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L1249 vcpu->stat.exits++;

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L1250 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L1251 * Back from guest

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, 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개 줄에 각각 설명을 붙였습니다.

L11924 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11925 if (vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED &&

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11926 !kvm_apic_init_sipi_allowed(vcpu))

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L11927 return -EINVAL;

이 함수가 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.

L11928(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L11929 return kvm_x86_call(vcpu_pre_run)(vcpu);

이 함수가 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.

L11930}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L11931(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L11932int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)

이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L11933{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L11934 struct kvm_queued_exception *ex = &vcpu->arch.exception;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L11935 struct kvm_run *kvm_run = vcpu->run;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L11936 u64 sync_valid_fields;

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L11937 int r;

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L11938(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L11939 r = kvm_mmu_post_init_vm(vcpu->kvm);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L11940 if (r)

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11941 return r;

이 함수가 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.

L11942(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L11943 vcpu_load(vcpu);

vCPU를 현재 physical CPU에 load해 VMCS/VMCB와 per-CPU owner를 준비한다.

L11944 kvm_sigset_activate(vcpu);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L11945 kvm_run->flags = 0;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L11946 kvm_load_guest_fpu(vcpu);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L11947(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L11948 kvm_vcpu_srcu_read_lock(vcpu);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L11949 if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)) {

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11950 if (!vcpu->wants_to_run) {

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11951 r = -EINTR;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L11952 goto out;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L11953 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L11954(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L11955 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11956 * Don't bother switching APIC timer emulation from the

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11957 * hypervisor timer to the software timer, the only way for the

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11958 * APIC timer to be active is if userspace stuffed vCPU state,

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11959 * i.e. put the vCPU into a nonsensical state. Only an INIT

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11960 * will transition the vCPU out of UNINITIALIZED (without more

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11961 * state stuffing from userspace), which will reset the local

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11962 * APIC and thus cancel the timer or drop the IRQ (if the timer

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11963 * already expired).

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11964 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11965 kvm_vcpu_srcu_read_unlock(vcpu);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L11966 kvm_vcpu_block(vcpu);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L11967 kvm_vcpu_srcu_read_lock(vcpu);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L11968(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L11969 if (kvm_apic_accept_events(vcpu) < 0) {

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11970 r = 0;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L11971 goto out;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L11972 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L11973 r = -EAGAIN;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L11974 if (signal_pending(current)) {

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11975 r = -EINTR;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L11976 kvm_run->exit_reason = KVM_EXIT_INTR;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L11977 ++vcpu->stat.signal_exits;

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L11978 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L11979 goto out;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L11980 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L11981(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L11982 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를 연결해 본다.

L11983 if ((kvm_run->kvm_valid_regs & ~sync_valid_fields) ||

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11984 (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를 분리해 해석한다.

L11985 r = -EINVAL;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L11986 goto out;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L11987 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L11988(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L11989 if (kvm_run->kvm_dirty_regs) {

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11990 r = sync_regs(vcpu);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L11991 if (r != 0)

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11992 goto out;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L11993 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L11994(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L11995 /* re-sync apic's tpr */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11996 if (!lapic_in_kernel(vcpu)) {

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11997 if (kvm_set_cr8(vcpu, kvm_run->cr8) != 0) {

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L11998 r = -EINVAL;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L11999 goto out;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L12000 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L12001 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L12002(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L12003 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L12004 * If userspace set a pending exception and L2 is active, convert it to

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L12005 * a pending VM-Exit if L1 wants to intercept the exception.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L12006 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L12007 if (vcpu->arch.exception_from_userspace && is_guest_mode(vcpu) &&

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L12008 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를 분리해 해석한다.

L12009 ex->error_code)) {

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L12010 kvm_queue_exception_vmexit(vcpu, ex->vector,

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L12011 ex->has_error_code, ex->error_code,

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L12012 ex->has_payload, ex->payload);

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L12013 ex->injected = false;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L12014 ex->pending = false;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L12015 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L12016 vcpu->arch.exception_from_userspace = false;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L12017(blank)

빈 줄은 x86-64 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L12018 if (unlikely(vcpu->arch.complete_userspace_io)) {

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L12019 int (*cui)(struct kvm_vcpu *) = vcpu->arch.complete_userspace_io;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L12020 vcpu->arch.complete_userspace_io = NULL;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L12021 r = cui(vcpu);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L12022 if (r <= 0)

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L12023 goto out;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L12024 } 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개 줄에 각각 설명을 붙였습니다.

L856 trap->scause = csr_read(CSR_SCAUSE);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L857 trap->stval = csr_read(CSR_STVAL);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L858(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L859 vcpu->arch.last_exit_cpu = vcpu->cpu;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L860 guest_state_exit_irqoff();

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L861 kvm_riscv_vcpu_swap_in_host_state(vcpu);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L862}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L863(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L864int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)

이 함수의 진입 계약이 시작된다. RISC-V에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L865{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L866 int ret;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L867 struct kvm_cpu_trap trap;

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L868 struct kvm_run *run = vcpu->run;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L869(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L870 if (!vcpu->arch.ran_atleast_once)

이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L871 kvm_riscv_vcpu_setup_config(vcpu);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L872(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L873 /* Mark this VCPU ran at least once */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L874 vcpu->arch.ran_atleast_once = true;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L875(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L876 kvm_vcpu_srcu_read_lock(vcpu);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L877(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L878 switch (run->exit_reason) {

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L879 case KVM_EXIT_MMIO:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L880 /* Process MMIO value returned from user-space */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L881 ret = kvm_riscv_vcpu_mmio_return(vcpu, vcpu->run);

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L882 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L883 case KVM_EXIT_RISCV_SBI:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L884 /* Process SBI value returned from user-space */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L885 ret = kvm_riscv_vcpu_sbi_return(vcpu, vcpu->run);

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L886 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L887 case KVM_EXIT_RISCV_CSR:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L888 /* Process CSR value returned from user-space */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L889 ret = kvm_riscv_vcpu_csr_return(vcpu, vcpu->run);

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L890 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L891 default:

분기 label이다. 이 위치로 들어오는 모든 선행 경로가 같은 register, stack, lock과 interrupt 상태를 만족하는지 비교한다.

L892 ret = 0;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L893 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L894 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L895 if (ret) {

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L896 kvm_vcpu_srcu_read_unlock(vcpu);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L897 return ret;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L898 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L899(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L900 if (!vcpu->wants_to_run) {

이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L901 kvm_vcpu_srcu_read_unlock(vcpu);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L902 return -EINTR;

이 함수가 KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.

L903 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L904(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L905 vcpu_load(vcpu);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L906(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L907 kvm_sigset_activate(vcpu);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L908(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L909 ret = 1;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L910 run->exit_reason = KVM_EXIT_UNKNOWN;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L911 while (ret > 0) {

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L912 /* Check conditions before entering the guest */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L913 ret = kvm_xfer_to_guest_mode_handle_work(vcpu);

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L914 if (ret)

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L915 continue;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L916 ret = 1;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L917(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L918 kvm_riscv_gstage_vmid_update(vcpu);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L919(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L920 ret = kvm_riscv_check_vcpu_requests(vcpu);

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L921 if (ret <= 0)

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L922 continue;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L923(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L924 preempt_disable();

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L925(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L926 /* Update AIA HW state before entering guest */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L927 ret = kvm_riscv_vcpu_aia_update(vcpu);

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L928 if (ret <= 0) {

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L929 preempt_enable();

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L930 continue;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L931 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L932(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L933 local_irq_disable();

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L934(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L935 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L936 * Ensure we set mode to IN_GUEST_MODE after we disable

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L937 * interrupts and before the final VCPU requests check.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L938 * See the comment in kvm_vcpu_exiting_guest_mode() and

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L939 * Documentation/virt/kvm/vcpu-requests.rst

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L940 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L941 vcpu->mode = IN_GUEST_MODE;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L942(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L943 kvm_vcpu_srcu_read_unlock(vcpu);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L944 smp_mb__after_srcu_read_unlock();

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L945(blank)

빈 줄은 RISC-V KVM virtualization: EL2, VMX/SVM과 RISC-V H-extension 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L946 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L947 * We might have got VCPU interrupts updated asynchronously

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L948 * so update it in HW.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L949 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L950 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

숫자로 검산하기

01

guest page fault와 stage-2 fault 구분

guest VA 0x4000 접근이 guest page table에서는 GPA 0x120000으로 변환되지만 해당 GPA memslot이 제거됐다고 가정한다.

  1. stage 1guest page walker는 GVA 0x4000을 GPA 0x120000으로 정상 변환한다.
  2. stage 2EL2/EPT/HGATP walker가 GPA에 대응하는 host mapping을 찾지 못해 virtualization exit를 만든다.
  3. decodearm64 HPFAR, x86 EPT qualification, RISC-V HTVAL이 guest physical fault 정보를 제공한다.
  4. resolutionKVM MMU가 새 memslot mapping을 만들 수 없으면 userspace에 memory fault/MMIO exit를 반환한다.

결론guest의 일반 page fault와 host가 관리하는 stage-2 fault는 faulting address 공간과 처리 주체가 다르다.

06 · DEEP DIVE

경계별 상세 분석

01

공통 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 경계를 지켜야 한다.

02

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을 본다.

03

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를 본다.

04

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을 확인한다.

05

객체 수명과 소유권을 먼저 고정한다

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와 폐기 지점을 같은 표에 기록한다.

06

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

실패를 어떤 증거로 나눌 것인가

분류관찰되는 결과첫 확인값
arm64VMID 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-64invalid 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-VVMID 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

재현과 계측 절차

  1. 같은 guest workload에서 hardware exit reason과 userspace exit reason을 따로 count해 virtualization overhead를 분해한다.
  2. memslot을 변경하며 remote vCPU kick, stage-2 invalidation과 guest 재진입 순서를 trace한다.
  3. 동일한 workload에서 세 architecture의 tracepoint 이름, CPU 번호, PC, stack pointer와 address-space identifier를 같은 열로 기록한다.
  4. 소스만 읽고 끝내지 않고 최종 vmlinuxobjdump -dr, readelf -SW 결과로 선택된 alternative와 section 배치를 확인한다.

09 · REFERENCES

원문 좌표

Linux kernel source: GPL-2.0-only. 이 글의 코드 발췌는 Linux v6.18.37 원문을 기준으로 하며, 분석 문장은 해당 코드의 실행 조건과 상태 경계를 설명합니다.