diff --git a/kernel/src/arch/aarch64/cpu.rs b/kernel/src/arch/aarch64/cpu.rs index b87c5044638..37738467f76 100644 --- a/kernel/src/arch/aarch64/cpu.rs +++ b/kernel/src/arch/aarch64/cpu.rs @@ -113,14 +113,16 @@ pub fn halt() -> ! { /// a 16-byte-aligned stack this CPU owns. pub unsafe fn run_on_stack(top: u64, func: extern "C" fn() -> !) -> ! { // SAFETY: the caller's contract. + // Every operand names its register: LLVM may give `x30` to a `reg` + // operand, and zeroing it would then branch to 0. unsafe { asm!( - "mov sp, {sp}", + "mov sp, x0", "mov x29, xzr", "mov x30, xzr", - "br {func}", - sp = in(reg) top, - func = in(reg) func as *const () as usize, + "br x16", + in("x0") top, + in("x16") func as *const () as usize, options(noreturn), ); } diff --git a/kernel/src/arch/x86_64/cpu.rs b/kernel/src/arch/x86_64/cpu.rs index eebe1400173..699d8c5e7d7 100644 --- a/kernel/src/arch/x86_64/cpu.rs +++ b/kernel/src/arch/x86_64/cpu.rs @@ -152,27 +152,15 @@ pub fn leaf_7() -> (u32, u32, u32, u32) { if cpuid(0, 0).0 >= 7 { cpuid(7, 0) } else { (0, 0, 0, 0) } } -/// CPUID with both index registers; `rbx` is saved by hand since Rust reserves it as an operand. +/// CPUID with both index registers, as `(eax, ebx, ecx, edx)`. +/// +/// `core`'s intrinsic and not an `asm!` of our own: `rbx` is CPUID's output +/// and LLVM's reserved register, yet LLVM may still allocate it to a `reg` +/// operand, so a hand-written save of `rbx` around a compiler-chosen +/// register can lose the output. pub fn cpuid(leaf: u32, subleaf: u32) -> (u32, u32, u32, u32) { - let eax: u32; - let ebx: u32; - let ecx: u32; - let edx: u32; - // SAFETY: cpuid can't fault; push/pop rbx is balanced, and every caller queries leaf 0 first, so an unsupported leaf isn't misread as data. - unsafe { - asm!( - "push rbx", - "cpuid", - "mov {ebx:e}, ebx", - "pop rbx", - ebx = out(reg) ebx, - inout("eax") leaf => eax, - inout("ecx") subleaf => ecx, - out("edx") edx, - options(nomem), - ); - } - (eax, ebx, ecx, edx) + let r = core::arch::x86_64::__cpuid_count(leaf, subleaf); + (r.eax, r.ebx, r.ecx, r.edx) } #[inline] diff --git a/kernel/src/arch/x86_64/vtd/table.rs b/kernel/src/arch/x86_64/vtd/table.rs index a8e923db8d5..0867e4b40fb 100644 --- a/kernel/src/arch/x86_64/vtd/table.rs +++ b/kernel/src/arch/x86_64/vtd/table.rs @@ -126,13 +126,15 @@ impl Table { // SAFETY: `entry` is 16 bytes of a table `Tables::alloc` never frees, // 16-byte aligned since a table is 4 KiB-aligned; `rbx`, which LLVM // reserves, holds `new`'s low half only between the two moves. + // Every operand names its register: LLVM may give `rbx` to a `reg` + // operand, and the `xchg` would then swap the address out of it. unsafe { core::arch::asm!( - "xchg {lo}, rbx", - "lock cmpxchg16b xmmword ptr [{entry}]", - "mov rbx, {lo}", - entry = in(reg) entry.addr(), - lo = inout(reg) new.0 => _, + "xchg r8, rbx", + "lock cmpxchg16b xmmword ptr [rsi]", + "mov rbx, r8", + in("rsi") entry.addr(), + inout("r8") new.0 => _, in("rcx") new.1, inout("rax") old.0 => found_lo, inout("rdx") old.1 => found_hi,