diff --git a/Cargo.lock b/Cargo.lock index 4d4d84e..bf132d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,6 +242,8 @@ dependencies = [ name = "riscv64" version = "0.1.0" dependencies = [ + "bit_field", + "bitflags 2.4.0", "port", ] diff --git a/aarch64/Cargo.toml b/aarch64/Cargo.toml index bd8b9bf..3c75f34 100644 --- a/aarch64/Cargo.toml +++ b/aarch64/Cargo.toml @@ -1,7 +1,10 @@ +cargo-features = ["per-package-target"] + [package] name = "aarch64" version = "0.1.0" edition = "2021" +default-target = "aarch64-unknown-none-softfloat" [dependencies] bitstruct = "0.1" diff --git a/port/src/fdt.rs b/port/src/fdt.rs index ab097f0..20a65a0 100644 --- a/port/src/fdt.rs +++ b/port/src/fdt.rs @@ -1,8 +1,11 @@ +use crate::println; use core::{ ffi::CStr, mem::{self, MaybeUninit}, }; +const DEBUG_DT_PARSING: bool = false; + #[derive(Debug)] pub enum ParseError { InvalidHeader, @@ -66,6 +69,9 @@ impl<'a> DeviceTree<'a> { /// Given a pointer to the dtb as a u64, return a DeviceTree struct. pub unsafe fn from_u64(ptr: u64) -> Result { let u8ptr = ptr as *const mem::MaybeUninit; + if DEBUG_DT_PARSING { + println!(" DT @ {u8ptr:x?}"); + } // Extract the real length from the header let dtb_buf_for_header: &[mem::MaybeUninit] = @@ -77,7 +83,11 @@ impl<'a> DeviceTree<'a> { // Extract the buffer for real let dtb_buf: &[mem::MaybeUninit] = unsafe { core::slice::from_raw_parts(u8ptr as *const MaybeUninit, len) }; - FdtHeader::new(dtb_buf, false).map(|header| Self { data: dtb_buf, header }) + let h = FdtHeader::new(dtb_buf, false); + if DEBUG_DT_PARSING { + println!(" DT header {:#x?}", h); + } + h.map(|header| Self { data: dtb_buf, header }) } /// Return slice containing `structs` area in FDT diff --git a/riscv64/Cargo.toml b/riscv64/Cargo.toml index 6ad82f7..8468937 100644 --- a/riscv64/Cargo.toml +++ b/riscv64/Cargo.toml @@ -1,10 +1,15 @@ +cargo-features = ["per-package-target"] + [package] name = "riscv64" version = "0.1.0" edition = "2021" +default-target = "riscv64imac-unknown-none-elf" [dependencies] port = { path = "../port" } +bitflags = "2.3.3" +bit_field = "0.10.2" [features] opensbi = [] diff --git a/riscv64/lib/config_default.toml b/riscv64/lib/config_default.toml index 4b7abea..dac2db1 100644 --- a/riscv64/lib/config_default.toml +++ b/riscv64/lib/config_default.toml @@ -7,7 +7,7 @@ buildflags = [ [link] arch = 'riscv' script = 'riscv64/lib/kernel.ld' -load-address = '0x80200000' +load-address = '0xffffffffc0200000' [config] platform = "virt" diff --git a/riscv64/lib/kernel.ld b/riscv64/lib/kernel.ld index 46e28d6..e64e021 100644 --- a/riscv64/lib/kernel.ld +++ b/riscv64/lib/kernel.ld @@ -3,36 +3,38 @@ ENTRY(start) SECTIONS { . = ${LOAD-ADDRESS}; + + text = .; .text : ALIGN(4096) { *(.text.entry) *(.text*) . = ALIGN(2097152); - PROVIDE(etext = .); + etext = .; } + rodata = .; .rodata : ALIGN(4096) { *(.rodata*) - *(.srodata*) . = ALIGN(2097152); - PROVIDE(erodata = .); + erodata = .; } + data = .; .data : ALIGN(4096) { *(.data*) - *(.sdata*) . = ALIGN(2097152); - PROVIDE(edata = .); + edata = .; } + bss = .; .bss : ALIGN(4096) { *(.bss*) - *(.sbss*) *(COMMON) . = ALIGN(2097152); - PROVIDE(end = .); } + end = .; /DISCARD/ : { - *(.eh_frame) + *(.eh_frame .note.GNU-stack) } } diff --git a/riscv64/src/l.S b/riscv64/src/l.S index 2ff4a6e..be35585 100644 --- a/riscv64/src/l.S +++ b/riscv64/src/l.S @@ -1,15 +1,87 @@ .section .text.entry .globl start start: - bnez a0, 1f - la sp, stack // set the stack pointer - li t0, 4096 * 4 - add sp, sp, t0 // add stack length - call main9 + // a0 == hartid + // pc == 0x80200000 + // sp == 0x800xxxxx + + // 1. set sp + // sp = bootstack + (hartid + 1) * 0x10000 + add t0, a0, 1 + slli t0, t0, 16 // ??? this is * 0x4000, not 0x10000 + lui sp, %hi(bootstack) + add sp, sp, t0 // add stack length + + // enable paging + // satp = (8 << 60) | PPN(boot_page_table) + // SATP = Supervisor Address Translation and Protection + // Register definition: + // | MODE | ASID | PPN | + // |[63..60]|[59..44]|[43..0]| + // PPN = Physical Page Number (of root page table) + // ASID = Address Space Identifier + // Volume II: RISC-V Privileged Architectures V20211203 p75 + // SXLEN 64, MODE 8: Sv39 aka Page-based 39-bit virtual addressing + lui t0, %hi(boot_page_table) + li t1, 0xffffffffc0000000 - 0x80000000 + sub t0, t0, t1 + srli t0, t0, 12 // page size 4k (2^12) + li t1, 8 << 60 // mode Sv39 + or t0, t0, t1 + csrw satp, t0 + sfence.vma + + // 3. jump to main9 (absolute address) + lui t0, %hi(main9) + addi t0, t0, %lo(main9) + jr t0 + 1: wfi j 1b -.bss -.balign 4096 -stack: .space 4096 * 4 +/* STACK */ +.section .bss.stack +.align 12 # page align +.global bootstack + +bootstack: +.space 4096 * 4 * 8 +.global bootstacktop + +bootstacktop: +.section .data +.align 12 // page align + +/* PAGING */ +boot_page_table: +// PTE (Page Table Entry) definition (SV39) +// NOTE: A page table entry is 8 bytes; 256 entries are 512*8=4k. +// +// N is reserved for Svnapot, PBMT is Page-based Memory Types +// | N | PBMT | Rsvd. | +// |[63]|[62..61]|[60..54]| +// The Physical Page Number (PPN) is 9+9+26 bits long, +// mapped to a 9+9+9 bit Virtual Page Number (VPN). +// | PPN[2] | PPN[1] | PPN[0] | +// |[53..28]|[27..19]|[18..10]| +// RSW is reserved for supervisor software control. +// D - dirty, A - accessed, G - global mapping, U - user mode access, +// XWR - exec/write/read (table 4.5, p80), V - entry is valid +// | RSW | D | A | G | U | X | W | R | V | +// |[9..8]|[7]|[6]|[5]|[4]|[3]|[2]|[1]|[0]| +// +// NOTE: quad = 4 words = 4 double bytes = 8 bytes +// https://ftp.gnu.org/old-gnu/Manuals/gas-2.9.1/html_chapter/as_7.html#SEC115 +.quad (0x00000 << 10) | 0xcf // VRWXAD +.quad (0x40000 << 10) | 0xcf // VRWXAD +// 3rd page: 0x00000000_c0000000 -> 0x80000000 (1G) +.quad (0x80000 << 10) | 0xcf // VRWXAD +// 504 empty entries +.zero 506 * 8 +// 3rd last page: 0xffffffff_00000000 -> 0x00000000 (1G) +.quad (0x00000 << 10) | 0xcf // VRWXAD +// 2nd last page: 0xffffffff_80000000 -> 0x40000000 (1G) +.quad (0x40000 << 10) | 0xcf // VRWXAD +// last page: 0xffffffff_c0000000 -> 0x80000000 (1G) +.quad (0x80000 << 10) | 0xcf // VRWXAD diff --git a/riscv64/src/main.rs b/riscv64/src/main.rs index 9172a2c..30e0047 100644 --- a/riscv64/src/main.rs +++ b/riscv64/src/main.rs @@ -1,37 +1,250 @@ #![feature(alloc_error_handler)] #![feature(asm_const)] #![feature(panic_info_message)] +#![feature(ptr_to_from_bits)] #![cfg_attr(not(any(test, feature = "cargo-clippy")), no_std)] #![cfg_attr(not(test), no_main)] #![allow(clippy::upper_case_acronyms)] #![forbid(unsafe_op_in_unsafe_fn)] +mod mem; +mod memory; mod platform; mod runtime; mod sbi; mod uart16550; -use port::println; - -use crate::platform::{devcons, platform_init}; +use crate::{ + memory::phys_to_virt, + platform::{devcons, platform_init}, +}; +use core::{ffi::c_void, slice}; use port::fdt::DeviceTree; +use port::{print, println}; #[cfg(not(test))] core::arch::global_asm!(include_str!("l.S")); +const LOGO: &str = " + ️_______________________________________ ️ +| ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️| +| ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️💻💻💻 ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️| +| ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️💻 ️ ️ ️ ️ ️💻 ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️| +| ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️💻 ️ ️ ️999 ️ ️💻💻 ️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️| +| ️ ️ ️ ️ ️ ️ ️ ️ ️💻💻💻 ️ ️ ️9 ️ ️ ️9 ️ ️ ️💻💻 ️ ️ ️ ️ ️ ️ ️ ️ ️| +| ️ ️ ️ ️ ️ ️💻💻 ️ ️ ️ ️💻 ️ ️ ️9999 ️ ️ ️ ️ ️💻 ️ ️ ️ ️ ️ ️ ️ ️ ️| +| ️ ️ ️💻💻 ️ ️RRR ️ ️ ️💻 ️ ️ ️ ️ ️9 ️ ️💻️💻💻💻 ️ ️ ️ ️ ️ ️| +| ️💻💻 ️ ️ ️ ️R ️ ️R ️ ️ ️💻 ️999 ️ ️💻 ️ ️ ️ ️ ️ ️💻💻 ️ ️ ️| +| ️💻💻️ ️ ️ ️ ️RRR ️ ️ ️ ️ ️💻️ ️ ️ ️ ️💻 ️ ️⌨️ ️ ️🖱️ ️ ️ ️💻💻 ️| +| ️ ️💻💻️ ️ ️ ️R ️ ️R ️ ️ ️ ️ ️💻️💻️💻️ ️ ️ ️ ️ ️ ️ ️ ️ ️ ️💻💻 ️| +| ️ ️ ️ ️ ️💻💻💻💻💻💻💻💻💻💻💻💻💻💻💻💻 ️ ️| +|_______________________________________| +"; + +const WHERE_ARE_WE: bool = true; +const WALK_DT: bool = false; + +/// debug helper - dump some memory range +pub fn dump(addr: usize, length: usize) { + let s = unsafe { slice::from_raw_parts(addr as *const u8, length) }; + println!("dump {length} bytes @{addr:x}"); + for w in s.iter() { + print!("{:02x}", w); + } + println!(); +} + +/// debug helper - dump some memory range, in chunks +pub fn dump_block(base: usize, size: usize, step_size: usize) { + println!("dump_block {base:x}:{:x}/{step_size:x}", base + size); + for b in (base..base + size).step_by(step_size) { + dump(b, step_size); + } +} + +/// nobody wants to write this out, we know it's unsafe... +fn read32(a: usize) -> u32 { + unsafe { core::ptr::read_volatile(a as *mut u32) } +} + +/// nobody wants to write this out, we know it's unsafe... +fn write32(a: usize, v: u32) { + unsafe { core::ptr::write_volatile(a as *mut u32, v) } +} + +/// print a memory range from given pointers - name, start, end, and size +unsafe fn print_memory_range(name: &str, start: &*const c_void, end: &*const c_void) { + let start = start as *const _ as u64; + let end = end as *const _ as u64; + let size = end - start; + println!(" {name}{start:#x}-{end:#x} ({size:#x})"); +} + +/// Print binary sections of the kernel: text, rodata, data, bss, total range. +/// NOTE: This needs to align with the corresponding linker script, where the +/// sections are defined. +fn print_binary_sections() { + extern "C" { + static text: *const c_void; + static etext: *const c_void; + static rodata: *const c_void; + static erodata: *const c_void; + static data: *const c_void; + static edata: *const c_void; + static bss: *const c_void; + static end: *const c_void; + } + + println!("Binary sections:"); + unsafe { + print_memory_range("text:\t\t", &text, &etext); + dump((&text) as *const _ as usize, 0x20); + print_memory_range("rodata:\t", &rodata, &erodata); + print_memory_range("data:\t\t", &data, &edata); + print_memory_range("bss:\t\t", &bss, &end); + print_memory_range("total:\t", &text, &end); + } +} + +fn consume_dt_block(name: &str, a: u64, l: u64) { + let v = phys_to_virt(a as usize); + // let v = a as usize; + println!("- {name}: {a:016x}:{v:016x} ({l:x})"); + match name { + "test@100000" => { + let x = read32(v); + println!("{name}[0]:{x:x}"); + write32(v, x | 0x1234_5678); + let x = read32(v); + println!("{name}[0]:{x:x}"); + } + "uart@10000000" => { + println!("{name}: {l:x}"); + dump(v, 0x4); + } + "plic@c000000" | "clint@2000000" => { + let x = read32(v); + println!("{name}[0]:{x:x}"); + } + "virtio_mmio@10001000" | "virtio_mmio@10002000" => { + dump(v, 0x20); + } + "flash@20000000" => { + dump(v, 0x20); + } + "pci@30000000" => { + // NOTE: v+l overflows usize, hitting 0 + // dump_block(v, (l - 0x40) as usize, 0x40); + dump_block(v, 0x40, 0x10); + } + _ => {} + } +} + +fn walk_dt(dt: &DeviceTree) { + dt.nodes().for_each(|n| { + let name = dt.node_name(&n); + if false { + if let Some(name) = name { + let p = dt.property(&n, "start"); + println!("{name:?}: {n:#?} {p:?}"); + } + } + dt.property_translated_reg_iter(n).next().and_then(|i| { + let b = i.regblock(); + if let Some(b) = b { + // println!("{b:#?}"); + let a = b.addr; + if let Some(name) = name { + if let Some(l) = b.len { + consume_dt_block(name, a, l); + } + } + } + b + }); + }); +} + +/// check on memory mapping foo +fn where_are_we() { + let x = "test"; + let p = x.as_ptr(); + // e.g., 0xffffffffc0400096 + println!("YOU ARE HERE (approx.): {p:#x?}"); +} + #[no_mangle] pub extern "C" fn main9(hartid: usize, dtb_ptr: u64) -> ! { + // devcons::init_sbi(); + // println!("\n--> SBI devcons\n"); + // QEMU: dtb@bf000000 + // println!("dtb@{dtb_ptr:x}"); let dt = unsafe { DeviceTree::from_u64(dtb_ptr).unwrap() }; - crate::devcons::init(&dt); - platform_init(); - println!(); + devcons::init(&dt); + println!("\n--> DT / native devcons\n"); + + platform_init(); println!("r9 from the Internet"); + println!("{LOGO}"); println!("Domain0 Boot HART = {hartid}"); println!("DTB found at: {dtb_ptr:#x}"); + print_binary_sections(); + + if WALK_DT { + walk_dt(&dt); + } + + if WHERE_ARE_WE { + where_are_we(); + } + + extern "C" { + static boot_page_table: *const c_void; + } + + let bpt_addr = unsafe { (&boot_page_table) as *const _ as u64 }; + println!("table addr: {:#x}", bpt_addr); + let bpt = mem::PageTable::new(bpt_addr); + println!("{:?}", bpt.dump_entry(508)); + println!("{:?}", bpt.dump_entry(509)); + println!("{:?}", bpt.dump_entry(510)); + println!("{:?}", bpt.dump_entry(511)); #[cfg(not(test))] sbi::shutdown(); #[cfg(test)] loop {} } + +#[cfg(test)] +mod tests { + use crate::{PageTableEntry, PageTableFlags}; + + #[test] + fn test_pagetableentry() { + { + let entry = PageTableEntry { + ppn2: 0.try_into().unwrap(), + ppn1: 1.try_into().unwrap(), + ppn0: 2.try_into().unwrap(), + flags: PageTableFlags::W.union(PageTableFlags::R), + }; + assert_eq!(entry.serialize(), 0b1_000000010_00_00000110); + } + + { + let entry = PageTableEntry { + ppn2: 0x03f0_0000.try_into().unwrap(), + ppn1: 1.try_into().unwrap(), + ppn0: 2.try_into().unwrap(), + flags: PageTableFlags::W.union(PageTableFlags::R), + }; + assert_eq!( + entry.serialize(), + 0b11_1111_0000_0000_0000_0000_0000__000000001__000000010__00__00000110 + ); + } + } +} diff --git a/riscv64/src/mem.rs b/riscv64/src/mem.rs new file mode 100644 index 0000000..39d93f4 --- /dev/null +++ b/riscv64/src/mem.rs @@ -0,0 +1,79 @@ +use bit_field::BitField; +use bitflags::bitflags; +use core::ptr::{read_volatile, write_volatile}; + +bitflags! { + #[derive(Debug)] + pub struct PageTableFlags: u8 { + const D = 1 << 7; + const A = 1 << 6; + const G = 1 << 5; + const U = 1 << 4; + const X = 1 << 3; + const W = 1 << 2; + const R = 1 << 1; + const V = 1; + } +} + +/// Used as an index for PPN and VPN +pub enum PageNumberSegment { + _0, + _1, + _2, +} + +#[derive(Debug)] +pub struct PageTableEntry(u64); + +impl PageTableEntry { + pub unsafe fn write_to(&self, addr: u64) { + unsafe { write_volatile(addr as *mut u64, self.0) } + } + + pub fn ppn(&self, i: PageNumberSegment) -> u64 { + use PageNumberSegment::*; + match i { + _0 => self.0.get_bits(10..=18), + _1 => self.0.get_bits(19..=27), + _2 => self.0.get_bits(28..=53), + } + } + + pub fn flags(&self) -> PageTableFlags { + let bits = self.0.get_bits(0..=7) as u8; + // safe to unwrap since all bits of a u8 are defined flags + PageTableFlags::from_bits(bits).unwrap() + } +} + +impl From for PageTableEntry { + fn from(value: u64) -> Self { + Self(value) + } +} + +impl From for u64 { + fn from(value: PageTableEntry) -> Self { + value.0 + } +} + +pub struct PageTable { + addr: u64, +} + +impl PageTable { + const ENTRY_SIZE: u64 = 8; // 64 bit + + pub fn new(addr: u64) -> Self { + Self { addr } + } + + pub fn dump_entry(&self, at: u16) -> PageTableEntry { + assert!(at < 512, "index out of range: page tables always have 512 entries"); + let addr = self.addr + (at as u64 * Self::ENTRY_SIZE); + let val = unsafe { read_volatile(addr as *const u64) }; + val.into() + } +} diff --git a/riscv64/src/memory.rs b/riscv64/src/memory.rs new file mode 100644 index 0000000..95800a5 --- /dev/null +++ b/riscv64/src/memory.rs @@ -0,0 +1,25 @@ +use crate::platform::PHYSICAL_MEMORY_OFFSET; +use port::println; + +const DEBUG_PHYS_TO_VIRT: bool = true; + +/// Convert physical address to virtual address +/// See 4.3.2 Virtual Address Translation Process, +/// Volume II: RISC-V Privileged Architectures V20211203 p82 +/// va.off = pa.off +/// +/// Physical address: +/// | VPN[2] | VPN[1] | VPN[0] | offset | +/// |[38..30]|[29..21]|[20..12]|[11..0] | +/// Virtual address: +/// | PPN[2] | PPN[1] | PPN[0] | offset | +/// | [55..30] |[29..21]|[20..12]|[11..0] | +/// NOTE: PPN[2] is 26 bits wide, VPN[2] only 9 +#[inline] +pub fn phys_to_virt(paddr: usize) -> usize { + let vaddr = PHYSICAL_MEMORY_OFFSET + paddr; + if DEBUG_PHYS_TO_VIRT { + println!("Physical address {paddr:x} translates to {vaddr:x}"); + } + vaddr +} diff --git a/riscv64/src/platform/virt/devcons.rs b/riscv64/src/platform/virt/devcons.rs index 49f2e73..dd3e848 100644 --- a/riscv64/src/platform/virt/devcons.rs +++ b/riscv64/src/platform/virt/devcons.rs @@ -2,6 +2,7 @@ use core::mem::MaybeUninit; +use crate::sbi::Sbi; use crate::uart16550::Uart16550; use port::{devcons::Console, fdt::DeviceTree}; @@ -14,7 +15,9 @@ pub fn init(dt: &DeviceTree) { .unwrap(); Console::new(|| { - let mut uart = Uart16550::new(ns16550a_reg); + let addr = ns16550a_reg.addr as usize; + let mut uart = Uart16550::new(addr); + uart.init(115_200); static mut UART: MaybeUninit = MaybeUninit::uninit(); @@ -25,3 +28,16 @@ pub fn init(dt: &DeviceTree) { } }); } + +pub fn init_sbi() { + Console::new(|| { + let uart = Sbi::new(); + + static mut UART: MaybeUninit = MaybeUninit::uninit(); + + unsafe { + UART.write(uart); + UART.assume_init_mut() + } + }); +} diff --git a/riscv64/src/platform/virt/mod.rs b/riscv64/src/platform/virt/mod.rs index 1947ba4..fb0d3e9 100644 --- a/riscv64/src/platform/virt/mod.rs +++ b/riscv64/src/platform/virt/mod.rs @@ -1,3 +1,8 @@ pub mod devcons; +use port::println; -pub fn platform_init() {} +pub const PHYSICAL_MEMORY_OFFSET: usize = 0xFFFF_FFFF_4000_0000; + +pub fn platform_init() { + println!("platform_init"); +} diff --git a/riscv64/src/sbi.rs b/riscv64/src/sbi.rs index 395ce76..4a7b9fb 100644 --- a/riscv64/src/sbi.rs +++ b/riscv64/src/sbi.rs @@ -4,6 +4,10 @@ #![cfg_attr(not(target_arch = "riscv64"), allow(dead_code))] +use core::fmt::Error; +use core::fmt::Write; +use port::devcons::Uart; + const SBI_SET_TIMER: usize = 0; const SBI_CONSOLE_PUTCHAR: usize = 1; const SBI_CONSOLE_GETCHAR: usize = 2; @@ -39,7 +43,7 @@ pub fn _set_timer(timer: usize) { } #[deprecated = "expected to be deprecated; no replacement"] -pub fn _consputb(c: u8) { +pub fn consputb(c: u8) { sbi_call_legacy(SBI_CONSOLE_PUTCHAR, c as usize, 0, 0); } @@ -52,3 +56,26 @@ pub fn shutdown() -> ! { sbi_call_legacy(SBI_SHUTDOWN, 0, 0, 0); panic!("shutdown failed!"); } + +pub struct Sbi {} + +impl Write for Sbi { + fn write_str(&mut self, out: &str) -> Result<(), Error> { + for c in out.bytes() { + consputb(c); + } + Ok(()) + } +} + +impl Uart for Sbi { + fn putb(&self, b: u8) { + consputb(b); + } +} + +impl Sbi { + pub fn new() -> Self { + Sbi {} + } +} diff --git a/riscv64/src/uart16550.rs b/riscv64/src/uart16550.rs index 594d02f..a8fa208 100644 --- a/riscv64/src/uart16550.rs +++ b/riscv64/src/uart16550.rs @@ -3,10 +3,9 @@ use core::fmt::Error; use core::fmt::Write; use port::devcons::Uart; -use port::fdt::RegBlock; pub struct Uart16550 { - pub ns16550a_reg: RegBlock, + base: *mut u8, } impl Write for Uart16550 { @@ -20,7 +19,7 @@ impl Write for Uart16550 { impl Uart for Uart16550 { fn putb(&self, b: u8) { - let ptr = self.ns16550a_reg.addr as *mut u8; + let ptr = self.base; unsafe { ptr.add(0).write_volatile(b); } @@ -28,29 +27,37 @@ impl Uart for Uart16550 { } impl Uart16550 { - pub fn new(ns16550a_reg: RegBlock) -> Self { - Uart16550 { ns16550a_reg } + pub fn new(addr: usize) -> Self { + Uart16550 { base: addr as *mut u8 } } + // see also https://www.lookrs232.com/rs232/dlab.htm pub fn init(&mut self, baud: u32) { - let ptr = self.ns16550a_reg.addr as *mut u8; + let ptr = self.base; + let divisor: u16 = (2_227_900 / (baud * 16)) as u16; // set baud rate + let divisor_least: u8 = (divisor & 0xff).try_into().unwrap(); + let divisor_most: u8 = (divisor >> 8).try_into().unwrap(); + let word_length = 3; unsafe { - let lcr = 3; // word length - ptr.add(3).write_volatile(lcr); // set word length - ptr.add(2).write_volatile(1); // enable FIFO - ptr.add(1).write_volatile(1); // enable receiver interrupts - let divisor: u16 = (2_227_900 / (baud * 16)) as u16; // set baud rate - let divisor_least: u8 = (divisor & 0xff).try_into().unwrap(); - let divisor_most: u8 = (divisor >> 8).try_into().unwrap(); - ptr.add(3).write_volatile(lcr | 1 << 7); // access DLAB - ptr.add(0).write_volatile(divisor_least); // DLL - ptr.add(1).write_volatile(divisor_most); // DLM - ptr.add(3).write_volatile(lcr); // close DLAB + // set word length + ptr.add(3).write_volatile(word_length); + // enable FIFO + ptr.add(2).write_volatile(1); + // enable receiver interrupts + ptr.add(1).write_volatile(1); + // access DLAB (Divisor Latch Access Bit) + ptr.add(3).write_volatile(word_length | 1 << 7); + // divisor low byte + ptr.add(0).write_volatile(divisor_least); + // divisor high byte + ptr.add(1).write_volatile(divisor_most); + // close DLAB + ptr.add(3).write_volatile(word_length); } } pub fn put(&mut self, c: u8) { - let ptr = self.ns16550a_reg.addr as *mut u8; + let ptr = self.base; unsafe { ptr.add(0).write_volatile(c); } @@ -58,7 +65,7 @@ impl Uart16550 { #[allow(dead_code)] pub fn get(&mut self) -> Option { - let ptr = self.ns16550a_reg.addr as *mut u8; + let ptr = self.base; unsafe { if ptr.add(5).read_volatile() & 1 == 0 { None diff --git a/x86_64/Cargo.toml b/x86_64/Cargo.toml index ffa35a1..f3e04b2 100644 --- a/x86_64/Cargo.toml +++ b/x86_64/Cargo.toml @@ -1,7 +1,10 @@ +cargo-features = ["per-package-target"] + [package] name = "x86_64" version = "0.1.0" edition = "2021" +default-target = "x86_64-unknown-none" [dependencies] bitstruct = "0.1" diff --git a/xtask/src/main.rs b/xtask/src/main.rs index c6082a5..7470335 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -501,7 +501,11 @@ fn run(build_params: &BuildParams) -> Result<()> { } cmd.arg("-d").arg("guest_errors,unimp"); cmd.arg("-kernel"); - cmd.arg(format!("target/{}/{}/riscv64", build_params.target(), build_params.dir())); + cmd.arg(format!( + "target/{}/{}/riscv64-qemu", + build_params.target(), + build_params.dir() + )); cmd.current_dir(workspace()); if build_params.verbose { println!("Executing {cmd:?}");