이 코드는 어떤 문제를 푸나요?
게스트가 만든 VA→IPA 변환 뒤에는 호스트가 관리하는 IPA→PA 변환이 있습니다. 이 두 번째 표로 게스트가 접근할 수 있는 메모리를 제한합니다. 아래는 v6.18.37 KVM이 장치 물리 범위를 게스트 IPA에 연결하는 작은 함수를 읽는 예이며 일반 RAM의 demand fault 전체를 대신하지는 않습니다.
읽을 범위: v6.18.37 · arch/arm64/kvm/mmu.c · kvm_phys_addr_ioremap 1144–1180행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
IPA
게스트가 물리 주소로 생각하는 주소입니다. 호스트 RAM의 최종 물리 주소와 같다고 가정하지 않습니다.
Stage-2 권한
게스트 Stage-1이 허용해도 Stage-2에서 막으면 접근이 실패할 수 있습니다.
장치 매핑
일반 RAM과 달리 장치 속성이 필요한 범위입니다. 잘못된 캐시 속성으로 MMIO를 접근하면 동작이 달라질 수 있습니다.
처음 읽을 때
VA, IPA, PA를 서로 다른 세 상자로 그려 두 변환을 따로 설명하십시오.
더 깊이 살펴볼 때
MMU 캐시 준비, 쓰기 권한, KVM mmu_lock과 매핑 실패 정리를 함께 검토하십시오.
그림으로 보는 변화

