updated + alignment does not exist

This commit is contained in:
Bryan McShea
2026-01-27 17:34:29 -05:00
parent 9a8b93848a
commit 7e81483f8e
19 changed files with 875 additions and 1813 deletions

View File

@@ -8,7 +8,7 @@ rustflags = [
"-C", "link-arg=-Tsrc/arch/riscv64/link.ld",
"-C", "link-arg=--omagic",
]
runner = "qemu-system-riscv64 -nographic -semihosting -cpu rv64 -machine virt -bios none -smp 4 -m 1G -device virtio-blk-pci,drive=test -drive file=test.raw,format=raw,id=test -kernel"
runner = "qemu-system-riscv64 -nographic -semihosting -cpu rv64 -machine virt -bios none -smp 4 -m 1G -device virtio-blk-pci,drive=mewhen -drive file=test.raw,format=raw,id=mewhen -kernel"
[unstable]
build-std = ["core", "compiler_builtins", "alloc"]

View File

@@ -1,4 +1,4 @@
use core::{arch::asm, ops::Range};
use core::{arch::naked_asm, ops::Range};
use crate::{
arch::{asm, csr, interrupts, paging, wait},
@@ -8,9 +8,9 @@ use crate::{
#[no_mangle]
#[link_section = ".text.init"]
#[naked]
unsafe extern "C" fn _start() -> ! {
asm!(
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
// disable interrupts
"csrw mie, zero",
// set up gp & sp
@@ -31,15 +31,13 @@ unsafe extern "C" fn _start() -> ! {
"la t0, {init}",
"csrw mepc, t0",
"mret",
init = sym init,
options(noreturn)
);
}
#[naked]
#[unsafe(naked)]
pub unsafe extern "C" fn to_supervisor() {
asm!(
naked_asm!(
"li t0, (1 << 8) | (1 << 5)",
"csrw sstatus, t0",
"li t0, (7 << 0) | (1 << 3)",
@@ -53,7 +51,6 @@ pub unsafe extern "C" fn to_supervisor() {
"csrw sepc, t0",
"sfence.vma",
"sret",
options(noreturn)
);
}
@@ -64,7 +61,7 @@ pub unsafe fn init() -> ! {
wait();
}
interrupts::init();
let fdt = FDT::from_addr(dt_addr);
let fdt = FDT::from_addr(dt_addr).expect("Failed to parse fdt");
let raw_mem_range = fdt.mem_range().expect("we lost guys");
let heap_start = paging::init(raw_mem_range.end());
let heap_mem = Range {

View File

@@ -4,7 +4,7 @@ pub fn init() {
csr::mtvec::init!(stuff);
}
#[repr(align(4))]
#[rustc_align(4)]
pub fn stuff() -> ! {
let mcause = csr::mcause::read();
crate::println!("interrupt triggered: {mcause:?}");

View File

@@ -1,35 +1,5 @@
use core::fmt::{self, Write};
use crate::util::mutex::Mutex;
use core::arch::asm;
// --machine sifive_u
// const UART_BASE: u32 = 0x10010000;
// --machine virt
const UART_BASE: u32 = 0x10000000;
static UART: Mutex<Uart> = Mutex::new(Uart::new(UART_BASE));
struct Uart {
base: u32,
}
impl Uart {
pub const fn new(base: u32) -> Self {
Self { base }
}
}
impl fmt::Write for Uart {
fn write_str(&mut self, s: &str) -> fmt::Result {
for b in s.as_bytes() {
#[allow(clippy::while_immutable_condition)]
while unsafe { *(self.base as *mut i32) } < 0 {}
unsafe { *(self.base as *mut i32) = *b as i32 }
}
Ok(())
}
}
pub fn exit(code: usize) -> ! {
let data = [0x20026, code];
unsafe {
@@ -53,14 +23,3 @@ unsafe fn semihost(call: usize, data: *const u8) {
)
}
pub fn _print(args: core::fmt::Arguments<'_>) {
// NOTE: something really dumb can happen here;
// if you evaluate an expression in a print statement, and that
// causes an interrupt, this will be left locked...
// Should I set up the heap before interrupts? or just avoid printing until both...?
// or maybe force unlock if there's an interrupt?
// or store the hart in the lock, and unlock if that hart was interrupted??
// or just have a constant-sized buffer?
// or create a "locked writer"?
UART.lock().write_fmt(args).unwrap();
}

View File

@@ -58,31 +58,28 @@ pub struct RawProp {
pub struct FDT {
pub header: &'static Header,
pub nodes: &'static [u8],
pub root: Node,
pub strings: &'static [u8],
}
impl FDT {
pub fn nodes(&self) -> NodeIter {
NodeIter {
pos: self.nodes,
strings: self.strings,
}
}
pub fn root(&self) -> Option<Node> {
self.nodes().next()
}
pub unsafe fn from_addr(addr: *mut u8) -> Self {
pub unsafe fn from_addr(addr: *mut u8) -> Result<Self, &'static str> {
let header: &Header = &*(addr as *const Header);
if header.magic.get() != MAGIC {
panic!("fdt magic wrong");
return Err("fdt magic wrong");
}
let data = slice::from_raw_parts(addr, header.totalsize.get() as usize);
Self {
let strings = &data[header.off_dt_strings.get() as usize..];
let node_data = &data[header.off_dt_struct.get() as usize..];
let root = match Node::from_bytes(node_data, strings) {
Some((node, _)) => node,
None => return Err("Could not parse nodes")
};
Ok(Self {
header,
nodes: &data[header.off_dt_struct.get() as usize..],
strings: &data[header.off_dt_strings.get() as usize..],
}
root,
strings,
})
}
}
@@ -107,6 +104,82 @@ impl Debug for Node {
}
impl Node {
pub fn from_bytes(bytes: &'static [u8], strings: &'static [u8]) -> Option<(Self, &'static [u8])> {
// first make sure this is actually a node
let mut pos = bytes;
if pos.is_empty() {
return None;
}
let token: Token = Token::from_bytes(pos)?;
let Token::BeginNode = token else {
return None;
};
pos = &pos[4..];
// then get the name
let name_start = pos;
let extra;
'outer: loop {
let bytes = &pos[..4];
pos = &pos[4..];
for (i, byte) in bytes.iter().enumerate() {
if *byte == 0 {
extra = 4 - i;
break 'outer;
}
}
}
let name = core::str::from_utf8(&name_start[..name_start.len() - pos.len() - extra])
.expect("har har har har har har har har har har. RAAAAAAAAAAAAAAAAAAAAAAAA");
// then props
let node_start = pos;
let node_data = if let Some(prop) = (PropIter {
strings,
pos,
})
.last()
{
let node_len =
(prop.data.as_ptr() as usize + prop.data.len()) - pos.as_ptr() as usize;
pos = &pos[node_len..];
&node_start[..node_len]
} else {
&[]
};
// then children
let children = match Token::from_bytes(pos) {
Some(Token::EndNode | Token::End) => {
pos = &pos[4..];
&[]
}
Some(Token::BeginNode) => {
let children = pos;
let mut iter = NodeIter {
pos: children,
strings,
};
// skip children pos
for _ in iter.by_ref() {}
pos = iter.pos;
match Token::from_bytes(pos) {
Some(Token::EndNode | Token::End) => pos = &pos[4..],
_ => panic!("wut du heeeeeal (toaken)"),
}
children
}
_ => {
println!("WARNING: token stuff XD");
&[]
}
};
// done
Some((Node {
name,
props: node_data,
strings,
children,
},pos))
}
pub fn children(&self) -> NodeIter {
NodeIter {
pos: self.children,
@@ -191,85 +264,16 @@ impl Iterator for PropIter {
pub struct NodeIter {
// I should make a type called ByteCursor or something for this
// so dealing with it is not cursed and dependent on data type
pub pos: &'static [u8],
pub strings: &'static [u8],
pos: &'static [u8],
strings: &'static [u8],
}
impl Iterator for NodeIter {
type Item = Node;
fn next(&mut self) -> Option<Self::Item> {
// first make sure this is actually a node
if self.pos.is_empty() {
return None;
}
let token: Token = Token::from_bytes(self.pos)?;
let Token::BeginNode = token else {
return None;
};
self.pos = &self.pos[4..];
// then get the name
let name_start = self.pos;
let extra;
'outer: loop {
let bytes = &self.pos[..4];
self.pos = &self.pos[4..];
for (i, byte) in bytes.iter().enumerate() {
if *byte == 0 {
extra = 4 - i;
break 'outer;
}
}
}
let name = core::str::from_utf8(&name_start[..name_start.len() - self.pos.len() - extra])
.expect("har har har har har har har har har har. RAAAAAAAAAAAAAAAAAAAAAAAA");
// then props
let node_start = self.pos;
let node_data = if let Some(prop) = (PropIter {
strings: self.strings,
pos: self.pos,
})
.last()
{
let node_len =
(prop.data.as_ptr() as usize + prop.data.len()) - self.pos.as_ptr() as usize;
self.pos = &self.pos[node_len..];
&node_start[..node_len]
} else {
&[]
};
// then children
let children = match Token::from_bytes(self.pos) {
Some(Token::EndNode | Token::End) => {
self.pos = &self.pos[4..];
&[]
}
Some(Token::BeginNode) => {
let children = self.pos;
let mut iter = Self {
pos: children,
strings: self.strings,
};
// skip children bytes
for _ in iter.by_ref() {}
self.pos = iter.pos;
match Token::from_bytes(self.pos) {
Some(Token::EndNode | Token::End) => self.pos = &self.pos[4..],
_ => panic!("wut du heeeeeal (toaken)"),
}
children
}
_ => {
println!("WARNING: token stuff XD");
&[]
}
};
// done
Some(Node {
name,
props: node_data,
strings: self.strings,
children,
})
let (node, pos) = Node::from_bytes(self.pos, self.strings)?;
self.pos = pos;
Some(node)
}
}

View File

@@ -10,8 +10,9 @@ use super::fdt::FDT;
impl FDT {
pub fn mem_range(&self) -> Option<FDTMemRange> {
let reg = self.root()?.child("memory")?.prop("reg")?;
let reg = self.root.child("memory")?.prop("reg")?;
let data = reg.data.chunks(size_of::<FDTMemRange>()).next()?;
// for now just get first
let data: [u8; size_of::<FDTMemRange>()] = data.try_into().unwrap();
unsafe { Some(transmute::<[u8; 16], FDTMemRange>(data)) }
}

View File

@@ -1,4 +1,4 @@
pub mod fdt;
pub mod mem;
pub mod uart;
pub mod pci;
pub mod uart;

View File

@@ -1,14 +0,0 @@
use crate::println;
use super::fdt::FDT;
impl FDT {
pub fn pci_devs(&self) -> Option<()> {
// for dev in self.nodes() {
// println!("{:#?}", dev);
// }
let node = self.root()?.child("soc")?.child("pci")?;
println!("{:#?}", node);
None
}
}

View File

@@ -0,0 +1,98 @@
use super::header::HeaderStart;
// this is technically less performant than making 16^2 values,
// but that cannot be worth it lmao +
// Reserved(u8) is a lot nicer than literally hundreds of enum values
#[derive(Debug)]
pub enum Class {
Unclassified,
MassStorageController,
NetworkController,
DisplayController,
MultimediaController,
MemoryController,
Bridge(Bridge),
SimpleCommunicationController,
BaseSystemPeripheral,
InputDeviceController,
DockingStation,
Processor,
SerialBusController,
WirelessController,
IntelligentController,
SatelliteCommunicationController,
EncryptionController,
SignalProcessingController,
ProcessingAccelerator,
NonEssentialInstrumentation,
CoProcessor,
UnassignedClass,
Reserved(u8),
}
impl Class {
pub fn from_header(header: &HeaderStart) -> Self {
match header.class_code {
0x0 => Self::Unclassified,
0x1 => Self::MassStorageController,
0x2 => Self::NetworkController,
0x3 => Self::DisplayController,
0x4 => Self::MultimediaController,
0x5 => Self::MemoryController,
0x6 => Self::Bridge(Bridge::from_header(header)),
0x7 => Self::SimpleCommunicationController,
0x8 => Self::BaseSystemPeripheral,
0x9 => Self::InputDeviceController,
0xa => Self::DockingStation,
0xb => Self::Processor,
0xc => Self::SerialBusController,
0xd => Self::WirelessController,
0xe => Self::IntelligentController,
0xf => Self::SatelliteCommunicationController,
0x10 => Self::EncryptionController,
0x11 => Self::SignalProcessingController,
0x12 => Self::ProcessingAccelerator,
0x13 => Self::NonEssentialInstrumentation,
0x40 => Self::CoProcessor,
0xff => Self::UnassignedClass,
c => Self::Reserved(c),
}
}
}
#[derive(Debug)]
pub enum Bridge {
Host,
ISA,
EISA,
MCA,
PCIToPCI,
PCMCIA,
NuBus,
CardBus,
RACEway,
PCIToPCISemiTransparent,
InfiniBandtoPCIHost,
Other,
Unknown(u8),
}
impl Bridge {
pub fn from_header(header: &HeaderStart) -> Self {
match header.subclass {
0x0 => Self::Host,
0x1 => Self::ISA,
0x2 => Self::EISA,
0x3 => Self::MCA,
0x4 => Self::PCIToPCI,
0x5 => Self::PCMCIA,
0x6 => Self::NuBus,
0x7 => Self::CardBus,
0x8 => Self::RACEway,
0x9 => Self::PCIToPCISemiTransparent,
0x0A => Self::InfiniBandtoPCIHost,
0x80 => Self::Other,
s => Self::Unknown(s),
}
}
}

View File

@@ -0,0 +1,84 @@
use super::class::Class;
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct HeaderStart {
pub vendor_id: u16,
pub device_id: u16,
pub command: u16,
pub status: u16,
pub rev_id: u8,
pub prog_if: u8,
pub subclass: u8,
pub class_code: u8,
pub cache_line_size: u8,
pub latency_timer: u8,
pub header_type: u8,
pub bist: u8,
}
impl HeaderStart {
pub fn class(&self) -> Class {
Class::from_header(self)
}
pub fn vendor(&self) -> Vendor {
Vendor::from_header(self)
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct GeneralHeader {
pub start: HeaderStart,
pub bar0: u32,
pub bar1: u32,
pub bar2: u32,
pub bar3: u32,
pub bar4: u32,
pub bar5: u32,
pub cardbus_cis_pointer: u32,
pub subsystem_vendor_id: u16,
pub subsystem_id: u16,
pub expansion_rom_base_addr: u32,
pub capabilities: u8,
pub reserved: [u8; 7],
pub interrupt_line: u8,
pub interrupt_pin: u8,
pub min_grant: u8,
pub max_latency: u8,
}
// due to how large this is and how it can change,
// this seems like not the best use case for enums
// but that's entirely a later problem
#[derive(Debug)]
pub enum Vendor {
RedHat(RedHatDevice),
Unknown(u16),
}
impl Vendor {
pub fn from_header(header: &HeaderStart) -> Self {
let id = header.device_id;
match header.vendor_id {
0x1b36 => Self::RedHat(RedHatDevice::from_id(id)),
i => Self::Unknown(i),
}
}
}
#[derive(Debug)]
pub enum RedHatDevice {
QemuPCIeHostBridge,
Unknown(u16),
}
impl RedHatDevice {
pub fn from_id(id: u16) -> Self {
match id {
0x0008 => Self::QemuPCIeHostBridge,
id => Self::Unknown(id),
}
}
}

45
kernel/src/dev/pci/mod.rs Normal file
View File

@@ -0,0 +1,45 @@
use super::fdt::FDT;
use crate::{dev::pci::header::{GeneralHeader, HeaderStart}, println, util::bits::Be};
use core::mem::transmute;
pub mod header;
pub mod class;
impl FDT {
pub fn pci_devs(&self) -> Option<()> {
// for dev in self.nodes() {
// println!("{:#?}", dev);
// }
let node = self.root.child("soc")?.child("pci")?;
// should make handling reg field nicer
let data = node.prop("reg")?.data;
println!("{:#?}", node);
let addr = unsafe {
transmute::<[u8; 8], Be<u64>>([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
])
};
let addr = addr.get() as *mut u8;
let len = unsafe {
transmute::<[u8; 8], Be<u64>>([
data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
])
};
let len = len.get() as usize;
println!("{:?}..{:?}", addr, unsafe { addr.byte_add(len) });
unsafe {
let header = *(addr as *mut HeaderStart);
if header.header_type != 0 {
panic!("nooooooooooooo");
}
let header = addr as *mut GeneralHeader;
println!("device: {:?}", (*header).start.vendor());
println!("class: {:?}", (*header).start.class());
println!("{:#?}", *header);
(*header).start.command = 0b0000_0000_0000_0110;
for _ in 0..1000000 {}
println!("{:b}", (*header).start.status);
}
None
}
}

View File

@@ -0,0 +1,23 @@
use core::fmt;
pub struct Uart {
base: u32,
}
impl Uart {
pub const fn new(base: u32) -> Self {
Self { base }
}
}
impl fmt::Write for Uart {
fn write_str(&mut self, s: &str) -> fmt::Result {
for b in s.as_bytes() {
#[allow(clippy::while_immutable_condition)]
while unsafe { *(self.base as *mut i32) } < 0 {}
unsafe { *(self.base as *mut i32) = *b as i32 }
}
Ok(())
}
}

40
kernel/src/log.rs Normal file
View File

@@ -0,0 +1,40 @@
use core::fmt::Write;
use crate::{dev::{fdt::FDT, uart::Uart}, util::mutex::Mutex};
// ok now it uses soc->serial, might be chillin?
// --machine sifive_u
// const UART_BASE: u32 = 0x10010000;
// --machine virt
const UART_BASE: u32 = 0x10000000;
static UART: Mutex<Uart> = Mutex::new(Uart::new(UART_BASE));
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::log::_print(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
pub fn init(dt: &FDT) -> Option<()> {
let node = dt.root.child("soc")?.child("serial")?;
let addr = u32::from_str_radix(node.address()?, 16).ok()?;
*UART.lock() = Uart::new(addr);
Some(())
}
pub fn _print(args: core::fmt::Arguments<'_>) {
// NOTE: something really dumb can happen here;
// if you evaluate an expression in a print statement, and that
// causes an interrupt, this will be left locked...
// Should I set up the heap before interrupts? or just avoid printing until both...?
// or maybe force unlock if there's an interrupt?
// or store the hart in the lock, and unlock if that hart was interrupted??
// or just have a constant-sized buffer?
// or create a "locked writer"?
UART.lock().write_fmt(args).unwrap();
}

View File

@@ -1,7 +1,6 @@
#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
#![feature(naked_functions)]
#![feature(fn_align)]
#![feature(custom_test_frameworks)]
#![test_runner(crate::test::test_runner)]
@@ -15,6 +14,7 @@ extern crate alloc;
pub mod arch;
pub mod dev;
pub mod log;
pub mod mem;
pub mod qemu;
#[cfg(test)]
@@ -23,7 +23,7 @@ pub mod util;
pub struct StartInfo {
mem_range: Range<*mut u8>,
dt: FDT
dt: FDT,
}
pub fn start(info: StartInfo) -> ! {
@@ -39,12 +39,18 @@ pub fn start(info: StartInfo) -> ! {
}
pub fn main(info: StartInfo) {
log::init(&info.dt);
println!("we out here vibin");
println!("memory range: {:?}", info.mem_range);
// println!("memory range: {:?}", info.mem_range);
unsafe {
ALLOCATOR.init(&info.mem_range);
}
info.dt.pci_devs();
// info.dt.pci_devs();
// println!("{:#?}", info.dt.root);
let test = alloc::vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let test2 = alloc::vec![1u8, 2, 3, 4];
drop(test);
ALLOCATOR.heap().print();
}
#[panic_handler]

View File

@@ -157,7 +157,7 @@ impl Heap {
return data;
}
}
return null_mut();
null_mut()
}
pub unsafe fn dealloc(&mut self, ptr: *mut u8, _: core::alloc::Layout) {

View File

@@ -1,12 +1 @@
pub use crate::arch::qemu::*;
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::arch::qemu::_print(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}

View File

@@ -79,6 +79,7 @@ pub fn run_tests() -> ! {
}
fn prepare(info: &StartInfo) {
crate::log::init(&info.dt);
unsafe {
ALLOCATOR.reset(&info.mem_range);
}

File diff suppressed because it is too large Load Diff