From 944fd7b4104352de7ec730fce237d7cae1d7e0e1 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 28 Nov 2024 17:25:45 +0100 Subject: [PATCH 01/12] kernel/console: Implement raw output function Implement the console_write() function which will take a u8 slice and write it to the console. This will be used to allow user-space code to write to the console. Signed-off-by: Joerg Roedel --- kernel/src/console.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/kernel/src/console.rs b/kernel/src/console.rs index 92d17f859..b7af0e051 100644 --- a/kernel/src/console.rs +++ b/kernel/src/console.rs @@ -18,13 +18,19 @@ struct Console { impl fmt::Write for Console { fn write_str(&mut self, s: &str) -> fmt::Result { - for ch in s.bytes() { - self.writer.put_byte(ch); - } + self.write_bytes(s.as_bytes()); Ok(()) } } +impl Console { + pub fn write_bytes(&self, buffer: &[u8]) { + for b in buffer.iter() { + self.writer.put_byte(*b); + } + } +} + static WRITER: SpinLock = SpinLock::new(Console { writer: &DEFAULT_SERIAL_PORT, }); @@ -55,6 +61,18 @@ pub fn _print(args: fmt::Arguments<'_>) { WRITER.lock().write_fmt(args).unwrap(); } +/// Writes all bytes from the slice to the console +/// +/// # Arguments: +/// +/// * `buffer`: u8 slice with bytes to write. +pub fn console_write(buffer: &[u8]) { + if !*CONSOLE_INITIALIZED { + return; + } + WRITER.lock().write_bytes(buffer); +} + #[derive(Clone, Copy, Debug)] struct ConsoleLogger { name: &'static str, From e9aa54e3b48ee4cedd23dc80667ebdbb3cff1e4d Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 4 Dec 2024 16:43:26 +0100 Subject: [PATCH 02/12] kernel/task: Introduce a user-task specific setup function Define a setup function which is only called for user tasks. Signed-off-by: Joerg Roedel --- kernel/src/cpu/idt/entry.S | 2 +- kernel/src/task/tasks.rs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/kernel/src/cpu/idt/entry.S b/kernel/src/cpu/idt/entry.S index 4b836000f..ed40f2914 100644 --- a/kernel/src/cpu/idt/entry.S +++ b/kernel/src/cpu/idt/entry.S @@ -336,7 +336,7 @@ return_user: .globl return_new_task return_new_task: - call setup_new_task + call setup_user_task jmp default_return // #DE Divide-by-Zero-Error Exception (Vector 0) diff --git a/kernel/src/task/tasks.rs b/kernel/src/task/tasks.rs index 8e98c6b41..7f9849da4 100644 --- a/kernel/src/task/tasks.rs +++ b/kernel/src/task/tasks.rs @@ -694,7 +694,16 @@ pub fn is_task_fault(vaddr: VirtAddr) -> bool { /// # Safety /// The caller is required to verify the correctness of the save area address. #[no_mangle] -unsafe fn setup_new_task(xsa_addr: u64) { +unsafe fn setup_user_task(xsa_addr: u64) { + // SAFETY: caller needs to make sure xsa_addr is valid and points to a + // memory region of sufficient size. + unsafe { + // Needs to be the first function called here. + setup_new_task_common(xsa_addr); + } +} + +unsafe fn setup_new_task_common(xsa_addr: u64) { // Re-enable IRQs here, as they are still disabled from the // schedule()/sched_init() functions. After the context switch the IrqGuard // from the previous task is not dropped, which causes IRQs to stay @@ -716,7 +725,7 @@ extern "C" fn run_kernel_task(entry: extern "C" fn(), xsa_addr: u64) { // SAFETY: the save area address is provided by the context switch assembly // code. unsafe { - setup_new_task(xsa_addr); + setup_new_task_common(xsa_addr); } entry(); } From d9946ef91d90904be1a234598a1a08de4b5bda30 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 5 Dec 2024 10:28:23 +0100 Subject: [PATCH 03/12] kernel/task: Add add_obj_at() method Add a method to struct Task to add an object with a defined handle. Signed-off-by: Joerg Roedel --- kernel/src/syscall/obj.rs | 1 + kernel/src/task/tasks.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/kernel/src/syscall/obj.rs b/kernel/src/syscall/obj.rs index ccbd78383..7f2a7752d 100644 --- a/kernel/src/syscall/obj.rs +++ b/kernel/src/syscall/obj.rs @@ -15,6 +15,7 @@ use alloc::sync::Arc; pub enum ObjError { InvalidHandle, NotFound, + Busy, } /// An object represents the type of resource like file, VM, vCPU in the diff --git a/kernel/src/task/tasks.rs b/kernel/src/task/tasks.rs index 7f9849da4..2614c7c5d 100644 --- a/kernel/src/task/tasks.rs +++ b/kernel/src/task/tasks.rs @@ -637,6 +637,34 @@ impl Task { Ok(id) } + /// Adds an object to the current task and maps it to a given object-id. + /// + /// # Arguments + /// + /// * `obj` - The object to be added. + /// * `handle` - Object handle to reference the object. + /// + /// # Returns + /// + /// * `Result` - Returns the object handle for the object + /// to be added if successful, or an `SvsmError` on failure. + /// + /// # Errors + /// + /// This function will return an error if allocating the object handle + /// fails or the object id is already in use. + pub fn add_obj_at(&self, obj: Arc, handle: ObjHandle) -> Result { + let mut objs = self.objs.lock_write(); + + if objs.get(&handle).is_some() { + return Err(SvsmError::from(ObjError::Busy)); + } + + objs.insert(handle, obj); + + Ok(handle) + } + /// Removes an object from the current task. /// /// # Arguments From 72b985d7af37dad049c2f90765e8a746f9f666df Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 5 Dec 2024 11:08:04 +0100 Subject: [PATCH 04/12] kernel/task: Introduce task names Add a string to struct Task representing the name of the task. Signed-off-by: Joerg Roedel --- kernel/src/cpu/percpu.rs | 3 ++- kernel/src/cpu/smp.rs | 7 ++++++- kernel/src/svsm.rs | 7 ++++++- kernel/src/task/exec.rs | 4 +++- kernel/src/task/schedule.rs | 8 +++++--- kernel/src/task/tasks.rs | 25 ++++++++++++++++++++++--- 6 files changed, 44 insertions(+), 10 deletions(-) diff --git a/kernel/src/cpu/percpu.rs b/kernel/src/cpu/percpu.rs index 5af0b6608..cc67a3bd7 100644 --- a/kernel/src/cpu/percpu.rs +++ b/kernel/src/cpu/percpu.rs @@ -48,6 +48,7 @@ use crate::types::{ PAGE_SHIFT, PAGE_SHIFT_2M, PAGE_SIZE, PAGE_SIZE_2M, SVSM_TR_ATTRIBUTES, SVSM_TSS, }; use crate::utils::MemoryRegion; +use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; use core::cell::{Cell, OnceCell, Ref, RefCell, RefMut, UnsafeCell}; @@ -763,7 +764,7 @@ impl PerCpu { } pub fn setup_idle_task(&self, entry: extern "C" fn()) -> Result<(), SvsmError> { - let idle_task = Task::create(self, entry)?; + let idle_task = Task::create(self, entry, String::from("idle"))?; self.runqueue.lock_read().set_idle_task(idle_task); Ok(()) } diff --git a/kernel/src/cpu/smp.rs b/kernel/src/cpu/smp.rs index 035ce78fb..2b0c72087 100644 --- a/kernel/src/cpu/smp.rs +++ b/kernel/src/cpu/smp.rs @@ -4,6 +4,8 @@ // // Author: Joerg Roedel +extern crate alloc; + use crate::acpi::tables::ACPICPUInfo; use crate::address::Address; use crate::cpu::percpu::{this_cpu, this_cpu_shared, PerCpu}; @@ -17,6 +19,8 @@ use crate::requests::{request_loop, request_processing_main}; use crate::task::{schedule_init, start_kernel_task}; use crate::utils::immut_after_init::immut_after_init_set_multithreaded; +use alloc::string::String; + fn start_cpu(platform: &dyn SvsmPlatform, apic_id: u32) -> Result<(), SvsmError> { let start_rip: u64 = (start_ap as *const u8) as u64; let percpu = PerCpu::alloc(apic_id)?; @@ -70,7 +74,8 @@ fn start_ap() { #[no_mangle] pub extern "C" fn ap_request_loop() { - start_kernel_task(request_processing_main).expect("Failed to launch request processing task"); + start_kernel_task(request_processing_main, String::from("request-processing")) + .expect("Failed to launch request processing task"); request_loop(); panic!("Returned from request_loop!"); } diff --git a/kernel/src/svsm.rs b/kernel/src/svsm.rs index 5331b257a..7fcdae931 100755 --- a/kernel/src/svsm.rs +++ b/kernel/src/svsm.rs @@ -7,6 +7,8 @@ #![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_main)] +extern crate alloc; + use bootlib::kernel_launch::KernelLaunchInfo; use core::arch::global_asm; use core::panic::PanicInfo; @@ -51,6 +53,8 @@ use svsm::vtpm::vtpm_init; use svsm::mm::validate::{init_valid_bitmap_ptr, migrate_valid_bitmap}; +use alloc::string::String; + extern "C" { pub static bsp_stack_end: u8; } @@ -319,7 +323,8 @@ pub extern "C" fn svsm_main() { panic!("Failed to launch FW: {e:#?}"); } - start_kernel_task(request_processing_main).expect("Failed to launch request processing task"); + start_kernel_task(request_processing_main, String::from("request-processing")) + .expect("Failed to launch request processing task"); #[cfg(test)] crate::test_main(); diff --git a/kernel/src/task/exec.rs b/kernel/src/task/exec.rs index 64cc311db..3476ee207 100644 --- a/kernel/src/task/exec.rs +++ b/kernel/src/task/exec.rs @@ -17,6 +17,8 @@ use crate::utils::align_up; use alloc::sync::Arc; use elf::{Elf64File, Elf64PhdrFlags}; +use alloc::string::String; + fn convert_elf_phdr_flags(flags: Elf64PhdrFlags) -> VMFileMappingFlags { let mut vm_flags = VMFileMappingFlags::Fixed; @@ -59,7 +61,7 @@ pub fn exec_user(binary: &str, root: Arc) -> Result = SpinLock::new(TaskList::new()); /// # Returns /// /// A new instance of [`TaskPointer`] on success, [`SvsmError`] on failure. -pub fn start_kernel_task(entry: extern "C" fn()) -> Result { +pub fn start_kernel_task(entry: extern "C" fn(), name: String) -> Result { let cpu = this_cpu(); - let task = Task::create(cpu, entry)?; + let task = Task::create(cpu, entry, name)?; TASKLIST.lock().list().push_back(task.clone()); // Put task on the runqueue of this CPU @@ -269,9 +270,10 @@ pub fn start_kernel_task(entry: extern "C" fn()) -> Result, + name: String, ) -> Result { let cpu = this_cpu(); - Task::create_user(cpu, user_entry, root) + Task::create_user(cpu, user_entry, root, name) } /// Finished user-space task creation by putting the task on the global diff --git a/kernel/src/task/tasks.rs b/kernel/src/task/tasks.rs index 2614c7c5d..dac56c880 100644 --- a/kernel/src/task/tasks.rs +++ b/kernel/src/task/tasks.rs @@ -7,6 +7,7 @@ extern crate alloc; use alloc::collections::btree_map::BTreeMap; +use alloc::string::String; use alloc::sync::Arc; use core::fmt; use core::mem::size_of; @@ -145,6 +146,9 @@ pub struct Task { /// State relevant for scheduler sched_state: RWLock, + /// User-visible name of task + name: String, + /// ID of the task id: u32, @@ -189,7 +193,11 @@ impl fmt::Debug for Task { } impl Task { - pub fn create(cpu: &PerCpu, entry: extern "C" fn()) -> Result { + pub fn create( + cpu: &PerCpu, + entry: extern "C" fn(), + name: String, + ) -> Result { let mut pgtable = cpu.get_pgtable().clone_shared()?; cpu.populate_page_table(&mut pgtable); @@ -256,6 +264,7 @@ impl Task { state: TaskState::RUNNING, cpu: cpu.get_apic_id(), }), + name, id: TASK_ID_ALLOCATOR.next_id(), rootdir: opendir("/")?, list_link: LinkedListAtomicLink::default(), @@ -268,6 +277,7 @@ impl Task { cpu: &PerCpu, user_entry: usize, root: Arc, + name: String, ) -> Result { let mut pgtable = cpu.get_pgtable().clone_shared()?; @@ -339,6 +349,7 @@ impl Task { state: TaskState::RUNNING, cpu: cpu.get_apic_id(), }), + name, id: TASK_ID_ALLOCATOR.next_id(), rootdir: root, list_link: LinkedListAtomicLink::default(), @@ -351,6 +362,10 @@ impl Task { self.stack_bounds } + pub fn get_task_name(&self) -> &String { + &self.name + } + pub fn get_task_id(&self) -> u32 { self.id } @@ -767,7 +782,9 @@ extern "C" fn task_exit() { #[cfg(test)] mod tests { + extern crate alloc; use crate::task::start_kernel_task; + use alloc::string::String; use core::arch::asm; use core::arch::global_asm; @@ -858,7 +875,8 @@ mod tests { #[test] #[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")] fn test_fpu_context_switch() { - start_kernel_task(task1).expect("Failed to launch request processing task"); + start_kernel_task(task1, String::from("task1")) + .expect("Failed to launch request processing task"); } extern "C" fn task1() { @@ -867,7 +885,8 @@ mod tests { asm!("call test_fpu", options(att_syntax)); } - start_kernel_task(task2).expect("Failed to launch request processing task"); + start_kernel_task(task2, String::from("task2")) + .expect("Failed to launch request processing task"); unsafe { asm!("call check_fpu", out("rax") ret, options(att_syntax)); From 03e7d07cbfe6a6e4f1002c1f5b2d9cc602be05a5 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 5 Dec 2024 11:14:00 +0100 Subject: [PATCH 05/12] kernel/task: Set user-mode task name to binary file name Use the file name of the binary as the name of the task. Signed-off-by: Joerg Roedel --- kernel/src/task/exec.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kernel/src/task/exec.rs b/kernel/src/task/exec.rs index 3476ee207..8b65c2389 100644 --- a/kernel/src/task/exec.rs +++ b/kernel/src/task/exec.rs @@ -33,6 +33,16 @@ fn convert_elf_phdr_flags(flags: Elf64PhdrFlags) -> VMFileMappingFlags { vm_flags } +/// Returns the name of the binary file without preceeding directories. This is +/// used as the official task name. +fn task_name(binary: &str) -> String { + let mut items = binary.split('/').filter(|x| !x.is_empty()); + match items.nth_back(0) { + Some(p) => String::from(p), + None => String::from("unknown"), + } +} + /// Loads and executes an ELF binary in user-mode. /// /// # Arguments @@ -61,7 +71,7 @@ pub fn exec_user(binary: &str, root: Arc) -> Result Date: Thu, 28 Nov 2024 17:28:12 +0100 Subject: [PATCH 06/12] kernel/fs: Add ConsoleFile type Add a file type for writing to the console. This will be wired up to user-space to allow modules to write messages to the console. Signed-off-by: Joerg Roedel --- kernel/src/fs/console.rs | 124 +++++++++++++++++++++++++++++++++++++++ kernel/src/fs/mod.rs | 2 + 2 files changed, 126 insertions(+) create mode 100644 kernel/src/fs/console.rs diff --git a/kernel/src/fs/console.rs b/kernel/src/fs/console.rs new file mode 100644 index 000000000..6e71eadad --- /dev/null +++ b/kernel/src/fs/console.rs @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2024 +// +// Author: Joerg Roedel + +extern crate alloc; + +use super::{Buffer, File, FsError}; +use crate::console::console_write; +use crate::cpu::percpu::current_task; +use crate::error::SvsmError; +use crate::locking::SpinLock; +use alloc::string::String; + +// With the value of 224 the ConsoleBuffer struct will be exactly 256 bytes +// large, avoiding memory waste due to internal fragmentation. +const CONSOLE_LINE_BUFFER_SIZE: usize = 224; + +#[derive(Debug)] +struct ConsoleBuffer { + prefix: String, + buffer: [u8; CONSOLE_LINE_BUFFER_SIZE], + fill: usize, +} + +impl ConsoleBuffer { + fn new() -> Self { + let task = current_task(); + let task_name = task.get_task_name(); + let mut prefix = String::from("["); + prefix.push_str(task_name.as_str()); + prefix.push_str("] "); + Self { + prefix, + buffer: [0u8; CONSOLE_LINE_BUFFER_SIZE], + fill: 0, + } + } + + fn push(&mut self, b: u8) { + let newline: u8 = '\n'.try_into().unwrap(); + + if self.fill + 1 == CONSOLE_LINE_BUFFER_SIZE { + self.buffer[self.fill] = newline; + self.fill += 1; + self.flush(); + } + + let index = self.fill; + self.buffer[index] = b; + self.fill += 1; + if b == newline { + self.flush(); + } + } + + pub fn flush(&mut self) { + console_write(self.prefix.as_bytes()); + console_write(&self.buffer[..self.fill]); + self.fill = 0; + } +} + +#[derive(Debug)] +pub struct ConsoleFile { + buffer: SpinLock, +} + +impl ConsoleFile { + pub fn new() -> Self { + Self { + buffer: SpinLock::new(ConsoleBuffer::new()), + } + } +} + +impl Default for ConsoleFile { + fn default() -> Self { + Self::new() + } +} + +impl File for ConsoleFile { + fn read(&self, _buf: &mut [u8], _offset: usize) -> Result { + Ok(0) + } + + fn read_buffer(&self, _buffer: &mut dyn Buffer, _offset: usize) -> Result { + Ok(0) + } + + fn write(&self, buf: &[u8], _offset: usize) -> Result { + let mut console = self.buffer.lock(); + + for c in buf.iter() { + console.push(*c); + } + + Ok(buf.len()) + } + + fn write_buffer(&self, buffer: &dyn Buffer, _offset: usize) -> Result { + let len = buffer.size(); + let mut offset: usize = 0; + + while offset < len { + let mut kernel_buffer: [u8; 16] = [0u8; 16]; + let read = buffer.read_buffer(&mut kernel_buffer, offset)?; + self.write(&kernel_buffer[0..read], 0)?; + offset += read; + } + + Ok(offset) + } + + fn truncate(&self, _size: usize) -> Result { + Err(SvsmError::FileSystem(FsError::not_supported())) + } + + fn size(&self) -> usize { + 0 + } +} diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index b0cdfa514..eaf5e16ef 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -6,6 +6,7 @@ mod api; mod buffer; +mod console; mod filesystem; mod init; mod obj; @@ -13,6 +14,7 @@ mod ramfs; pub use api::*; pub use buffer::*; +pub use console::ConsoleFile; pub use filesystem::*; pub use init::populate_ram_fs; pub use obj::FsObj; From c625e59c6fcb7297d43413b3362f8b6a580cb196 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 5 Dec 2024 10:35:38 +0100 Subject: [PATCH 07/12] kernel/fs: Add stdout_open() function Implement a function to open a console and return a file handle. Signed-off-by: Joerg Roedel --- kernel/src/fs/console.rs | 12 +++++++++++- kernel/src/fs/mod.rs | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/kernel/src/fs/console.rs b/kernel/src/fs/console.rs index 6e71eadad..cc3ac1a36 100644 --- a/kernel/src/fs/console.rs +++ b/kernel/src/fs/console.rs @@ -6,12 +6,15 @@ extern crate alloc; -use super::{Buffer, File, FsError}; +use super::{Buffer, File, FileHandle, FsError}; use crate::console::console_write; use crate::cpu::percpu::current_task; use crate::error::SvsmError; +use crate::fs::obj::FsObj; use crate::locking::SpinLock; +use crate::syscall::Obj; use alloc::string::String; +use alloc::sync::Arc; // With the value of 224 the ConsoleBuffer struct will be exactly 256 bytes // large, avoiding memory waste due to internal fragmentation. @@ -122,3 +125,10 @@ impl File for ConsoleFile { 0 } } + +pub fn stdout_open() -> Arc { + let console_file: Arc = Arc::new(ConsoleFile::new()); + + // Stdout is write-only. + Arc::new(FsObj::new_file(FileHandle::new(&console_file, false, true))) +} diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index eaf5e16ef..97edb5711 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -14,7 +14,7 @@ mod ramfs; pub use api::*; pub use buffer::*; -pub use console::ConsoleFile; +pub use console::{stdout_open, ConsoleFile}; pub use filesystem::*; pub use init::populate_ram_fs; pub use obj::FsObj; From 109b4f3b91c7ebcddcc86c5a8c1b33e70d34f2bf Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 5 Dec 2024 11:45:11 +0100 Subject: [PATCH 08/12] kernel/task: Attach console file to user-space tasks Attach a handle to a console file to every user task, so that they can write to the console. Signed-off-by: Joerg Roedel --- kernel/src/task/tasks.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/kernel/src/task/tasks.rs b/kernel/src/task/tasks.rs index dac56c880..e68d2ad7e 100644 --- a/kernel/src/task/tasks.rs +++ b/kernel/src/task/tasks.rs @@ -17,13 +17,13 @@ use core::sync::atomic::{AtomicU32, Ordering}; use crate::address::{Address, VirtAddr}; use crate::cpu::idt::svsm::return_new_task; use crate::cpu::irq_state::EFLAGS_IF; -use crate::cpu::percpu::PerCpu; +use crate::cpu::percpu::{current_task, PerCpu}; use crate::cpu::shadow_stack::is_cet_ss_supported; use crate::cpu::sse::{get_xsave_area_size, sse_restore_context}; use crate::cpu::X86ExceptionContext; use crate::cpu::{irqs_enable, X86GeneralRegs}; use crate::error::SvsmError; -use crate::fs::{opendir, Directory, FileHandle}; +use crate::fs::{opendir, stdout_open, Directory, FileHandle}; use crate::locking::{RWLock, SpinLock}; use crate::mm::pagetable::{PTEntryFlags, PageTable}; use crate::mm::vm::{ @@ -731,6 +731,14 @@ pub fn is_task_fault(vaddr: VirtAddr) -> bool { || (vaddr >= SVSM_PERTASK_BASE && vaddr < SVSM_PERTASK_END) } +fn task_attach_console() { + let file_handle = stdout_open(); + let obj_handle = ObjHandle::new(0); + current_task() + .add_obj_at(file_handle, obj_handle) + .expect("Failed to attach console"); +} + /// Runs the first time a new task is scheduled, in the context of the new /// task. Any first-time initialization and setup work for a new task that /// needs to happen in its context must be done here. @@ -744,6 +752,7 @@ unsafe fn setup_user_task(xsa_addr: u64) { // Needs to be the first function called here. setup_new_task_common(xsa_addr); } + task_attach_console(); } unsafe fn setup_new_task_common(xsa_addr: u64) { From b9c305584e26b419c37d031ae4ad9b1cc261c4fb Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 5 Dec 2024 12:04:37 +0100 Subject: [PATCH 09/12] syscall: Add console writing abstractions Create a static FSObjHandle which is used to write to the console and a function to perform the writes. Signed-off-by: Joerg Roedel --- syscall/src/class1.rs | 6 ++++++ syscall/src/console.rs | 13 +++++++++++++ syscall/src/lib.rs | 2 ++ syscall/src/obj.rs | 2 +- 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 syscall/src/console.rs diff --git a/syscall/src/class1.rs b/syscall/src/class1.rs index 9f7ee2f6b..7922d4536 100644 --- a/syscall/src/class1.rs +++ b/syscall/src/class1.rs @@ -15,6 +15,12 @@ use core::ffi::CStr; #[derive(Debug)] pub struct FsObjHandle(ObjHandle); +impl FsObjHandle { + pub(crate) const fn new(obj: ObjHandle) -> Self { + Self(obj) + } +} + impl Obj for FsObjHandle { fn id(&self) -> u32 { u32::from(&self.0) diff --git a/syscall/src/console.rs b/syscall/src/console.rs new file mode 100644 index 000000000..e759507f4 --- /dev/null +++ b/syscall/src/console.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2024 SUSE LLC +// +// Author: Joerg Roedel + +use crate::{write, FsObjHandle, ObjHandle, SysCallError}; + +static CONSOLE_HANDLE: FsObjHandle = FsObjHandle::new(ObjHandle::new(0)); + +pub fn write_console(buf: &[u8]) -> Result { + write(&CONSOLE_HANDLE, buf) +} diff --git a/syscall/src/lib.rs b/syscall/src/lib.rs index 119160c36..54d0c18bb 100644 --- a/syscall/src/lib.rs +++ b/syscall/src/lib.rs @@ -8,11 +8,13 @@ mod call; mod class0; mod class1; +mod console; mod def; mod obj; pub use call::SysCallError; pub use class0::*; pub use class1::*; +pub use console::*; pub use def::*; pub use obj::*; diff --git a/syscall/src/obj.rs b/syscall/src/obj.rs index 772c90c3a..e2b5bbd60 100644 --- a/syscall/src/obj.rs +++ b/syscall/src/obj.rs @@ -18,7 +18,7 @@ use super::SYS_CLOSE; pub struct ObjHandle(u32); impl ObjHandle { - pub(crate) fn new(id: u32) -> Self { + pub(crate) const fn new(id: u32) -> Self { Self(id) } } From 6796f88e0f6bab51efc7f2dbb663465be0e8f785 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 5 Dec 2024 14:14:22 +0100 Subject: [PATCH 10/12] userlib: Add SpinLock Implementation Add a simple SpinLock implementation to the user library to protect access to global state. At some point this needs to be replaces with a non-busy waiting locking implementation. Signed-off-by: Joerg Roedel --- user/lib/src/lib.rs | 3 ++ user/lib/src/locking.rs | 106 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 user/lib/src/locking.rs diff --git a/user/lib/src/lib.rs b/user/lib/src/lib.rs index 958f16e97..365831150 100644 --- a/user/lib/src/lib.rs +++ b/user/lib/src/lib.rs @@ -6,7 +6,10 @@ #![no_std] +pub mod locking; + use core::panic::PanicInfo; +pub use locking::*; pub use syscall::*; #[macro_export] diff --git a/user/lib/src/locking.rs b/user/lib/src/locking.rs new file mode 100644 index 000000000..cf3fd2538 --- /dev/null +++ b/user/lib/src/locking.rs @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2024 SUSE LLC +// +// Author: Joerg Roedel + +use core::cell::UnsafeCell; +use core::ops::{Deref, DerefMut}; +use core::sync::atomic::{AtomicU64, Ordering}; + +/// A lock guard obtained from a [`SpinLock`]. This lock guard +/// provides exclusive access to the data protected by a [`SpinLock`], +/// ensuring that the lock is released when it goes out of scope. +/// +/// # Examples +/// +/// ``` +/// use userlib::SpinLock; +/// +/// let data = 42; +/// let spin_lock = SpinLock::new(data); +/// +/// { +/// let mut guard = spin_lock.lock(); +/// *guard += 1; // Modify the protected data. +/// }; // Lock is automatically released when `guard` goes out of scope. +/// ``` + +#[derive(Debug)] +pub struct LockGuard<'a, T> { + holder: &'a AtomicU64, + data: &'a mut T, +} + +impl Drop for LockGuard<'_, T> { + fn drop(&mut self) { + self.holder.fetch_add(1, Ordering::Release); + } +} + +impl Deref for LockGuard<'_, T> { + type Target = T; + fn deref(&self) -> &T { + self.data + } +} + +impl DerefMut for LockGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + self.data + } +} + +#[derive(Debug)] +pub struct SpinLock { + current: AtomicU64, + holder: AtomicU64, + data: UnsafeCell, +} + +// SAFETY: SpinLock guarantees mutually exclusive access to wrapped data. +unsafe impl Sync for SpinLock {} + +impl<'a, T> SpinLock { + /// Create a new `SpinLock` + /// + /// # Arguments: + /// + /// * `data`: Data to be protected by the `SpinLock`. The data is moved into + /// and from this point on owned by the SpinLock. + /// + /// # Returns: + /// + /// New instance of `SpinLock` containing `data`. + pub const fn new(data: T) -> Self { + SpinLock { + current: AtomicU64::new(0), + holder: AtomicU64::new(0), + data: UnsafeCell::new(data), + } + } + + /// Takes lock and returns [`LockGuard`] which gives exclusive access to + /// the protected data. + /// + /// # Returns: + /// + /// Instance of [`LockGuard`] to exclusivly access the data a release the + /// lock when it goes out of scope. + pub fn lock(&'a self) -> LockGuard<'a, T> { + let ticket = self.current.fetch_add(1, Ordering::Relaxed); + loop { + let h = self.holder.load(Ordering::Acquire); + if h == ticket { + break; + } + } + + LockGuard { + holder: &self.holder, + // SAFETY: Safe because at this point the lock is held and this is + // guaranteed to be the only reference. + data: unsafe { &mut *self.data.get() }, + } + } +} From ac7437063c8e0ada3f7e838aff03a6fa711eedae Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 5 Dec 2024 14:17:17 +0100 Subject: [PATCH 11/12] userlib: Add print! and println! support Implement console access and provide the the print! and println! macros. Use the new macros to print a message in the panic handler. Signed-off-by: Joerg Roedel --- user/lib/src/console.rs | 45 +++++++++++++++++++++++++++++++++++++++++ user/lib/src/lib.rs | 5 ++++- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 user/lib/src/console.rs diff --git a/user/lib/src/console.rs b/user/lib/src/console.rs new file mode 100644 index 000000000..a79c56bab --- /dev/null +++ b/user/lib/src/console.rs @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2024 SUSE LLC +// +// Author: Joerg Roedel + +use crate::SpinLock; +use core::fmt; +use syscall::write_console; + +#[derive(Debug, Default)] +struct ConsoleWriter {} + +impl ConsoleWriter { + const fn new() -> Self { + Self {} + } +} + +impl fmt::Write for ConsoleWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + // Ignore any errors from console writing. + let _ = write_console(s.as_bytes()); + Ok(()) + } +} + +static CONSOLE_WRITER: SpinLock = SpinLock::new(ConsoleWriter::new()); + +#[doc(hidden)] +pub fn console_print(args: fmt::Arguments<'_>) { + use core::fmt::Write; + CONSOLE_WRITER.lock().write_fmt(args).unwrap() +} + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => (console_print(format_args!($($arg)*))) +} + +#[macro_export] +macro_rules! println { + () => (print!("\n")); + ($($arg:tt)*) => (print!("{}\n", format_args!($($arg)*))); +} diff --git a/user/lib/src/lib.rs b/user/lib/src/lib.rs index 365831150..a85d7c3fe 100644 --- a/user/lib/src/lib.rs +++ b/user/lib/src/lib.rs @@ -6,8 +6,10 @@ #![no_std] +pub mod console; pub mod locking; +pub use console::*; use core::panic::PanicInfo; pub use locking::*; pub use syscall::*; @@ -27,6 +29,7 @@ macro_rules! declare_main { } #[panic_handler] -fn panic(_info: &PanicInfo<'_>) -> ! { +fn panic(info: &PanicInfo<'_>) -> ! { + println!("Panic: {}", info); exit(!0); } From f7f8bf1806e45f3b317f879c682e0879d3cac5aa Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 5 Dec 2024 14:23:20 +0100 Subject: [PATCH 12/12] userinit: Print startup message Use the console support to print a startup message from the init process. Signed-off-by: Joerg Roedel --- user/init/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/user/init/src/main.rs b/user/init/src/main.rs index 91604013b..1023311b6 100644 --- a/user/init/src/main.rs +++ b/user/init/src/main.rs @@ -26,6 +26,8 @@ fn write(arr: &mut [u64; 128], val: u64) { declare_main!(main); fn main() -> u32 { + println!("COCONUT-SVSM init process starting"); + // SAFETY: Single-threaded process, so no data races. Safe to access global // mutable data. unsafe {