Linux v6.18.37 · 개념과 코드 읽기

RISC-V 64 head.S의 hart·DTB 수신과 SATP 전환

이 코드는 어떤 문제를 푸나요?

MMU를 켜면 같은 PC 숫자가 다른 주소로 해석될 수 있습니다. RISC-V의 relocate_enable_mmu는 복귀 주소와 trap 벡터를 먼저 맞춘 뒤 임시 표와 정식 표를 순서대로 적재합니다. v6.18.37의 이 루틴 전체를 읽어 주소 전환 도중에도 다음 명령을 가져올 수 있어야 한다는 문제를 설명합니다.

읽을 범위: v6.18.37 · arch/riscv/kernel/head.S · relocate_enable_mmu 73–125행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.

먼저 알아둘 개념

SATP

주소 변환 모드·ASID·최상위 표의 물리 페이지 번호를 담는 CSR입니다.

RA 보정

나중에 ret할 주소도 가상 주소로 맞춰야 새 주소 환경에서 올바른 위치로 돌아갑니다.

trampoline 표

주소 전환을 안전하게 넘기는 최소 임시 매핑입니다. 정식 커널 주소 공간 전체를 뜻하지 않습니다.

처음 읽을 때

전환 전 PC, 전환 후 PC, trap 목적지, 복귀 주소를 네 칸으로 적어 보십시오.

더 깊이 살펴볼 때

VA와 PA가 같은 경우의 순차 진행과 다른 경우의 trap 이동, 두 SFENCE.VMA의 목적을 구분하십시오.

그림으로 보는 변화

RISC-V 64 head.S의 hart·DTB 수신과 SATP 전환의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 주소 보정

커널 가상 시작점과 현재 물리 시작점의 차이로 RA와 trap 목적지를 맞춥니다.

화살표는 주소값 계산이며 메모리 복사가 아닙니다.

2. 임시 변환 활성화

PTE 쓰기를 동기화한 뒤 trampoline SATP를 적재합니다.

VA와 PA 관계에 따라 trap으로 다음 가상 위치로 이동할 수 있습니다.

3. 정식 표로 교체

정식 SATP와 fence를 적용한 뒤 보정한 RA로 돌아갑니다.

마지막 화살표는 제어 흐름의 복귀입니다.

relocate_enable_mmu를 한 줄씩 읽기

줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.

	.global relocate_enable_mmu
relocate_enable_mmu:
	/* Relocate return address */
	la a1, kernel_map
	XIP_FIXUP_OFFSET a1
	REG_L a1, KERNEL_MAP_VIRT_ADDR(a1)
	la a2, _start
	sub a1, a1, a2
	add ra, ra, a1

	/* Point stvec to virtual address of intruction after satp write */
	la a2, 1f
	add a2, a2, a1
	csrw CSR_TVEC, a2

	/* Compute satp for kernel page tables, but don't load it yet */
	srl a2, a0, PAGE_SHIFT
	la a1, satp_mode
	XIP_FIXUP_OFFSET a1
	REG_L a1, 0(a1)
	or a2, a2, a1

	/*
	 * Load trampoline page directory, which will cause us to trap to
	 * stvec if VA != PA, or simply fall through if VA == PA.  We need a
	 * full fence here because setup_vm() just wrote these PTEs and we need
	 * to ensure the new translations are in use.
	 */
	la a0, trampoline_pg_dir
	XIP_FIXUP_OFFSET a0
	srl a0, a0, PAGE_SHIFT
	or a0, a0, a1
	sfence.vma
	csrw CSR_SATP, a0
.align 2
1:
	/* Set trap vector to spin forever to help debug */
	la a0, .Lsecondary_park
	csrw CSR_TVEC, a0

	/* Reload the global pointer */
	load_global_pointer

	/*
	 * Switch to kernel page tables.  A full fence is necessary in order to
	 * avoid using the trampoline translations, which are only correct for
	 * the first superpage.  Fetching the fence is guaranteed to work
	 * because that first superpage is translated the same way.
	 */
	csrw CSR_SATP, a2
	sfence.vma

	ret
	.global relocate_enable_mmu

다른 파일에서 참조할 수 있도록 relocate_enable_mmu 기호를 공개합니다.

relocate_enable_mmu:

주소 전환 루틴의 시작 위치입니다. a0에는 커널 페이지 표 루트의 물리 주소가 전달됩니다.

	la a1, kernel_map

kernel_map 자료의 현재 접근 가능한 주소를 a1에 얻습니다.

	XIP_FIXUP_OFFSET a1

