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

ARM MTE와 Linux fault 처리 소스 분석

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

MTE는 포인터와 메모리의 태그를 비교하지만 검사 방식은 task가 요청한 정책과 CPU의 선호 설정에 따라 달라집니다. v6.18.37의 mte_update_sctlr_user는 그 선택을 SCTLR의 사용자 설정값으로 계산합니다. 이 함수가 실제 SCTLR 하드웨어 쓰기까지 끝낸다고 읽지 않는 것이 핵심입니다.

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

먼저 알아둘 개념

태그 비교

주소의 논리 태그와 메모리 granule의 할당 태그를 비교합니다. 일반적인 주소 범위 검사와 동일하지 않습니다.

동기와 비동기 보고

검사 실패를 즉시 예외로 알리는지, 누적 상태로 나중에 알리는지에 차이가 있습니다.

task별 정책

프로그램이 요청한 허용 mode와 CPU의 선호 mode를 조합합니다. 다른 CPU로 이동하면 판단에 사용하는 CPU 정보도 중요합니다.

처음 읽을 때

요청한 검사 mode와 실제 선택된 mode를 두 칸으로 나누어 설명하십시오.

더 깊이 살펴볼 때

선점 금지 요구, ASYMM·ASYNC·SYNC 우선순위와 최종 SCTLR 쓰기의 호출자 책임을 확인하십시오.

그림으로 보는 변화

ARM MTE와 Linux fault 처리 소스 분석의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 요청과 선호 읽기

task 정책과 현재 CPU의 선호값을 읽습니다.

화살표는 정책 입력의 결합입니다.

2. 하드웨어 필드 계산

기존 TCF0·TCSO0를 지우고 선택한 mode를 인코딩합니다.

비트 계산이며 아직 하드웨어 레지스터 쓰기가 아닙니다.

3. task 상태에 보관

sctlr_user에 결과를 기록합니다.

실제 CPU 반영은 같은 선점 금지 구간의 호출자 작업으로 이어집니다.

mte_update_sctlr_user를 한 줄씩 읽기

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

static void mte_update_sctlr_user(struct task_struct *task)
{
	/*
	 * This must be called with preemption disabled and can only be called
	 * on the current or next task since the CPU must match where the thread
	 * is going to run. The caller is responsible for calling
	 * update_sctlr_el1() later in the same preemption disabled block.
	 */
	unsigned long sctlr = task->thread.sctlr_user;
	unsigned long mte_ctrl = task->thread.mte_ctrl;
	unsigned long pref, resolved_mte_tcf;

	pref = __this_cpu_read(mte_tcf_preferred);
	/*
	 * If there is no overlap between the system preferred and
	 * program requested values go with what was requested.
	 */
	resolved_mte_tcf = (mte_ctrl & pref) ? pref : mte_ctrl;
	sctlr &= ~(SCTLR_EL1_TCF0_MASK | SCTLR_EL1_TCSO0_MASK);
	/*
	 * Pick an actual setting. The order in which we check for
	 * set bits and map into register values determines our
	 * default order.
	 */
	if (resolved_mte_tcf & MTE_CTRL_TCF_ASYMM)
		sctlr |= SYS_FIELD_PREP_ENUM(SCTLR_EL1, TCF0, ASYMM);
	else if (resolved_mte_tcf & MTE_CTRL_TCF_ASYNC)
		sctlr |= SYS_FIELD_PREP_ENUM(SCTLR_EL1, TCF0, ASYNC);
	else if (resolved_mte_tcf & MTE_CTRL_TCF_SYNC)
		sctlr |= SYS_FIELD_PREP_ENUM(SCTLR_EL1, TCF0, SYNC);

	if (mte_ctrl & MTE_CTRL_STORE_ONLY)
		sctlr |= SYS_FIELD_PREP(SCTLR_EL1, TCSO0, 1);

	task->thread.sctlr_user = sctlr;
}
static void mte_update_sctlr_user(struct task_struct *task)