1. 범위와 권한 준비
게스트 IPA와 호스트 물리 범위, 장치 속성을 준비합니다.
첫 화살표는 두 주소 범위를 대응시키는 준비입니다.
2. 페이지 표 자원 준비
Stage-2 표 생성에 필요한 메모리를 마련합니다.
게스트 데이터 페이지를 복사하는 단계는 아닙니다.
3. Stage-2 매핑 설치
잠금 아래 표를 만들고 준비한 자원을 정리합니다.
이후 게스트 접근에 적용될 번역과 권한을 설치합니다.
kvm_phys_addr_ioremap를 한 줄씩 읽기
줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
phys_addr_t pa, unsigned long size, bool writable)
{
phys_addr_t addr;
int ret = 0;
struct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO };
struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
struct kvm_pgtable *pgt = mmu->pgt;
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_DEVICE |
KVM_PGTABLE_PROT_R |
(writable ? KVM_PGTABLE_PROT_W : 0);
if (is_protected_kvm_enabled())
return -EPERM;
size += offset_in_page(guest_ipa);
guest_ipa &= PAGE_MASK;
for (addr = guest_ipa; addr < guest_ipa + size; addr += PAGE_SIZE) {
ret = kvm_mmu_topup_memory_cache(&cache,
kvm_mmu_cache_min_pages(mmu));
if (ret)
break;
write_lock(&kvm->mmu_lock);
ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, addr, PAGE_SIZE,
pa, prot, &cache, 0);
write_unlock(&kvm->mmu_lock);
if (ret)
break;
pa += PAGE_SIZE;
}
kvm_mmu_free_memory_cache(&cache);
return ret;
}int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,VM과 장치를 보이게 할 게스트 IPA 시작 주소를 받습니다. 사용자 VA를 받는 함수로 읽지 않습니다.
phys_addr_t pa, unsigned long size, bool writable)실제 물리 시작점·크기·쓰기 허용 여부를 받습니다. 호출자가 제공하는 주소 범위와 페이지 정렬 조건을 주변 경로와 함께 확인해야 합니다.
phys_addr_t addr;한 페이지씩 처리할 게스트 주소를 보관합니다.
int ret = 0;아직 오류가 없으므로 결과를 0으로 시작합니다.
struct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO };Stage-2 표를 만들 때 사용할 메모리 캐시를 준비합니다. 새 표가 0으로 초기화되도록 __GFP_ZERO를 설정합니다.
struct kvm_s2_mmu *mmu = &kvm->arch.mmu;이 VM의 Stage-2 MMU 상태를 선택합니다.
struct kvm_pgtable *pgt = mmu->pgt;그 MMU가 사용하는 페이지 표 객체를 꺼냅니다.
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_DEVICE |장치용 메모리 속성을 매핑 권한에 포함합니다. 일반 cacheable RAM 매핑으로 취급하지 않습니다.
KVM_PGTABLE_PROT_R |장치 매핑에 읽기 권한을 포함합니다.
(writable ? KVM_PGTABLE_PROT_W : 0);호출자가 writable을 요청한 경우에만 쓰기 권한을 더합니다.
if (is_protected_kvm_enabled())보호 KVM 구성이 활성화된 경우인지 확인합니다.
return -EPERM;이 경로에서 허용하지 않는 보호 KVM 구성이면 EPERM으로 반환합니다. 모든 보호 VM 매핑을 이 함수가 지원한다고 말할 수 없는 근거입니다.
size += offset_in_page(guest_ipa);게스트 IPA의 페이지 내부 offset을 길이에 더하여 정렬 전의 끝 범위를 포함하게 합니다.
guest_ipa &= PAGE_MASK;게스트 시작 주소를 페이지 경계로 내립니다. 이 두 줄이 모든 잘못된 입력을 검증하는 일반 범위 검사라고 가정하지 않습니다.
for (addr = guest_ipa; addr < guest_ipa + size; addr += PAGE_SIZE) {준비한 게스트 범위를 PAGE_SIZE 단위로 반복합니다. 반복 변수는 IPA이며 실제 물리 주소 pa도 아래에서 함께 증가합니다.
ret = kvm_mmu_topup_memory_cache(&cache,잠금을 잡기 전에 페이지 표 구축에 필요한 캐시 메모리를 채웁니다.
kvm_mmu_cache_min_pages(mmu));현재 MMU에 필요한 최소 페이지 수를 계산해 준비 요청에 넘깁니다.
if (ret)메모리 준비 중 오류가 발생했는지 확인합니다.
break;오류면 더 매핑하지 않고 반복을 끝냅니다. 아래의 공통 캐시 정리로 이어집니다.
write_lock(&kvm->mmu_lock);다른 경로의 Stage-2 표 변경과 충돌하지 않도록 쓰기 잠금을 잡습니다.
ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, addr, PAGE_SIZE,Stage-2 매핑 함수를 통해 현재 IPA의 한 페이지를 연결합니다. KVM_PGT_FN은 구성에 맞는 표 함수 연결 매크로입니다.
pa, prot, &cache, 0);대응하는 호스트 물리 주소, 권한, 준비한 표 메모리 캐시를 매핑 함수에 전달합니다.
write_unlock(&kvm->mmu_lock);한 페이지 설치 시도가 끝나면 MMU 잠금을 풉니다.
if (ret)표 설치가 실패했는지 검사합니다.
break;실패하면 다음 페이지를 계속 설치하지 않습니다. 이 함수가 이미 성공한 앞선 매핑을 여기서 전부 되돌린다고 설명하면 안 됩니다.
pa += PAGE_SIZE;다음 IPA와 대응할 물리 주소도 한 페이지 전진시킵니다.
kvm_mmu_free_memory_cache(&cache);남은 페이지 표 캐시 자원을 정리합니다. 이것이 앞에서 설치된 실제 Stage-2 표를 모두 해제한다는 뜻은 아닙니다.
return ret;성공 또는 중간 오류 값을 호출자에게 반환합니다.
함께 생각해 볼 질문
게스트가 자기 페이지 표를 바꾸면 호스트 전체 RAM을 읽습니까?
Stage-2가 허용한 범위와 권한이 추가로 적용됩니다.
이 함수는 모든 guest page fault 경로입니까?
아닙니다. 장치 범위를 매핑하는 helper를 대표로 읽습니다.
Stage-2 fault는 항상 오류 종료입니까?
지연 매핑으로 해결되거나 MMIO 처리로 이어질 수도 있으며 원인에 따라 다릅니다.
출처와 읽은 범위
Linux stable v6.18.37 · arch/arm64/kvm/mmu.c
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고
