diff --git a/src/core/arm/nce/arm_nce_cpp_impl.cpp b/src/core/arm/nce/arm_nce_cpp_impl.cpp index aa9b260d13..92421a5e0d 100644 --- a/src/core/arm/nce/arm_nce_cpp_impl.cpp +++ b/src/core/arm/nce/arm_nce_cpp_impl.cpp @@ -7,47 +7,48 @@ namespace Core { HaltReason ArmNce::ReturnToRunCodeByTrampoline(void* tpidr, GuestContext* ctx, uint64_t trampoline_addr) { - // This function needs to be implemented with inline assembly because it - // involves saving/restoring registers and modifying the stack pointer + // This function needs to be implemented with inline assembly because it + // involves saving/restoring registers and modifying the stack pointer + HaltReason result; asm volatile( // Back up host sp and tpidr_el0 - "mov x3, sp\n" - "mrs x4, tpidr_el0\n" + "mov x3, sp\n" // Save current stack pointer + "mrs x4, tpidr_el0\n" // Save current thread-local storage pointer // Load guest sp - "ldr x5, [%1, %4]\n" - "mov sp, x5\n" + "ldr x5, [%1, %4]\n" // Load guest's stack pointer from GuestContext + "mov sp, x5\n" // Switch to guest's stack - // Offset GuestContext pointer to the host member - "add x5, %1, %5\n" + // Setup pointer to host context for saving registers + "add x5, %1, %5\n" // Calculate pointer to HostContext // Save original host sp and tpidr_el0 to host context - "stp x3, x4, [x5, %6]\n" + "stp x3, x4, [x5, %6]\n" // Store sp and tpidr_el0 pair - // Save all callee-saved host GPRs - "stp x19, x20, [x5, #0x0]\n" - "stp x21, x22, [x5, #0x10]\n" - "stp x23, x24, [x5, #0x20]\n" - "stp x25, x26, [x5, #0x30]\n" - "stp x27, x28, [x5, #0x40]\n" - "stp x29, x30, [x5, #0x50]\n" + // Save all callee-saved host GPRs (x19-x30) + "stp x19, x20, [x5, #0x0]\n" // host_saved_regs[0-1] + "stp x21, x22, [x5, #0x10]\n" // host_saved_regs[2-3] + "stp x23, x24, [x5, #0x20]\n" // host_saved_regs[4-5] + "stp x25, x26, [x5, #0x30]\n" // host_saved_regs[6-7] + "stp x27, x28, [x5, #0x40]\n" // host_saved_regs[8-9] + "stp x29, x30, [x5, #0x50]\n" // host_saved_regs[10-11] (fp and lr) - // Save all callee-saved host FPRs - "stp q8, q9, [x5, #0x0 + %7]\n" - "stp q10, q11, [x5, #0x20 + %7]\n" - "stp q12, q13, [x5, #0x40 + %7]\n" - "stp q14, q15, [x5, #0x60 + %7]\n" + // Save all callee-saved host FPRs (q8-q15) + "stp q8, q9, [x5, #0x0 + %7]\n" // host_saved_vregs[0-1] + "stp q10, q11, [x5, #0x20 + %7]\n" // host_saved_vregs[2-3] + "stp q12, q13, [x5, #0x40 + %7]\n" // host_saved_vregs[4-5] + "stp q14, q15, [x5, #0x60 + %7]\n" // host_saved_vregs[6-7] - // Load guest tpidr_el0 from argument - "msr tpidr_el0, %0\n" + // Setup guest context - switch thread-local storage pointer + "msr tpidr_el0, %0\n" // Set tpidr_el0 to the thread parameters - // Tail call the trampoline to restore guest state - "blr %2\n" + // Call the trampoline which will restore guest registers and return to guest code + "blr %2\n" // Jump to trampoline function - // Return value will be in x0, store it in result - "mov %3, x0\n" + // When control returns here, save the result + "mov %3, x0\n" // Save halt reason to result : "+r"(tpidr), "+r"(ctx), "+r"(trampoline_addr), "=r"(result) : "i"(GuestContextSp), "i"(GuestContextHostContext), @@ -59,20 +60,29 @@ HaltReason ArmNce::ReturnToRunCodeByTrampoline(void* tpidr, GuestContext* ctx, u } HaltReason ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr) { - // This function uses syscall to send a signal - register long x8 asm("x8") = __NR_tkill; - register long x0 asm("x0") = tid; - register long x1 asm("x1") = ReturnToRunCodeByExceptionLevelChangeSignal; - register void* x9 asm("x9") = tpidr; // Preserve tpidr + // This function sends a signal to the specified thread using the tkill syscall + // The thread-local storage pointer is preserved across the syscall - asm volatile( - "svc #0\n" - : "+r"(x0) - : "r"(x8), "r"(x1), "r"(x9) - : "memory", "cc" - ); + // The original assembly implementation was: + // register long x8 asm("x8") = __NR_tkill; + // register long x0 asm("x0") = tid; + // register long x1 asm("x1") = ReturnToRunCodeByExceptionLevelChangeSignal; + // register void* x9 asm("x9") = tpidr; // Preserve tpidr + // asm volatile("svc #0\n" : "+r"(x0) : "r"(x8), "r"(x1), "r"(x9) : "memory", "cc"); - // Should never reach here, but if it does, return BreakLoop + // We can achieve the same with a direct syscall in C++ + // Store the tpidr_el0 value so we can restore it after the syscall + uint64_t current_tpidr; + asm volatile("mrs %0, tpidr_el0" : "=r"(current_tpidr) :: "memory"); + + // Call the tkill syscall to send the signal + syscall(__NR_tkill, tid, ReturnToRunCodeByExceptionLevelChangeSignal); + + // Restore tpidr_el0 after the syscall + asm volatile("msr tpidr_el0, %0" :: "r"(current_tpidr) : "memory"); + + // Should never reach here (the signal handler should take over), + // but if it does, return BreakLoop return HaltReason::BreakLoop; } @@ -85,14 +95,14 @@ void ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* i auto* guest_ctx = static_cast(params->native_context); // Save the old value of tpidr_el0 - uint64_t old_tpidr; + uintptr_t old_tpidr; asm volatile("mrs %0, tpidr_el0" : "=r"(old_tpidr)); // Store it in the guest context guest_ctx->host_ctx.host_tpidr_el0 = reinterpret_cast(old_tpidr); // Set our new tpidr_el0 - asm volatile("msr tpidr_el0, %0" : : "r"(tpidr) : "memory"); + asm volatile("msr tpidr_el0, %0" : : "r"(tpidr)); // Unlock the context UnlockThreadParameters(tpidr); @@ -100,135 +110,72 @@ void ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* i // Returning from here will enter the guest } -void ArmNce::BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context) { - // Check if we have the correct TLS magic - uint64_t tpidr_value; - uint32_t magic_value; +void ArmNce::BreakFromRunCodeSignalHandler(int sig, void* raw_info, void* raw_context) { + // Extract the guest context from tpidr_el0 + uint64_t tpidr_el0; + asm volatile("mrs %0, tpidr_el0" : "=r"(tpidr_el0)); - asm volatile( - "mrs %0, tpidr_el0\n" - : "=r"(tpidr_value) - : - : "memory" - ); + auto* tpidr = reinterpret_cast(tpidr_el0); - auto* tpidr = reinterpret_cast(tpidr_value); - magic_value = tpidr->magic; - - if (magic_value != TlsMagic) { - // Incorrect TLS magic, so this is a spurious signal + // Check if the magic value is correct + if (tpidr->magic != TpidrEl0TlsMagic) { return; } - // Correct TLS magic, so this is a guest interrupt - // Restore host tpidr_el0 + // Save the guest context and unlock the thread auto* guest_ctx = static_cast(tpidr->native_context); - uint64_t host_tpidr = reinterpret_cast(guest_ctx->host_ctx.host_tpidr_el0); - - asm volatile( - "msr tpidr_el0, %0\n" - : - : "r"(host_tpidr) - : "memory" - ); - - // Save the guest context SaveGuestContext(guest_ctx, raw_context); + UnlockThreadParameters(tpidr); + + // Exit from running guest code by marking is_running as false + tpidr->is_running = false; } -void ArmNce::GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context) { - // Check if we have the correct TLS magic - uint64_t tpidr_value; - uint32_t magic_value; +void ArmNce::GuestAlignmentFaultSignalHandler(int sig, void* raw_info, void* raw_context) { + // Extract the guest context from tpidr_el0 + uint64_t tpidr_el0; + asm volatile("mrs %0, tpidr_el0" : "=r"(tpidr_el0)); - asm volatile( - "mrs %0, tpidr_el0\n" - : "=r"(tpidr_value) - : - : "memory" - ); + auto* tpidr = reinterpret_cast(tpidr_el0); - auto* tpidr = reinterpret_cast(tpidr_value); - magic_value = tpidr->magic; - - if (magic_value != TlsMagic) { - // Incorrect TLS magic, so this is a host fault - HandleHostAlignmentFault(sig, info, raw_context); + // Check if the magic value is correct and the context is locked + if (tpidr->magic != TpidrEl0TlsMagic || tpidr->lock.load(std::memory_order_relaxed) != SpinLockLocked) { + // Not our context, call original handler + HandleHostAlignmentFault(sig, raw_info, raw_context); return; } - // Correct TLS magic, so this is a guest fault auto* guest_ctx = static_cast(tpidr->native_context); - uint64_t guest_tpidr = tpidr_value; - uint64_t host_tpidr = reinterpret_cast(guest_ctx->host_ctx.host_tpidr_el0); - // Restore host tpidr_el0 - asm volatile( - "msr tpidr_el0, %0\n" - : - : "r"(host_tpidr) - : "memory" - ); - - // Call the handler - bool restore_guest = HandleGuestAlignmentFault(guest_ctx, info, raw_context); - - // If the handler returned true, restore guest tpidr_el0 - if (restore_guest) { - asm volatile( - "msr tpidr_el0, %0\n" - : - : "r"(guest_tpidr) - : "memory" - ); + // Call the handler and check if we should restore the guest context + if (HandleGuestAlignmentFault(guest_ctx, raw_info, raw_context)) { + // Restore the guest context + tpidr_el0 = reinterpret_cast(RestoreGuestContext(raw_context)); + asm volatile("msr tpidr_el0, %0" :: "r"(tpidr_el0)); } } -void ArmNce::GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context) { - // Check if we have the correct TLS magic - uint64_t tpidr_value; - uint32_t magic_value; +void ArmNce::GuestAccessFaultSignalHandler(int sig, void* raw_info, void* raw_context) { + // Extract the guest context from tpidr_el0 + uint64_t tpidr_el0; + asm volatile("mrs %0, tpidr_el0" : "=r"(tpidr_el0) :: "memory"); - asm volatile( - "mrs %0, tpidr_el0\n" - : "=r"(tpidr_value) - : - : "memory" - ); + auto* tpidr = reinterpret_cast(tpidr_el0); - auto* tpidr = reinterpret_cast(tpidr_value); - magic_value = tpidr->magic; - - if (magic_value != TlsMagic) { - // Incorrect TLS magic, so this is a host fault - HandleHostAccessFault(sig, info, raw_context); + // Check if the magic value is correct and the context is locked + if (tpidr->magic != TpidrEl0TlsMagic || tpidr->lock.load(std::memory_order_relaxed) != SpinLockLocked) { + // Not our context, call original handler + HandleHostAccessFault(sig, raw_info, raw_context); return; } - // Correct TLS magic, so this is a guest fault auto* guest_ctx = static_cast(tpidr->native_context); - uint64_t guest_tpidr = tpidr_value; - uint64_t host_tpidr = reinterpret_cast(guest_ctx->host_ctx.host_tpidr_el0); - // Restore host tpidr_el0 - asm volatile( - "msr tpidr_el0, %0\n" - : - : "r"(host_tpidr) - : "memory" - ); - - // Call the handler - bool restore_guest = HandleGuestAccessFault(guest_ctx, info, raw_context); - - // If the handler returned true, restore guest tpidr_el0 - if (restore_guest) { - asm volatile( - "msr tpidr_el0, %0\n" - : - : "r"(guest_tpidr) - : "memory" - ); + // Call the handler and check if we should restore the guest context + if (HandleGuestAccessFault(guest_ctx, raw_info, raw_context)) { + // Restore the guest context + tpidr_el0 = reinterpret_cast(RestoreGuestContext(raw_context)); + asm volatile("msr tpidr_el0, %0" :: "r"(tpidr_el0) : "memory"); } }