task의 MTE 정책을 실제 SCTLR 사용자 필드 표현으로 계산하는 함수입니다. 주석처럼 호출자는 CPU가 바뀌지 않게 선점을 막고 뒤에서 하드웨어 갱신을 수행해야 합니다.

	unsigned long sctlr = task->thread.sctlr_user;

기존 사용자 SCTLR 설정을 지역 변수로 가져옵니다. MTE와 관계없는 비트를 보존할 출발점입니다.

	unsigned long mte_ctrl = task->thread.mte_ctrl;

이 task가 요청한 MTE 검사 정책과 태그 관련 제어값을 읽습니다.

	unsigned long pref, resolved_mte_tcf;

CPU 선호값과 최종 후보 mode를 보관할 변수를 준비합니다.

	pref = __this_cpu_read(mte_tcf_preferred);

현재 CPU에 설정된 MTE 검사 mode 선호값을 읽습니다. 그래서 전환 도중 다른 CPU로 이동하면 안 됩니다.

	resolved_mte_tcf = (mte_ctrl & pref) ? pref : mte_ctrl;

요청한 mode와 CPU 선호가 겹치면 선호를 사용하고, 겹치지 않으면 프로그램 요청을 따릅니다.

	sctlr &= ~(SCTLR_EL1_TCF0_MASK | SCTLR_EL1_TCSO0_MASK);

기존 TCF0와 TCSO0 비트를 지워 새 선택을 넣을 자리를 만듭니다. 다른 SCTLR 비트는 유지합니다.

	if (resolved_mte_tcf & MTE_CTRL_TCF_ASYMM)

최종 후보가 비대칭 mode를 허용하는지 먼저 검사합니다. 검사 순서가 기본 우선순위가 됩니다.

		sctlr |= SYS_FIELD_PREP_ENUM(SCTLR_EL1, TCF0, ASYMM);

TCF0 필드에 ASYMM 인코딩을 넣습니다. SYS_FIELD_PREP_ENUM은 필드와 열거값의 비트를 맞추는 매크로입니다.

	else if (resolved_mte_tcf & MTE_CTRL_TCF_ASYNC)

ASYMM을 선택하지 않았다면 비동기 mode 허용 여부를 검사합니다.

		sctlr |= SYS_FIELD_PREP_ENUM(SCTLR_EL1, TCF0, ASYNC);

비동기 태그 검사 보고 mode를 SCTLR 값에 넣습니다.

	else if (resolved_mte_tcf & MTE_CTRL_TCF_SYNC)

앞 두 mode를 선택하지 않았다면 동기 mode를 확인합니다.

		sctlr |= SYS_FIELD_PREP_ENUM(SCTLR_EL1, TCF0, SYNC);

동기 mode의 TCF0 인코딩을 넣습니다. 어느 조건도 맞지 않으면 앞에서 지운 TCF0가 유지됩니다.

	if (mte_ctrl & MTE_CTRL_STORE_ONLY)

저장 접근만 검사하도록 요청한 정책 비트를 확인합니다.

		sctlr |= SYS_FIELD_PREP(SCTLR_EL1, TCSO0, 1);

해당하면 TCSO0 필드를 1로 설정합니다. 앞의 보고 시점 선택과 별도 정책입니다.

	task->thread.sctlr_user = sctlr;

계산한 결과를 task의 소프트웨어 상태에 저장합니다. 여기서 SCTLR_EL1 하드웨어에 직접 쓰지 않으며 호출자가 후속 갱신을 맡습니다.

함께 생각해 볼 질문

MTE를 지원하면 모든 프로그램에서 자동 사용합니까?

프로그램·매핑·커널 정책에 따른 활성화 조건이 필요합니다.

이 함수가 write_sysreg를 호출합니까?

아닙니다. task의 sctlr_user 값을 계산해 남깁니다.

태그가 맞으면 모든 메모리 오류가 없어집니까?

아닙니다. 태그 검사 범위와 충돌 가능성 등 한계가 있습니다.

출처와 읽은 범위

Linux stable v6.18.37 · arch/arm64/kernel/mte.c

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

맨 위로 ↑