Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arm fault handler: add emulation of MRS instruction #2088

Merged
merged 3 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/aarch64/crt0.S
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ context_suspend:
ldr x0, [x18]
b context_suspend_finish

.globl sysreg_get_id_aa64zfr0
sysreg_get_id_aa64zfr0:
.long (INSN_MRS(0) | SYSREG_ID_AA64ZFR0_EL1)
ret

.globl arm_hvc
arm_hvc:
// incomplete, just enough to issue power off
Expand Down
27 changes: 27 additions & 0 deletions src/aarch64/kernel_machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,33 @@ void clone_frame_pstate(context_frame dest, context_frame src)
runtime_memcpy(dest, src, sizeof(u64) * FRAME_N_PSTATE);
}

boolean insn_emulate(context_frame f)
{
#define CASE_SYSREG(id) case SYSREG_##id: val = read_psr(id); break

u32 *insn_ptr = (u32 *)frame_fault_pc(f);
u32 insn = *insn_ptr;
if ((insn & 0xfff80000) == 0xd5380000) {
/* read from non-debug system registers and special-purpose registers (op0 = 3) */
u64 val;
switch (insn & 0x001fffe0) {
CASE_SYSREG(ID_AA64PFR0_EL1);
CASE_SYSREG(ID_AA64ISAR0_EL1);
CASE_SYSREG(ID_AA64ISAR1_EL1);
case SYSREG_ID_AA64ZFR0_EL1:
val = sysreg_get_id_aa64zfr0();
break;
default:
return false;
}
u64 *dest = &f[FRAME_X0] + (insn & 0x0000001f); /* destination register */
*dest = val;
frame_set_insn_ptr(f, u64_from_pointer(insn_ptr + 1)); /* go to next instruction */
return true;
}
return false;
}

