blob: d3dafbd51bc5d1d1e10e2163eca97b5729bd3ccd [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fuchsia_zircon as zx;
use crate::arch::registers::RegisterState;
use crate::mm::vmo::round_up_to_increment;
use crate::signals::{SignalInfo, SignalState};
use crate::task::{CurrentTask, Task};
use extended_pstate::riscv64::{RiscvVectorCsrs, NUM_V_REGISTERS, VLEN};
use extended_pstate::ExtendedPstateState;
use starnix_logging::log_debug;
use starnix_uapi::errors::{Errno, ErrnoCode, ERESTART_RESTARTBLOCK};
use starnix_uapi::user_address::UserAddress;
use starnix_uapi::{self as uapi, error, sigaction, sigaltstack, sigcontext, siginfo_t, ucontext};
/// The size of the red zone.
pub const RED_ZONE_SIZE: u64 = 0;
/// The size of the syscall instruction in bytes. `ECALL` is not compressed, i.e. it always takes 4
/// bytes.
pub const SYSCALL_INSTRUCTION_SIZE_BYTES: u64 = 4;
/// The size, in bytes, of the signal stack frame.
pub const SIG_STACK_SIZE: usize = std::mem::size_of::<SignalStackFrame>();
/// A `SignalStackFrame` contains all the state that is stored on the stack prior to executing a
/// signal handler. The exact layout of this structure is part of the platform's ABI.
#[repr(C)]
pub struct SignalStackFrame {
pub siginfo_bytes: [u8; std::mem::size_of::<siginfo_t>()],
pub context: ucontext,
// On RISC-V, state for extensions not included in `ucontext` can be stored immediately after
// `ucontext`. Each extension starts with a header defined by `__riscv_ctx_hdr`. The header for
// the first extensions is placed at the tail of `ucontext`. The end of these extension is
// marked by the header containing zeros (see `end_header` below).
// Currently only V extension is supported.
v_state: VState,
// Should be set to `{END_MAGIC, END_HDR_SIZE}`
end_header: uapi::__riscv_ctx_hdr,
}
pub const V_REGISTERS_SIZE: usize = VLEN / 8 * NUM_V_REGISTERS;
#[repr(C)]
struct VState {
v_csrs: uapi::__riscv_v_ext_state,
v_registers: [u8; V_REGISTERS_SIZE],
}
impl SignalStackFrame {
pub fn new(
task: &Task,
registers: &mut RegisterState,
extended_pstate: &ExtendedPstateState,
signal_state: &SignalState,
siginfo: &SignalInfo,
_action: sigaction,
stack_pointer: UserAddress,
) -> SignalStackFrame {
let context = ucontext {
uc_flags: 0,
uc_link: Default::default(),
uc_stack: signal_state
.alt_stack
.map(|stack| sigaltstack {
ss_sp: stack.ss_sp.into(),
ss_flags: stack.ss_flags as i32,
ss_size: stack.ss_size as u64,
..Default::default()
})
.unwrap_or_default(),
uc_sigmask: signal_state.mask().into(),
uc_mcontext: sigcontext {
sc_regs: registers.to_user_regs_struct(),
__bindgen_anon_1: uapi::sigcontext__bindgen_ty_1 {
sc_fpregs: extended_pstate_to_riscv_fpregs(extended_pstate),
},
},
..Default::default()
};
let vdso_sigreturn_offset = task.kernel().vdso.sigreturn_offset;
let sigreturn_addr = task.mm().state.read().vdso_base.ptr() as u64 + vdso_sigreturn_offset;
registers.ra = sigreturn_addr;
let v_registers_addr = stack_pointer.ptr()
+ memoffset::offset_of!(SignalStackFrame, v_state)
+ memoffset::offset_of!(VState, v_registers);
let state = extended_pstate.get_riscv64_state();
let v_state = VState {
v_csrs: uapi::__riscv_v_ext_state {
vstart: state.vcsrs.vstart,
vl: state.vcsrs.vl,
vtype: state.vcsrs.vtype,
vcsr: state.vcsrs.vcsr,
vlenb: VLEN as u64 / 8,
datap: uapi::uaddr { addr: v_registers_addr as u64 },
},
v_registers: unsafe { std::mem::transmute(state.v_registers) },
};
let mut sigstack = SignalStackFrame {
siginfo_bytes: siginfo.as_siginfo_bytes(),
context,
v_state,
end_header: uapi::__riscv_ctx_hdr { magic: uapi::END_MAGIC, size: uapi::END_HDR_SIZE },
};
// Setup headers for `VState`. It follows immediately after `context`. Size of this
// section also includes size of the header.
sigstack.context.uc_mcontext.__bindgen_anon_1.sc_extdesc.hdr = uapi::__riscv_ctx_hdr {
magic: uapi::RISCV_V_MAGIC,
size: (std::mem::size_of::<VState>() + std::mem::size_of::<uapi::__riscv_ctx_hdr>())
as u32,
};
sigstack
}
pub fn as_bytes(&self) -> &[u8; SIG_STACK_SIZE] {
unsafe { std::mem::transmute(self) }
}
pub fn from_bytes(bytes: [u8; SIG_STACK_SIZE]) -> SignalStackFrame {
unsafe { std::mem::transmute(bytes) }
}
}
pub fn restore_registers(
current_task: &mut CurrentTask,
signal_stack_frame: &SignalStackFrame,
stack_pointer: UserAddress,
) -> Result<(), Errno> {
let regs = &signal_stack_frame.context.uc_mcontext.sc_regs;
// Restore the register state from before executing the signal handler.
current_task.thread_state.registers = zx::sys::zx_thread_state_general_regs_t {
pc: regs.pc,
ra: regs.ra,
sp: regs.sp,
gp: regs.gp,
tp: regs.tp,
t0: regs.t0,
t1: regs.t1,
t2: regs.t2,
s0: regs.s0,
s1: regs.s1,
a0: regs.a0,
a1: regs.a1,
a2: regs.a2,
a3: regs.a3,
a4: regs.a4,
a5: regs.a5,
a6: regs.a6,
a7: regs.a7,
s2: regs.s2,
s3: regs.s3,
s4: regs.s4,
s5: regs.s5,
s6: regs.s6,
s7: regs.s7,
s8: regs.s8,
s9: regs.s9,
s10: regs.s10,
s11: regs.s11,
t3: regs.t3,
t4: regs.t4,
t5: regs.t5,
t6: regs.t6,
}
.into();
let v_registers_addr = stack_pointer.ptr()
+ memoffset::offset_of!(SignalStackFrame, v_state)
+ memoffset::offset_of!(VState, v_registers);
let hdr = unsafe { &signal_stack_frame.context.uc_mcontext.__bindgen_anon_1.sc_extdesc.hdr };
let vcsrs = &signal_stack_frame.v_state.v_csrs;
if hdr.magic != uapi::RISCV_V_MAGIC
|| hdr.size as usize
!= std::mem::size_of::<VState>() + std::mem::size_of::<uapi::__riscv_ctx_hdr>()
|| vcsrs.vlenb != (VLEN as u64 / 8)
|| vcsrs.datap.addr != v_registers_addr as u64
{
log_debug!("Invalid V state found in signal stack frame.");
return error!(EINVAL);
}
let d_state = unsafe { &signal_stack_frame.context.uc_mcontext.__bindgen_anon_1.sc_fpregs.d };
let state = current_task.thread_state.extended_pstate.get_riscv64_state_mut();
state.fp_registers = d_state.f;
state.fcsr = d_state.fcsr;
state.vcsrs = RiscvVectorCsrs {
vstart: vcsrs.vstart,
vl: vcsrs.vl,
vtype: vcsrs.vtype,
vcsr: vcsrs.vcsr,
};
unsafe {
state.v_registers = std::mem::transmute(signal_stack_frame.v_state.v_registers);
};
Ok(())
}
pub fn align_stack_pointer(pointer: u64) -> u64 {
round_up_to_increment(pointer as usize, 16).expect("Failed to round up stack pointer") as u64
}
pub fn update_register_state_for_restart(registers: &mut RegisterState, err: ErrnoCode) {
if err == ERESTART_RESTARTBLOCK {
// Update the register containing the syscall number to reference `restart_syscall`.
registers.a7 = uapi::__NR_restart_syscall as u64;
}
// Reset the a0 register value to what it was when the original syscall trap occurred. This
// needs to be done because a0 may have been overwritten in the syscall dispatch loop.
registers.a0 = registers.orig_a0;
}
// Generates `__riscv_fp_state` struct from ExtendedPstateState.
fn extended_pstate_to_riscv_fpregs(
extended_pstate: &ExtendedPstateState,
) -> starnix_uapi::__riscv_fp_state {
let state = extended_pstate.get_riscv64_state();
let d_state = starnix_uapi::__riscv_d_ext_state {
f: state.fp_registers,
fcsr: state.fcsr,
__bindgen_padding_0: [0u8; 4],
};
unsafe {
let mut r: starnix_uapi::__riscv_fp_state = std::mem::zeroed();
r.d = d_state;
r
}
}
#[cfg(test)]
mod tests {
use super::*;
#[::fuchsia::test]
fn sigstack_layout() {
// There should be no padding between `context`, `v_state` and `end_header` in `SignalStackFrame`.
assert_eq!(
memoffset::offset_of!(SignalStackFrame, context) + std::mem::size_of::<ucontext>(),
memoffset::offset_of!(SignalStackFrame, v_state)
);
assert_eq!(
memoffset::offset_of!(SignalStackFrame, v_state) + std::mem::size_of::<VState>(),
memoffset::offset_of!(SignalStackFrame, end_header)
);
}
}