Skip to content

Commit 489aff6

Browse files
arch: Added x86-next crate
Signed-off-by: Ioan-Cristian CÎRSTEA <ioan.cirstea@oxidos.io>
1 parent bb5f85f commit 489aff6

51 files changed

Lines changed: 7073 additions & 1 deletion

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ members = [
1414
"arch/riscv",
1515
"arch/rv32i",
1616
"arch/x86",
17+
"arch/x86-next",
1718
"boards/nano_rp2040_connect",
1819
"boards/arty_e21",
1920
"boards/opentitan/earlgrey-cw310",
@@ -52,6 +53,7 @@ members = [
5253
"boards/nano33ble",
5354
"boards/nano33ble_rev2",
5455
"boards/qemu_i486_q35",
56+
"boards/qemu_i486_q35-next",
5557
"boards/qemu_rv32_virt",
5658
"boards/weact_f401ccu6/",
5759
"boards/configurations/nrf52840dk/nrf52840dk-test-appid-ecdsap256",
@@ -88,6 +90,7 @@ members = [
8890
"chips/nrf52840",
8991
"chips/nrf5x",
9092
"chips/x86_q35",
93+
"chips/x86_q35-next",
9194
"chips/qemu_rv32_virt_chip",
9295
"chips/psoc62xa",
9396
"chips/rp2040",

arch/x86-next/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Licensed under the Apache License, Version 2.0 or the MIT License.
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
# Copyright Tock Contributors 2024.
4+
5+
[package]
6+
name = "x86-next"
7+
version.workspace = true
8+
authors.workspace = true
9+
edition.workspace = true
10+
11+
[dependencies]
12+
kernel = { path = "../../kernel" }
13+
tock-cells = { path = "../../libraries/tock-cells" }
14+
tock-registers = { path = "../../libraries/tock-register-interface" }
15+
16+
[lints]
17+
workspace = true

arch/x86-next/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
x86 Architecture Support Components
2+
===================================
3+
4+
This crate includes low-level code for x86 32-bit CPU architectures.
5+
6+
This crate implements flat segmentation for memory management. The entire address space of 4Gb is contained in a single unbroken ("flat") memory segment.
7+
8+
Virtual memory is used as an MPU with a 4KB section granularity.
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// Licensed under the Apache License, Version 2.0 or the MIT License.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// Copyright Tock Contributors 2024.
4+
5+
use core::fmt::Write;
6+
7+
use crate::registers::bits32::eflags::{EFlags, EFLAGS};
8+
9+
use kernel::process::FunctionCall;
10+
use kernel::syscall::{ContextSwitchReason, Syscall, SyscallReturn, UserspaceKernelBoundary};
11+
use kernel::ErrorCode;
12+
13+
use crate::interrupts::{IDT_RESERVED_EXCEPTIONS, SYSCALL_VECTOR};
14+
use crate::segmentation::{USER_CODE, USER_DATA};
15+
16+
use super::UserContext;
17+
18+
/// Defines the usermode-kernelmode ABI for x86 platforms.
19+
pub struct Boundary;
20+
21+
impl Default for Boundary {
22+
fn default() -> Self {
23+
Self::new()
24+
}
25+
}
26+
27+
impl Boundary {
28+
/// Minimum required size for initial process memory.
29+
///
30+
/// Need at least 5 dwords of initial stack space for CRT 0:
31+
///
32+
/// - 1 dword for initial upcall return address (although this will be zero for init_fn)
33+
/// - 4 dwords of scratch space for invoking memop syscalls
34+
const MIN_APP_BRK: u32 = 5 * core::mem::size_of::<usize>() as u32;
35+
36+
/// Constructs a new instance of `SysCall`.
37+
pub fn new() -> Self {
38+
Self
39+
}
40+
}
41+
42+
impl UserspaceKernelBoundary for Boundary {
43+
type StoredState = UserContext;
44+
45+
fn initial_process_app_brk_size(&self) -> usize {
46+
Self::MIN_APP_BRK as usize
47+
}
48+
49+
unsafe fn initialize_process(
50+
&self,
51+
accessible_memory_start: *const u8,
52+
app_brk: *const u8,
53+
state: &mut Self::StoredState,
54+
) -> Result<(), ()> {
55+
if (app_brk as u32 - accessible_memory_start as u32) < Self::MIN_APP_BRK {
56+
return Err(());
57+
}
58+
59+
// We pre-allocate 16 bytes on the stack for initial upcall arguments.
60+
let esp = (app_brk as u32) - 16;
61+
62+
let mut eflags = EFlags::new();
63+
eflags.0.modify(EFLAGS::FLAGS_IF::SET);
64+
65+
state.eax = 0;
66+
state.ebx = 0;
67+
state.ecx = 0;
68+
state.edx = 0;
69+
state.esi = 0;
70+
state.edi = 0;
71+
state.ebp = 0;
72+
state.esp = esp;
73+
state.eip = 0;
74+
state.eflags = eflags.0.get();
75+
state.cs = USER_CODE.bits() as u32;
76+
state.ss = USER_DATA.bits() as u32;
77+
state.ds = USER_DATA.bits() as u32;
78+
state.es = USER_DATA.bits() as u32;
79+
state.fs = USER_DATA.bits() as u32;
80+
state.gs = USER_DATA.bits() as u32;
81+
82+
Ok(())
83+
}
84+
85+
unsafe fn set_syscall_return_value(
86+
&self,
87+
_accessible_memory_start: *const u8,
88+
_app_brk: *const u8,
89+
state: &mut Self::StoredState,
90+
return_value: SyscallReturn,
91+
) -> Result<(), ()> {
92+
kernel::utilities::arch_helpers::encode_syscall_return_trd104(
93+
&kernel::utilities::arch_helpers::TRD104SyscallReturn::from_syscall_return(
94+
return_value,
95+
),
96+
&mut state.ebx,
97+
&mut state.ecx,
98+
&mut state.edx,
99+
&mut state.edi,
100+
);
101+
102+
Ok(())
103+
}
104+
105+
unsafe fn set_process_function(
106+
&self,
107+
_accessible_memory_start: *const u8,
108+
_app_brk: *const u8,
109+
state: &mut Self::StoredState,
110+
upcall: FunctionCall,
111+
) -> Result<(), ()> {
112+
state.ebx = upcall.argument0 as u32;
113+
state.ecx = upcall.argument1 as u32;
114+
state.edx = upcall.argument2 as u32;
115+
state.edi = upcall.argument3.as_usize() as u32;
116+
117+
// The next time we switch to this process, we will directly jump to the upcall. When the
118+
// upcall issues `ret`, it will return to wherever the yield syscall was invoked.
119+
state.eip = upcall.pc.addr() as u32;
120+
121+
Ok(())
122+
}
123+
124+
unsafe fn switch_to_process(
125+
&self,
126+
_accessible_memory_start: *const u8,
127+
_app_brk: *const u8,
128+
state: &mut Self::StoredState,
129+
) -> (ContextSwitchReason, Option<*const u8>) {
130+
// Sanity check: don't try to run a faulted app
131+
if state.exception != 0 || state.err_code != 0 {
132+
let stack_ptr = state.esp as *mut u8;
133+
return (ContextSwitchReason::Fault, Some(stack_ptr));
134+
}
135+
136+
let mut err_code = 0;
137+
let int_num = unsafe { super::switch_to_user(state, &mut err_code) };
138+
139+
let reason = match int_num as u8 {
140+
0..IDT_RESERVED_EXCEPTIONS => {
141+
state.exception = int_num as u8;
142+
state.err_code = err_code;
143+
ContextSwitchReason::Fault
144+
}
145+
146+
SYSCALL_VECTOR => {
147+
let num = state.eax as u8;
148+
149+
let arg0 = state.ebx;
150+
let arg1 = state.ecx;
151+
let arg2 = state.edx;
152+
let arg3 = state.edi;
153+
154+
Syscall::from_register_arguments(
155+
num,
156+
arg0 as usize,
157+
(arg1 as usize).into(),
158+
(arg2 as usize).into(),
159+
(arg3 as usize).into(),
160+
)
161+
.map_or(ContextSwitchReason::Fault, |syscall| {
162+
ContextSwitchReason::SyscallFired { syscall }
163+
})
164+
}
165+
_ => ContextSwitchReason::Interrupted,
166+
};
167+
168+
let stack_ptr = state.esp as *const u8;
169+
170+
(reason, Some(stack_ptr))
171+
}
172+
173+
unsafe fn print_context(
174+
&self,
175+
_accessible_memory_start: *const u8,
176+
_app_brk: *const u8,
177+
state: &Self::StoredState,
178+
writer: &mut dyn Write,
179+
) {
180+
let _ = writeln!(writer, "{}", state);
181+
}
182+
183+
fn store_context(
184+
&self,
185+
_state: &Self::StoredState,
186+
_out: &mut [u8],
187+
) -> Result<usize, ErrorCode> {
188+
unimplemented!()
189+
}
190+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Licensed under the Apache License, Version 2.0 or the MIT License.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// Copyright Tock Contributors 2024.
4+
5+
use core::fmt::{self, Display, Formatter};
6+
7+
use crate::registers::irq::EXCEPTIONS;
8+
9+
/// Stored CPU state of a user-mode app
10+
///
11+
/// This struct stores the complete CPU state of a user-mode Tock application on x86.
12+
///
13+
/// We access this struct from several assembly routines to perform context switching between user
14+
/// and kernel mode. For this reason, it is **critical** that the struct have a deterministic layout
15+
/// in memory. We use `#[repr(C)]` for this.
16+
#[repr(C)]
17+
#[derive(Default)]
18+
pub struct UserContext {
19+
pub eax: u32, // Offset: 0
20+
pub ebx: u32, // Offset: 4
21+
pub ecx: u32, // Offset: 8
22+
pub edx: u32, // Offset: 12
23+
pub esi: u32, // Offset: 16
24+
pub edi: u32, // Offset: 20
25+
pub ebp: u32, // Offset: 24
26+
pub esp: u32, // Offset: 28
27+
pub eip: u32, // Offset: 32
28+
pub eflags: u32, // Offset: 36
29+
pub cs: u32, // Offset: 40
30+
pub ss: u32, // Offset: 44
31+
pub ds: u32, // Offset: 48
32+
pub es: u32, // Offset: 52
33+
pub fs: u32, // Offset: 56
34+
pub gs: u32, // Offset: 60
35+
36+
/// If the process triggers a CPU exception, this field will be populated with the
37+
/// exception number. Otherwise this field must remain as zero.
38+
pub exception: u8,
39+
40+
/// If the process triggers a CPU exception with an associated error code, this field will be
41+
/// populated with the error code value. Otherwise this field must remain zero.
42+
pub err_code: u32,
43+
}
44+
45+
impl Display for UserContext {
46+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
47+
writeln!(f)?;
48+
writeln!(f, " CPU Registers:")?;
49+
writeln!(f)?;
50+
writeln!(f, " EAX: {:#010x} EBX: {:#010x}", self.eax, self.ebx)?;
51+
writeln!(f, " ECX: {:#010x} EDX: {:#010x}", self.ecx, self.edx)?;
52+
writeln!(f, " ESI: {:#010x} EDI: {:#010x}", self.esi, self.edi)?;
53+
writeln!(f, " EBP: {:#010x} ESP: {:#010x}", self.ebp, self.esp)?;
54+
writeln!(
55+
f,
56+
" EIP: {:#010x} EFLAGS: {:#010x}",
57+
self.eip, self.eflags
58+
)?;
59+
writeln!(
60+
f,
61+
" CS: {:#06x} SS: {:#06x}",
62+
self.cs, self.ss
63+
)?;
64+
writeln!(
65+
f,
66+
" DS: {:#06x} ES: {:#06x}",
67+
self.ds, self.es
68+
)?;
69+
writeln!(
70+
f,
71+
" FS: {:#06x} GS: {:#06x}",
72+
self.fs, self.gs
73+
)?;
74+
75+
if self.exception != 0 || self.err_code != 0 {
76+
writeln!(f)?;
77+
if let Some(msg) = EXCEPTIONS.get(self.exception as usize) {
78+
writeln!(f, " Exception: {}", msg)?;
79+
} else {
80+
writeln!(f, " Exception Number: {}", self.exception)?;
81+
}
82+
writeln!(f, " Error code: {:#010x}", self.err_code)?;
83+
}
84+
Ok(())
85+
}
86+
}

0 commit comments

Comments
 (0)