Skip to content

0.9.0 —— armv7a 后端,以及它回答的那个宽度问题 - #9

Merged
Sunrisepeak merged 1 commit into
mainfrom
armv7a-backend
Sep 4, 2026
Merged

0.9.0 —— armv7a 后端,以及它回答的那个宽度问题#9
Sunrisepeak merged 1 commit into
mainfrom
armv7a-backend

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

这个后端是为了回答一个问题而存在的:32 位页表项能不能通过一个把返回值定为 arch_u64 的接口表达。

答案是「值可以,存储不行」。ARMv7-A 短描述符是 32 位;pte_encode.h 原文写着「a page-table entry is 64 bits on every machine here」。用 sizeof(arch::pte) 给页表定尺寸的内核会建出两倍大的表,硬件把每隔一个字读成条目 —— 没有任何东西会诊断它:表是良构的,条目是对的,机器读的是空隙。

新增 arch_pte_entry_bytes(),五个后端各自实现(riscv64/aarch64/x86_64 为 8,armv7a 为 4,Cortex-M 为 0)。

Cortex-M 没能settle 这个问题:它确实是 32 位,但有 MPU 没有页表,pte 组是「存在并拒绝」。32 位而不分页的机器把问题留在原地。

后端四组齐全。openarch:preemption 被扣住——ARMv7-A 的恢复地址在 SVC 栈上而不在寄存器里,trap 中途换栈会改变 rfeia 弹出的帧,只有当被恢复的上下文也经同一路径挂起时才良定义。这是个真实设计,不是本后端测过的,所以在解析期按名字拒绝,与 Cortex-M 扣住 openarch:address-space 同一机制。

判据:tests/pte_encoding 加了照架构手册手写的位模式与「整条目落在低 32 位」;新 CI job 在 qemu -M virt -cpu cortex-a15 上启动程序,通过 ABI 读回宽度,只有为 4 才退 0(提交前实测:armv7a pte width 4, entry fits 32 bits,退 0);再加一条「一行写法够不够得到后端」——那正是 0.8.0 缺的那条。

THE FIRST MACHINE HERE WHOSE PAGE-TABLE ENTRY IS NOT EIGHT BYTES.

openarch was designed on three application-class 64-bit machines, and its page
table interface carries an entry in an `arch_u64` while saying nothing about how
one is STORED. `pte_encode.h` recorded the assumption as a statement of fact:
"a page-table entry is 64 bits on every machine here".

ARMv7-A's short-descriptor entry is 32 bits. The VALUE fits, so the carrier did
not have to change; what was missing is a way to ASK. A kernel sizing a table
from `sizeof(arch::pte)` builds one twice as large as the hardware walks, and
the walker then reads every second word as an entry. Nothing diagnoses that —
the table is well formed, the entries are correct, and the machine reads the
gaps.

`arch_pte_entry_bytes()` is the answer, and every backend implements it: 8 on
riscv64, aarch64 and x86_64, 4 on armv7a, 0 on Cortex-M, where the refusal is
already carried by `provides` withholding `openarch:address-space`.

CORTEX-M COULD NOT SETTLE THIS, AND IT IS WORTH SAYING WHY. It is a 32-bit
machine, which is what the plan expected to surface the assumption — but it has
an MPU and no page table, so its pte group exists and refuses. A 32-bit machine
WITHOUT paging leaves the question exactly where it was. The machine that
settles it is 32-bit AND paged.

## The backend

All four groups: cpu (TPIDRPRW/TPIDRURW, which unlike riscv's `tp` do not
compete), pte (short-descriptor small pages), context (r4-r11, SP, LR — ten
words, the smallest saved context here), trap (an eight-entry vector table of
one instruction each).

`srsdb`/`rfeia` RATHER THAN FOUR MODE STACKS. Each ARMv7-A exception is taken in
its own processor mode with its own banked SP, so the obvious implementation
requires a board to allocate and install four stacks before the first exception
can be reported — four more things to get wrong in the code that runs before
anything can report a fault. `srsdb` writes the return state onto the SVC stack
from whichever mode is current, so the kernel's one stack serves every
exception.

THE RETURN OFFSET IS WRITTEN OUT PER EXCEPTION rather than folded into a macro
parameter: 4 for IRQ, FIQ and prefetch abort, 8 for data abort, 0 for SVC and
undefined. One wrong value resumes into the middle of an instruction.

`openarch:preemption` IS WITHHELD. `arch_trap_switch` asks a trap to resume a
different context; here the resumption address is on the SVC stack rather than
in a register, so switching stacks mid-trap changes which frame `rfeia` pops and
is well defined only if the resumed context was suspended through the same path.
That is a real design and not one this backend has measured, so a consumer that
needs it is refused by name at resolution. The Cortex-M backend already
withholds `openarch:address-space` by the same mechanism.

## What is asserted

`tests/pte_encoding` gains the armv7a bit patterns, read off the architecture
manual rather than produced by the code under test, and the width property
itself: the whole entry must lie in the low 32 bits.

A new CI job boots a program on qemu `-M virt -cpu cortex-a15` that reads the
width back THROUGH THE ABI and exits 0 only if it is 4 — the half a host test
cannot reach. Measured before commit: `armv7a pte width 4, entry fits 32 bits`,
exit 0.

A second step asks whether the one-line usage REACHES the backend, for both
armv7a triples. That is the check 0.8.0 shipped without: it carried a Cortex-M
backend `backend-auto` never bound, and everything was green because nothing
asked.

The job's two fixtures are base64 rather than heredocs. A heredoc inside a YAML
block scalar needs its terminator at column zero, which ends the block, and
indenting it stops bash recognising it — both spellings fail, and the second
fails at run time.
@Sunrisepeak
Sunrisepeak merged commit e3711c6 into main Sep 4, 2026
10 checks passed
@Sunrisepeak
Sunrisepeak deleted the armv7a-backend branch September 4, 2026 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants