diff --git a/docs/mem_layout.md b/docs/mem_layout.md index 0a4172f..110ce9f 100644 --- a/docs/mem_layout.md +++ b/docs/mem_layout.md @@ -17,7 +17,21 @@ | Reserved | 0x0c8000 - 0x0f0000 | bios expansions | | Reserved | 0x0f0000 - 0x100000 | motherboard bios | +### Allocations + +- 0x00500 - 0x04000: stage 1/2 stack +- 0x04000 - 0x06a00: globals +- 0x07c00 - 0x07e00: stage 1 text +- 0x07e00 - 0x08000: gpt partition header +- 0x08000 - 0x08200: stage 2 partition table entry +- 0x08200 - 0x0c000: stage 2 text, stage 3 stack +- 0x0c000 : gdt +- 0x10000 : stage 3 text + 0x10000: stage 3 -TODO: once we're in real mode, repurpose s2 and s3 for a stack -TODO: load s4 into a separate memory region +TODO: once we're in real mode, repurpose s2 for a stack +TODO: load s3 into a separate memory region + +TODO: use some fixed known memory address for the GDT, rather than putting it in stage 2. + That way, we can safely overwrite stage 2 once we're done with it. diff --git a/include/layout.s b/include/layout.s index 77aeef8..b4c04ed 100644 --- a/include/layout.s +++ b/include/layout.s @@ -9,6 +9,7 @@ %define S2_DATA S2_LOAD_ADDR %define S2_ENTRYPOINT (S2_DATA_ADDR + S2_DATA_LEN) +%define GDT_ADDR 0xc000 %define S3_LOAD_ADDR 0x10000 ; %define MEMMAP 0x6a00 diff --git a/justfile b/justfile index a0bc69f..28fde3f 100644 --- a/justfile +++ b/justfile @@ -9,29 +9,33 @@ run: -m 512M \ -drive format=raw,file=disk.bin -#-bios seabios/out/bios.bin +run_serial: + qemu-system-x86_64 \ + -serial stdio \ + -no-reboot \ + -m 512M \ + -drive format=raw,file=disk.bin + +run_reset: + qemu-system-x86_64 \ + -monitor stdio \ + -no-reboot \ + -m 512M \ + -drive if=pflash,file=reset.bin,format=raw -build: build_stage_1 build_stage_2 +build_reset: + nasm -f bin -werror -o reset.bin reset.s -build_stage_1: - nasm -f bin {{common_flags}} -o stage_1/stage_1.bin stage_1/main.s +#-bios seabios/out/bios.bin -build_stage_2: - nasm -f elf {{common_flags}} -o stage_2/prelude.o stage_2/prelude.s - nasm -f elf {{common_flags}} -o stage_2/main.o stage_2/main.s - nasm -f elf {{common_flags}} -o stage_2/a20.o stage_2/a20.s - {{ld32}} -m elf_i386 -T stage_2/link.ld -o stage_2/stage_2.bin stage_2/*.o +build: + cargo xtask build mkimg: dd if=/dev/zero of=disk.bin bs=440 count=1 conv=notrunc dd if=build/stage_1/s1.bin of=disk.bin conv=notrunc dd if=build/s23.bin of=disk.bin bs=512 seek=70 conv=notrunc -# build: -# nasm -f bin -Iinclude -o boot0.bin boot0.s -# cd boot1; cargo build --release -# # nasm -f bin -Iinclude -o boot1.bin boot1.s - zero_disk: dd if=/dev/zero of=disk.bin bs=512 count=1000 diff --git a/reset.s b/reset.s new file mode 100644 index 0000000..2e42d4e --- /dev/null +++ b/reset.s @@ -0,0 +1,14 @@ +; flash seems to get mapped to ff000 +; reset vector is f000:fff0 = ffff0 +; so there's ff0 = 4080 bytes of something before the reset vector. what is it? + +[bits 16] +[org 0xff000] + +times (0xff0 - ($ - $$)) db 0x00 + + mov eax, 0xcafeface + hlt + +times (0x1000 - ($ - $$)) db 0xf4 + diff --git a/stage_2/main.s b/stage_2/main.s index 91461bb..2341436 100644 --- a/stage_2/main.s +++ b/stage_2/main.s @@ -36,6 +36,12 @@ s2_main: mov cx, 0x3f00 int 0x10 + ; Copy the GDT + mov cx, GDT_FLAT_LEN + mov si, gdt_flat + mov di, GDT_ADDR + rep movsb + ; Ensure interrupts are definitely disabled. cli @@ -124,7 +130,6 @@ global gdt_flat_slice ; - 7: present (must be set) ; -; FIXME: copy this to a fixed memory location align 8 gdt_flat: ; First GDT entry must be 0. @@ -161,8 +166,6 @@ gdt_flat: 11001111b, \ 0x00 -global gdt_flat - GDT_FLAT_LEN equ ($ - gdt_flat) GDT_FLAT_IDX_CODE_32 equ (gdt_flat.segment_code_32 - gdt_flat) diff --git a/stage_3/src/com.rs b/stage_3/src/com.rs new file mode 100644 index 0000000..8d80ca5 --- /dev/null +++ b/stage_3/src/com.rs @@ -0,0 +1,92 @@ +use core::{fmt, hint}; + +use crate::ioport; + +pub struct Com { + port: u16, +} + +pub struct ComError; + +impl Com { + pub unsafe fn init(port: u16) -> Result { + const ECHO_BYTE: u8 = 0x5a; + + let echo = unsafe { + // Unset DLAB + ioport::outb(port + 3, 0x00); + // Disable interrupts + ioport::outb(port + 1, 0x00); + // Set DLAB + ioport::outb(port + 3, 0x80); + // Set baud rate divisor to 00 01 (115200) + ioport::outb(port + 0, 0x01); + ioport::outb(port + 1, 0x00); + // 8 bits, no parity, one stop bit + ioport::outb(port + 3, 0x03); + // Enable FIFO, clear them, with 14-byte threshold + ioport::outb(port + 2, 0xc7); + // IRQs enabled, RTS/DSR set + ioport::outb(port + 4, 0x0b); + // Set loopback mode + ioport::outb(port + 4, 0x1e); + ioport::outb(port + 0, ECHO_BYTE); + + ioport::inb(port + 0) + }; + + if echo != ECHO_BYTE { + return Err(ComError); + } + + unsafe { + ioport::outb(port + 4, 0x0f); + } + + Ok(Self { + port, + }) + } + + pub fn poll_has_data(&self) -> bool { + unsafe { ioport::inb(self.port + 5) & 0x01 != 0 } + } + + pub fn read_poll(&mut self) -> u8 { + while !self.poll_has_data() { + hint::spin_loop(); + } + unsafe { ioport::inb(self.port) } + } + + pub fn poll_has_space(&self) -> bool { + unsafe { ioport::inb(self.port + 5) & 0x20 != 0 } + } + + pub fn write_poll(&mut self, x: u8) { + while !self.poll_has_space() { + hint::spin_loop(); + } + unsafe { + ioport::outb(self.port, x); + } + } +} + +impl fmt::Write for Com { + fn write_str(&mut self, s: &str) -> fmt::Result { + for b in s.bytes() { + self.write_poll(b); + } + Ok(()) + } + + fn write_char(&mut self, c: char) -> fmt::Result { + let mut buf = [0u8; 4]; + let s = c.encode_utf8(&mut buf); + for b in s.bytes() { + self.write_poll(b); + } + Ok(()) + } +} diff --git a/stage_3/src/ioport.rs b/stage_3/src/ioport.rs new file mode 100644 index 0000000..203d743 --- /dev/null +++ b/stage_3/src/ioport.rs @@ -0,0 +1,23 @@ +use core::arch::asm; + +pub unsafe fn inb(port: u16) -> u8 { + let x: u8; + unsafe { + asm!( + "in al, dx", + in("dx") port, + lateout("al") x + ); + } + x +} + +pub unsafe fn outb(port: u16, x: u8) { + unsafe { + asm!( + "out dx, al", + in("al") x, + in("dx") port, + ); + } +} diff --git a/stage_3/src/main.rs b/stage_3/src/main.rs index 3924d8c..3bfdfeb 100644 --- a/stage_3/src/main.rs +++ b/stage_3/src/main.rs @@ -4,9 +4,11 @@ #![test_runner(crate::test_runner)] mod spin; +mod ioport; mod vga; +mod com; -use core::{arch::{asm, global_asm}, panic::PanicInfo, fmt::Write}; +use core::{arch::{asm, global_asm}, fmt::Write, panic::PanicInfo, ptr, slice}; global_asm!(include_str!("asm/trampoline.s")); @@ -20,11 +22,26 @@ fn panic(info: &PanicInfo) -> ! { hlt() } +const COM1_PORT: u16 = 0x3f8; + #[unsafe(no_mangle)] pub extern "C" fn _start() -> ! { vga::vga_init(); + let gdt_ptr = ptr::with_exposed_provenance::(0xc000); + let gdt_slice = unsafe { slice::from_raw_parts(gdt_ptr, 4) }; + vga_println!("hello from rust :)"); + vga_println!("{:016x}", gdt_slice[0]); + vga_println!("{:016x}", gdt_slice[1]); + vga_println!("{:016x}", gdt_slice[2]); + vga_println!("{:016x}", gdt_slice[3]); + + let mut com = unsafe { com::Com::init(COM1_PORT) }; + + if let Ok(com) = &mut com { + writeln!(com, "hello serial").ok(); + } hlt() } diff --git a/xtask/src/mkimg.rs b/xtask/src/mkimg.rs index 8596002..44d4c33 100644 --- a/xtask/src/mkimg.rs +++ b/xtask/src/mkimg.rs @@ -1,8 +1,5 @@ use crate::Context; -const BLOCK_SIZE: u64 = 512; -const PARTITION_ENTRY_SIZE: u64 = 128; - // LBA 0: MBR // LBA 1: partition header // LBA 2..33: partition table entries