Add simple uart driver, more functionality etc etc etc
This commit is contained in:
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Build artifacts
|
||||||
|
target/
|
||||||
|
|
||||||
|
# UEFI boot partition (built output)
|
||||||
|
esp/
|
||||||
|
|
||||||
|
# QEMU OVMF variable store (written at runtime)
|
||||||
|
OVMF_VARS*.fd
|
||||||
|
|
||||||
|
# Cargo config is per-machine (custom linker/target settings)
|
||||||
|
.cargo/
|
||||||
|
|
||||||
|
# Editor / IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS junk
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
12
kernel/Cargo.lock
generated
12
kernel/Cargo.lock
generated
@@ -5,3 +5,15 @@ version = 4
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "kernel"
|
name = "kernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"uefi",
|
||||||
|
"x86_drivers",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uefi"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x86_drivers"
|
||||||
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -4,3 +4,5 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
uefi = {path = "../uefi"}
|
||||||
|
x86_drivers = {path = "../x86_drivers"}
|
||||||
|
|||||||
10
kernel/run.sh
Executable file
10
kernel/run.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
mkdir -p esp/EFI/BOOT
|
||||||
|
cargo b --target x86_64-unknown-uefi --release
|
||||||
|
cp target/x86_64-unknown-uefi/release/kernel.efi esp/EFI/BOOT/BOOTX64.EFI
|
||||||
|
|
||||||
|
qemu-system-x86_64 \
|
||||||
|
-drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd \
|
||||||
|
-drive if=pflash,format=raw,file=OVMF_VARS.4m.fd \
|
||||||
|
-drive format=raw,file=fat:rw:esp \
|
||||||
|
-net none \
|
||||||
|
-serial stdio
|
||||||
64
kernel/src/lib.rs
Normal file
64
kernel/src/lib.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![deny(clippy::perf, clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use core::{
|
||||||
|
mem::MaybeUninit,
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use uefi::EfiSystemTable;
|
||||||
|
pub use x86_drivers::prelude::*;
|
||||||
|
use x86_drivers::uart::Uart1;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! print {
|
||||||
|
($($tt:tt)+) => {
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
'body: {
|
||||||
|
use core::fmt::Write as _;
|
||||||
|
if let Some(uart) = $crate::uart_is_init() {
|
||||||
|
let _ = core::write!(uart, $($tt)*);
|
||||||
|
break 'body;
|
||||||
|
}
|
||||||
|
let table = unsafe { $crate::TABLE.assume_init().as_mut().unwrap_unchecked() };
|
||||||
|
let _ = core::write!(table.std_out(), $($tt)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Panics
|
||||||
|
/// Panics if the UART `MaybeUninit` pointer is null (should never happen).
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
pub fn uart_is_init() -> Option<&'static mut Uart<Uart1>> {
|
||||||
|
unsafe {
|
||||||
|
UART.0
|
||||||
|
.load(Ordering::Relaxed)
|
||||||
|
.then_some(UART.1.as_mut_ptr().as_mut().expect("uart ptr is non-null"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static mut TABLE: MaybeUninit<*mut EfiSystemTable> = MaybeUninit::uninit();
|
||||||
|
static mut UART: (AtomicBool, MaybeUninit<Uart<Uart1>>) =
|
||||||
|
(AtomicBool::new(false), MaybeUninit::uninit());
|
||||||
|
|
||||||
|
#[repr(align(8))]
|
||||||
|
pub struct Aligned<T>(pub T);
|
||||||
|
|
||||||
|
#[used]
|
||||||
|
pub static mut MEMORY_MAP_ARENA: Aligned<[u8; 32 * 1_024]> = Aligned([0; _]);
|
||||||
|
|
||||||
|
/// # Panics
|
||||||
|
/// Panics if UART has already been specified.
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
pub fn specify_uart(uart: Uart<Uart1>) {
|
||||||
|
let is_specified = unsafe { UART.0.load(Ordering::Acquire) };
|
||||||
|
assert!(!is_specified, "Uart already specified");
|
||||||
|
unsafe { UART.1.write(uart) };
|
||||||
|
unsafe { UART.0.store(true, Ordering::Release) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
print!("PANIC: {:?}\r\n", info);
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
@@ -1,3 +1,109 @@
|
|||||||
fn main() {
|
#![no_std]
|
||||||
println!("Hello, world!");
|
#![no_main]
|
||||||
|
#![deny(clippy::perf, clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use uefi::{EfiMemoryType, EfiSystemTable, ExitBootServicesKey, Handle, UefiStatus};
|
||||||
|
|
||||||
|
use kernel::print;
|
||||||
|
use x86_drivers::gdt::{DPL, GdtEntry, GdtPointer, GdtSelector};
|
||||||
|
pub use x86_drivers::prelude::*;
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
extern "efiapi" fn efi_main(handle: Handle, table: *mut EfiSystemTable) -> UefiStatus {
|
||||||
|
let peripherals = x86_drivers::core::Core::take();
|
||||||
|
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
unsafe {
|
||||||
|
kernel::TABLE.write(table)
|
||||||
|
};
|
||||||
|
let table = unsafe { table.as_mut().expect("efi table ptr is non-null") };
|
||||||
|
let res = table.std_out().print("Hello world\r\n");
|
||||||
|
if let Err(Some(e)) = res {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel::specify_uart(peripherals.uart1.init());
|
||||||
|
|
||||||
|
print!("Hello from uart!\r\n");
|
||||||
|
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
let arena = unsafe { &mut kernel::MEMORY_MAP_ARENA.0 };
|
||||||
|
let (_key, map, _version) = table
|
||||||
|
.boot_services()
|
||||||
|
.memory_map(arena)
|
||||||
|
.expect("memory map failed");
|
||||||
|
for el in map
|
||||||
|
.filter_map(|segment| segment.memory_type.interpret().map(|f| (f, segment)))
|
||||||
|
.filter(|(t, _segment)| *t == EfiMemoryType::ConventionalMemory)
|
||||||
|
{
|
||||||
|
print!("Map element {:?}\r\n", el);
|
||||||
|
}
|
||||||
|
|
||||||
|
let default = ExitBootServicesKey::default();
|
||||||
|
print!("Default {:?}", default);
|
||||||
|
|
||||||
|
let boot_services = table.boot_services();
|
||||||
|
boot_services
|
||||||
|
.exit_boot_services(handle, arena)
|
||||||
|
.expect("exit boot services failed");
|
||||||
|
print!("Exited boot services!\r\n");
|
||||||
|
|
||||||
|
print!("kernel_code: {:#018x}\r\n", GdtEntry::kernel_code().raw());
|
||||||
|
print!("kernel_data: {:#018x}\r\n", GdtEntry::kernel_data().raw());
|
||||||
|
|
||||||
|
print!(
|
||||||
|
"Initiating GDT to {:?}, CODE_SEL: {:?}, DATA SEL: {:?}",
|
||||||
|
GDT_TABLE, GDT_KERNEL_CODE_SELECTOR, GDT_KERNEL_DATA_SELECTOR
|
||||||
|
);
|
||||||
|
gdt_init();
|
||||||
|
print!("Initiated GDT");
|
||||||
|
#[allow(clippy::empty_loop)]
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GDT_TABLE: [GdtEntry; 3] = [
|
||||||
|
GdtEntry::null(),
|
||||||
|
GdtEntry::kernel_code(),
|
||||||
|
GdtEntry::kernel_data(),
|
||||||
|
];
|
||||||
|
|
||||||
|
static GDT_KERNEL_CODE_SELECTOR: GdtSelector = GdtSelector::new(DPL::Kernel, &GDT_TABLE, 1);
|
||||||
|
static GDT_KERNEL_DATA_SELECTOR: GdtSelector = GdtSelector::new(DPL::Kernel, &GDT_TABLE, 2);
|
||||||
|
|
||||||
|
fn gdt_init() {
|
||||||
|
let ptr = GdtPointer::new(&GDT_TABLE);
|
||||||
|
print!("GDTR: {:?}\r\n", ptr,);
|
||||||
|
print!("GDT_TABLE addr: {:#x}\r\n", &GDT_TABLE as *const _ as u64);
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"lgdt [{}]",
|
||||||
|
in(reg) &ptr,
|
||||||
|
options(readonly, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
print!("Passed lgdt");
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"push {code_sel}",
|
||||||
|
"lea {tmp}, [rip + 2f]",
|
||||||
|
"push {tmp}",
|
||||||
|
"retfq",
|
||||||
|
"2:",
|
||||||
|
code_sel = const GDT_KERNEL_CODE_SELECTOR.raw() as u64,
|
||||||
|
tmp = out(reg) _,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
print!("CS reloaded\r\n");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov ds, {data_sel:x}",
|
||||||
|
"mov es, {data_sel:x}",
|
||||||
|
"mov fs, {data_sel:x}",
|
||||||
|
"mov gs, {data_sel:x}",
|
||||||
|
"mov ss, {data_sel:x}",
|
||||||
|
data_sel = in(reg) GDT_KERNEL_DATA_SELECTOR.raw(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
print!("data segments reloaded\r\n");
|
||||||
}
|
}
|
||||||
|
|||||||
7
uefi/Cargo.lock
generated
Normal file
7
uefi/Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uefi"
|
||||||
|
version = "0.1.0"
|
||||||
6
uefi/Cargo.toml
Normal file
6
uefi/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "uefi"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
BIN
uefi/libstatus.rlib
Normal file
BIN
uefi/libstatus.rlib
Normal file
Binary file not shown.
317
uefi/src/lib.rs
Normal file
317
uefi/src/lib.rs
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
mod memory;
|
||||||
|
mod status;
|
||||||
|
pub use memory::EfiMemoryType;
|
||||||
|
pub use status::UefiStatus;
|
||||||
|
|
||||||
|
use crate::{memory::EfiMemoryDescriptor, status::RawUefiStatus};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct EfiTableHeader {
|
||||||
|
signature: u64,
|
||||||
|
revision: u32,
|
||||||
|
header_size: u32,
|
||||||
|
crc32: u32,
|
||||||
|
reserved: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct EfiSystemTable {
|
||||||
|
efi_table_header: EfiTableHeader,
|
||||||
|
firmware_vendor: *const u16,
|
||||||
|
firmware_revision: u32,
|
||||||
|
console_in_handle: Handle,
|
||||||
|
// TODO: Handle input.
|
||||||
|
efi_simple_text_input_protocol: *mut core::ffi::c_void,
|
||||||
|
console_out_handle: Handle,
|
||||||
|
console_out: *mut SimpleTextOutputProtocol,
|
||||||
|
standard_error_handle: Handle,
|
||||||
|
standard_error: *mut SimpleTextOutputProtocol,
|
||||||
|
runtime_services: *mut core::ffi::c_void,
|
||||||
|
boot_services: *mut EfiBootServices,
|
||||||
|
number_of_table_entries: usize,
|
||||||
|
configuration_table: *mut core::ffi::c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SimpleTextOutputProtocol {
|
||||||
|
reset: extern "efiapi" fn(this: *mut Self, extended_verification: bool) -> RawUefiStatus,
|
||||||
|
output_string: extern "efiapi" fn(this: *mut Self, str: *const u16) -> RawUefiStatus,
|
||||||
|
test_string: extern "efiapi" fn(this: *mut Self, str: *const u16) -> RawUefiStatus,
|
||||||
|
query_mode: extern "efiapi" fn(
|
||||||
|
this: *mut Self,
|
||||||
|
mode_number: usize,
|
||||||
|
n_columns: *mut usize,
|
||||||
|
n_rows: *mut usize,
|
||||||
|
) -> RawUefiStatus,
|
||||||
|
set_mode: extern "efiapi" fn(this: *mut Self, mode_number: usize) -> RawUefiStatus,
|
||||||
|
set_atribute: extern "efiapi" fn(this: *mut Self, attribute: usize) -> RawUefiStatus,
|
||||||
|
clear_screen: extern "efiapi" fn(this: *mut Self) -> RawUefiStatus,
|
||||||
|
set_cursor_position:
|
||||||
|
extern "efiapi" fn(this: *mut Self, column: usize, row: usize) -> RawUefiStatus,
|
||||||
|
enable_cursor: extern "efiapi" fn(this: *mut Self, visible: bool) -> RawUefiStatus,
|
||||||
|
mode: *mut SimpleTextOutputMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SimpleTextOutputMode {
|
||||||
|
pub max_mode: i32,
|
||||||
|
pub mode: i32,
|
||||||
|
pub attribute: i32,
|
||||||
|
pub cursor_column: i32,
|
||||||
|
pub cursor_row: i32,
|
||||||
|
pub cursor_visible: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
/// Identifier used by UEFI.
|
||||||
|
pub struct Handle(*mut ());
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ExitBootServicesKey(usize);
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct EfiBootServices {
|
||||||
|
hdr: EfiTableHeader,
|
||||||
|
//
|
||||||
|
// Task Priority Services
|
||||||
|
//
|
||||||
|
raise_tpl: *const core::ffi::c_void,
|
||||||
|
restore_tpl: *const core::ffi::c_void,
|
||||||
|
//
|
||||||
|
// Memory Services
|
||||||
|
//
|
||||||
|
allocate_pages: *const core::ffi::c_void,
|
||||||
|
free_pages: *const core::ffi::c_void,
|
||||||
|
get_memory_map: extern "efiapi" fn(
|
||||||
|
memory_map_size: *mut usize, // Passed by us as core::mem::size_of(descriptors) returned as size in bytes of
|
||||||
|
// allocated descriptors.
|
||||||
|
descriptors: *mut u8,
|
||||||
|
key: *mut ExitBootServicesKey, // Key used to exit bootloader
|
||||||
|
descriptor_size: *mut usize, // This ought to be
|
||||||
|
descriptor_version: *mut u32,
|
||||||
|
) -> RawUefiStatus,
|
||||||
|
allocate_pool: *const core::ffi::c_void, // EFI 1.0
|
||||||
|
free_pool: *const core::ffi::c_void,
|
||||||
|
//
|
||||||
|
// Event & Timer Services
|
||||||
|
//
|
||||||
|
create_event: *const core::ffi::c_void,
|
||||||
|
set_timer: *const core::ffi::c_void,
|
||||||
|
wait_for_event: *const core::ffi::c_void,
|
||||||
|
signal_event: *const core::ffi::c_void,
|
||||||
|
close_event: *const core::ffi::c_void,
|
||||||
|
check_event: *const core::ffi::c_void,
|
||||||
|
//
|
||||||
|
// Protocol Handler Services
|
||||||
|
//
|
||||||
|
install_protocol_interface: *const core::ffi::c_void,
|
||||||
|
reinstall_protocol_interface: *const core::ffi::c_void,
|
||||||
|
uninstall_protocol_interface: *const core::ffi::c_void,
|
||||||
|
handle_protocol: *const core::ffi::c_void,
|
||||||
|
reserved: *const core::ffi::c_void,
|
||||||
|
register_protocol_notify: *const core::ffi::c_void,
|
||||||
|
locate_handle: *const core::ffi::c_void,
|
||||||
|
locate_device_path: *const core::ffi::c_void,
|
||||||
|
install_configuration_table: *const core::ffi::c_void,
|
||||||
|
//
|
||||||
|
// Image Services
|
||||||
|
//
|
||||||
|
load_image: *const core::ffi::c_void,
|
||||||
|
start_image: *const core::ffi::c_void,
|
||||||
|
exit: *const core::ffi::c_void,
|
||||||
|
unload_image: *const core::ffi::c_void,
|
||||||
|
exit_boot_services:
|
||||||
|
extern "efiapi" fn(efi_handle: Handle, key: ExitBootServicesKey) -> RawUefiStatus,
|
||||||
|
//
|
||||||
|
// Miscellaneous Services
|
||||||
|
//
|
||||||
|
get_next_monotonic_count: *const core::ffi::c_void,
|
||||||
|
stall: *const core::ffi::c_void,
|
||||||
|
set_watchdog_timer: *const core::ffi::c_void,
|
||||||
|
//
|
||||||
|
// DriverSupport Services
|
||||||
|
//
|
||||||
|
connect_controller: *const core::ffi::c_void,
|
||||||
|
disconnect_controller: *const core::ffi::c_void,
|
||||||
|
//
|
||||||
|
// Open and Close Protocol Services
|
||||||
|
//
|
||||||
|
open_protocol: *const core::ffi::c_void,
|
||||||
|
close_protocol: *const core::ffi::c_void,
|
||||||
|
open_protocol_information: *const core::ffi::c_void,
|
||||||
|
//
|
||||||
|
// Library Services
|
||||||
|
//
|
||||||
|
protocols_per_handle: *const core::ffi::c_void,
|
||||||
|
locate_handle_buffer: *const core::ffi::c_void,
|
||||||
|
locate_protocol: *const core::ffi::c_void,
|
||||||
|
install_multiple_protocol_interfaces: *const core::ffi::c_void,
|
||||||
|
uninstall_multiple_protocol_interfaces: *const core::ffi::c_void,
|
||||||
|
//
|
||||||
|
// 32-bit CRC Services
|
||||||
|
//
|
||||||
|
calculate_crc32: *const core::ffi::c_void,
|
||||||
|
//
|
||||||
|
// Miscellaneous Services
|
||||||
|
//
|
||||||
|
copy_mem: *const core::ffi::c_void,
|
||||||
|
set_mem: *const core::ffi::c_void,
|
||||||
|
create_event_ex: *const core::ffi::c_void, // UEFI 2.0+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleTextOutputProtocol {
|
||||||
|
pub fn print(&mut self, text: &str) -> Result<(), Option<UefiStatus>> {
|
||||||
|
let f = self.output_string;
|
||||||
|
|
||||||
|
let data = text.encode_utf16();
|
||||||
|
let mut buffer = [0; 1024];
|
||||||
|
buffer
|
||||||
|
.iter_mut()
|
||||||
|
.zip(data)
|
||||||
|
.for_each(|(target, val)| *target = val);
|
||||||
|
|
||||||
|
if buffer[buffer.len() - 1] != 0 {
|
||||||
|
return Err(Some(UefiStatus::BufferTooSmall));
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(val) = f(core::ptr::from_mut(self), buffer.as_ptr()).interpret() else {
|
||||||
|
return Err(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
if val.is_success() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Some(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExitBootServicesKey {
|
||||||
|
pub const fn default() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PageIter<'a> {
|
||||||
|
buffer: &'a [u8],
|
||||||
|
size: usize,
|
||||||
|
offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PageIter<'a> {
|
||||||
|
const fn new(buffer: &'a [u8], size: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
buffer,
|
||||||
|
size,
|
||||||
|
offset: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for PageIter<'a> {
|
||||||
|
type Item = &'a EfiMemoryDescriptor;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if (self.offset + core::mem::size_of::<EfiMemoryDescriptor>()) >= self.buffer.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let start = self.offset;
|
||||||
|
self.offset += self.size;
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
Some(unsafe {
|
||||||
|
self.buffer
|
||||||
|
.as_ptr()
|
||||||
|
.add(start)
|
||||||
|
.cast::<EfiMemoryDescriptor>()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EfiBootServices {
|
||||||
|
pub fn memory_map<'a, 'b>(
|
||||||
|
&mut self,
|
||||||
|
arena: &'a mut [u8],
|
||||||
|
) -> Result<(ExitBootServicesKey, PageIter<'b>, u32), Option<UefiStatus>>
|
||||||
|
where
|
||||||
|
'a: 'b,
|
||||||
|
{
|
||||||
|
let mut key = ExitBootServicesKey::default();
|
||||||
|
let mut size = 0;
|
||||||
|
let mut version = 0;
|
||||||
|
let mut buffer_size = core::mem::size_of_val(arena);
|
||||||
|
|
||||||
|
let code = (self.get_memory_map)(
|
||||||
|
core::ptr::from_mut(&mut buffer_size),
|
||||||
|
arena.as_mut_ptr(),
|
||||||
|
core::ptr::from_mut(&mut key),
|
||||||
|
core::ptr::from_mut(&mut size),
|
||||||
|
core::ptr::from_mut(&mut version),
|
||||||
|
);
|
||||||
|
|
||||||
|
let Some(code) = code.interpret() else {
|
||||||
|
return Err(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
if !code.is_success() {
|
||||||
|
return Err(Some(code));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
size >= core::mem::size_of::<EfiMemoryDescriptor>(),
|
||||||
|
"size {}",
|
||||||
|
size
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok((key, PageIter::new(&arena[..buffer_size], size), version))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_boot_services(
|
||||||
|
&mut self,
|
||||||
|
handle: Handle,
|
||||||
|
arena: &mut [u8],
|
||||||
|
) -> Result<(), Option<UefiStatus>> {
|
||||||
|
let (key, ..) = self.memory_map(arena)?;
|
||||||
|
|
||||||
|
let Some(res) = (self.exit_boot_services)(handle, key).interpret() else {
|
||||||
|
return Err(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
if res.is_success() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(Some(res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EfiSystemTable {
|
||||||
|
pub const fn std_out(&mut self) -> &mut SimpleTextOutputProtocol {
|
||||||
|
unsafe { self.console_out.as_mut().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn std_err(&mut self) -> &mut SimpleTextOutputProtocol {
|
||||||
|
unsafe { self.standard_error.as_mut().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn boot_services(&mut self) -> &mut EfiBootServices {
|
||||||
|
unsafe { self.boot_services.as_mut().unwrap() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Write for SimpleTextOutputProtocol {
|
||||||
|
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||||
|
let _ = self.print(s);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ExitBootServicesKey {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
99
uefi/src/memory.rs
Normal file
99
uefi/src/memory.rs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum EfiMemoryType {
|
||||||
|
ReservedMemoryType = 0,
|
||||||
|
LoaderCode = 1,
|
||||||
|
LoaderData = 2,
|
||||||
|
BootServicesCode = 3,
|
||||||
|
BootServicesData = 4,
|
||||||
|
RuntimeServicesCode = 5,
|
||||||
|
RuntimeServicesData = 6,
|
||||||
|
ConventionalMemory = 7,
|
||||||
|
UnusableMemory = 8,
|
||||||
|
AcpiReclaimMemory = 9,
|
||||||
|
AcpiMemoryNvs = 10,
|
||||||
|
MemoryMappedIo = 11,
|
||||||
|
MemoryMappedIoPortSpace = 12,
|
||||||
|
PalCode = 13,
|
||||||
|
PersistentMemory = 14,
|
||||||
|
UnacceptedMemoryType = 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct RawMemoryType(u32);
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct EfiPhysicalAddress(u64);
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct EfiVirtualAddress(u64);
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct EfiMemoryDescriptor {
|
||||||
|
pub memory_type: RawMemoryType,
|
||||||
|
pub physical_start: EfiPhysicalAddress,
|
||||||
|
pub virtual_start: EfiVirtualAddress,
|
||||||
|
pub number_of_pages: u64,
|
||||||
|
pub attribute: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawMemoryType {
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn interpret(&self) -> Option<EfiMemoryType> {
|
||||||
|
EfiMemoryType::try_from_raw(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EfiMemoryType {
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn try_from_raw(raw: &RawMemoryType) -> Option<Self> {
|
||||||
|
Self::from_u32(raw.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn as_u32(self) -> u32 {
|
||||||
|
self as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn from_u32(value: u32) -> Option<Self> {
|
||||||
|
Some(match value {
|
||||||
|
0 => Self::ReservedMemoryType,
|
||||||
|
1 => Self::LoaderCode,
|
||||||
|
2 => Self::LoaderData,
|
||||||
|
3 => Self::BootServicesCode,
|
||||||
|
4 => Self::BootServicesData,
|
||||||
|
5 => Self::RuntimeServicesCode,
|
||||||
|
6 => Self::RuntimeServicesData,
|
||||||
|
7 => Self::ConventionalMemory,
|
||||||
|
8 => Self::UnusableMemory,
|
||||||
|
9 => Self::AcpiReclaimMemory,
|
||||||
|
10 => Self::AcpiMemoryNvs,
|
||||||
|
11 => Self::MemoryMappedIo,
|
||||||
|
12 => Self::MemoryMappedIoPortSpace,
|
||||||
|
13 => Self::PalCode,
|
||||||
|
14 => Self::PersistentMemory,
|
||||||
|
15 => Self::UnacceptedMemoryType,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EfiMemoryType> for u32 {
|
||||||
|
fn from(memory_type: EfiMemoryType) -> Self {
|
||||||
|
memory_type.as_u32()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u32> for EfiMemoryType {
|
||||||
|
type Error = u32;
|
||||||
|
|
||||||
|
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||||
|
Self::from_u32(value).ok_or(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
221
uefi/src/status.rs
Normal file
221
uefi/src/status.rs
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
#![allow(
|
||||||
|
clippy::module_name_repetitions,
|
||||||
|
reason = "UefiStatus is the canonical UEFI name"
|
||||||
|
)]
|
||||||
|
|
||||||
|
/// High bit mask — set on all error codes, clear on warnings and success.
|
||||||
|
const ERROR: usize = 1 << (usize::BITS - 1);
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct RawUefiStatus(usize);
|
||||||
|
|
||||||
|
/// UEFI status code (UEFI spec Appendix D).
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
#[repr(usize)]
|
||||||
|
pub enum UefiStatus {
|
||||||
|
// ── Success (Table D.2) ──────────────────────────────────────────
|
||||||
|
/// The operation completed successfully.
|
||||||
|
Success = 0,
|
||||||
|
|
||||||
|
// ── Warnings (Table D.4, high bit clear) ─────────────────────────
|
||||||
|
/// String contained characters the device could not render.
|
||||||
|
WarnUnknownGlyph = 1,
|
||||||
|
/// Handle closed, but file was not deleted.
|
||||||
|
WarnDeleteFailure = 2,
|
||||||
|
/// Handle closed, but data was not flushed properly.
|
||||||
|
WarnWriteFailure = 3,
|
||||||
|
/// Buffer was too small; data was truncated.
|
||||||
|
WarnBufferTooSmall = 4,
|
||||||
|
/// Data has not been updated within local policy timeframe.
|
||||||
|
WarnStaleData = 5,
|
||||||
|
/// Buffer contains UEFI-compliant file system.
|
||||||
|
WarnFileSystem = 6,
|
||||||
|
/// Operation will be processed across a system reset.
|
||||||
|
WarnResetRequired = 7,
|
||||||
|
|
||||||
|
// ── Errors (Table D.3, high bit set) ─────────────────────────────
|
||||||
|
/// Image failed to load.
|
||||||
|
LoadError = ERROR | 1,
|
||||||
|
/// A parameter was incorrect.
|
||||||
|
InvalidParameter = ERROR | 2,
|
||||||
|
/// Operation is not supported.
|
||||||
|
Unsupported = ERROR | 3,
|
||||||
|
/// Buffer was not the proper size for the request.
|
||||||
|
BadBufferSize = ERROR | 4,
|
||||||
|
/// Buffer is not large enough to hold the requested data.
|
||||||
|
BufferTooSmall = ERROR | 5,
|
||||||
|
/// No data pending upon return.
|
||||||
|
NotReady = ERROR | 6,
|
||||||
|
/// Physical device reported an error.
|
||||||
|
DeviceError = ERROR | 7,
|
||||||
|
/// Device cannot be written to.
|
||||||
|
WriteProtected = ERROR | 8,
|
||||||
|
/// A resource has run out.
|
||||||
|
OutOfResources = ERROR | 9,
|
||||||
|
/// File system inconsistency detected.
|
||||||
|
VolumeCorrupted = ERROR | 10,
|
||||||
|
/// No more space on the file system.
|
||||||
|
VolumeFull = ERROR | 11,
|
||||||
|
/// Device does not contain any medium.
|
||||||
|
NoMedia = ERROR | 12,
|
||||||
|
/// Medium in the device has changed since last access.
|
||||||
|
MediaChanged = ERROR | 13,
|
||||||
|
/// Item was not found.
|
||||||
|
NotFound = ERROR | 14,
|
||||||
|
/// Access was denied.
|
||||||
|
AccessDenied = ERROR | 15,
|
||||||
|
/// Server was not found or did not respond.
|
||||||
|
NoResponse = ERROR | 16,
|
||||||
|
/// Mapping to a device does not exist.
|
||||||
|
NoMapping = ERROR | 17,
|
||||||
|
/// Timeout expired.
|
||||||
|
Timeout = ERROR | 18,
|
||||||
|
/// Protocol has not been started.
|
||||||
|
NotStarted = ERROR | 19,
|
||||||
|
/// Protocol has already been started.
|
||||||
|
AlreadyStarted = ERROR | 20,
|
||||||
|
/// Operation was aborted.
|
||||||
|
Aborted = ERROR | 21,
|
||||||
|
/// ICMP error occurred during network operation.
|
||||||
|
IcmpError = ERROR | 22,
|
||||||
|
/// TFTP error occurred during network operation.
|
||||||
|
TftpError = ERROR | 23,
|
||||||
|
/// Protocol error occurred during network operation.
|
||||||
|
ProtocolError = ERROR | 24,
|
||||||
|
/// Internal version incompatible with caller's requested version.
|
||||||
|
IncompatibleVersion = ERROR | 25,
|
||||||
|
/// Operation not performed due to a security violation.
|
||||||
|
SecurityViolation = ERROR | 26,
|
||||||
|
/// CRC error detected.
|
||||||
|
CrcError = ERROR | 27,
|
||||||
|
/// Beginning or end of media was reached.
|
||||||
|
EndOfMedia = ERROR | 28,
|
||||||
|
/// End of file was reached.
|
||||||
|
EndOfFile = ERROR | 31,
|
||||||
|
/// Specified language was invalid.
|
||||||
|
InvalidLanguage = ERROR | 32,
|
||||||
|
/// Security status of data is unknown or compromised.
|
||||||
|
CompromisedData = ERROR | 33,
|
||||||
|
/// Address conflict during address allocation.
|
||||||
|
IpAddressConflict = ERROR | 34,
|
||||||
|
/// HTTP error occurred during network operation.
|
||||||
|
HttpError = ERROR | 35,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawUefiStatus {
|
||||||
|
pub const fn interpret(&self) -> Option<UefiStatus> {
|
||||||
|
UefiStatus::try_from_raw(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UefiStatus {
|
||||||
|
/// Tries to convert from a raw uefi status.
|
||||||
|
pub const fn try_from_raw(raw: &RawUefiStatus) -> Option<Self> {
|
||||||
|
Self::from_usize(raw.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this status represents an error (high bit set).
|
||||||
|
#[must_use]
|
||||||
|
pub const fn is_error(self) -> bool {
|
||||||
|
self.as_usize() & ERROR != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this status represents a warning (non-zero, high bit clear).
|
||||||
|
#[must_use]
|
||||||
|
pub const fn is_warning(self) -> bool {
|
||||||
|
let v = self.as_usize();
|
||||||
|
v != 0 && v & ERROR == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this status is `Success`.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn is_success(self) -> bool {
|
||||||
|
self.as_usize() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert to the raw `usize` representation.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn as_usize(self) -> usize {
|
||||||
|
self as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert from a raw `usize`. Returns `None` for unrecognised codes.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn from_usize(value: usize) -> Option<Self> {
|
||||||
|
let code = value & !ERROR;
|
||||||
|
if value & ERROR != 0 {
|
||||||
|
Self::from_error_code(code)
|
||||||
|
} else {
|
||||||
|
Self::from_warning_code(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn from_warning_code(code: usize) -> Option<Self> {
|
||||||
|
Some(match code {
|
||||||
|
0 => Self::Success,
|
||||||
|
1 => Self::WarnUnknownGlyph,
|
||||||
|
2 => Self::WarnDeleteFailure,
|
||||||
|
3 => Self::WarnWriteFailure,
|
||||||
|
4 => Self::WarnBufferTooSmall,
|
||||||
|
5 => Self::WarnStaleData,
|
||||||
|
6 => Self::WarnFileSystem,
|
||||||
|
7 => Self::WarnResetRequired,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn from_error_code(code: usize) -> Option<Self> {
|
||||||
|
Some(match code {
|
||||||
|
1 => Self::LoadError,
|
||||||
|
2 => Self::InvalidParameter,
|
||||||
|
3 => Self::Unsupported,
|
||||||
|
4 => Self::BadBufferSize,
|
||||||
|
5 => Self::BufferTooSmall,
|
||||||
|
6 => Self::NotReady,
|
||||||
|
7 => Self::DeviceError,
|
||||||
|
8 => Self::WriteProtected,
|
||||||
|
9 => Self::OutOfResources,
|
||||||
|
10 => Self::VolumeCorrupted,
|
||||||
|
11 => Self::VolumeFull,
|
||||||
|
12 => Self::NoMedia,
|
||||||
|
13 => Self::MediaChanged,
|
||||||
|
14 => Self::NotFound,
|
||||||
|
15 => Self::AccessDenied,
|
||||||
|
16 => Self::NoResponse,
|
||||||
|
17 => Self::NoMapping,
|
||||||
|
18 => Self::Timeout,
|
||||||
|
19 => Self::NotStarted,
|
||||||
|
20 => Self::AlreadyStarted,
|
||||||
|
21 => Self::Aborted,
|
||||||
|
22 => Self::IcmpError,
|
||||||
|
23 => Self::TftpError,
|
||||||
|
24 => Self::ProtocolError,
|
||||||
|
25 => Self::IncompatibleVersion,
|
||||||
|
26 => Self::SecurityViolation,
|
||||||
|
27 => Self::CrcError,
|
||||||
|
28 => Self::EndOfMedia,
|
||||||
|
31 => Self::EndOfFile,
|
||||||
|
32 => Self::InvalidLanguage,
|
||||||
|
33 => Self::CompromisedData,
|
||||||
|
34 => Self::IpAddressConflict,
|
||||||
|
35 => Self::HttpError,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UefiStatus> for usize {
|
||||||
|
fn from(status: UefiStatus) -> Self {
|
||||||
|
status.as_usize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<usize> for UefiStatus {
|
||||||
|
type Error = usize;
|
||||||
|
|
||||||
|
/// Returns `Err(raw_value)` if the code is not recognised.
|
||||||
|
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||||
|
Self::from_usize(value).ok_or(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
7
x86_drivers/Cargo.lock
generated
Normal file
7
x86_drivers/Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x86_drivers"
|
||||||
|
version = "0.1.0"
|
||||||
6
x86_drivers/Cargo.toml
Normal file
6
x86_drivers/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "x86_drivers"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
37
x86_drivers/src/core.rs
Normal file
37
x86_drivers/src/core.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
use crate::uart::{Uart1, Uart2, Uart3, Uart4};
|
||||||
|
|
||||||
|
static TAKEN: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub struct Core {
|
||||||
|
pub uart1: Uart1,
|
||||||
|
pub uart2: Uart2,
|
||||||
|
pub uart3: Uart3,
|
||||||
|
pub uart4: Uart4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Core {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
uart1: Uart1::new(),
|
||||||
|
uart2: Uart2::new(),
|
||||||
|
uart3: Uart3::new(),
|
||||||
|
uart4: Uart4::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all of the peripherals this core has.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If this has already been called it panics.
|
||||||
|
pub fn take() -> Self {
|
||||||
|
assert!(
|
||||||
|
!TAKEN.swap(true, Ordering::SeqCst),
|
||||||
|
"Core already initialized"
|
||||||
|
);
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
198
x86_drivers/src/gdt.rs
Normal file
198
x86_drivers/src/gdt.rs
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
#[repr(transparent)]
|
||||||
|
#[must_use]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GdtEntry {
|
||||||
|
inner: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl GdtEntry {
|
||||||
|
const LIMIT_L: u64 = 0;
|
||||||
|
const LIMIT_H: u64 = 15;
|
||||||
|
const BASE_L: u64 = 16;
|
||||||
|
const BASE_H: u64 = 39;
|
||||||
|
const ACCESS_L: u64 = 40;
|
||||||
|
const ACCESS_H: u64 = 47;
|
||||||
|
const FLAGS_L: u64 = 52;
|
||||||
|
const FLAGS_H: u64 = 55;
|
||||||
|
|
||||||
|
// NOTE: Skipping higher bits since they are zeroed.
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn access_code(&self) -> u8 {
|
||||||
|
self.inner as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn new(access_code: AccessFlags, flags: Flags) -> Self {
|
||||||
|
let mut rpr = 0u64;
|
||||||
|
rpr |= (access_code.to_bits() as u64) << Self::ACCESS_L;
|
||||||
|
rpr |= (flags.to_bits() as u64) << Self::FLAGS_L;
|
||||||
|
Self { inner: rpr }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn raw(&self) -> u64 {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn null() -> Self {
|
||||||
|
Self { inner: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn user_code() -> Self {
|
||||||
|
Self::new(
|
||||||
|
AccessFlags {
|
||||||
|
ring: DPL::User,
|
||||||
|
data_kind: DataKind::ReadableCodeContained,
|
||||||
|
},
|
||||||
|
Flags::new(true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn kernel_code() -> Self {
|
||||||
|
Self::new(
|
||||||
|
AccessFlags {
|
||||||
|
ring: DPL::Kernel,
|
||||||
|
data_kind: DataKind::ReadableCodeContained,
|
||||||
|
},
|
||||||
|
Flags::new(true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn kernel_data() -> Self {
|
||||||
|
Self::new(
|
||||||
|
AccessFlags {
|
||||||
|
ring: DPL::Kernel,
|
||||||
|
data_kind: DataKind::WriteableDataIncreasing,
|
||||||
|
},
|
||||||
|
Flags::new(false),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn user_data() -> Self {
|
||||||
|
Self::new(
|
||||||
|
AccessFlags {
|
||||||
|
ring: DPL::User,
|
||||||
|
data_kind: DataKind::WriteableDataIncreasing,
|
||||||
|
},
|
||||||
|
Flags::new(false),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[must_use]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DPL {
|
||||||
|
Kernel = 0,
|
||||||
|
Ring1 = 1,
|
||||||
|
Ring2 = 2,
|
||||||
|
User = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DataKind {
|
||||||
|
// Means descriptor == 0, executable = 0, direction = dc, pull to 0 for good measure.
|
||||||
|
System = 0b000,
|
||||||
|
// Desc == 1, exec = 0, direction = 1, W = 0
|
||||||
|
ProtectedDataDecreasing = 0b1010,
|
||||||
|
// Desc == 1, exec = 0, direction = 1, W = 1
|
||||||
|
WriteableDataDecreasing = 0b1011,
|
||||||
|
// Desc == 1, exec = 0, direction = 0, W = 0
|
||||||
|
ProtectedDataIncreasing = 0b1000,
|
||||||
|
// Desc == 1, exec = 0, direction = 0, W = 1
|
||||||
|
WriteableDataIncreasing = 0b1001,
|
||||||
|
// Desc == 1, exec = 1, direction = 0, R = 0
|
||||||
|
//
|
||||||
|
// This can only be reached at the appropriated DPL.
|
||||||
|
ProtectedCodeContained = 0b1100,
|
||||||
|
// Desc == 1, exec = 1, direction = 0, R = 1
|
||||||
|
//
|
||||||
|
// This can only be reached at the appropriated DPL.
|
||||||
|
ReadableCodeContained = 0b1101,
|
||||||
|
// Desc == 1, exec = 1, direction = 1, R = 0
|
||||||
|
//
|
||||||
|
// This can be called from lower priorities, think syscalls. But requires long jumps.
|
||||||
|
ProtectedCodeExposed = 0b1110,
|
||||||
|
// Desc == 1, exec = 1, direction = 1, R = 1
|
||||||
|
//
|
||||||
|
// This can be called from lower priorities, think syscalls. But requires long jumps.
|
||||||
|
ReadableCodeExposed = 0b1111,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AccessFlags {
|
||||||
|
ring: DPL,
|
||||||
|
data_kind: DataKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccessFlags {
|
||||||
|
pub const fn new(ring: DPL, data_kind: DataKind) -> Self {
|
||||||
|
Self { ring, data_kind }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
const fn to_bits(self) -> u8 {
|
||||||
|
let mut ret = 0b1000_0001;
|
||||||
|
ret |= (self.ring as u8) << 5;
|
||||||
|
ret |= (self.data_kind as u8) << 1;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add remaining flags.
|
||||||
|
pub struct Flags {
|
||||||
|
long_mode: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Flags {
|
||||||
|
pub const fn new(long_mode: bool) -> Self {
|
||||||
|
Self { long_mode }
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn to_bits(&self) -> u8 {
|
||||||
|
0b0 | ((self.long_mode as u8) << 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GdtSelector {
|
||||||
|
inner: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GdtSelector {
|
||||||
|
/// Returns a new gdt selector.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function panics if the index is out of bounds.
|
||||||
|
pub const fn new(priviledge_level: DPL, table: &[GdtEntry], idx: u16) -> Self {
|
||||||
|
let _ = table[idx as usize]; // Rust provided length check.
|
||||||
|
Self {
|
||||||
|
inner: (idx << 3) | priviledge_level as u16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const fn raw(&self) -> u16 {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GdtPointer {
|
||||||
|
limit: u16,
|
||||||
|
base: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GdtPointer {
|
||||||
|
pub fn new(table: &[GdtEntry]) -> Self {
|
||||||
|
Self {
|
||||||
|
limit: core::mem::size_of_val(table) as u16 - 1,
|
||||||
|
base: table.as_ptr() as u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
x86_drivers/src/idt.rs
Normal file
56
x86_drivers/src/idt.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
use crate::gdt::{DPL, GdtSelector};
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct InterruptTableDescriptor {
|
||||||
|
size: u16,
|
||||||
|
offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct IdtTableEntry {
|
||||||
|
inner: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ISR = extern "x86-interrupt" fn(InterruptStackFrame) -> ();
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct InterruptStackFrame {
|
||||||
|
ip: usize,
|
||||||
|
cs: usize,
|
||||||
|
flags: usize,
|
||||||
|
sp: usize,
|
||||||
|
ss: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum Gate {
|
||||||
|
Interrupt = 0b1110,
|
||||||
|
Trap = 0b1111,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdtTableEntry {
|
||||||
|
pub fn new(isr: ISR, selector: GdtSelector, ist: u8, gate: Gate, priority: DPL) -> Self {
|
||||||
|
let mut inner = 0b0;
|
||||||
|
let offset = (isr as *const ()) as u64;
|
||||||
|
|
||||||
|
let bytes = (offset >> 32) as u32;
|
||||||
|
inner |= (bytes as u128) << 64;
|
||||||
|
|
||||||
|
let bytes = (offset as u32) >> 16;
|
||||||
|
inner |= (bytes as u128) << 48;
|
||||||
|
|
||||||
|
let bytes = offset as u16;
|
||||||
|
inner |= bytes as u128;
|
||||||
|
|
||||||
|
inner |= (selector.raw() as u128) << 16;
|
||||||
|
|
||||||
|
assert!(ist <= 0b111);
|
||||||
|
|
||||||
|
inner |= (ist as u128) << 32;
|
||||||
|
inner |= (gate as u128) << 40;
|
||||||
|
inner |= (priority as u128) << 45;
|
||||||
|
inner |= 1 << 47;
|
||||||
|
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
42
x86_drivers/src/lib.rs
Normal file
42
x86_drivers/src/lib.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![feature(abi_x86_interrupt)]
|
||||||
|
#![deny(
|
||||||
|
clippy::all,
|
||||||
|
clippy::perf,
|
||||||
|
clippy::pedantic,
|
||||||
|
clippy::nursery,
|
||||||
|
clippy::complexity,
|
||||||
|
clippy::correctness,
|
||||||
|
clippy::style,
|
||||||
|
clippy::suspicious,
|
||||||
|
clippy::undocumented_unsafe_blocks,
|
||||||
|
rustdoc::all,
|
||||||
|
rust_2018_idioms,
|
||||||
|
warnings
|
||||||
|
)]
|
||||||
|
|
||||||
|
#[allow(
|
||||||
|
missing_docs,
|
||||||
|
clippy::inline_always,
|
||||||
|
clippy::missing_safety_doc,
|
||||||
|
clippy::undocumented_unsafe_blocks,
|
||||||
|
clippy::unused_self,
|
||||||
|
clippy::must_use_candidate,
|
||||||
|
clippy::new_without_default,
|
||||||
|
clippy::cast_possible_truncation
|
||||||
|
)]
|
||||||
|
pub mod uart;
|
||||||
|
|
||||||
|
pub mod gdt;
|
||||||
|
pub mod idt;
|
||||||
|
|
||||||
|
pub mod core;
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod prelude {
|
||||||
|
pub use crate::core::Core;
|
||||||
|
pub use crate::uart::{InitUart, Uart};
|
||||||
|
}
|
||||||
234
x86_drivers/src/uart/mod.rs
Normal file
234
x86_drivers/src/uart/mod.rs
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
mod pac;
|
||||||
|
|
||||||
|
use core::{fmt, marker::PhantomData};
|
||||||
|
|
||||||
|
use pac::{
|
||||||
|
COM1_BASE, DLH, DLL, FCR, IER, LCR, LSR, LineControlRegister, MCR, P_CLK_FREQ, RBR, THR,
|
||||||
|
WordLength, inb, outb,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::uart::pac::{COM2_BASE, COM3_BASE, COM4_BASE, LineStatus};
|
||||||
|
|
||||||
|
// TODO: wrap all of these in a single core critical section.
|
||||||
|
pub trait HwUart: crate::private::Sealed {
|
||||||
|
const BASE: u16;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn tx_byte(val: u8) {
|
||||||
|
unsafe { outb(Self::BASE + THR, val) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn line_status() -> LineStatus {
|
||||||
|
unsafe {
|
||||||
|
LineStatus {
|
||||||
|
bits: inb(Self::BASE + LSR),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
#[allow(unused)]
|
||||||
|
fn rx_byte() -> u8 {
|
||||||
|
unsafe { inb(Self::BASE + RBR) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn set_baudrate(cfg: &UartConfig<true>) {
|
||||||
|
let [upper, lower] = cfg.prescaler.to_ne_bytes();
|
||||||
|
let mut lcr = Self::read_control_register();
|
||||||
|
lcr.enable_baud_config();
|
||||||
|
Self::write_control_register(lcr.bits());
|
||||||
|
unsafe { outb(Self::BASE + DLL, lower) };
|
||||||
|
unsafe { outb(Self::BASE + DLH, upper) };
|
||||||
|
let mut lcr = Self::read_control_register();
|
||||||
|
lcr.disable_baud_config();
|
||||||
|
Self::write_control_register(lcr.bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn set_word_length(length: WordLength) {
|
||||||
|
let mut lcr = Self::read_control_register();
|
||||||
|
lcr.set_word_length(length);
|
||||||
|
Self::write_control_register(lcr.bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_control_register() -> LineControlRegister {
|
||||||
|
unsafe {
|
||||||
|
LineControlRegister {
|
||||||
|
bits: inb(Self::BASE + LCR),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_control_register(data: u8) {
|
||||||
|
unsafe { outb(Self::BASE + LCR, data) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn disable_interrupts() {
|
||||||
|
unsafe { outb(Self::BASE + IER, 0) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn clear_fifo_and_modem_setting() {
|
||||||
|
unsafe {
|
||||||
|
outb(Self::BASE + FCR, 0);
|
||||||
|
outb(Self::BASE + MCR, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InitUart<M: HwUart> {
|
||||||
|
fn init(&self) -> Uart<M> {
|
||||||
|
Uart::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct UartConfig<const BAUD_SET: bool> {
|
||||||
|
prescaler: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum BaudRate {
|
||||||
|
B115_200 = 115_200,
|
||||||
|
B57_600 = 57_600,
|
||||||
|
B38_400 = 38_400,
|
||||||
|
B19_200 = 19_200,
|
||||||
|
B9_600 = 9_600,
|
||||||
|
B1_200 = 1_200,
|
||||||
|
B300 = 300,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Uart1 {
|
||||||
|
_private: PhantomData<()>,
|
||||||
|
}
|
||||||
|
pub struct Uart2 {
|
||||||
|
_private: PhantomData<()>,
|
||||||
|
}
|
||||||
|
pub struct Uart3 {
|
||||||
|
_private: PhantomData<()>,
|
||||||
|
}
|
||||||
|
pub struct Uart4 {
|
||||||
|
_private: PhantomData<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 16550 UART driver.
|
||||||
|
#[must_use]
|
||||||
|
pub struct Uart<M: HwUart> {
|
||||||
|
_t: PhantomData<M>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl UartConfig<false> {
|
||||||
|
const fn set_baud(self, target_baud: BaudRate) -> UartConfig<true> {
|
||||||
|
let pres = P_CLK_FREQ / (16 * target_baud as u32);
|
||||||
|
UartConfig {
|
||||||
|
prescaler: pres as u16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
const fn set_baud_raw(self, target_baud: u16) -> Option<UartConfig<true>> {
|
||||||
|
let pres = P_CLK_FREQ / (16 * target_baud as u32);
|
||||||
|
let real_baud = P_CLK_FREQ / (pres * 16);
|
||||||
|
if target_baud != real_baud as u16 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(UartConfig {
|
||||||
|
prescaler: pres as u16,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M: HwUart> Uart<M> {
|
||||||
|
fn new() -> Self {
|
||||||
|
let uart = Self { _t: PhantomData };
|
||||||
|
uart.init();
|
||||||
|
uart
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&self) {
|
||||||
|
let config = UartConfig::default().set_baud(BaudRate::B115_200);
|
||||||
|
M::disable_interrupts();
|
||||||
|
M::set_baudrate(&config);
|
||||||
|
M::set_word_length(WordLength::W8);
|
||||||
|
M::clear_fifo_and_modem_setting();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_raw(&self, byte: u8) {
|
||||||
|
M::tx_byte(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spin_write_byte(&self, byte: u8) {
|
||||||
|
while !M::line_status().tx_ready() {}
|
||||||
|
self.write_raw(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::private::Sealed for Uart1 {}
|
||||||
|
|
||||||
|
impl HwUart for Uart1 {
|
||||||
|
const BASE: u16 = COM1_BASE;
|
||||||
|
}
|
||||||
|
impl crate::private::Sealed for Uart2 {}
|
||||||
|
|
||||||
|
impl HwUart for Uart2 {
|
||||||
|
const BASE: u16 = COM2_BASE;
|
||||||
|
}
|
||||||
|
impl crate::private::Sealed for Uart3 {}
|
||||||
|
|
||||||
|
impl HwUart for Uart3 {
|
||||||
|
const BASE: u16 = COM3_BASE;
|
||||||
|
}
|
||||||
|
impl crate::private::Sealed for Uart4 {}
|
||||||
|
|
||||||
|
impl HwUart for Uart4 {
|
||||||
|
const BASE: u16 = COM4_BASE;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M: HwUart> InitUart<M> for M {}
|
||||||
|
|
||||||
|
impl<M: HwUart> fmt::Write for Uart<M> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
for byte in s.bytes() {
|
||||||
|
if byte == b'\n' {
|
||||||
|
self.spin_write_byte(b'\r');
|
||||||
|
}
|
||||||
|
self.spin_write_byte(byte);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Uart1 {
|
||||||
|
pub(crate) const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
_private: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Uart2 {
|
||||||
|
pub(crate) const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
_private: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Uart3 {
|
||||||
|
pub(crate) const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
_private: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Uart4 {
|
||||||
|
pub(crate) const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
_private: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
106
x86_drivers/src/uart/pac.rs
Normal file
106
x86_drivers/src/uart/pac.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
pub const COM1_BASE: u16 = 0x3F8;
|
||||||
|
pub const COM2_BASE: u16 = 0x2F8; // IRQ 3
|
||||||
|
pub const COM3_BASE: u16 = 0x3E8; // IRQ 4 (shared with COM1)
|
||||||
|
pub const COM4_BASE: u16 = 0x2E8; // IRQ 3 (shared with COM2)
|
||||||
|
|
||||||
|
// Write here to pend a transfer.
|
||||||
|
pub const THR: u16 = 0;
|
||||||
|
// Holds a byte if there are any.
|
||||||
|
pub const RBR: u16 = 0;
|
||||||
|
pub const DLL: u16 = 0;
|
||||||
|
pub const DLH: u16 = 1;
|
||||||
|
pub const IER: u16 = 1;
|
||||||
|
pub const FCR: u16 = 2;
|
||||||
|
pub const LCR: u16 = 3;
|
||||||
|
pub const MCR: u16 = 4;
|
||||||
|
pub const LSR: u16 = 5;
|
||||||
|
|
||||||
|
pub const P_CLK_FREQ: u32 = 1_843_200;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn inb(port: u16) -> u8 {
|
||||||
|
let value: u8;
|
||||||
|
// SAFETY: port address is valid 16550 register offset from a standard COM base.
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"in al, dx",
|
||||||
|
out("al") value,
|
||||||
|
in("dx") port,
|
||||||
|
options(nomem, nostack, preserves_flags),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn outb(port: u16, value: u8) {
|
||||||
|
// SAFETY: port address is valid 16550 register offset from a standard COM base.
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"out dx, al",
|
||||||
|
in("dx") port,
|
||||||
|
in("al") value,
|
||||||
|
options(nomem, nostack, preserves_flags),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[must_use]
|
||||||
|
pub struct LineStatus {
|
||||||
|
pub bits: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl LineStatus {
|
||||||
|
const DR: u8 = 0;
|
||||||
|
const OE: u8 = 1;
|
||||||
|
const PE: u8 = 2;
|
||||||
|
const FE: u8 = 3;
|
||||||
|
const BI: u8 = 4;
|
||||||
|
const THRE: u8 = 5;
|
||||||
|
const TEMT: u8 = 6;
|
||||||
|
const E_RCVR_FIFO: u8 = 7;
|
||||||
|
|
||||||
|
pub const fn rx_ready(&self) -> bool {
|
||||||
|
(self.bits & (1 << Self::DR)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn tx_ready(&self) -> bool {
|
||||||
|
(self.bits & (1 << Self::THRE)) != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[must_use]
|
||||||
|
pub struct LineControlRegister {
|
||||||
|
pub bits: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineControlRegister {
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn bits(&self) -> u8 {
|
||||||
|
self.bits
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn enable_baud_config(&mut self) {
|
||||||
|
self.bits |= 1 << 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn disable_baud_config(&mut self) {
|
||||||
|
self.bits &= !(1 << 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn set_word_length(&mut self, word_length: WordLength) {
|
||||||
|
const BIT_MASK: u8 = 0b0000_0011u8;
|
||||||
|
self.bits = (self.bits & !BIT_MASK) | (word_length as u8 & BIT_MASK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum WordLength {
|
||||||
|
W8 = 0b11,
|
||||||
|
W7 = 0b10,
|
||||||
|
W6 = 0b01,
|
||||||
|
W5 = 0b00,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user