# ARM MTE와 Linux fault 처리 소스 분석

v6.18.37 / arch/arm64/kernel/mte.c

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

## mte_update_sctlr_user

```c

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;
}

```

### 203행

```c

static void mte_update_sctlr_user(struct task_struct *task)

```

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

### 211행

```c

	unsigned long sctlr = task->thread.sctlr_user;

```

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

### 212행

```c

	unsigned long mte_ctrl = task->thread.mte_ctrl;

```

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

### 213행

```c

	unsigned long pref, resolved_mte_tcf;

```

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

### 215행

```c

	pref = __this_cpu_read(mte_tcf_preferred);

```

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

### 220행

```c

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

```

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

### 221행

```c

	sctlr &= ~(SCTLR_EL1_TCF0_MASK | SCTLR_EL1_TCSO0_MASK);

```

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

### 227행

```c

	if (resolved_mte_tcf & MTE_CTRL_TCF_ASYMM)

```

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

### 228행

```c

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

```

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

### 229행

```c

	else if (resolved_mte_tcf & MTE_CTRL_TCF_ASYNC)

```

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

### 230행

```c

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

```

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

### 231행

```c

	else if (resolved_mte_tcf & MTE_CTRL_TCF_SYNC)

```

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

### 232행

```c

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

```

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

### 234행

```c

	if (mte_ctrl & MTE_CTRL_STORE_ONLY)

```

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

### 235행

```c

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

```

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

### 237행

```c

	task->thread.sctlr_user = sctlr;

```

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