XIP 빌드에서 필요한 주소 보정을 매크로에 맡깁니다. 일반 RAM 실행과 주소 배치가 다를 수 있습니다.

	REG_L a1, KERNEL_MAP_VIRT_ADDR(a1)

kernel_map의 커널 가상 시작 주소를 읽습니다. REG_L은 XLEN에 맞는 load를 선택합니다.

	la a2, _start

현재 _start 기호 주소를 a2에 가져옵니다.

	sub a1, a1, a2

가상 시작과 현재 시작 사이의 차이를 a1에 계산합니다. 이것이 코드 주소를 보정할 값입니다.

	add ra, ra, a1

RA에 주소 차이를 더해 MMU 전환 후 돌아갈 위치도 가상 주소 기준으로 맞춥니다.

	la a2, 1f

뒤에 나오는 1번 지역 라벨의 현재 주소를 구합니다. 1f의 f는 앞으로 나오는 라벨을 뜻합니다.

	add a2, a2, a1

그 라벨 주소에도 같은 차이를 더해 전환 후의 가상 목적지를 만듭니다.

	csrw CSR_TVEC, a2

CSR_TVEC에 그 목적지를 설치합니다. 전환 직후 VA와 PA가 다르면 trap을 통해 이 지점에 도달할 수 있게 합니다.

	srl a2, a0, PAGE_SHIFT

커널 루트의 물리 주소를 PAGE_SHIFT만큼 내려 SATP가 요구하는 물리 페이지 번호로 만듭니다.

	la a1, satp_mode

사용할 SATP 변환 모드가 들어 있는 변수의 주소를 얻습니다.

	XIP_FIXUP_OFFSET a1

XIP 구성의 주소 보정을 적용합니다.

	REG_L a1, 0(a1)

satp_mode의 실제 값을 읽습니다. 변수 주소와 그 안의 모드값을 구분합니다.

	or a2, a2, a1

커널 표의 PPN과 모드 비트를 합친 SATP 값을 a2에 보관합니다. 아직 이 정식 표를 설치하지 않습니다.

	la a0, trampoline_pg_dir

우선 설치할 임시 trampoline 페이지 표 주소를 얻습니다.

	XIP_FIXUP_OFFSET a0

임시 표 주소에도 XIP 보정을 적용합니다.

	srl a0, a0, PAGE_SHIFT

임시 표 주소를 SATP용 PPN으로 변환합니다.

	or a0, a0, a1

임시 PPN과 앞에서 읽은 모드 비트를 합칩니다.

	sfence.vma

setup_vm이 기록한 PTE와 주소 변환 접근의 순서를 맞추는 SFENCE.VMA를 수행합니다.

	csrw CSR_SATP, a0

임시 SATP를 설치합니다. 현재 주소가 같은 방식으로 번역되지 않으면 앞서 준비한 TVEC 경로로 이어집니다.

.align 2

이어질 라벨을 어셈블러의 2^2 바이트 경계에 정렬합니다. 실행 중 주소를 반올림하는 명령은 아닙니다.

1:

전환 후 도달할 1번 지역 라벨입니다. 위의 1f 참조와 연결됩니다.

	la a0, .Lsecondary_park

후속 예외에서 대기할 디버깅용 park 루프의 주소를 구합니다.

	csrw CSR_TVEC, a0

TVEC를 park 루프로 다시 바꿉니다. 초기 전환용 단발 목적지를 계속 사용하지 않기 위해서입니다.

	load_global_pointer

가상 주소 환경에 맞는 global pointer를 다시 준비합니다. 구체적인 PC 상대 로딩은 매크로 정의에 있습니다.

	csrw CSR_SATP, a2

a2에 준비해 둔 정식 커널 SATP를 설치합니다.

	sfence.vma

임시 표에서 얻은 번역이 이후 접근에 잘못 남지 않도록 SFENCE.VMA를 수행합니다.

	ret

처음에 가상 주소로 보정한 RA로 돌아갑니다. MMU 전환 함수의 호출자 실행이 새 주소 환경에서 이어집니다.

함께 생각해 볼 질문

SATP를 쓰면 RA 값도 자동 변환됩니까?

아닙니다. 코드에서 주소 차이를 더해 직접 보정합니다.

trampoline 표가 영구 커널 표입니까?

아닙니다. 전환을 통과한 뒤 정식 표로 교체합니다.

SFENCE.VMA는 일반 데이터 캐시 비우기입니까?

아닙니다. 주소 변환과 페이지 표 접근의 동기화에 관련된 명령입니다.

출처와 읽은 범위

Linux stable v6.18.37 · arch/riscv/kernel/head.S

해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고

맨 위로 ↑