void interrupt_exit(void)
{
gic_eoi(gic_dispatch_int());
Expand Down
22 changes: 21 additions & 1 deletion src/aarch64/kernel_machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@

#define VIRTUAL_ADDRESS_BITS 48

#define INSN_MRS(Rt) (0xd5300000 | Rt)

#define SYSREG(op0, op1, CRn, CRm, op2) \
(((op0) << 19) | ((op1) << 16) | ((CRn) << 12) | ((CRm) << 8) | ((op2) << 5))

#define SYSREG_ID_AA64PFR0_EL1 SYSREG(3, 0, 0, 4, 0)
#define SYSREG_ID_AA64ZFR0_EL1 SYSREG(3, 0, 0, 4, 4)
#define SYSREG_ID_AA64ISAR0_EL1 SYSREG(3, 0, 0, 6, 0)
#define SYSREG_ID_AA64ISAR1_EL1 SYSREG(3, 0, 0, 6, 1)

#define CNTV_CTL_EL0_ISTATUS 4
#define CNTV_CTL_EL0_MASK 2
#define CNTV_CTL_EL0_ENABLE 1
Expand All @@ -38,6 +48,7 @@
#define ESR_EC_UNKNOWN 0x00
#define ESR_EC_ILL_EXEC 0x0e
#define ESR_EC_SVC_AARCH64 0x15
#define ESR_EC_MSR_MRS 0x18
#define ESR_EC_INST_ABRT_LEL 0x20
#define ESR_EC_INST_ABRT 0x21
#define ESR_EC_PC_ALIGN_FAULT 0x22
Expand Down Expand Up @@ -239,6 +250,10 @@ MK_MMIO_WRITE(64, "", "x");
#define read_psr_s(rstr) ({ register u64 r; asm volatile("mrs %0, " rstr : "=r"(r)); r;})
#define write_psr_s(rstr, v) do { asm volatile("msr " rstr ", %0" : : "r"((u64)(v))); } while (0)

/* Manually encoded system register access instructions for registers that are not supported with
* the processor features enabled in the `-march` compiler flags. */
u64 sysreg_get_id_aa64zfr0(void);

struct cpuinfo_machine {
/*** Fields accessed by low-level entry points. ***/
/* Don't move these without updating x18-relative accesses in crt0.s ***/
Expand Down Expand Up @@ -272,6 +287,8 @@ static inline cpuinfo current_cpu(void)
extern void clone_frame_pstate(context_frame dest, context_frame src);
extern void init_extended_frame(context_frame f);

boolean insn_emulate(context_frame f);

static inline boolean is_pte_error(context_frame f)
{
// arm equivalent?
Expand Down Expand Up @@ -367,8 +384,11 @@ static inline boolean is_illegal_instruction(context_frame f)
{
u64 esr = esr_from_frame(f);
u32 ec = field_from_u64(esr, ESR_EC);
if (ec == ESR_EC_UNKNOWN)
switch (ec) {
case ESR_EC_UNKNOWN:
case ESR_EC_MSR_MRS:
return true;
}
return false;
}

Expand Down
2 changes: 2 additions & 0 deletions src/riscv64/kernel_machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ static inline cpuinfo current_cpu(void)

extern void clone_frame_pstate(context_frame dest, context_frame src);

#define insn_emulate(f) false

static inline boolean is_pte_error(context_frame f)
{
// riscv equivalent?
Expand Down
32 changes: 17 additions & 15 deletions src/unix/unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,13 @@ closure_func_basic(fault_handler, context, unix_fault_handler,
context ctx)
{
sstring errmsg = sstring_empty();
u64 fault_pc = frame_fault_pc(ctx->frame);
context_frame f = ctx->frame;
u64 fault_pc = frame_fault_pc(f);
boolean user = (current_cpu()->state == cpu_user);
process p = struct_from_field(closure_self(), process, fault_handler);
thread t = user ? (thread)ctx : 0;

if (is_div_by_zero(ctx->frame)) {
if (is_div_by_zero(f)) {
if (user) {
deliver_fault_signal(SIGFPE, t, fault_pc, FPE_INTDIV);
schedule_thread(t);
Expand All @@ -265,30 +266,31 @@ closure_func_basic(fault_handler, context, unix_fault_handler,
errmsg = ss("Divide by zero occurs in kernel mode");
goto bug;
}
} else if (is_illegal_instruction(ctx->frame)) {
} else if (is_illegal_instruction(f)) {
if (user) {
pf_debug("invalid opcode fault in user mode, rip 0x%lx", fault_pc);
deliver_fault_signal(SIGILL, t, fault_pc, ILL_ILLOPC);
if (!insn_emulate(f))
deliver_fault_signal(SIGILL, t, fault_pc, ILL_ILLOPC);
schedule_thread(t);
return 0;
} else {
errmsg = ss("Illegal instruction in kernel mode");
goto bug;
}
} else if (is_trap(ctx->frame)) {
} else if (is_trap(f)) {
if (user) {
pf_debug("trap in user mode, rip 0x%lx", fault_pc);
if (!ltrace_handle_trap(ctx->frame))
if (!ltrace_handle_trap(f))
deliver_fault_signal(SIGTRAP, t, fault_pc,
is_breakpoint(ctx->frame) ? TRAP_BRKPT : TRAP_TRACE);
is_breakpoint(f) ? TRAP_BRKPT : TRAP_TRACE);
schedule_thread(t);
return 0;
} else {
errmsg = ss("Breakpoint in kernel mode");
goto bug;
}
} else if (is_page_fault(ctx->frame)) {
u64 vaddr = frame_fault_address(ctx->frame);
} else if (is_page_fault(f)) {
u64 vaddr = frame_fault_address(f);
vmap_lock(p);
vmap vm;
if (vaddr >= MIN(p->mmap_min_addr, PAGESIZE) && vaddr < USER_LIMIT)
Expand All @@ -313,21 +315,21 @@ closure_func_basic(fault_handler, context, unix_fault_handler,
return 0;
}

if (is_pte_error(ctx->frame)) {
if (is_pte_error(f)) {
vmap_unlock(p);
/* no SEGV on reserved PTEs */
errmsg = ss("bug: pte entries reserved or corrupt");
dump_page_tables(vaddr, 8);
goto bug;
}

if (is_instruction_fault(ctx->frame) && !user) {
if (is_instruction_fault(f) && !user) {
vmap_unlock(p);
msg_err("%s: kernel instruction fault", func_ss);
goto bug;
}

if (is_protection_fault(ctx->frame)) {
if (is_protection_fault(f)) {
if (handle_protection_fault(ctx, vaddr, vm)) {
vmap_unlock(p);
if (!is_thread_context(ctx))
Expand All @@ -354,7 +356,7 @@ closure_func_basic(fault_handler, context, unix_fault_handler,
}
/* XXX arch dep */
#ifdef __x86_64__
else if (ctx->frame[FRAME_VECTOR] == 13) {
else if (f[FRAME_VECTOR] == 13) {
if (user) {
pf_debug("general protection fault in user mode, rip 0x%lx", fault_pc);
deliver_fault_signal(SIGSEGV, t, 0, SI_KERNEL);
Expand All @@ -367,7 +369,7 @@ closure_func_basic(fault_handler, context, unix_fault_handler,
error:
if (context_err_is_set(ctx)) {
kernel_context kc = (kernel_context)ctx;
err_frame_apply(kc->err_frame, ctx->frame);
err_frame_apply(kc->err_frame, f);
context_clear_err(ctx);
return ctx;
}
Expand All @@ -377,7 +379,7 @@ closure_func_basic(fault_handler, context, unix_fault_handler,
rprintf("\n%s\n", errmsg);
rprintf("cpu: %d, context type: %d\n", current_cpu()->id, ctx->type);
dump_context(ctx);
ctx->frame[FRAME_FULL] = false;
f[FRAME_FULL] = false;

if (get(p->process_root, sym(fault))) {
rputs("TODO: in-kernel gdb needs revisiting\n");
Expand Down
2 changes: 2 additions & 0 deletions src/x86_64/kernel_machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ static inline void frame_disable_interrupts(context_frame f)
extern void xsave(context_frame f);
extern void clone_frame_pstate(context_frame dest, context_frame src);

#define insn_emulate(f) false

static inline boolean is_protection_fault(context_frame f)
{
return (f[FRAME_ERROR_CODE] & FRAME_ERROR_PF_P) != 0;
Expand Down