Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • pigeonmoelleux/skavos
1 result
Show changes
This diff is collapsed.
//! Interface to manage storage devices and controllers
use alloc::sync::Arc;
use alloc::vec::Vec;
use spin::Mutex;
use super::block::Manipulation;
/// Type alias representing a device UUID
pub type Uuid = [u8; 20];
/// Represents storage devices such as hard drive, SSDs, etc.
pub trait Device: Manipulation {
/// Returns the size of the device in blocks
fn size_in_blocks(&self) -> usize;
/// Returns a unique identifier for the device
///
/// This allows to be sure a same device will not be found several times.
fn uuid(&self) -> Uuid;
}
/// Represents a storage controller
pub trait Controller {
/// Returns the list of the [`Device`] controlled by this structure
fn devices<'device>(&self) -> Vec<Arc<Mutex<dyn Device + Send + 'device>>>;
}
......@@ -48,6 +48,7 @@ impl Display for Rtc {
/// See the [OSdev wiki](https://wiki.osdev.org/CMOS#Getting_Current_Date_and_Time_from_RTC) for more details
#[repr(u8)]
#[derive(Debug, Clone)]
#[allow(unused)]
enum Register {
/// Second register
Second = 0x00,
......@@ -213,6 +214,7 @@ impl Cmos {
}
/// Waits until the CMOS is updated
#[allow(unused)]
fn wait_while_updating(&mut self) {
while self.is_updating() {
spin_loop();
......@@ -220,6 +222,7 @@ impl Cmos {
}
/// Returns the current Real-Time Clock
#[allow(unused)]
pub fn rtc(&mut self) -> Rtc {
/// Believe in the docs
const SRB_BCD_MODE: u8 = 0x04;
......
......@@ -24,6 +24,12 @@ pub(super) enum InterruptIndex {
/// Real-time controller interrupt
Rtc = PIC2_OFFSET,
/// Standard ATA primary interrupt (0xE above base offset)
AtaPrimary = PIC1_OFFSET + 0x0E,
/// Standard ATA primary interrupt (0xF above base offset)
AtaSecondary = PIC1_OFFSET + 0x0F,
}
impl From<InterruptIndex> for u8 {
......@@ -46,6 +52,8 @@ pub(super) static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
set_handler_fn(&mut idt, InterruptIndex::Timer, timer_interrupt_handler);
set_handler_fn(&mut idt, InterruptIndex::Keyboard, keyboard_interrupt_handler);
set_handler_fn(&mut idt, InterruptIndex::Rtc, rtc_interrupt_handler);
set_handler_fn(&mut idt, InterruptIndex::AtaPrimary, primary_ata_handler);
set_handler_fn(&mut idt, InterruptIndex::AtaSecondary, secondary_ata_handler);
idt
});
......@@ -102,7 +110,7 @@ extern "x86-interrupt" fn rtc_interrupt_handler(_stack_frame: InterruptStackFram
LAST_RTC_UPDATE.store(pit::ticks(), Ordering::Relaxed);
CMOS.notify_end_of_interrupt();
// SAFETY: Timer index is the same everywhere it should
// SAFETY: RTC index is the same everywhere it should
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::Rtc.into());
};
......@@ -112,12 +120,28 @@ extern "x86-interrupt" fn rtc_interrupt_handler(_stack_frame: InterruptStackFram
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
KeyQueue::handle_interrupt();
// SAFETY: Timer index is the same everywhere it should
// SAFETY: Keyboard index is the same everywhere it should
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::Keyboard.into());
};
}
/// Primary ATA interrupt handler
extern "x86-interrupt" fn primary_ata_handler(_stack_frame: InterruptStackFrame) {
// SAFETY: ATA primary index is the standard one
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::AtaPrimary.into());
};
}
/// Secondary ATA interrupt handler
extern "x86-interrupt" fn secondary_ata_handler(_stack_frame: InterruptStackFrame) {
// SAFETY: ATA secondary index is the standard one
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::AtaSecondary.into());
};
}
#[cfg(test)]
mod test {
use x86_64::instructions::interrupts;
......
......@@ -151,7 +151,8 @@ impl KeyQueue {
///
/// Moreover, the following keys combinations are used :
/// - `CTRL` + `ALT` + `DEL`: reboots the computer (not implemented yet)
/// - `ALT` + `L`: flushes the logger
/// - `ALT` + `P`: displays all logs and clears the logger
/// - `ALT` + `L`: displays the oldest log then removes it from the logger
#[allow(clippy::wildcard_enum_match_arm)]
pub fn handle_key(key: DecodedKey) {
/// Tabulation char
......@@ -171,11 +172,16 @@ impl KeyQueue {
DecodedKey::RawKey(KeyCode::ArrowLeft) => Self::send_csi("1D"),
DecodedKey::Unicode(TAB) if shift_pressed => Self::send_csi("2"),
DecodedKey::Unicode(DEL) if alt_pressed && ctrl_pressed => warn!("TODO: implement reboot"),
DecodedKey::Unicode('l') if alt_pressed => {
DecodedKey::Unicode('p') if alt_pressed => {
let mut logger = SYS_LOGGER.lock();
logger.display_messages();
logger.clear();
},
DecodedKey::Unicode('l') if alt_pressed => {
let mut logger = SYS_LOGGER.lock();
logger.display_next_message();
logger.remove_oldest();
},
DecodedKey::Unicode(key) => Self::push_key(key),
_ => {},
}
......
......@@ -2,6 +2,7 @@
//!
//! This kernel is meant to be minimal and make easy the loading of other external modules
pub mod device;
pub mod interrupt;
pub mod memory;
pub mod screen;
......@@ -15,9 +16,7 @@ use x86_64::VirtAddr;
use self::memory::paging::{PhysFrameAllocator, MAPPER};
use self::memory::vmm::VIRTUAL_MEMORY_MANAGER;
use crate::kernel::interrupt::cmos::CMOS;
use crate::kernel::screen::buffer::Buffer;
use crate::println;
/// Initializes the kernel
pub fn init(boot_info: &'static mut BootInfo) {
......@@ -65,9 +64,10 @@ pub fn init(boot_info: &'static mut BootInfo) {
// Initialization of the CPU interruptions
interrupt::init(rsdp_addr);
info!("Interruptions initialized");
println!("Hello world!");
println!("It is currently {}.", CMOS.rtc());
// Initialization of the devices
device::init();
info!("Devices initialized");
}
//! Implementation of a simple competitive task executor
//! Implementation of a simple cooperative task executor
use alloc::collections::BTreeMap;
use alloc::sync::Arc;
......
......@@ -57,14 +57,22 @@
#![feature(const_mut_refs)]
#![feature(const_trait_impl)]
#![feature(custom_test_frameworks)]
#![feature(error_in_core)]
#![feature(extend_one)]
#![feature(int_roundings)]
#![feature(iter_advance_by)]
#![feature(new_uninit)]
#![feature(no_coverage)]
#![feature(strict_provenance)]
#![feature(trait_upcasting)]
#![feature(vec_into_raw_parts)]
#![reexport_test_harness_main = "test_main"]
#![test_runner(test::runner)]
extern crate alloc;
mod encoding;
mod fs;
mod kernel;
mod macros;
mod mutex;
......@@ -74,13 +82,18 @@ mod user;
#[cfg(test)]
mod test;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::alloc::Layout;
use bootloader_api::config::{Mapping, Mappings};
use bootloader_api::{entry_point, BootInfo, BootloaderConfig};
use kernel::device::storage::Device;
use kernel::device::{STORAGE_CONTROLLERS, UEFI_UUID};
use kernel::task::executor::Executor;
use kernel::task::Task;
use log::{logger, set_logger, set_max_level, LevelFilter};
use spin::Mutex;
#[cfg(test)]
use test::exit;
use user::tty::print_key;
......@@ -88,6 +101,7 @@ use user::tty::print_key;
use x86_64::instructions;
use self::syslog::SYS_LOGGER;
use crate::kernel::interrupt::cmos::CMOS;
/// Configuration of the bootloader
///
......@@ -108,12 +122,31 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
kernel::init(boot_info);
let storage_devices = STORAGE_CONTROLLERS
.get()
.expect("Storage controllers have not been initialized yet")
.iter()
.flat_map(|controller| controller.lock().devices())
.collect::<Vec<Arc<Mutex<dyn Device + Send>>>>()
.into_iter()
.filter(|device| device.lock().uuid() != UEFI_UUID)
.collect::<Vec<Arc<Mutex<dyn Device + Send>>>>();
if let Some(device) = storage_devices.first() {
fs::init(&Arc::clone(device));
}
#[cfg(test)]
{
test_main();
exit();
};
println!("Hello world!");
let rtc = CMOS.rtc();
println!("It is currently : {:0>2}:{:0>2}:{:0>2} (UTC+0)", rtc.hour, rtc.minute, rtc.second);
let mut executor = Executor::new();
executor.spawn(Task::new(print_key()));
executor.run();
......
//! Simple logger for `SkavOS`
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::string::String;
use core::fmt::Display;
use core::sync::atomic::{AtomicU64, Ordering};
use circular_buffer::CircularBuffer;
use log::{Level, Log, Metadata, Record};
use crate::kernel::screen::color::{Color, CYAN, ORANGE, RED, WHITE, YELLOW};
......@@ -85,19 +85,32 @@ impl Message {
#[derive(Debug)]
pub struct SysLog {
/// Messages that have not been flushed yet
messages: BTreeMap<MessageId, Message>,
messages: CircularBuffer<100, Message>,
}
impl SysLog {
/// Creates a new logger
const fn new() -> Self {
Self {
messages: BTreeMap::new(),
messages: CircularBuffer::new(),
}
}
/// Prints the next message that have not been displayed yet
pub fn display_next_message(&mut self) {
if self.messages.is_empty() {
println!("Empty logger");
return;
}
// SAFETY: `self.messages` is not empty
unsafe {
self.messages.front().unwrap_unchecked().print();
};
}
/// Prints all messages contained in the logger
pub fn display_messages(&mut self) {
pub fn display_messages(&self) {
if self.messages.is_empty() {
println!("Empty logger");
return;
......@@ -105,11 +118,16 @@ impl SysLog {
println!("Logs:");
for message in self.messages.values() {
for message in &self.messages {
message.print();
}
}
/// Removes the oldest message saved in the logger
pub fn remove_oldest(&mut self) {
self.messages.pop_front();
}
/// Removes all the messages saved in the logger
pub fn clear(&mut self) {
self.messages.clear();
......@@ -124,12 +142,7 @@ impl Log for Locked<SysLog> {
fn log(&self, record: &Record) {
let content = format!("{}", record.args());
let message = Message::new(record.level(), content);
let message_id = message.id;
assert!(
self.lock().messages.insert(message.id, message).is_none(),
"The message with ID {message_id} has already been logged"
);
self.lock().messages.push_back(message);
}
fn flush(&self) {}
......@@ -182,14 +195,15 @@ mod test {
.args(format_args!("Hello world! {}", 42_usize))
.build(),
);
let previous_message_id = NEXT_MESSAGE_ID.load(Ordering::Relaxed) - 1;
assert_eq!(&syslog.lock().messages[&MessageId(previous_message_id)], &Message {
assert_eq!(syslog.lock().messages.back().unwrap(), &Message {
id: MessageId(previous_message_id),
level: Level::Info,
content: "Hello world! 42".to_owned()
});
syslog.log(&Record::builder().level(Level::Warn).args(format_args!("1 + {} = 2", 1_usize)).build());
assert_eq!(&syslog.lock().messages[&MessageId(previous_message_id + 1)], &Message {
assert_eq!(syslog.lock().messages.back().unwrap(), &Message {
id: MessageId(previous_message_id + 1),
level: Level::Warn,
content: "1 + 1 = 2".to_owned()
......