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

RISC-V trap frame과 SBI secondary hart 기동

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

보조 hart는 이미 실행 중인 task의 예외 복귀와 달리 시작 주소와 초기 task·스택을 새로 받아야 합니다. v6.18.37 sbi_cpu_start 전체를 읽어 부트 데이터 공개와 SBI HSM 기동 요청을 연결합니다. trap frame의 자세한 분석은 기존 글에 남겨 두고 이 단원은 SMP 기동의 작은 완결된 함수를 대표로 사용합니다.

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

먼저 알아둘 개념

hart ID

하드웨어 실행 단위의 식별자입니다. Linux 논리 CPU 번호와 다를 수 있습니다.

boot data

새 hart가 사용할 task 포인터와 스택 포인터를 준비한 공유 메모리입니다.

메모리 장벽

원격 hart를 깨우기 전에 부트 데이터가 적절한 순서로 공개되게 해야 합니다. 함수 호출 순서만 보고 다른 CPU의 관찰 순서를 단정하지 않습니다.

처음 읽을 때

기동시키는 CPU와 새 hart를 두 세로선으로 그리고 데이터 준비 뒤 알림을 보내는 순서를 설명하십시오.

더 깊이 살펴볼 때

boot data의 물리 주소 전달과 그 안의 task·SP 주소가 언제 유효해지는지 secondary_start_sbi와 함께 읽으십시오.

그림으로 보는 변화

RISC-V trap frame과 SBI secondary hart 기동의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 진입 주소와 hart 선택

secondary_start_sbi 물리 주소와 대상 hart ID를 구합니다.

화살표는 Linux 내부 식별자를 펌웨어 입력으로 변환합니다.

2. 부트 자료 공개

idle task와 커널 스택을 기록하고 장벽을 수행합니다.

공유 메모리 쓰기가 기동 요청보다 앞서야 함을 나타냅니다.

3. SBI에 기동 요청

HSM에 대상과 시작 주소, 부트 자료 물리 주소를 넘깁니다.

요청 성공과 Linux CPU online 완료는 별도 단계입니다.

sbi_cpu_start를 한 줄씩 읽기

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

static int sbi_cpu_start(unsigned int cpuid, struct task_struct *tidle)
{
	unsigned long boot_addr = __pa_symbol(secondary_start_sbi);
	unsigned long hartid = cpuid_to_hartid_map(cpuid);
	unsigned long hsm_data;
	struct sbi_hart_boot_data *bdata = &boot_data[cpuid];

	/* Make sure tidle is updated */
	smp_mb();
	bdata->task_ptr = tidle;
	bdata->stack_ptr = task_pt_regs(tidle);
	/* Make sure boot data is updated */
	smp_mb();
	hsm_data = __pa(bdata);
	return sbi_hsm_hart_start(hartid, boot_addr, hsm_data);
}
static int sbi_cpu_start(unsigned int cpuid, struct task_struct *tidle)

기동할 논리 CPU와 그 CPU의 idle task를 받아 SBI HSM 기동 요청을 준비합니다.

	unsigned long boot_addr = __pa_symbol(secondary_start_sbi);

새 hart가 들어올 secondary_start_sbi의 물리 주소를 구합니다. 아직 그 hart의 정상 가상 주소 실행을 전제하지 않습니다.

	unsigned long hartid = cpuid_to_hartid_map(cpuid);

Linux 논리 CPU 번호를 펌웨어의 hart ID로 바꿉니다. 두 번호가 우연히 같아도 역할은 다릅니다.

	unsigned long hsm_data;

나중에 SBI에 전달할 부트 자료 물리 주소를 보관할 변수를 선언합니다.

	struct sbi_hart_boot_data *bdata = &boot_data[cpuid];

대상 CPU 전용 부트 자료 슬롯을 선택합니다. 여러 CPU 기동 요청의 데이터를 한 슬롯에 덮어쓰지 않도록 나뉘어 있습니다.

	smp_mb();

idle task 관련 준비가 뒤의 부트 자료 공개보다 앞서도록 CPU 간 메모리 순서를 맞춥니다.

	bdata->task_ptr = tidle;

새 hart가 현재 task로 사용할 idle task 포인터를 기록합니다.

	bdata->stack_ptr = task_pt_regs(tidle);

그 task의 pt_regs 위치를 초기 스택 포인터로 기록합니다. 스택 상단 배치 규칙과 연결되는 값입니다.

	smp_mb();

task와 stack 포인터 쓰기가 완료되기 전에 원격 hart가 기동 자료를 읽는 순서 문제가 생기지 않도록 장벽을 둡니다.

	hsm_data = __pa(bdata);

boot data 자체의 주소를 물리 주소로 바꿔 펌웨어에 전달할 값을 만듭니다.

	return sbi_hsm_hart_start(hartid, boot_addr, hsm_data);

hart ID, 시작 물리 주소, 부트 자료 물리 주소로 HSM 시작을 요청하고 결과를 그대로 반환합니다. 실제 secondary 초기화는 다른 hart에서 이어집니다.

함께 생각해 볼 질문

cpuid와 hartid는 같은 값입니까?

같다고 가정하면 안 됩니다. 명시적인 매핑 함수를 거칩니다.

bdata 주소를 왜 __pa로 변환합니까?

기동 인터페이스가 새 hart에 전달할 부트 데이터의 물리 주소를 필요로 하기 때문입니다.

smp_mb가 새 hart를 실행시킵니까?

아닙니다. 메모리 순서를 보장하며 실제 기동은 뒤의 SBI 호출이 요청합니다.

출처와 읽은 범위

Linux stable v6.18.37 · arch/riscv/kernel/cpu_ops_sbi.c

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

맨 위로 ↑