Skip to content

Commit

Permalink
Merge pull request #551 from joergroedel/user-console
Browse files Browse the repository at this point in the history
User-mode console support
  • Loading branch information
joergroedel authored Dec 13, 2024
2 parents 8b5bc8a + f7f8bf1 commit b50c35d
Show file tree
Hide file tree
Showing 19 changed files with 445 additions and 20 deletions.
24 changes: 21 additions & 3 deletions kernel/src/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Console> = SpinLock::new(Console {
writer: &DEFAULT_SERIAL_PORT,
});
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion kernel/src/cpu/idt/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion kernel/src/cpu/percpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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(())
}
Expand Down
7 changes: 6 additions & 1 deletion kernel/src/cpu/smp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//
// Author: Joerg Roedel <[email protected]>

extern crate alloc;

use crate::acpi::tables::ACPICPUInfo;
use crate::address::Address;
use crate::cpu::percpu::{this_cpu, this_cpu_shared, PerCpu};
Expand All @@ -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)?;
Expand Down Expand Up @@ -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!");
}
134 changes: 134 additions & 0 deletions kernel/src/fs/console.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2024
//
// Author: Joerg Roedel <[email protected]>

extern crate alloc;

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.
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<ConsoleBuffer>,
}

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<usize, SvsmError> {
Ok(0)
}

fn read_buffer(&self, _buffer: &mut dyn Buffer, _offset: usize) -> Result<usize, SvsmError> {
Ok(0)
}

fn write(&self, buf: &[u8], _offset: usize) -> Result<usize, SvsmError> {
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<usize, SvsmError> {
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<usize, SvsmError> {
Err(SvsmError::FileSystem(FsError::not_supported()))
}

fn size(&self) -> usize {
0
}
}

pub fn stdout_open() -> Arc<dyn Obj> {
let console_file: Arc<dyn File> = Arc::new(ConsoleFile::new());

// Stdout is write-only.
Arc::new(FsObj::new_file(FileHandle::new(&console_file, false, true)))
}
2 changes: 2 additions & 0 deletions kernel/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@

mod api;
mod buffer;
mod console;
mod filesystem;
mod init;
mod obj;
mod ramfs;

pub use api::*;
pub use buffer::*;
pub use console::{stdout_open, ConsoleFile};
pub use filesystem::*;
pub use init::populate_ram_fs;
pub use obj::FsObj;
7 changes: 6 additions & 1 deletion kernel/src/svsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions kernel/src/syscall/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion kernel/src/task/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -31,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
Expand Down Expand Up @@ -59,7 +71,7 @@ pub fn exec_user(binary: &str, root: Arc<dyn Directory>) -> Result<u32, SvsmErro
let virt_base = alloc_info.range.vaddr_begin;
let entry = elf_bin.get_entry(virt_base);

let new_task = create_user_task(entry.try_into().unwrap(), root)?;
let new_task = create_user_task(entry.try_into().unwrap(), root, task_name(binary))?;

for seg in elf_bin.image_load_segment_iter(virt_base) {
let virt_start = VirtAddr::from(seg.vaddr_range.vaddr_begin);
Expand Down
8 changes: 5 additions & 3 deletions kernel/src/task/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ use crate::error::SvsmError;
use crate::fs::Directory;
use crate::locking::SpinLock;
use crate::mm::{STACK_TOTAL_SIZE, SVSM_CONTEXT_SWITCH_SHADOW_STACK, SVSM_CONTEXT_SWITCH_STACK};
use alloc::string::String;
use alloc::sync::Arc;
use core::arch::{asm, global_asm};
use core::cell::OnceCell;
Expand Down Expand Up @@ -243,9 +244,9 @@ pub static TASKLIST: SpinLock<TaskList> = 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<TaskPointer, SvsmError> {
pub fn start_kernel_task(entry: extern "C" fn(), name: String) -> Result<TaskPointer, SvsmError> {
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
Expand All @@ -269,9 +270,10 @@ pub fn start_kernel_task(entry: extern "C" fn()) -> Result<TaskPointer, SvsmErro
pub fn create_user_task(
user_entry: usize,
root: Arc<dyn Directory>,
name: String,
) -> Result<TaskPointer, SvsmError> {
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
Expand Down
Loading

0 comments on commit b50c35d

Please sign in to comment.