From b9217309a99cd520d96eaba3a5957fefd5fbb4f7 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Tue, 28 Apr 2020 11:29:19 +0100 Subject: [PATCH 01/61] Merge rybot666/bootloader --- .gitignore | 3 +- real_mode/.cargo/config | 3 ++ real_mode/Cargo.lock | 21 +++++++++ real_mode/Cargo.toml | 11 +++++ real_mode/linker.ld | 47 +++++++++++++++++++++ real_mode/rust-toolchain | 1 + real_mode/src/bootsector/Cargo.toml | 11 +++++ real_mode/src/bootsector/src/bootstrap.s | 28 ++++++++++++ real_mode/src/bootsector/src/errors.rs | 18 ++++++++ real_mode/src/bootsector/src/main.rs | 54 ++++++++++++++++++++++++ real_mode/src/shared/Cargo.toml | 7 +++ real_mode/src/shared/src/console.rs | 22 ++++++++++ real_mode/src/shared/src/dap.rs | 39 +++++++++++++++++ real_mode/src/shared/src/lib.rs | 7 +++ real_mode/src/shared/src/macros.rs | 12 ++++++ real_mode/src/shared/src/utils.rs | 6 +++ real_mode/src/stage_2/Cargo.toml | 10 +++++ real_mode/src/stage_2/src/lib.rs | 9 ++++ real_mode/src/v86/Cargo.toml | 7 +++ real_mode/src/v86/src/lib.rs | 2 + real_mode/src/v86/src/monitor.rs | 0 real_mode/x86_64-real_mode.json | 21 +++++++++ 22 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 real_mode/.cargo/config create mode 100644 real_mode/Cargo.lock create mode 100644 real_mode/Cargo.toml create mode 100644 real_mode/linker.ld create mode 100644 real_mode/rust-toolchain create mode 100644 real_mode/src/bootsector/Cargo.toml create mode 100644 real_mode/src/bootsector/src/bootstrap.s create mode 100644 real_mode/src/bootsector/src/errors.rs create mode 100644 real_mode/src/bootsector/src/main.rs create mode 100644 real_mode/src/shared/Cargo.toml create mode 100644 real_mode/src/shared/src/console.rs create mode 100644 real_mode/src/shared/src/dap.rs create mode 100644 real_mode/src/shared/src/lib.rs create mode 100644 real_mode/src/shared/src/macros.rs create mode 100644 real_mode/src/shared/src/utils.rs create mode 100644 real_mode/src/stage_2/Cargo.toml create mode 100644 real_mode/src/stage_2/src/lib.rs create mode 100644 real_mode/src/v86/Cargo.toml create mode 100644 real_mode/src/v86/src/lib.rs create mode 100644 real_mode/src/v86/src/monitor.rs create mode 100644 real_mode/x86_64-real_mode.json diff --git a/.gitignore b/.gitignore index eccd7b4a..b5b49403 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -/target/ +**/target/ **/*.rs.bk +**/*.bin \ No newline at end of file diff --git a/real_mode/.cargo/config b/real_mode/.cargo/config new file mode 100644 index 00000000..02647447 --- /dev/null +++ b/real_mode/.cargo/config @@ -0,0 +1,3 @@ +[build] +target = "x86_64-real_mode.json" +rustflags = ["-C", "link-arg=-Tlinker.ld"] \ No newline at end of file diff --git a/real_mode/Cargo.lock b/real_mode/Cargo.lock new file mode 100644 index 00000000..85faf3c3 --- /dev/null +++ b/real_mode/Cargo.lock @@ -0,0 +1,21 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bootsector" +version = "0.1.0" +dependencies = [ + "shared 0.1.0", + "stage_2 0.1.0", +] + +[[package]] +name = "shared" +version = "0.1.0" + +[[package]] +name = "stage_2" +version = "0.1.0" +dependencies = [ + "shared 0.1.0", +] + diff --git a/real_mode/Cargo.toml b/real_mode/Cargo.toml new file mode 100644 index 00000000..36a15a34 --- /dev/null +++ b/real_mode/Cargo.toml @@ -0,0 +1,11 @@ +[workspace] +members = [ + "src/shared", + "src/v86", + "src/bootsector", + "src/stage_2" +] + +[profile.release] +opt-level = "z" +panic = "abort" \ No newline at end of file diff --git a/real_mode/linker.ld b/real_mode/linker.ld new file mode 100644 index 00000000..0873e460 --- /dev/null +++ b/real_mode/linker.ld @@ -0,0 +1,47 @@ +ENTRY(_start) + +SECTIONS { + . = 0x500; + _stack_start = .; + . = 0x7c00; + _stack_end = .; + + .bootsector : + { + _bootloader_start = .; + + *(.bootstrap) + *bootsector* + + *shared* + } + + /* We write the magic number in the linker script to cause a link-time error if the bootloader overflows the 512 byte limit */ + . = 0x7c00 + 510; + .magic : + { + SHORT(0xaa55) + } + + _rest_of_bootloader_start = .; + + .stage_2 : + { + + _second_stage_start = .; + + *stage_2* + + _second_stage_end = .; + } + + . = ALIGN(512); + + _rest_of_bootloader_end = .; + _bootloader_end = .; + + /DISCARD/ : + { + *(.eh_frame) + } +} \ No newline at end of file diff --git a/real_mode/rust-toolchain b/real_mode/rust-toolchain new file mode 100644 index 00000000..07ade694 --- /dev/null +++ b/real_mode/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file diff --git a/real_mode/src/bootsector/Cargo.toml b/real_mode/src/bootsector/Cargo.toml new file mode 100644 index 00000000..6960247b --- /dev/null +++ b/real_mode/src/bootsector/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "bootsector" +version = "0.1.0" +authors = ["Ryland Morgan "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +shared = { path = "../shared" } +stage_2 = { path = "../stage_2" } \ No newline at end of file diff --git a/real_mode/src/bootsector/src/bootstrap.s b/real_mode/src/bootsector/src/bootstrap.s new file mode 100644 index 00000000..9e995e7f --- /dev/null +++ b/real_mode/src/bootsector/src/bootstrap.s @@ -0,0 +1,28 @@ +.section .bootstrap, "awx" +.global _start +.intel_syntax noprefix +.code16 + +_start: + # Zero segment registers + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + mov fs, ax + mov gs, ax + + # Setup the stack + lea ebx, _stack_end + mov esp, ebx + + # Push the drive number as first argument + push dx + + # Call rust + call rust_start + +spin: + cli + hlt + jmp spin \ No newline at end of file diff --git a/real_mode/src/bootsector/src/errors.rs b/real_mode/src/bootsector/src/errors.rs new file mode 100644 index 00000000..e8e5625e --- /dev/null +++ b/real_mode/src/bootsector/src/errors.rs @@ -0,0 +1,18 @@ +use shared::utils; +use shared::console::println; + +#[no_mangle] +pub extern "C" fn dap_load_failed() -> ! { + println(b"[!] DAP Load Failed"); + loop { + utils::hlt() + } +} + +#[no_mangle] +pub extern "C" fn no_int13h_extensions() -> ! { + println(b"[!] No int13h Extensions"); + loop { + utils::hlt() + } +} \ No newline at end of file diff --git a/real_mode/src/bootsector/src/main.rs b/real_mode/src/bootsector/src/main.rs new file mode 100644 index 00000000..b1f61226 --- /dev/null +++ b/real_mode/src/bootsector/src/main.rs @@ -0,0 +1,54 @@ +#![feature(asm, global_asm)] + +#![no_std] +#![no_main] + +#![allow(dead_code)] + +mod errors; + +use core::panic::PanicInfo; +use stage_2::second_stage; + +use shared::console::println; +use shared::{dap, utils, linker_symbol}; + +global_asm!(include_str!("bootstrap.s")); + +#[no_mangle] +unsafe extern "C" fn rust_start(disk_number: u16) -> ! { + println(b"Stage 1"); + + check_int13h_extensions(disk_number); + + let dap = dap::DiskAddressPacket::new( + linker_symbol!(_rest_of_bootloader_start) as u16, + (linker_symbol!(_rest_of_bootloader_start) - linker_symbol!(_bootloader_start)) as u64, + linker_symbol!(_rest_of_bootloader_end) - linker_symbol!(_rest_of_bootloader_start) + ); + + dap.perform_load(disk_number); + + second_stage(); + + loop { + utils::hlt(); + } +} + +#[panic_handler] +pub fn panic(_info: &PanicInfo) -> ! { + println(b"[!] Rust Panic"); + loop { + utils::hlt() + } +} + +pub fn check_int13h_extensions(disk_number: u16) { + unsafe { + asm!(" + int 0x13 + jc no_int13h_extensions + " :: "{ah}"(0x41), "{bx}"(0x55aa), "{dl}"(disk_number) :: "intel", "volatile"); + } +} \ No newline at end of file diff --git a/real_mode/src/shared/Cargo.toml b/real_mode/src/shared/Cargo.toml new file mode 100644 index 00000000..d9c9c70e --- /dev/null +++ b/real_mode/src/shared/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "shared" +version = "0.1.0" +authors = ["Ryland Morgan "] +edition = "2018" + +[dependencies] diff --git a/real_mode/src/shared/src/console.rs b/real_mode/src/shared/src/console.rs new file mode 100644 index 00000000..7bcfac5c --- /dev/null +++ b/real_mode/src/shared/src/console.rs @@ -0,0 +1,22 @@ +#[inline(never)] +pub fn println(s: &[u8]) { + print(s); + print_char(b'\n'); +} + +pub fn print(s: &[u8]) { + let mut i = 0; + + while i < s.len() { + print_char(s[i]); + i += 1; + } +} + +#[inline(always)] +pub fn print_char(c: u8) { + let ax = u16::from(c) | 0x0e00; + unsafe { + asm!("int 0x10" :: "{ax}"(ax) :: "intel" ); + } +} \ No newline at end of file diff --git a/real_mode/src/shared/src/dap.rs b/real_mode/src/shared/src/dap.rs new file mode 100644 index 00000000..c9ef7e34 --- /dev/null +++ b/real_mode/src/shared/src/dap.rs @@ -0,0 +1,39 @@ +#[repr(packed)] +#[allow(dead_code)] +pub struct DiskAddressPacket { + /// Size of the DAP structure + packet_size: u8, + /// always zero + zero: u8, + /// Number of sectors to transfer + number_of_sectors: u16, + /// Offset to memory buffer + offset: u16, + /// Segment of memory buffer + segment: u16, + /// Start logical block address + start_lba: u64, +} + +impl DiskAddressPacket { + #[inline(always)] + pub fn new(memory_buffer_start: u16, file_offset: u64, bytes: u32) -> Self { + Self { + packet_size: 0x10, + zero: 0, + number_of_sectors: (bytes / 512) as u16, + offset: memory_buffer_start, + segment: 0, + start_lba: file_offset / 512, + } + } + + #[inline(always)] + pub unsafe fn perform_load(&self, disk_number: u16) { + let self_addr = self as *const Self as u16; + asm!(" + int 0x13 + jc dap_load_failed + " :: "{si}"(self_addr), "{ax}"(0x4200), "{dx}"(disk_number) : "bx" : "intel", "volatile"); + } +} diff --git a/real_mode/src/shared/src/lib.rs b/real_mode/src/shared/src/lib.rs new file mode 100644 index 00000000..a7a0ed9f --- /dev/null +++ b/real_mode/src/shared/src/lib.rs @@ -0,0 +1,7 @@ +#![no_std] +#![feature(asm)] + +pub mod console; +pub mod dap; +pub mod utils; +#[macro_use] pub mod macros; \ No newline at end of file diff --git a/real_mode/src/shared/src/macros.rs b/real_mode/src/shared/src/macros.rs new file mode 100644 index 00000000..b55b2113 --- /dev/null +++ b/real_mode/src/shared/src/macros.rs @@ -0,0 +1,12 @@ +#[macro_export] +macro_rules! linker_symbol { + ($symbol_name:ident) => {{ + let symbol_value: u32; + + asm!(concat!("lea eax, ", stringify!($symbol_name)) + : "={eax}"(symbol_value) + ::: "intel", "volatile"); + + symbol_value + }}; +} \ No newline at end of file diff --git a/real_mode/src/shared/src/utils.rs b/real_mode/src/shared/src/utils.rs new file mode 100644 index 00000000..f2cdee74 --- /dev/null +++ b/real_mode/src/shared/src/utils.rs @@ -0,0 +1,6 @@ +#[inline(always)] +pub fn hlt() { + unsafe { + asm!("hlt" :::: "intel","volatile"); + } +} \ No newline at end of file diff --git a/real_mode/src/stage_2/Cargo.toml b/real_mode/src/stage_2/Cargo.toml new file mode 100644 index 00000000..ca736e07 --- /dev/null +++ b/real_mode/src/stage_2/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "stage_2" +version = "0.1.0" +authors = ["Ryland Morgan "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +shared = { path = "../shared" } \ No newline at end of file diff --git a/real_mode/src/stage_2/src/lib.rs b/real_mode/src/stage_2/src/lib.rs new file mode 100644 index 00000000..a2fa131f --- /dev/null +++ b/real_mode/src/stage_2/src/lib.rs @@ -0,0 +1,9 @@ +#![no_std] + +use shared::console::println; + +#[no_mangle] +pub fn second_stage() -> u16 { + println(b"Stage 2"); + return 12345; +} \ No newline at end of file diff --git a/real_mode/src/v86/Cargo.toml b/real_mode/src/v86/Cargo.toml new file mode 100644 index 00000000..b2ed9448 --- /dev/null +++ b/real_mode/src/v86/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "v86" +version = "0.1.0" +authors = ["Ryland Morgan "] +edition = "2018" + +[dependencies] diff --git a/real_mode/src/v86/src/lib.rs b/real_mode/src/v86/src/lib.rs new file mode 100644 index 00000000..880f48d2 --- /dev/null +++ b/real_mode/src/v86/src/lib.rs @@ -0,0 +1,2 @@ +#![no_std] +pub mod monitor; \ No newline at end of file diff --git a/real_mode/src/v86/src/monitor.rs b/real_mode/src/v86/src/monitor.rs new file mode 100644 index 00000000..e69de29b diff --git a/real_mode/x86_64-real_mode.json b/real_mode/x86_64-real_mode.json new file mode 100644 index 00000000..35bd2784 --- /dev/null +++ b/real_mode/x86_64-real_mode.json @@ -0,0 +1,21 @@ +{ + "arch": "x86", + "cpu": "i386", + "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", + "dynamic-linking": false, + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "llvm-target": "i386-unknown-none-code16", + "max-atomic-width": 64, + "position-independent-executables": false, + "disable-redzone": true, + "target-c-int-width": "32", + "target-pointer-width": "32", + "target-endian": "little", + "panic-strategy": "abort", + "os": "none", + "vendor": "unknown", + "relocation_model": "static", + "eliminate_frame_pointer": true +} \ No newline at end of file From 875f8707707d839e1ff247639ede5e7aab749226 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Tue, 28 Apr 2020 11:33:20 +0100 Subject: [PATCH 02/61] Move real_mode to top level --- .cargo/config | 3 +- Cargo.lock | 103 +---- Cargo.toml | 60 +-- linker.ld | 71 ++- real_mode/.cargo/config | 3 - real_mode/Cargo.lock | 21 - real_mode/Cargo.toml | 11 - real_mode/linker.ld | 47 -- real_mode/rust-toolchain | 1 - src/boot_info.rs | 31 -- src/bootinfo/memory_map.rs | 236 ---------- src/bootinfo/mod.rs | 114 ----- {real_mode/src => src}/bootsector/Cargo.toml | 0 .../src => src}/bootsector/src/bootstrap.s | 0 .../src => src}/bootsector/src/errors.rs | 0 {real_mode/src => src}/bootsector/src/main.rs | 0 src/e820.s | 55 --- src/frame_allocator.rs | 132 ------ src/level4_entries.rs | 45 -- src/lib.rs | 42 -- src/main.rs | 403 ------------------ src/page_table.rs | 228 ---------- src/printer/mod.rs | 11 - src/printer/vga_320x200.rs | 80 ---- src/printer/vga_text_80x25.rs | 37 -- {real_mode/src => src}/shared/Cargo.toml | 0 {real_mode/src => src}/shared/src/console.rs | 0 {real_mode/src => src}/shared/src/dap.rs | 0 {real_mode/src => src}/shared/src/lib.rs | 0 {real_mode/src => src}/shared/src/macros.rs | 0 {real_mode/src => src}/shared/src/utils.rs | 0 src/sse.rs | 17 - src/stage_1.s | 233 ---------- src/stage_2.s | 105 ----- {real_mode/src => src}/stage_2/Cargo.toml | 0 {real_mode/src => src}/stage_2/src/lib.rs | 0 src/stage_3.s | 199 --------- {real_mode/src => src}/v86/Cargo.toml | 0 {real_mode/src => src}/v86/src/lib.rs | 0 {real_mode/src => src}/v86/src/monitor.rs | 0 src/video_mode/vga_320x200.s | 90 ---- src/video_mode/vga_text_80x25.s | 90 ---- x86_64-bootloader.json | 21 - ...64-real_mode.json => x86_64-real_mode.json | 0 44 files changed, 54 insertions(+), 2435 deletions(-) delete mode 100644 real_mode/.cargo/config delete mode 100644 real_mode/Cargo.lock delete mode 100644 real_mode/Cargo.toml delete mode 100644 real_mode/linker.ld delete mode 100644 real_mode/rust-toolchain delete mode 100644 src/boot_info.rs delete mode 100644 src/bootinfo/memory_map.rs delete mode 100644 src/bootinfo/mod.rs rename {real_mode/src => src}/bootsector/Cargo.toml (100%) rename {real_mode/src => src}/bootsector/src/bootstrap.s (100%) rename {real_mode/src => src}/bootsector/src/errors.rs (100%) rename {real_mode/src => src}/bootsector/src/main.rs (100%) delete mode 100644 src/e820.s delete mode 100644 src/frame_allocator.rs delete mode 100644 src/level4_entries.rs delete mode 100644 src/lib.rs delete mode 100644 src/main.rs delete mode 100644 src/page_table.rs delete mode 100644 src/printer/mod.rs delete mode 100644 src/printer/vga_320x200.rs delete mode 100644 src/printer/vga_text_80x25.rs rename {real_mode/src => src}/shared/Cargo.toml (100%) rename {real_mode/src => src}/shared/src/console.rs (100%) rename {real_mode/src => src}/shared/src/dap.rs (100%) rename {real_mode/src => src}/shared/src/lib.rs (100%) rename {real_mode/src => src}/shared/src/macros.rs (100%) rename {real_mode/src => src}/shared/src/utils.rs (100%) delete mode 100644 src/sse.rs delete mode 100644 src/stage_1.s delete mode 100644 src/stage_2.s rename {real_mode/src => src}/stage_2/Cargo.toml (100%) rename {real_mode/src => src}/stage_2/src/lib.rs (100%) delete mode 100644 src/stage_3.s rename {real_mode/src => src}/v86/Cargo.toml (100%) rename {real_mode/src => src}/v86/src/lib.rs (100%) rename {real_mode/src => src}/v86/src/monitor.rs (100%) delete mode 100644 src/video_mode/vga_320x200.s delete mode 100644 src/video_mode/vga_text_80x25.s delete mode 100644 x86_64-bootloader.json rename real_mode/x86_64-real_mode.json => x86_64-real_mode.json (100%) diff --git a/.cargo/config b/.cargo/config index fb871b1b..02647447 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,2 +1,3 @@ [build] -target = "x86_64-bootloader.json" +target = "x86_64-real_mode.json" +rustflags = ["-C", "link-arg=-Tlinker.ld"] \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 767178c0..85faf3c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,106 +1,21 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "bit_field" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bit_field" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bootloader" -version = "0.9.2" -dependencies = [ - "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fixedvec 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "font8x8 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "x86_64 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cast" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fixedvec" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "font8x8" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "llvm-tools" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "toml" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "bootsector" +version = "0.1.0" dependencies = [ - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "shared 0.1.0", + "stage_2 0.1.0", ] [[package]] -name = "usize_conversions" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "shared" +version = "0.1.0" [[package]] -name = "x86_64" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "stage_2" +version = "0.1.0" dependencies = [ - "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "shared 0.1.0", ] -[[package]] -name = "xmas-elf" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zero" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" -"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" -"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" -"checksum fixedvec 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b395ef2adf62bdeefcd1b59ad0dd2225c6c333ec79656ea79ac5285c46d051ea" -"checksum font8x8 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be" -"checksum llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" -"checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" -"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" -"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" -"checksum x86_64 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5d9e3e26fcb51976eafa310e8f2b7a5a83ae8c185443efe50cbc6534a4fffa0d" -"checksum xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22678df5df766e8d1e5d609da69f0c3132d794edf6ab5e75e7abcd2270d4cf58" -"checksum zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5" diff --git a/Cargo.toml b/Cargo.toml index 420553b6..36a15a34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,53 +1,11 @@ -[package] -name = "bootloader" -version = "0.9.2" -authors = ["Philipp Oppermann "] -license = "MIT/Apache-2.0" -description = "An experimental pure-Rust x86 bootloader." -repository = "https://github.com/rust-osdev/bootloader" -edition = "2018" -build = "build.rs" - -[[bin]] -name = "bootloader" -required-features = ["binary"] - -[dependencies] -xmas-elf = { version = "0.6.2", optional = true } -x86_64 = { version = "0.8.3", optional = true } -usize_conversions = { version = "0.2.0", optional = true } -fixedvec = { version = "0.2.4", optional = true } -bit_field = { version = "0.10.0", optional = true } - -[dependencies.font8x8] -version = "0.2.4" -default-features = false -features = ["unicode"] -optional = true - -[build-dependencies] -llvm-tools = { version = "0.1", optional = true } -toml = { version = "0.5.1", optional = true } - -[features] -default = [] -binary = ["xmas-elf", "x86_64", "usize_conversions", "fixedvec", "llvm-tools", "toml"] -vga_320x200 = ["font8x8"] -recursive_page_table = [] -map_physical_memory = [] -sse = ["bit_field"] - -[profile.dev] -panic = "abort" +[workspace] +members = [ + "src/shared", + "src/v86", + "src/bootsector", + "src/stage_2" +] [profile.release] -panic = "abort" -lto = false -debug = true - -[package.metadata.bootloader] -target = "x86_64-bootloader.json" - -[package.metadata.docs.rs] -features = [ "recursive_page_table", "map_physical_memory" ] -default-target = "x86_64-unknown-linux-gnu" +opt-level = "z" +panic = "abort" \ No newline at end of file diff --git a/linker.ld b/linker.ld index 4e58320a..0873e460 100644 --- a/linker.ld +++ b/linker.ld @@ -2,49 +2,46 @@ ENTRY(_start) SECTIONS { . = 0x500; - /* buffer for loading the kernel */ - _kernel_buffer = .; - . += 512; - /* page tables */ - . = ALIGN(0x1000); - __page_table_start = .; - _p4 = .; - . += 0x1000; - _p3 = .; - . += 0x1000; - _p2 = .; - . += 0x1000; - _p1 = .; - . += 0x1000; - __page_table_end = .; - __bootloader_start = .; - _memory_map = .; - . += 0x1000; - _stack_start = .; . = 0x7c00; _stack_end = .; - .bootloader : + .bootsector : + { + _bootloader_start = .; + + *(.bootstrap) + *bootsector* + + *shared* + } + + /* We write the magic number in the linker script to cause a link-time error if the bootloader overflows the 512 byte limit */ + . = 0x7c00 + 510; + .magic : + { + SHORT(0xaa55) + } + + _rest_of_bootloader_start = .; + + .stage_2 : { - /* first stage */ - *(.boot-first-stage) - - /* rest of bootloader */ - _rest_of_bootloader_start_addr = .; - *(.boot) - *(.context_switch) - *(.text .text.*) - *(.rodata .rodata.*) - *(.data .data.*) - *(.got) - . = ALIGN(512); - _rest_of_bootloader_end_addr = .; - __bootloader_end = .; + + _second_stage_start = .; + + *stage_2* + + _second_stage_end = .; } - .kernel : + . = ALIGN(512); + + _rest_of_bootloader_end = .; + _bootloader_end = .; + + /DISCARD/ : { - KEEP(*(.kernel)) + *(.eh_frame) } -} +} \ No newline at end of file diff --git a/real_mode/.cargo/config b/real_mode/.cargo/config deleted file mode 100644 index 02647447..00000000 --- a/real_mode/.cargo/config +++ /dev/null @@ -1,3 +0,0 @@ -[build] -target = "x86_64-real_mode.json" -rustflags = ["-C", "link-arg=-Tlinker.ld"] \ No newline at end of file diff --git a/real_mode/Cargo.lock b/real_mode/Cargo.lock deleted file mode 100644 index 85faf3c3..00000000 --- a/real_mode/Cargo.lock +++ /dev/null @@ -1,21 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "bootsector" -version = "0.1.0" -dependencies = [ - "shared 0.1.0", - "stage_2 0.1.0", -] - -[[package]] -name = "shared" -version = "0.1.0" - -[[package]] -name = "stage_2" -version = "0.1.0" -dependencies = [ - "shared 0.1.0", -] - diff --git a/real_mode/Cargo.toml b/real_mode/Cargo.toml deleted file mode 100644 index 36a15a34..00000000 --- a/real_mode/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[workspace] -members = [ - "src/shared", - "src/v86", - "src/bootsector", - "src/stage_2" -] - -[profile.release] -opt-level = "z" -panic = "abort" \ No newline at end of file diff --git a/real_mode/linker.ld b/real_mode/linker.ld deleted file mode 100644 index 0873e460..00000000 --- a/real_mode/linker.ld +++ /dev/null @@ -1,47 +0,0 @@ -ENTRY(_start) - -SECTIONS { - . = 0x500; - _stack_start = .; - . = 0x7c00; - _stack_end = .; - - .bootsector : - { - _bootloader_start = .; - - *(.bootstrap) - *bootsector* - - *shared* - } - - /* We write the magic number in the linker script to cause a link-time error if the bootloader overflows the 512 byte limit */ - . = 0x7c00 + 510; - .magic : - { - SHORT(0xaa55) - } - - _rest_of_bootloader_start = .; - - .stage_2 : - { - - _second_stage_start = .; - - *stage_2* - - _second_stage_end = .; - } - - . = ALIGN(512); - - _rest_of_bootloader_end = .; - _bootloader_end = .; - - /DISCARD/ : - { - *(.eh_frame) - } -} \ No newline at end of file diff --git a/real_mode/rust-toolchain b/real_mode/rust-toolchain deleted file mode 100644 index 07ade694..00000000 --- a/real_mode/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly \ No newline at end of file diff --git a/src/boot_info.rs b/src/boot_info.rs deleted file mode 100644 index f50cf2a5..00000000 --- a/src/boot_info.rs +++ /dev/null @@ -1,31 +0,0 @@ -use core::slice; - -use bootloader::bootinfo::{E820MemoryRegion, MemoryMap, MemoryRegion, MemoryRegionType}; -use usize_conversions::usize_from; -use x86_64::VirtAddr; - -pub(crate) fn create_from(memory_map_addr: VirtAddr, entry_count: u64) -> MemoryMap { - let memory_map_start_ptr = usize_from(memory_map_addr.as_u64()) as *const E820MemoryRegion; - let e820_memory_map = - unsafe { slice::from_raw_parts(memory_map_start_ptr, usize_from(entry_count)) }; - - let mut memory_map = MemoryMap::new(); - for region in e820_memory_map { - memory_map.add_region(MemoryRegion::from(*region)); - } - - memory_map.sort(); - - let mut iter = memory_map.iter_mut().peekable(); - while let Some(region) = iter.next() { - if let Some(next) = iter.peek() { - if region.range.end_frame_number > next.range.start_frame_number - && region.region_type == MemoryRegionType::Usable - { - region.range.end_frame_number = next.range.start_frame_number; - } - } - } - - memory_map -} diff --git a/src/bootinfo/memory_map.rs b/src/bootinfo/memory_map.rs deleted file mode 100644 index bd9eb7fb..00000000 --- a/src/bootinfo/memory_map.rs +++ /dev/null @@ -1,236 +0,0 @@ -use core::fmt; -use core::ops::{Deref, DerefMut}; - -const PAGE_SIZE: u64 = 4096; - -const MAX_MEMORY_MAP_SIZE: usize = 64; - -/// A map of the physical memory regions of the underlying machine. -#[repr(C)] -pub struct MemoryMap { - entries: [MemoryRegion; MAX_MEMORY_MAP_SIZE], - // u64 instead of usize so that the structure layout is platform - // independent - next_entry_index: u64, -} - -#[doc(hidden)] -#[allow(clippy::new_without_default)] -impl MemoryMap { - pub fn new() -> Self { - MemoryMap { - entries: [MemoryRegion::empty(); MAX_MEMORY_MAP_SIZE], - next_entry_index: 0, - } - } - - pub fn add_region(&mut self, region: MemoryRegion) { - assert!( - self.next_entry_index() < MAX_MEMORY_MAP_SIZE, - "too many memory regions in memory map" - ); - self.entries[self.next_entry_index()] = region; - self.next_entry_index += 1; - self.sort(); - } - - pub fn sort(&mut self) { - use core::cmp::Ordering; - - self.entries.sort_unstable_by(|r1, r2| { - if r1.range.is_empty() { - Ordering::Greater - } else if r2.range.is_empty() { - Ordering::Less - } else { - let ordering = r1 - .range - .start_frame_number - .cmp(&r2.range.start_frame_number); - - if ordering == Ordering::Equal { - r1.range.end_frame_number.cmp(&r2.range.end_frame_number) - } else { - ordering - } - } - }); - if let Some(first_zero_index) = self.entries.iter().position(|r| r.range.is_empty()) { - self.next_entry_index = first_zero_index as u64; - } - } - - fn next_entry_index(&self) -> usize { - self.next_entry_index as usize - } -} - -impl Deref for MemoryMap { - type Target = [MemoryRegion]; - - fn deref(&self) -> &Self::Target { - &self.entries[0..self.next_entry_index()] - } -} - -impl DerefMut for MemoryMap { - fn deref_mut(&mut self) -> &mut Self::Target { - let next_index = self.next_entry_index(); - &mut self.entries[0..next_index] - } -} - -impl fmt::Debug for MemoryMap { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -/// Represents a region of physical memory. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct MemoryRegion { - /// The range of frames that belong to the region. - pub range: FrameRange, - /// The type of the region. - pub region_type: MemoryRegionType, -} - -#[doc(hidden)] -impl MemoryRegion { - pub fn empty() -> Self { - MemoryRegion { - range: FrameRange { - start_frame_number: 0, - end_frame_number: 0, - }, - region_type: MemoryRegionType::Empty, - } - } -} - -/// A range of frames with an exclusive upper bound. -#[derive(Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct FrameRange { - /// The frame _number_ of the first 4KiB frame in the region. - /// - /// This convert this frame number to a physical address, multiply it with the - /// page size (4KiB). - pub start_frame_number: u64, - /// The frame _number_ of the first 4KiB frame that does no longer belong to the region. - /// - /// This convert this frame number to a physical address, multiply it with the - /// page size (4KiB). - pub end_frame_number: u64, -} - -impl FrameRange { - /// Create a new FrameRange from the passed start_addr and end_addr. - /// - /// The end_addr is exclusive. - pub fn new(start_addr: u64, end_addr: u64) -> Self { - let last_byte = end_addr - 1; - FrameRange { - start_frame_number: start_addr / PAGE_SIZE, - end_frame_number: (last_byte / PAGE_SIZE) + 1, - } - } - - /// Returns true if the frame range contains no frames. - pub fn is_empty(&self) -> bool { - self.start_frame_number == self.end_frame_number - } - - /// Returns the physical start address of the memory region. - pub fn start_addr(&self) -> u64 { - self.start_frame_number * PAGE_SIZE - } - - /// Returns the physical end address of the memory region. - pub fn end_addr(&self) -> u64 { - self.end_frame_number * PAGE_SIZE - } -} - -impl fmt::Debug for FrameRange { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "FrameRange({:#x}..{:#x})", - self.start_addr(), - self.end_addr() - ) - } -} - -/// Represents possible types for memory regions. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub enum MemoryRegionType { - /// Unused memory, can be freely used by the kernel. - Usable, - /// Memory that is already in use. - InUse, - /// Memory reserved by the hardware. Not usable. - Reserved, - /// ACPI reclaimable memory - AcpiReclaimable, - /// ACPI NVS memory - AcpiNvs, - /// Area containing bad memory - BadMemory, - /// Memory used for loading the kernel. - Kernel, - /// Memory used for the kernel stack. - KernelStack, - /// Memory used for creating page tables. - PageTable, - /// Memory used by the bootloader. - Bootloader, - /// Frame at address zero. - /// - /// (shouldn't be used because it's easy to make mistakes related to null pointers) - FrameZero, - /// An empty region with size 0 - Empty, - /// Memory used for storing the boot information. - BootInfo, - /// Memory used for storing the supplied package - Package, - /// Additional variant to ensure that we can add more variants in the future without - /// breaking backwards compatibility. - #[doc(hidden)] - NonExhaustive, -} - -#[doc(hidden)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct E820MemoryRegion { - pub start_addr: u64, - pub len: u64, - pub region_type: u32, - pub acpi_extended_attributes: u32, -} - -impl From for MemoryRegion { - fn from(region: E820MemoryRegion) -> MemoryRegion { - let region_type = match region.region_type { - 1 => MemoryRegionType::Usable, - 2 => MemoryRegionType::Reserved, - 3 => MemoryRegionType::AcpiReclaimable, - 4 => MemoryRegionType::AcpiNvs, - 5 => MemoryRegionType::BadMemory, - t => panic!("invalid region type {}", t), - }; - MemoryRegion { - range: FrameRange::new(region.start_addr, region.start_addr + region.len), - region_type, - } - } -} - -extern "C" { - fn _improper_ctypes_check(_boot_info: MemoryMap); -} diff --git a/src/bootinfo/mod.rs b/src/bootinfo/mod.rs deleted file mode 100644 index 16f6c4c1..00000000 --- a/src/bootinfo/mod.rs +++ /dev/null @@ -1,114 +0,0 @@ -//! Provides boot information to the kernel. - -#![deny(improper_ctypes)] - -pub use self::memory_map::*; - -mod memory_map; - -/// This structure represents the information that the bootloader passes to the kernel. -/// -/// The information is passed as an argument to the entry point: -/// -/// ```ignore -/// pub extern "C" fn _start(boot_info: &'static BootInfo) -> ! { -/// // […] -/// } -/// ``` -/// -/// Note that no type checking occurs for the entry point function, so be careful to -/// use the correct argument types. To ensure that the entry point function has the correct -/// signature, use the [`entry_point`] macro. -#[derive(Debug)] -#[repr(C)] -pub struct BootInfo { - /// A map of the physical memory regions of the underlying machine. - /// - /// The bootloader queries this information from the BIOS/UEFI firmware and translates this - /// information to Rust types. It also marks any memory regions that the bootloader uses in - /// the memory map before passing it to the kernel. Regions marked as usable can be freely - /// used by the kernel. - pub memory_map: MemoryMap, - /// The virtual address of the recursively mapped level 4 page table. - #[cfg(feature = "recursive_page_table")] - pub recursive_page_table_addr: u64, - /// The offset into the virtual address space where the physical memory is mapped. - /// - /// Physical addresses can be converted to virtual addresses by adding this offset to them. - /// - /// The mapping of the physical memory allows to access arbitrary physical frames. Accessing - /// frames that are also mapped at other virtual addresses can easily break memory safety and - /// cause undefined behavior. Only frames reported as `USABLE` by the memory map in the `BootInfo` - /// can be safely accessed. - #[cfg(feature = "map_physical_memory")] - pub physical_memory_offset: u64, - tls_template: TlsTemplate, - _non_exhaustive: u8, // `()` is not FFI safe -} - -impl BootInfo { - /// Create a new boot information structure. This function is only for internal purposes. - #[allow(unused_variables)] - #[doc(hidden)] - pub fn new( - memory_map: MemoryMap, - tls_template: Option, - recursive_page_table_addr: u64, - physical_memory_offset: u64, - ) -> Self { - let tls_template = tls_template.unwrap_or(TlsTemplate { - start_addr: 0, - file_size: 0, - mem_size: 0, - }); - BootInfo { - memory_map, - tls_template, - #[cfg(feature = "recursive_page_table")] - recursive_page_table_addr, - #[cfg(feature = "map_physical_memory")] - physical_memory_offset, - _non_exhaustive: 0, - } - } - - /// Returns information about the thread local storage segment of the kernel. - /// - /// Returns `None` if the kernel has no thread local storage segment. - /// - /// (The reason this is a method instead of a normal field is that `Option` - /// is not FFI-safe.) - pub fn tls_template(&self) -> Option { - if self.tls_template.mem_size > 0 { - Some(self.tls_template) - } else { - None - } - } -} - -/// Information about the thread local storage (TLS) template. -/// -/// This template can be used to set up thread local storage for threads. For -/// each thread, a new memory location of size `mem_size` must be initialized. -/// Then the first `file_size` bytes of this template needs to be copied to the -/// location. The additional `mem_size - file_size` bytes must be initialized with -/// zero. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct TlsTemplate { - /// The virtual start address of the thread local storage template. - pub start_addr: u64, - /// The number of data bytes in the template. - /// - /// Corresponds to the length of the `.tdata` section. - pub file_size: u64, - /// The total number of bytes that the TLS segment should have in memory. - /// - /// Corresponds to the combined length of the `.tdata` and `.tbss` sections. - pub mem_size: u64, -} - -extern "C" { - fn _improper_ctypes_check(_boot_info: BootInfo); -} diff --git a/real_mode/src/bootsector/Cargo.toml b/src/bootsector/Cargo.toml similarity index 100% rename from real_mode/src/bootsector/Cargo.toml rename to src/bootsector/Cargo.toml diff --git a/real_mode/src/bootsector/src/bootstrap.s b/src/bootsector/src/bootstrap.s similarity index 100% rename from real_mode/src/bootsector/src/bootstrap.s rename to src/bootsector/src/bootstrap.s diff --git a/real_mode/src/bootsector/src/errors.rs b/src/bootsector/src/errors.rs similarity index 100% rename from real_mode/src/bootsector/src/errors.rs rename to src/bootsector/src/errors.rs diff --git a/real_mode/src/bootsector/src/main.rs b/src/bootsector/src/main.rs similarity index 100% rename from real_mode/src/bootsector/src/main.rs rename to src/bootsector/src/main.rs diff --git a/src/e820.s b/src/e820.s deleted file mode 100644 index 114de231..00000000 --- a/src/e820.s +++ /dev/null @@ -1,55 +0,0 @@ -.section .boot, "awx" -.intel_syntax noprefix -.code16 - -# From http://wiki.osdev.org/Detecting_Memory_(x86)#Getting_an_E820_Memory_Map - -# use the INT 0x15, eax= 0xE820 BIOS function to get a memory map -# inputs: es:di -> destination buffer for 24 byte entries -# outputs: bp = entry count, trashes all registers except esi -do_e820: - xor ebx, ebx # ebx must be 0 to start - xor bp, bp # keep an entry count in bp - mov edx, 0x0534D4150 # Place "SMAP" into edx - mov eax, 0xe820 - mov dword ptr es:[di + 20], 1 # force a valid ACPI 3.X entry - mov ecx, 24 # ask for 24 bytes - int 0x15 - jc .failed # carry set on first call means "unsupported function" - mov edx, 0x0534D4150 # Some BIOSes apparently trash this register? - cmp eax, edx # on success, eax must have been reset to "SMAP" - jne .failed - test ebx, ebx # ebx = 0 implies list is only 1 entry long (worthless) - je .failed - jmp .jmpin -.e820lp: - mov eax, 0xe820 # eax, ecx get trashed on every int 0x15 call - mov dword ptr es:[di + 20], 1 # force a valid ACPI 3.X entry - mov ecx, 24 # ask for 24 bytes again - int 0x15 - jc .e820f # carry set means "end of list already reached" - mov edx, 0x0534D4150 # repair potentially trashed register -.jmpin: - jcxz .skipent # skip any 0 length entries - cmp cl, 20 # got a 24 byte ACPI 3.X response? - jbe .notext - test byte ptr es:[di + 20], 1 # if so: is the "ignore this data" bit clear? - je .skipent -.notext: - mov ecx, es:[di + 8] # get lower uint32_t of memory region length - or ecx, es:[di + 12] # "or" it with upper uint32_t to test for zero - jz .skipent # if length uint64_t is 0, skip entry - inc bp # got a good entry: ++count, move to next storage spot - add di, 24 -.skipent: - test ebx, ebx # if ebx resets to 0, list is complete - jne .e820lp -.e820f: - mov [mmap_ent], bp # store the entry count - clc # there is "jc" on end of list to this point, so the carry must be cleared - ret -.failed: - stc # "function unsupported" error exit - ret - -mmap_ent: .word 0 diff --git a/src/frame_allocator.rs b/src/frame_allocator.rs deleted file mode 100644 index 5630702f..00000000 --- a/src/frame_allocator.rs +++ /dev/null @@ -1,132 +0,0 @@ -use super::{frame_range, phys_frame_range}; -use bootloader::bootinfo::{MemoryMap, MemoryRegion, MemoryRegionType}; -use x86_64::structures::paging::{frame::PhysFrameRange, PhysFrame, UnusedPhysFrame}; - -pub(crate) struct FrameAllocator<'a> { - pub memory_map: &'a mut MemoryMap, -} - -impl<'a> FrameAllocator<'a> { - pub(crate) fn allocate_frame( - &mut self, - region_type: MemoryRegionType, - ) -> Option { - // try to find an existing region of same type that can be enlarged - let mut iter = self.memory_map.iter_mut().peekable(); - while let Some(region) = iter.next() { - if region.region_type == region_type { - if let Some(next) = iter.peek() { - if next.range.start_frame_number == region.range.end_frame_number - && next.region_type == MemoryRegionType::Usable - && !next.range.is_empty() - { - let frame = - unsafe { UnusedPhysFrame::new(phys_frame_range(region.range).end) }; - region.range.end_frame_number += 1; - iter.next().unwrap().range.start_frame_number += 1; - return Some(frame); - } - } - } - } - - fn split_usable_region<'a, I>(iter: &mut I) -> Option<(UnusedPhysFrame, PhysFrameRange)> - where - I: Iterator, - { - for region in iter { - if region.region_type != MemoryRegionType::Usable { - continue; - } - if region.range.is_empty() { - continue; - } - - let physframe = phys_frame_range(region.range).start; - let unused_frame = unsafe { UnusedPhysFrame::new(physframe) }; - region.range.start_frame_number += 1; - return Some((unused_frame, PhysFrame::range(physframe, physframe + 1))); - } - None - } - - let result = if region_type == MemoryRegionType::PageTable { - // prevent fragmentation when page tables are allocated in between - split_usable_region(&mut self.memory_map.iter_mut().rev()) - } else { - split_usable_region(&mut self.memory_map.iter_mut()) - }; - - if let Some((frame, range)) = result { - self.memory_map.add_region(MemoryRegion { - range: frame_range(range), - region_type, - }); - Some(frame) - } else { - None - } - } - - /// Marks the passed region in the memory map. - /// - /// Panics if a non-usable region (e.g. a reserved region) overlaps with the passed region. - pub(crate) fn mark_allocated_region(&mut self, region: MemoryRegion) { - for r in self.memory_map.iter_mut() { - if region.range.start_frame_number >= r.range.end_frame_number { - continue; - } - if region.range.end_frame_number <= r.range.start_frame_number { - continue; - } - - if r.region_type != MemoryRegionType::Usable { - panic!( - "region {:x?} overlaps with non-usable region {:x?}", - region, r - ); - } - - if region.range.start_frame_number == r.range.start_frame_number { - if region.range.end_frame_number < r.range.end_frame_number { - // Case: (r = `r`, R = `region`) - // ----rrrrrrrrrrr---- - // ----RRRR----------- - r.range.start_frame_number = region.range.end_frame_number; - self.memory_map.add_region(region); - } else { - // Case: (r = `r`, R = `region`) - // ----rrrrrrrrrrr---- - // ----RRRRRRRRRRRRRR- - *r = region; - } - } else if region.range.start_frame_number > r.range.start_frame_number { - if region.range.end_frame_number < r.range.end_frame_number { - // Case: (r = `r`, R = `region`) - // ----rrrrrrrrrrr---- - // ------RRRR--------- - let mut behind_r = r.clone(); - behind_r.range.start_frame_number = region.range.end_frame_number; - r.range.end_frame_number = region.range.start_frame_number; - self.memory_map.add_region(behind_r); - self.memory_map.add_region(region); - } else { - // Case: (r = `r`, R = `region`) - // ----rrrrrrrrrrr---- - // -----------RRRR---- or - // -------------RRRR-- - r.range.end_frame_number = region.range.start_frame_number; - self.memory_map.add_region(region); - } - } else { - // Case: (r = `r`, R = `region`) - // ----rrrrrrrrrrr---- - // --RRRR------------- - r.range.start_frame_number = region.range.end_frame_number; - self.memory_map.add_region(region); - } - return; - } - panic!("region {:x?} is not a usable memory region", region); - } -} diff --git a/src/level4_entries.rs b/src/level4_entries.rs deleted file mode 100644 index 60ace846..00000000 --- a/src/level4_entries.rs +++ /dev/null @@ -1,45 +0,0 @@ -use core::convert::TryInto; -use fixedvec::FixedVec; -use x86_64::{ - structures::paging::{Page, PageTableIndex}, - VirtAddr, -}; -use xmas_elf::program::ProgramHeader64; - -pub struct UsedLevel4Entries { - entry_state: [bool; 512], // whether an entry is in use by the kernel -} - -impl UsedLevel4Entries { - pub fn new(segments: &FixedVec) -> Self { - let mut used = UsedLevel4Entries { - entry_state: [false; 512], - }; - - used.entry_state[0] = true; // TODO: Can we do this dynamically? - - for segment in segments { - let start_page: Page = Page::containing_address(VirtAddr::new(segment.virtual_addr)); - let end_page: Page = - Page::containing_address(VirtAddr::new(segment.virtual_addr + segment.mem_size)); - - for p4_index in u64::from(start_page.p4_index())..=u64::from(end_page.p4_index()) { - used.entry_state[p4_index as usize] = true; - } - } - - used - } - - pub fn get_free_entry(&mut self) -> PageTableIndex { - let (idx, entry) = self - .entry_state - .iter_mut() - .enumerate() - .find(|(_, &mut entry)| entry == false) - .expect("no usable level 4 entries found"); - - *entry = true; - PageTableIndex::new(idx.try_into().unwrap()) - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 90d8eebf..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! This library part of the bootloader allows kernels to retrieve information from the bootloader. -//! -//! To combine your kernel with the bootloader crate you need a tool such -//! as [`bootimage`](https://github.com/rust-osdev/bootimage). See the -//! [_Writing an OS in Rust_](https://os.phil-opp.com/minimal-rust-kernel/#creating-a-bootimage) -//! blog for an explanation. - -#![no_std] -#![warn(missing_docs)] - -pub use crate::bootinfo::BootInfo; - -pub mod bootinfo; - -#[cfg(target_arch = "x86")] -compile_error!( - "This crate currently does not support 32-bit protected mode. \ - See https://github.com/rust-osdev/bootloader/issues/70 for more information." -); - -#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))] -compile_error!("This crate only supports the x86_64 architecture."); - -/// Defines the entry point function. -/// -/// The function must have the signature `fn(&'static BootInfo) -> !`. -/// -/// This macro just creates a function named `_start`, which the linker will use as the entry -/// point. The advantage of using this macro instead of providing an own `_start` function is -/// that the macro ensures that the function and argument types are correct. -#[macro_export] -macro_rules! entry_point { - ($path:path) => { - #[export_name = "_start"] - pub extern "C" fn __impl_start(boot_info: &'static $crate::bootinfo::BootInfo) -> ! { - // validate the signature of the program entry point - let f: fn(&'static $crate::bootinfo::BootInfo) -> ! = $path; - - f(boot_info) - } - }; -} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index ce7bcc50..00000000 --- a/src/main.rs +++ /dev/null @@ -1,403 +0,0 @@ -#![feature(lang_items)] -#![feature(global_asm)] -#![feature(step_trait)] -#![feature(llvm_asm)] -#![feature(nll)] -#![feature(const_fn)] -#![no_std] -#![no_main] - -#[cfg(not(target_os = "none"))] -compile_error!("The bootloader crate must be compiled for the `x86_64-bootloader.json` target"); - -use bootloader::bootinfo::{BootInfo, FrameRange}; -use core::convert::TryInto; -use core::panic::PanicInfo; -use core::{mem, slice}; -use fixedvec::alloc_stack; -use usize_conversions::usize_from; -use x86_64::instructions::tlb; -use x86_64::structures::paging::{ - frame::PhysFrameRange, page_table::PageTableEntry, Mapper, Page, PageTable, PageTableFlags, - PageTableIndex, PhysFrame, RecursivePageTable, Size2MiB, Size4KiB, UnusedPhysFrame, -}; -use x86_64::{PhysAddr, VirtAddr}; - -// The bootloader_config.rs file contains some configuration constants set by the build script: -// PHYSICAL_MEMORY_OFFSET: The offset into the virtual address space where the physical memory -// is mapped if the `map_physical_memory` feature is activated. -// -// KERNEL_STACK_ADDRESS: The virtual address of the kernel stack. -// -// KERNEL_STACK_SIZE: The number of pages in the kernel stack. -include!(concat!(env!("OUT_DIR"), "/bootloader_config.rs")); - -global_asm!(include_str!("stage_1.s")); -global_asm!(include_str!("stage_2.s")); -global_asm!(include_str!("e820.s")); -global_asm!(include_str!("stage_3.s")); - -#[cfg(feature = "vga_320x200")] -global_asm!(include_str!("video_mode/vga_320x200.s")); -#[cfg(not(feature = "vga_320x200"))] -global_asm!(include_str!("video_mode/vga_text_80x25.s")); - -unsafe fn context_switch(boot_info: VirtAddr, entry_point: VirtAddr, stack_pointer: VirtAddr) -> ! { - llvm_asm!("call $1; ${:private}.spin.${:uid}: jmp ${:private}.spin.${:uid}" :: - "{rsp}"(stack_pointer), "r"(entry_point), "{rdi}"(boot_info) :: "intel"); - ::core::hint::unreachable_unchecked() -} - -mod boot_info; -mod frame_allocator; -mod level4_entries; -mod page_table; -mod printer; -#[cfg(feature = "sse")] -mod sse; - -pub struct IdentityMappedAddr(PhysAddr); - -impl IdentityMappedAddr { - fn phys(&self) -> PhysAddr { - self.0 - } - - fn virt(&self) -> VirtAddr { - VirtAddr::new(self.0.as_u64()) - } - - fn as_u64(&self) -> u64 { - self.0.as_u64() - } -} - -// Symbols defined in `linker.ld` -extern "C" { - static mmap_ent: usize; - static _memory_map: usize; - static _kernel_start_addr: usize; - static _kernel_end_addr: usize; - static _kernel_size: usize; - static __page_table_start: usize; - static __page_table_end: usize; - static __bootloader_end: usize; - static __bootloader_start: usize; - static _p4: usize; -} - -#[no_mangle] -pub unsafe extern "C" fn stage_4() -> ! { - // Set stack segment - llvm_asm!("mov bx, 0x0 - mov ss, bx" ::: "bx" : "intel"); - - let kernel_start = 0x400000; - let kernel_size = &_kernel_size as *const _ as u64; - let memory_map_addr = &_memory_map as *const _ as u64; - let memory_map_entry_count = (mmap_ent & 0xff) as u64; // Extract lower 8 bits - let page_table_start = &__page_table_start as *const _ as u64; - let page_table_end = &__page_table_end as *const _ as u64; - let bootloader_start = &__bootloader_start as *const _ as u64; - let bootloader_end = &__bootloader_end as *const _ as u64; - let p4_physical = &_p4 as *const _ as u64; - - bootloader_main( - IdentityMappedAddr(PhysAddr::new(kernel_start)), - kernel_size, - VirtAddr::new(memory_map_addr), - memory_map_entry_count, - PhysAddr::new(page_table_start), - PhysAddr::new(page_table_end), - PhysAddr::new(bootloader_start), - PhysAddr::new(bootloader_end), - PhysAddr::new(p4_physical), - ) -} - -fn bootloader_main( - kernel_start: IdentityMappedAddr, - kernel_size: u64, - memory_map_addr: VirtAddr, - memory_map_entry_count: u64, - page_table_start: PhysAddr, - page_table_end: PhysAddr, - bootloader_start: PhysAddr, - bootloader_end: PhysAddr, - p4_physical: PhysAddr, -) -> ! { - use bootloader::bootinfo::{MemoryRegion, MemoryRegionType}; - use fixedvec::FixedVec; - use xmas_elf::program::{ProgramHeader, ProgramHeader64}; - - printer::Printer.clear_screen(); - - let mut memory_map = boot_info::create_from(memory_map_addr, memory_map_entry_count); - - let max_phys_addr = memory_map - .iter() - .map(|r| r.range.end_addr()) - .max() - .expect("no physical memory regions found"); - - // Extract required information from the ELF file. - let mut preallocated_space = alloc_stack!([ProgramHeader64; 32]); - let mut segments = FixedVec::new(&mut preallocated_space); - let entry_point; - { - let kernel_start_ptr = usize_from(kernel_start.as_u64()) as *const u8; - let kernel = unsafe { slice::from_raw_parts(kernel_start_ptr, usize_from(kernel_size)) }; - let elf_file = xmas_elf::ElfFile::new(kernel).unwrap(); - xmas_elf::header::sanity_check(&elf_file).unwrap(); - - entry_point = elf_file.header.pt2.entry_point(); - - for program_header in elf_file.program_iter() { - match program_header { - ProgramHeader::Ph64(header) => segments - .push(*header) - .expect("does not support more than 32 program segments"), - ProgramHeader::Ph32(_) => panic!("does not support 32 bit elf files"), - } - } - } - - // Mark used virtual addresses - let mut level4_entries = level4_entries::UsedLevel4Entries::new(&segments); - - // Enable support for the no-execute bit in page tables. - enable_nxe_bit(); - - // Create a recursive page table entry - let recursive_index = PageTableIndex::new(level4_entries.get_free_entry().try_into().unwrap()); - let mut entry = PageTableEntry::new(); - entry.set_addr( - p4_physical, - PageTableFlags::PRESENT | PageTableFlags::WRITABLE, - ); - - // Write the recursive entry into the page table - let page_table = unsafe { &mut *(p4_physical.as_u64() as *mut PageTable) }; - page_table[recursive_index] = entry; - tlb::flush_all(); - - let recursive_page_table_addr = Page::from_page_table_indices( - recursive_index, - recursive_index, - recursive_index, - recursive_index, - ) - .start_address(); - let page_table = unsafe { &mut *(recursive_page_table_addr.as_mut_ptr()) }; - let mut rec_page_table = - RecursivePageTable::new(page_table).expect("recursive page table creation failed"); - - // Create a frame allocator, which marks allocated frames as used in the memory map. - let mut frame_allocator = frame_allocator::FrameAllocator { - memory_map: &mut memory_map, - }; - - // Mark already used memory areas in frame allocator. - { - let zero_frame: PhysFrame = PhysFrame::from_start_address(PhysAddr::new(0)).unwrap(); - frame_allocator.mark_allocated_region(MemoryRegion { - range: frame_range(PhysFrame::range(zero_frame, zero_frame + 1)), - region_type: MemoryRegionType::FrameZero, - }); - let bootloader_start_frame = PhysFrame::containing_address(bootloader_start); - let bootloader_end_frame = PhysFrame::containing_address(bootloader_end - 1u64); - let bootloader_memory_area = - PhysFrame::range(bootloader_start_frame, bootloader_end_frame + 1); - frame_allocator.mark_allocated_region(MemoryRegion { - range: frame_range(bootloader_memory_area), - region_type: MemoryRegionType::Bootloader, - }); - let kernel_start_frame = PhysFrame::containing_address(kernel_start.phys()); - let kernel_end_frame = - PhysFrame::containing_address(kernel_start.phys() + kernel_size - 1u64); - let kernel_memory_area = PhysFrame::range(kernel_start_frame, kernel_end_frame + 1); - frame_allocator.mark_allocated_region(MemoryRegion { - range: frame_range(kernel_memory_area), - region_type: MemoryRegionType::Kernel, - }); - let page_table_start_frame = PhysFrame::containing_address(page_table_start); - let page_table_end_frame = PhysFrame::containing_address(page_table_end - 1u64); - let page_table_memory_area = - PhysFrame::range(page_table_start_frame, page_table_end_frame + 1); - frame_allocator.mark_allocated_region(MemoryRegion { - range: frame_range(page_table_memory_area), - region_type: MemoryRegionType::PageTable, - }); - } - - // Unmap the ELF file. - let kernel_start_page: Page = Page::containing_address(kernel_start.virt()); - let kernel_end_page: Page = - Page::containing_address(kernel_start.virt() + kernel_size - 1u64); - for page in Page::range_inclusive(kernel_start_page, kernel_end_page) { - rec_page_table.unmap(page).expect("dealloc error").1.flush(); - } - - // Map a page for the boot info structure - let boot_info_page = { - let page: Page = match BOOT_INFO_ADDRESS { - Some(addr) => Page::containing_address(VirtAddr::new(addr)), - None => Page::from_page_table_indices( - level4_entries.get_free_entry(), - PageTableIndex::new(0), - PageTableIndex::new(0), - PageTableIndex::new(0), - ), - }; - let frame = frame_allocator - .allocate_frame(MemoryRegionType::BootInfo) - .expect("frame allocation failed"); - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { - page_table::map_page( - page, - frame, - flags, - &mut rec_page_table, - &mut frame_allocator, - ) - } - .expect("Mapping of bootinfo page failed") - .flush(); - page - }; - - // If no kernel stack address is provided, map the kernel stack after the boot info page - let kernel_stack_address = match KERNEL_STACK_ADDRESS { - Some(addr) => Page::containing_address(VirtAddr::new(addr)), - None => boot_info_page + 1, - }; - - // Map kernel segments. - let kernel_memory_info = page_table::map_kernel( - kernel_start.phys(), - kernel_stack_address, - KERNEL_STACK_SIZE, - &segments, - &mut rec_page_table, - &mut frame_allocator, - ) - .expect("kernel mapping failed"); - - let physical_memory_offset = if cfg!(feature = "map_physical_memory") { - let physical_memory_offset = PHYSICAL_MEMORY_OFFSET.unwrap_or_else(|| { - // If offset not manually provided, find a free p4 entry and map memory here. - // One level 4 entry spans 2^48/512 bytes (over 500gib) so this should suffice. - assert!(max_phys_addr < (1 << 48) / 512); - Page::from_page_table_indices_1gib( - level4_entries.get_free_entry(), - PageTableIndex::new(0), - ) - .start_address() - .as_u64() - }); - - let virt_for_phys = - |phys: PhysAddr| -> VirtAddr { VirtAddr::new(phys.as_u64() + physical_memory_offset) }; - - let start_frame = PhysFrame::::containing_address(PhysAddr::new(0)); - let end_frame = PhysFrame::::containing_address(PhysAddr::new(max_phys_addr)); - - for frame in PhysFrame::range_inclusive(start_frame, end_frame) { - let page = Page::containing_address(virt_for_phys(frame.start_address())); - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { - page_table::map_page( - page, - UnusedPhysFrame::new(frame), - flags, - &mut rec_page_table, - &mut frame_allocator, - ) - } - .expect("Mapping of bootinfo page failed") - .flush(); - } - - physical_memory_offset - } else { - 0 // Value is unused by BootInfo::new, so this doesn't matter - }; - - // Construct boot info structure. - let mut boot_info = BootInfo::new( - memory_map, - kernel_memory_info.tls_segment, - recursive_page_table_addr.as_u64(), - physical_memory_offset, - ); - boot_info.memory_map.sort(); - - // Write boot info to boot info page. - let boot_info_addr = boot_info_page.start_address(); - unsafe { boot_info_addr.as_mut_ptr::().write(boot_info) }; - - // Make sure that the kernel respects the write-protection bits, even when in ring 0. - enable_write_protect_bit(); - - if cfg!(not(feature = "recursive_page_table")) { - // unmap recursive entry - rec_page_table - .unmap(Page::::containing_address( - recursive_page_table_addr, - )) - .expect("error deallocating recursive entry") - .1 - .flush(); - mem::drop(rec_page_table); - } - - #[cfg(feature = "sse")] - sse::enable_sse(); - - let entry_point = VirtAddr::new(entry_point); - unsafe { context_switch(boot_info_addr, entry_point, kernel_memory_info.stack_end) }; -} - -fn enable_nxe_bit() { - use x86_64::registers::control::{Efer, EferFlags}; - unsafe { Efer::update(|efer| *efer |= EferFlags::NO_EXECUTE_ENABLE) } -} - -fn enable_write_protect_bit() { - use x86_64::registers::control::{Cr0, Cr0Flags}; - unsafe { Cr0::update(|cr0| *cr0 |= Cr0Flags::WRITE_PROTECT) }; -} - -#[panic_handler] -#[no_mangle] -pub fn panic(info: &PanicInfo) -> ! { - use core::fmt::Write; - write!(printer::Printer, "{}", info).unwrap(); - loop {} -} - -#[lang = "eh_personality"] -#[no_mangle] -pub extern "C" fn eh_personality() { - loop {} -} - -#[no_mangle] -pub extern "C" fn _Unwind_Resume() { - loop {} -} - -fn phys_frame_range(range: FrameRange) -> PhysFrameRange { - PhysFrameRange { - start: PhysFrame::from_start_address(PhysAddr::new(range.start_addr())).unwrap(), - end: PhysFrame::from_start_address(PhysAddr::new(range.end_addr())).unwrap(), - } -} - -fn frame_range(range: PhysFrameRange) -> FrameRange { - FrameRange::new( - range.start.start_address().as_u64(), - range.end.start_address().as_u64(), - ) -} diff --git a/src/page_table.rs b/src/page_table.rs deleted file mode 100644 index 8f943afb..00000000 --- a/src/page_table.rs +++ /dev/null @@ -1,228 +0,0 @@ -use crate::frame_allocator::FrameAllocator; -use bootloader::bootinfo::MemoryRegionType; -use bootloader::bootinfo::TlsTemplate; -use fixedvec::FixedVec; -use x86_64::structures::paging::mapper::{MapToError, MapperFlush, UnmapError}; -use x86_64::structures::paging::{ - self, Mapper, Page, PageSize, PageTableFlags, PhysFrame, RecursivePageTable, Size4KiB, - UnusedPhysFrame, -}; -use x86_64::{align_up, PhysAddr, VirtAddr}; -use xmas_elf::program::{self, ProgramHeader64}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MemoryInfo { - pub stack_end: VirtAddr, - pub tls_segment: Option, -} - -#[derive(Debug)] -pub enum MapKernelError { - Mapping(MapToError), - MultipleTlsSegments, -} - -impl From for MapKernelError { - fn from(e: MapToError) -> Self { - MapKernelError::Mapping(e) - } -} - -pub(crate) fn map_kernel( - kernel_start: PhysAddr, - stack_start: Page, - stack_size: u64, - segments: &FixedVec, - page_table: &mut RecursivePageTable, - frame_allocator: &mut FrameAllocator, -) -> Result { - let mut tls_segment = None; - for segment in segments { - let tls = map_segment(segment, kernel_start, page_table, frame_allocator)?; - if let Some(tls) = tls { - if tls_segment.replace(tls).is_some() { - return Err(MapKernelError::MultipleTlsSegments); - } - } - } - - // Create a stack - let stack_start = stack_start + 1; // Leave the first page unmapped as a 'guard page' - let stack_end = stack_start + stack_size; // stack_size is in pages - - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - let region_type = MemoryRegionType::KernelStack; - - for page in Page::range(stack_start, stack_end) { - let frame = frame_allocator - .allocate_frame(region_type) - .ok_or(MapToError::FrameAllocationFailed)?; - unsafe { map_page(page, frame, flags, page_table, frame_allocator)? }.flush(); - } - - Ok(MemoryInfo { - stack_end: stack_end.start_address(), - tls_segment, - }) -} - -pub(crate) fn map_segment( - segment: &ProgramHeader64, - kernel_start: PhysAddr, - page_table: &mut RecursivePageTable, - frame_allocator: &mut FrameAllocator, -) -> Result, MapToError> { - let typ = segment.get_type().unwrap(); - match typ { - program::Type::Load => { - let mem_size = segment.mem_size; - let file_size = segment.file_size; - let file_offset = segment.offset; - let phys_start_addr = kernel_start + file_offset; - let virt_start_addr = VirtAddr::new(segment.virtual_addr); - - let start_page: Page = Page::containing_address(virt_start_addr); - let start_frame = PhysFrame::containing_address(phys_start_addr); - let end_frame = PhysFrame::containing_address(phys_start_addr + file_size - 1u64); - - let flags = segment.flags; - let mut page_table_flags = PageTableFlags::PRESENT; - if !flags.is_execute() { - page_table_flags |= PageTableFlags::NO_EXECUTE - }; - if flags.is_write() { - page_table_flags |= PageTableFlags::WRITABLE - }; - - for frame in PhysFrame::range_inclusive(start_frame, end_frame) { - let offset = frame - start_frame; - let page = start_page + offset; - unsafe { - map_page( - page, - UnusedPhysFrame::new(frame), - page_table_flags, - page_table, - frame_allocator, - )? - } - .flush(); - } - - if mem_size > file_size { - // .bss section (or similar), which needs to be zeroed - let zero_start = virt_start_addr + file_size; - let zero_end = virt_start_addr + mem_size; - if zero_start.as_u64() & 0xfff != 0 { - // A part of the last mapped frame needs to be zeroed. This is - // not possible since it could already contains parts of the next - // segment. Thus, we need to copy it before zeroing. - - // TODO: search for a free page dynamically - let temp_page: Page = Page::containing_address(VirtAddr::new(0xfeeefeee000)); - let new_frame = frame_allocator - .allocate_frame(MemoryRegionType::Kernel) - .ok_or(MapToError::FrameAllocationFailed)?; - unsafe { - map_page( - temp_page.clone(), - UnusedPhysFrame::new(new_frame.clone()), - page_table_flags, - page_table, - frame_allocator, - )? - } - .flush(); - - type PageArray = [u64; Size4KiB::SIZE as usize / 8]; - - let last_page = Page::containing_address(virt_start_addr + file_size - 1u64); - let last_page_ptr = last_page.start_address().as_ptr::(); - let temp_page_ptr = temp_page.start_address().as_mut_ptr::(); - - unsafe { - // copy contents - temp_page_ptr.write(last_page_ptr.read()); - } - - // remap last page - if let Err(e) = page_table.unmap(last_page.clone()) { - return Err(match e { - UnmapError::ParentEntryHugePage => MapToError::ParentEntryHugePage, - UnmapError::PageNotMapped => unreachable!(), - UnmapError::InvalidFrameAddress(_) => unreachable!(), - }); - } - - unsafe { - map_page( - last_page, - new_frame, - page_table_flags, - page_table, - frame_allocator, - )? - } - .flush(); - } - - // Map additional frames. - let start_page: Page = Page::containing_address(VirtAddr::new(align_up( - zero_start.as_u64(), - Size4KiB::SIZE, - ))); - let end_page = Page::containing_address(zero_end); - for page in Page::range_inclusive(start_page, end_page) { - let frame = frame_allocator - .allocate_frame(MemoryRegionType::Kernel) - .ok_or(MapToError::FrameAllocationFailed)?; - unsafe { - map_page(page, frame, page_table_flags, page_table, frame_allocator)? - } - .flush(); - } - - // zero - for offset in file_size..mem_size { - let addr = virt_start_addr + offset; - unsafe { addr.as_mut_ptr::().write(0) }; - } - } - - Ok(None) - } - program::Type::Tls => Ok(Some(TlsTemplate { - start_addr: segment.virtual_addr, - mem_size: segment.mem_size, - file_size: segment.file_size, - })), - _ => Ok(None), - } -} - -pub(crate) unsafe fn map_page<'a, S>( - page: Page, - phys_frame: UnusedPhysFrame, - flags: PageTableFlags, - page_table: &mut RecursivePageTable<'a>, - frame_allocator: &mut FrameAllocator, -) -> Result, MapToError> -where - S: PageSize, - RecursivePageTable<'a>: Mapper, -{ - struct PageTableAllocator<'a, 'b: 'a>(&'a mut FrameAllocator<'b>); - - unsafe impl<'a, 'b> paging::FrameAllocator for PageTableAllocator<'a, 'b> { - fn allocate_frame(&mut self) -> Option> { - self.0.allocate_frame(MemoryRegionType::PageTable) - } - } - - page_table.map_to( - page, - phys_frame, - flags, - &mut PageTableAllocator(frame_allocator), - ) -} diff --git a/src/printer/mod.rs b/src/printer/mod.rs deleted file mode 100644 index 726bb765..00000000 --- a/src/printer/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -#[cfg(not(feature = "vga_320x200"))] -pub use self::vga_text_80x25::*; - -#[cfg(feature = "vga_320x200")] -pub use self::vga_320x200::*; - -#[cfg(feature = "vga_320x200")] -mod vga_320x200; - -#[cfg(not(feature = "vga_320x200"))] -mod vga_text_80x25; diff --git a/src/printer/vga_320x200.rs b/src/printer/vga_320x200.rs deleted file mode 100644 index fb1e36af..00000000 --- a/src/printer/vga_320x200.rs +++ /dev/null @@ -1,80 +0,0 @@ -use core::fmt::{Result, Write}; -use core::sync::atomic::{AtomicUsize, Ordering}; - -const VGA_BUFFER: *mut u8 = 0xa0000 as *mut _; -const SCREEN_WIDTH: usize = 320; -const SCREEN_HEIGHT: usize = 200; - -// must not be 0 so that we don't have a .bss section -pub static X_POS: AtomicUsize = AtomicUsize::new(1); -pub static Y_POS: AtomicUsize = AtomicUsize::new(1); - -pub struct Printer; - -impl Printer { - pub fn clear_screen(&mut self) { - for i in 0..(SCREEN_WIDTH * SCREEN_HEIGHT) { - unsafe { - VGA_BUFFER.offset(i as isize).write_volatile(0); - } - } - - X_POS.store(0, Ordering::SeqCst); - Y_POS.store(0, Ordering::SeqCst); - } - - fn newline(&mut self) { - let y_pos = Y_POS.fetch_add(8, Ordering::SeqCst); - X_POS.store(0, Ordering::SeqCst); - if y_pos >= SCREEN_HEIGHT { - self.clear_screen(); - } - } - - fn write_char(&mut self, c: char) { - use font8x8::UnicodeFonts; - - if c == '\n' { - self.newline(); - return; - } - - let x_pos = X_POS.fetch_add(8, Ordering::SeqCst); - let y_pos = Y_POS.load(Ordering::SeqCst); - - match c { - ' '..='~' => { - let rendered = font8x8::BASIC_FONTS - .get(c) - .expect("character not found in basic font"); - for (y, byte) in rendered.iter().enumerate() { - for (x, bit) in (0..8).enumerate() { - if *byte & (1 << bit) == 0 { - continue; - } - let color = 0xf; - let idx = (y_pos + y) * SCREEN_WIDTH + x_pos + x; - unsafe { - VGA_BUFFER.offset(idx as isize).write_volatile(color); - } - } - } - } - _ => panic!("unprintable character"), - } - - if x_pos + 8 >= SCREEN_WIDTH { - self.newline(); - } - } -} - -impl Write for Printer { - fn write_str(&mut self, s: &str) -> Result { - for c in s.chars() { - self.write_char(c); - } - - Ok(()) - } -} diff --git a/src/printer/vga_text_80x25.rs b/src/printer/vga_text_80x25.rs deleted file mode 100644 index fd689987..00000000 --- a/src/printer/vga_text_80x25.rs +++ /dev/null @@ -1,37 +0,0 @@ -use core::fmt::{Result, Write}; -use core::sync::atomic::{AtomicUsize, Ordering}; - -const VGA_BUFFER: *mut u8 = 0xb8000 as *mut _; -const SCREEN_SIZE: usize = 80 * 25; - -// must not be 0 so that we don't have a .bss section -pub static CURRENT_OFFSET: AtomicUsize = AtomicUsize::new(160); - -pub struct Printer; - -impl Printer { - pub fn clear_screen(&mut self) { - for i in 0..SCREEN_SIZE { - unsafe { - VGA_BUFFER.offset(i as isize).write_volatile(0); - } - } - - CURRENT_OFFSET.store(0, Ordering::Relaxed); - } -} - -impl Write for Printer { - fn write_str(&mut self, s: &str) -> Result { - for byte in s.bytes() { - let index = CURRENT_OFFSET.fetch_add(2, Ordering::Relaxed) as isize; - - unsafe { - VGA_BUFFER.offset(index).write_volatile(byte); - VGA_BUFFER.offset(index + 1).write_volatile(0x4f); - } - } - - Ok(()) - } -} diff --git a/real_mode/src/shared/Cargo.toml b/src/shared/Cargo.toml similarity index 100% rename from real_mode/src/shared/Cargo.toml rename to src/shared/Cargo.toml diff --git a/real_mode/src/shared/src/console.rs b/src/shared/src/console.rs similarity index 100% rename from real_mode/src/shared/src/console.rs rename to src/shared/src/console.rs diff --git a/real_mode/src/shared/src/dap.rs b/src/shared/src/dap.rs similarity index 100% rename from real_mode/src/shared/src/dap.rs rename to src/shared/src/dap.rs diff --git a/real_mode/src/shared/src/lib.rs b/src/shared/src/lib.rs similarity index 100% rename from real_mode/src/shared/src/lib.rs rename to src/shared/src/lib.rs diff --git a/real_mode/src/shared/src/macros.rs b/src/shared/src/macros.rs similarity index 100% rename from real_mode/src/shared/src/macros.rs rename to src/shared/src/macros.rs diff --git a/real_mode/src/shared/src/utils.rs b/src/shared/src/utils.rs similarity index 100% rename from real_mode/src/shared/src/utils.rs rename to src/shared/src/utils.rs diff --git a/src/sse.rs b/src/sse.rs deleted file mode 100644 index b4e0a9f2..00000000 --- a/src/sse.rs +++ /dev/null @@ -1,17 +0,0 @@ -/// Enables Streaming SIMD Extensions (SSE) support for loaded kernels. -pub fn enable_sse() { - use x86_64::registers::control::{Cr0, Cr0Flags, Cr4, Cr4Flags}; - let mut flags = Cr0::read(); - flags.remove(Cr0Flags::EMULATE_COPROCESSOR); - flags.insert(Cr0Flags::MONITOR_COPROCESSOR); - unsafe { - Cr0::write(flags); - } - - let mut flags = Cr4::read(); - flags.insert(Cr4Flags::OSFXSR); - flags.insert(Cr4Flags::OSXMMEXCPT_ENABLE); - unsafe { - Cr4::write(flags); - } -} diff --git a/src/stage_1.s b/src/stage_1.s deleted file mode 100644 index ebfa1a08..00000000 --- a/src/stage_1.s +++ /dev/null @@ -1,233 +0,0 @@ -.section .boot-first-stage, "awx" -.global _start -.intel_syntax noprefix -.code16 - -# This stage initializes the stack, enables the A20 line, loads the rest of -# the bootloader from disk, and jumps to stage_2. - -_start: - # zero segment registers - xor ax, ax - mov ds, ax - mov es, ax - mov ss, ax - mov fs, ax - mov gs, ax - - # clear the direction flag (e.g. go forward in memory when using - # instructions like lodsb) - cld - - # initialize stack - mov sp, 0x7c00 - - lea si, boot_start_str - call real_mode_println - -enable_a20: - # enable A20-Line via IO-Port 92, might not work on all motherboards - in al, 0x92 - test al, 2 - jnz enable_a20_after - or al, 2 - and al, 0xFE - out 0x92, al -enable_a20_after: - - -enter_protected_mode: - # clear interrupts - cli - push ds - push es - - lgdt [gdt32info] - - mov eax, cr0 - or al, 1 # set protected mode bit - mov cr0, eax - - jmp protected_mode # tell 386/486 to not crash - -protected_mode: - mov bx, 0x10 - mov ds, bx # set data segment - mov es, bx # set extra segment - - and al, 0xfe # clear protected mode bit - mov cr0, eax - -unreal_mode: - pop es # get back old extra segment - pop ds # get back old data segment - sti - - # back to real mode, but internal data segment register is still loaded - # with gdt segment -> we can access the full 4GiB of memory - - mov bx, 0x0f01 # attrib/char of smiley - mov eax, 0xb8f00 # note 32 bit offset - mov word ptr ds:[eax], bx - -check_int13h_extensions: - mov ah, 0x41 - mov bx, 0x55aa - # dl contains drive number - int 0x13 - jc no_int13h_extensions - -load_rest_of_bootloader_from_disk: - lea eax, _rest_of_bootloader_start_addr - - # start of memory buffer - mov [dap_buffer_addr], ax - - # number of disk blocks to load - lea ebx, _rest_of_bootloader_end_addr - sub ebx, eax # end - start - shr ebx, 9 # divide by 512 (block size) - mov [dap_blocks], bx - - # number of start block - lea ebx, _start - sub eax, ebx - shr eax, 9 # divide by 512 (block size) - mov [dap_start_lba], eax - - lea si, dap - mov ah, 0x42 - int 0x13 - jc rest_of_bootloader_load_failed - -jump_to_second_stage: - lea eax, [stage_2] - jmp eax - -spin: - jmp spin - -# print a string and a newline -# IN -# si: points at zero-terminated String -# CLOBBER -# ax -real_mode_println: - call real_mode_print - mov al, 13 # \r - call real_mode_print_char - mov al, 10 # \n - jmp real_mode_print_char - -# print a string -# IN -# si: points at zero-terminated String -# CLOBBER -# ax -real_mode_print: - cld -real_mode_print_loop: - # note: if direction flag is set (via std) - # this will DECREMENT the ptr, effectively - # reading/printing in reverse. - lodsb al, BYTE PTR [si] - test al, al - jz real_mode_print_done - call real_mode_print_char - jmp real_mode_print_loop -real_mode_print_done: - ret - -# print a character -# IN -# al: character to print -# CLOBBER -# ah -real_mode_print_char: - mov ah, 0x0e - int 0x10 - ret - -# print a number in hex -# IN -# bx: the number -# CLOBBER -# al, cx -real_mode_print_hex: - mov cx, 4 -.lp: - mov al, bh - shr al, 4 - - cmp al, 0xA - jb .below_0xA - - add al, 'A' - 0xA - '0' -.below_0xA: - add al, '0' - - call real_mode_print_char - - shl bx, 4 - loop .lp - - ret - -real_mode_error: - call real_mode_println - jmp spin - -no_int13h_extensions: - lea si, no_int13h_extensions_str - jmp real_mode_error - -rest_of_bootloader_load_failed: - lea si, rest_of_bootloader_load_failed_str - jmp real_mode_error - -boot_start_str: .asciz "Booting (first stage)..." -error_str: .asciz "Error: " -no_int13h_extensions_str: .asciz "No support for int13h extensions" -rest_of_bootloader_load_failed_str: .asciz "Failed to load rest of bootloader" - -gdt32info: - .word gdt32_end - gdt32 - 1 # last byte in table - .word gdt32 # start of table - -gdt32: - # entry 0 is always unused - .quad 0 -codedesc: - .byte 0xff - .byte 0xff - .byte 0 - .byte 0 - .byte 0 - .byte 0x9a - .byte 0xcf - .byte 0 -datadesc: - .byte 0xff - .byte 0xff - .byte 0 - .byte 0 - .byte 0 - .byte 0x92 - .byte 0xcf - .byte 0 -gdt32_end: - -dap: # disk access packet - .byte 0x10 # size of dap - .byte 0 # unused -dap_blocks: - .word 0 # number of sectors -dap_buffer_addr: - .word 0 # offset to memory buffer -dap_buffer_seg: - .word 0 # segment of memory buffer -dap_start_lba: - .quad 0 # start logical block address - -.org 510 -.word 0xaa55 # magic number for bootable disk diff --git a/src/stage_2.s b/src/stage_2.s deleted file mode 100644 index 5486439a..00000000 --- a/src/stage_2.s +++ /dev/null @@ -1,105 +0,0 @@ -.section .boot, "awx" -.intel_syntax noprefix -.code16 - -# This stage sets the target operating mode, loads the kernel from disk, -# creates an e820 memory map, enters protected mode, and jumps to the -# third stage. - -second_stage_start_str: .asciz "Booting (second stage)..." -kernel_load_failed_str: .asciz "Failed to load kernel from disk" - -kernel_load_failed: - lea si, [kernel_load_failed_str] - call real_mode_println -kernel_load_failed_spin: - jmp kernel_load_failed_spin - -stage_2: - lea si, [second_stage_start_str] - call real_mode_println - -set_target_operating_mode: - # Some BIOSs assume the processor will only operate in Legacy Mode. We change the Target - # Operating Mode to "Long Mode Target Only", so the firmware expects each CPU to enter Long Mode - # once and then stay in it. This allows the firmware to enable mode-specifc optimizations. - # We save the flags, because CF is set if the callback is not supported (in which case, this is - # a NOP) - pushf - mov ax, 0xec00 - mov bl, 0x2 - int 0x15 - popf - -load_kernel_from_disk: - # start of memory buffer - lea eax, _kernel_buffer - mov [dap_buffer_addr], ax - - # number of disk blocks to load - mov word ptr [dap_blocks], 1 - - # number of start block - lea eax, _kernel_start_addr - lea ebx, _start - sub eax, ebx - shr eax, 9 # divide by 512 (block size) - mov [dap_start_lba], eax - - # destination address - mov edi, 0x400000 - - # block count - lea ecx, _kernel_size - add ecx, 511 # align up - shr ecx, 9 - -load_next_kernel_block_from_disk: - # load block from disk - lea si, dap - mov ah, 0x42 - int 0x13 - jc kernel_load_failed - - # copy block to 2MiB - push ecx - push esi - mov ecx, 512 / 4 - # move with zero extension - # because we are moving a word ptr - # to esi, a 32-bit register. - movzx esi, word ptr [dap_buffer_addr] - # move from esi to edi ecx times. - rep movsd [edi], [esi] - pop esi - pop ecx - - # next block - mov eax, [dap_start_lba] - add eax, 1 - mov [dap_start_lba], eax - - sub ecx, 1 - jnz load_next_kernel_block_from_disk - -create_memory_map: - lea di, es:[_memory_map] - call do_e820 - -video_mode_config: - call config_video_mode - -enter_protected_mode_again: - cli - lgdt [gdt32info] - mov eax, cr0 - or al, 1 # set protected mode bit - mov cr0, eax - - push 0x8 - lea eax, [stage_3] - push eax - retf - -spin32: - jmp spin32 diff --git a/real_mode/src/stage_2/Cargo.toml b/src/stage_2/Cargo.toml similarity index 100% rename from real_mode/src/stage_2/Cargo.toml rename to src/stage_2/Cargo.toml diff --git a/real_mode/src/stage_2/src/lib.rs b/src/stage_2/src/lib.rs similarity index 100% rename from real_mode/src/stage_2/src/lib.rs rename to src/stage_2/src/lib.rs diff --git a/src/stage_3.s b/src/stage_3.s deleted file mode 100644 index 06d753c3..00000000 --- a/src/stage_3.s +++ /dev/null @@ -1,199 +0,0 @@ -.section .boot, "awx" -.intel_syntax noprefix -.code32 - -# This stage performs some checks on the CPU (cpuid, long mode), sets up an -# initial page table mapping (identity map the bootloader, map the P4 -# recursively, map the kernel blob to 4MB), enables paging, switches to long -# mode, and jumps to stage_4. - -stage_3: - mov bx, 0x10 - mov ds, bx # set data segment - mov es, bx # set extra segment - mov ss, bx # set stack segment - - lea si, boot_third_stage_str - call vga_println - -check_cpu: - call check_cpuid - call check_long_mode - - cli # disable interrupts - - lidt zero_idt # Load a zero length IDT so that any NMI causes a triple fault. - -# enter long mode - -set_up_page_tables: - # zero out buffer for page tables - lea edi, [__page_table_start] - lea ecx, [__page_table_end] - sub ecx, edi - shr ecx, 2 # one stosd zeros 4 bytes -> divide by 4 - xor eax, eax - rep stosd - - # p4 - lea eax, [_p3] - or eax, (1 | 2) - mov [_p4], eax - # p3 - lea eax, [_p2] - or eax, (1 | 2) - mov [_p3], eax - # p2 - lea eax, [_p1] - or eax, (1 | 2) - mov [_p2], eax - mov eax, (0x400000 | 1 | 2 | (1 << 7)) - mov ecx, 2 - lea edx, _kernel_size - add edx, 0x400000 # start address - add edx, 0x200000 - 1 # align up - shr edx, 12 + 9 # end huge page number - map_p2_table: - mov [_p2 + ecx * 8], eax - add eax, 0x200000 - add ecx, 1 - cmp ecx, edx - jb map_p2_table - # p1 - # start mapping from __page_table_start, as we need to be able to access - # the p4 table from rust. stop mapping at __bootloader_end - lea eax, __page_table_start - and eax, 0xfffff000 - or eax, (1 | 2) - lea ecx, __page_table_start - shr ecx, 12 # start page number - lea edx, __bootloader_end - add edx, 4096 - 1 # align up - shr edx, 12 # end page number - map_p1_table: - mov [_p1 + ecx * 8], eax - add eax, 4096 - add ecx, 1 - cmp ecx, edx - jb map_p1_table - map_framebuffer: - call vga_map_frame_buffer - -enable_paging: - # Write back cache and add a memory fence. I'm not sure if this is - # necessary, but better be on the safe side. - wbinvd - mfence - - # load P4 to cr3 register (cpu uses this to access the P4 table) - lea eax, [_p4] - mov cr3, eax - - # enable PAE-flag in cr4 (Physical Address Extension) - mov eax, cr4 - or eax, (1 << 5) - mov cr4, eax - - # set the long mode bit in the EFER MSR (model specific register) - mov ecx, 0xC0000080 - rdmsr - or eax, (1 << 8) - wrmsr - - # enable paging in the cr0 register - mov eax, cr0 - or eax, (1 << 31) - mov cr0, eax - -load_64bit_gdt: - lgdt gdt_64_pointer # Load GDT.Pointer defined below. - -jump_to_long_mode: - push 0x8 - lea eax, [stage_4] - push eax - retf # Load CS with 64 bit segment and flush the instruction cache - -spin_here: - jmp spin_here - -check_cpuid: - # Check if CPUID is supported by attempting to flip the ID bit (bit 21) - # in the FLAGS register. If we can flip it, CPUID is available. - - # Copy FLAGS in to EAX via stack - pushfd - pop eax - - # Copy to ECX as well for comparing later on - mov ecx, eax - - # Flip the ID bit - xor eax, (1 << 21) - - # Copy EAX to FLAGS via the stack - push eax - popfd - - # Copy FLAGS back to EAX (with the flipped bit if CPUID is supported) - pushfd - pop eax - - # Restore FLAGS from the old version stored in ECX (i.e. flipping the - # ID bit back if it was ever flipped). - push ecx - popfd - - # Compare EAX and ECX. If they are equal then that means the bit - # wasn't flipped, and CPUID isn't supported. - cmp eax, ecx - je no_cpuid - ret -no_cpuid: - lea esi, no_cpuid_str - call vga_println -no_cpuid_spin: - hlt - jmp no_cpuid_spin - -check_long_mode: - # test if extended processor info in available - mov eax, 0x80000000 # implicit argument for cpuid - cpuid # get highest supported argument - cmp eax, 0x80000001 # it needs to be at least 0x80000001 - jb no_long_mode # if it's less, the CPU is too old for long mode - - # use extended info to test if long mode is available - mov eax, 0x80000001 # argument for extended processor info - cpuid # returns various feature bits in ecx and edx - test edx, (1 << 29) # test if the LM-bit is set in the D-register - jz no_long_mode # If it's not set, there is no long mode - ret -no_long_mode: - lea esi, no_long_mode_str - call vga_println -no_long_mode_spin: - hlt - jmp no_long_mode_spin - - -.align 4 -zero_idt: - .word 0 - .byte 0 - -gdt_64: - .quad 0x0000000000000000 # Null Descriptor - should be present. - .quad 0x00209A0000000000 # 64-bit code descriptor (exec/read). - .quad 0x0000920000000000 # 64-bit data descriptor (read/write). - -.align 4 - .word 0 # Padding to make the "address of the GDT" field aligned on a 4-byte boundary - -gdt_64_pointer: - .word gdt_64_pointer - gdt_64 - 1 # 16-bit Size (Limit) of GDT. - .long gdt_64 # 32-bit Base Address of GDT. (CPU will zero extend to 64-bit) - -boot_third_stage_str: .asciz "Booting (third stage)..." -no_cpuid_str: .asciz "Error: CPU does not support CPUID" -no_long_mode_str: .asciz "Error: CPU does not support long mode" diff --git a/real_mode/src/v86/Cargo.toml b/src/v86/Cargo.toml similarity index 100% rename from real_mode/src/v86/Cargo.toml rename to src/v86/Cargo.toml diff --git a/real_mode/src/v86/src/lib.rs b/src/v86/src/lib.rs similarity index 100% rename from real_mode/src/v86/src/lib.rs rename to src/v86/src/lib.rs diff --git a/real_mode/src/v86/src/monitor.rs b/src/v86/src/monitor.rs similarity index 100% rename from real_mode/src/v86/src/monitor.rs rename to src/v86/src/monitor.rs diff --git a/src/video_mode/vga_320x200.s b/src/video_mode/vga_320x200.s deleted file mode 100644 index 17d4a743..00000000 --- a/src/video_mode/vga_320x200.s +++ /dev/null @@ -1,90 +0,0 @@ -.section .boot, "awx" -.intel_syntax noprefix -.code16 - -config_video_mode: - mov ah, 0 - mov al, 0x13 # 320x200 256 color graphics - int 0x10 - ret - -.code32 - -vga_map_frame_buffer: - mov eax, 0xa0000 - or eax, (1 | 2) -vga_map_frame_buffer_loop: - mov ecx, eax - shr ecx, 12 - mov [_p1 + ecx * 8], eax - - add eax, 4096 - cmp eax, 0xc0000 - jl vga_map_frame_buffer_loop - - ret - -# print a string and a newline -# IN -# esi: points at zero-terminated String -vga_println: - push eax - push ebx - push ecx - push edx - - call vga_print - - # newline - mov edx, 0 - mov eax, vga_position - mov ecx, 80 * 2 - div ecx - add eax, 1 - mul ecx - mov vga_position, eax - - pop edx - pop ecx - pop ebx - pop eax - - ret - -# print a string -# IN -# esi: points at zero-terminated String -# CLOBBER -# ah, ebx -vga_print: - cld -vga_print_loop: - # note: if direction flag is set (via std) - # this will DECREMENT the ptr, effectively - # reading/printing in reverse. - lodsb al, BYTE PTR [esi] - test al, al - jz vga_print_done - call vga_print_char - jmp vga_print_loop -vga_print_done: - ret - - -# print a character -# IN -# al: character to print -# CLOBBER -# ah, ebx -vga_print_char: - mov ebx, vga_position - mov ah, 0x0f - mov [ebx + 0xa0000], ax - - add ebx, 2 - mov [vga_position], ebx - - ret - -vga_position: - .double 0 diff --git a/src/video_mode/vga_text_80x25.s b/src/video_mode/vga_text_80x25.s deleted file mode 100644 index 0ddb0d6f..00000000 --- a/src/video_mode/vga_text_80x25.s +++ /dev/null @@ -1,90 +0,0 @@ -.section .boot, "awx" -.intel_syntax noprefix -.code16 - -config_video_mode: - mov ah, 0 - mov al, 0x03 # 80x25 16 color text - int 0x10 - ret - -.code32 - -vga_map_frame_buffer: - mov eax, 0xa0000 - or eax, (1 | 2) -vga_map_frame_buffer_loop: - mov ecx, eax - shr ecx, 12 - mov [_p1 + ecx * 8], eax - - add eax, 4096 - cmp eax, 0xc0000 - jl vga_map_frame_buffer_loop - - ret - -# print a string and a newline -# IN -# esi: points at zero-terminated String -vga_println: - push eax - push ebx - push ecx - push edx - - call vga_print - - # newline - mov edx, 0 - mov eax, vga_position - mov ecx, 80 * 2 - div ecx - add eax, 1 - mul ecx - mov vga_position, eax - - pop edx - pop ecx - pop ebx - pop eax - - ret - -# print a string -# IN -# esi: points at zero-terminated String -# CLOBBER -# ah, ebx -vga_print: - cld -vga_print_loop: - # note: if direction flag is set (via std) - # this will DECREMENT the ptr, effectively - # reading/printing in reverse. - lodsb al, BYTE PTR [esi] - test al, al - jz vga_print_done - call vga_print_char - jmp vga_print_loop -vga_print_done: - ret - - -# print a character -# IN -# al: character to print -# CLOBBER -# ah, ebx -vga_print_char: - mov ebx, vga_position - mov ah, 0x0f - mov [ebx + 0xb8000], ax - - add ebx, 2 - mov [vga_position], ebx - - ret - -vga_position: - .double 0 diff --git a/x86_64-bootloader.json b/x86_64-bootloader.json deleted file mode 100644 index ad13109d..00000000 --- a/x86_64-bootloader.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "llvm-target": "x86_64-unknown-none-gnu", - "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", - "linker-flavor": "ld.lld", - "linker": "rust-lld", - "pre-link-args": { - "ld.lld": [ - "--script=linker.ld" - ] - }, - "target-endian": "little", - "target-pointer-width": "64", - "target-c-int-width": "32", - "arch": "x86_64", - "os": "none", - "features": "-mmx,-sse,+soft-float", - "disable-redzone": true, - "panic": "abort", - "executables": true, - "relocation_model": "static" -} diff --git a/real_mode/x86_64-real_mode.json b/x86_64-real_mode.json similarity index 100% rename from real_mode/x86_64-real_mode.json rename to x86_64-real_mode.json From 54c024bd3b95746358bd7617de4387a0eef0dac2 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Tue, 28 Apr 2020 12:29:43 +0100 Subject: [PATCH 03/61] GPF Handler --- src/v86/src/lib.rs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/v86/src/lib.rs b/src/v86/src/lib.rs index 880f48d2..4c2c541d 100644 --- a/src/v86/src/lib.rs +++ b/src/v86/src/lib.rs @@ -1,2 +1,37 @@ #![no_std] -pub mod monitor; \ No newline at end of file +pub mod monitor; + +pub struct V86 {} + +impl V86 { + fn gpf_handler(frame: InterruptStackFrame) { + let instruction_pointer = ((frame.cs << 4) + frame.eip) as u16; + let ivt = 0u16; + let stack = ((frame.ss << 4) + frame.esp) as u16; + let stack32 = stack as u32; + + match instruction_pointer[0] { + 0xcd => match instruction_pointer[1] { + _ => { + stack -= 3; + frame.esp = ((frame.esp) & 0xffff) - 6) & 0xffff; + + stack[0] = (frame.eip + 2) as u16; + stack[1] = frame.cs; + stack[2] = frame.eflags as u16; + + if current.v86_if { + stack[2] |= EFLAG_IF; + } else { + stack[2] &= ~EFLAG_IF; + } + + current.v86_if = false; + frame.cs = ivt[instruction_pointer[1] * 2 + 1]; + frame.eip = ivt[instruction_pointer[1] * 2]; + } + }, + _ => {} + } + } +} \ No newline at end of file From 00b01c61789d34dec26dcfe8c7484058a190a920 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 28 Apr 2020 13:04:05 +0200 Subject: [PATCH 04/61] Add 32-bit IDT types to second stage --- src/stage_2/src/lib.rs | 4 +- src/v86/src/idt.rs | 214 +++++++++++++++++++++++++++++++++++++++++ src/v86/src/lib.rs | 7 +- 3 files changed, 222 insertions(+), 3 deletions(-) create mode 100644 src/v86/src/idt.rs diff --git a/src/stage_2/src/lib.rs b/src/stage_2/src/lib.rs index a2fa131f..b1f11ba3 100644 --- a/src/stage_2/src/lib.rs +++ b/src/stage_2/src/lib.rs @@ -4,6 +4,6 @@ use shared::console::println; #[no_mangle] pub fn second_stage() -> u16 { - println(b"Stage 2"); + println(b"Stage 2"); return 12345; -} \ No newline at end of file +} diff --git a/src/v86/src/idt.rs b/src/v86/src/idt.rs new file mode 100644 index 00000000..2d89cce4 --- /dev/null +++ b/src/v86/src/idt.rs @@ -0,0 +1,214 @@ +use core::marker::PhantomData; + +/// An Interrupt Descriptor Table with 32 entries. +#[derive(Clone)] +#[repr(C, align(16))] +pub struct InterruptDescriptorTable { + pub divide_error: Entry, + pub debug: Entry, + pub non_maskable_interrupt: Entry, + pub breakpoint: Entry, + pub overflow: Entry, + pub bound_range_exceeded: Entry, + pub invalid_opcode: Entry, + pub device_not_available: Entry, + pub double_fault: Entry, + coprocessor_segment_overrun: Entry, + pub invalid_tss: Entry, + pub segment_not_present: Entry, + pub stack_segment_fault: Entry, + pub general_protection_fault: Entry, + pub page_fault: Entry, + reserved_1: Entry, + pub x87_floating_point: Entry, + pub alignment_check: Entry, + pub machine_check: Entry, + pub simd_floating_point: Entry, + pub virtualization: Entry, + reserved_2: [Entry; 9], + pub security_exception: Entry, + reserved_3: Entry, +} + +impl InterruptDescriptorTable { + /// Creates a new IDT filled with non-present entries. + #[inline] + pub const fn new() -> InterruptDescriptorTable { + InterruptDescriptorTable { + divide_error: Entry::missing(), + debug: Entry::missing(), + non_maskable_interrupt: Entry::missing(), + breakpoint: Entry::missing(), + overflow: Entry::missing(), + bound_range_exceeded: Entry::missing(), + invalid_opcode: Entry::missing(), + device_not_available: Entry::missing(), + double_fault: Entry::missing(), + coprocessor_segment_overrun: Entry::missing(), + invalid_tss: Entry::missing(), + segment_not_present: Entry::missing(), + stack_segment_fault: Entry::missing(), + general_protection_fault: Entry::missing(), + page_fault: Entry::missing(), + reserved_1: Entry::missing(), + x87_floating_point: Entry::missing(), + alignment_check: Entry::missing(), + machine_check: Entry::missing(), + simd_floating_point: Entry::missing(), + virtualization: Entry::missing(), + reserved_2: [Entry::missing(); 9], + security_exception: Entry::missing(), + reserved_3: Entry::missing(), + } + } + + /// Loads the IDT in the CPU using the `lidt` command. + pub fn load(&'static self) { + unsafe { self.load_unsafe() } + } + + /// Loads the IDT in the CPU using the `lidt` command. + /// + /// # Safety + /// + /// As long as it is the active IDT, you must ensure that: + /// + /// - `self` is never destroyed. + /// - `self` always stays at the same memory location. It is recommended to wrap it in + /// a `Box`. + /// + pub unsafe fn load_unsafe(&self) { + use core::mem::size_of; + + let ptr = DescriptorTablePointer { + base: self as *const _ as u32, + limit: (size_of::() - 1) as u16, + }; + + llvm_asm!("lidt ($0)" :: "r" (&ptr) : "memory"); + } +} + +/// A struct describing a pointer to a descriptor table (GDT / IDT). +/// This is in a format suitable for giving to 'lgdt' or 'lidt'. +#[derive(Debug, Clone, Copy)] +#[repr(C, packed)] +pub struct DescriptorTablePointer { + /// Size of the DT. + pub limit: u16, + /// Pointer to the memory region containing the DT. + pub base: u32, +} + +/// An Interrupt Descriptor Table entry. +/// +/// The generic parameter can either be `HandlerFunc` or `HandlerFuncWithErrCode`, depending +/// on the interrupt vector. +#[derive(Debug, Clone, Copy, PartialEq)] +#[repr(C)] +pub struct Entry { + offset_low: u16, + gdt_selector: u16, + zero: u8, + options: EntryOptions, + offset_high: u16, + phantom: PhantomData, +} + +impl Entry { + /// Creates a non-present IDT entry (but sets the must-be-one bits). + #[inline] + pub const fn missing() -> Self { + Entry { + gdt_selector: 0, + offset_low: 0, + offset_high: 0, + zero: 0, + options: EntryOptions::minimal(), + phantom: PhantomData, + } + } + /// Set the handler address for the IDT entry and sets the present bit. + /// + /// For the code selector field, this function uses the code segment selector currently + /// active in the CPU. + /// + /// The function returns a mutable reference to the entry's options that allows + /// further customization. + #[inline] + fn set_handler_addr(&mut self, addr: u32) -> &mut EntryOptions { + self.offset_low = addr as u16; + self.offset_high = (addr >> 16) as u16; + + let segment: u16; + unsafe { llvm_asm!("mov %cs, $0" : "=r" (segment) ) }; + + self.gdt_selector = segment; + + self.options.set_present(true); + &mut self.options + } +} + +macro_rules! impl_set_handler_fn { + ($h:ty) => { + impl Entry<$h> { + /// Set the handler function for the IDT entry and sets the present bit. + /// + /// For the code selector field, this function uses the code segment selector currently + /// active in the CPU. + /// + /// The function returns a mutable reference to the entry's options that allows + /// further customization. + #[inline] + pub fn set_handler_fn(&mut self, handler: $h) -> &mut EntryOptions { + self.set_handler_addr(handler as u32) + } + } + }; +} + +impl_set_handler_fn!(HandlerFunc); +impl_set_handler_fn!(HandlerFuncWithErrCode); +impl_set_handler_fn!(DivergingHandlerFunc); +impl_set_handler_fn!(DivergingHandlerFuncWithErrCode); + +/// Represents the options field of an IDT entry. +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct EntryOptions(u8); + +impl EntryOptions { + /// Creates a minimal options field with all the must-be-one bits set. + #[inline] + const fn minimal() -> Self { + EntryOptions(0b1110) + } + + /// Set or reset the preset bit. + #[inline] + pub fn set_present(&mut self, present: bool) -> &mut Self { + self.0.set_bit(15, present); + self + } +} + +/// A handler function for an interrupt or an exception without error code. +pub type HandlerFunc = extern "x86-interrupt" fn(&mut InterruptStackFrame); +/// A handler function for an exception that pushes an error code. +pub type HandlerFuncWithErrCode = + extern "x86-interrupt" fn(&mut InterruptStackFrame, error_code: u64); +/// A handler function that must not return, e.g. for a machine check exception. +pub type DivergingHandlerFunc = extern "x86-interrupt" fn(&mut InterruptStackFrame) -> !; +/// A handler function with an error code that must not return, e.g. for a double fault exception. +pub type DivergingHandlerFuncWithErrCode = + extern "x86-interrupt" fn(&mut InterruptStackFrame, error_code: u64) -> !; + +/// Represents the interrupt stack frame pushed by the CPU on interrupt or exception entry. +#[derive(Clone)] +#[repr(C)] +pub struct InterruptStackFrame { + eip: u32, + cs: u32, + eflags: u32, +} diff --git a/src/v86/src/lib.rs b/src/v86/src/lib.rs index 880f48d2..1f812dc8 100644 --- a/src/v86/src/lib.rs +++ b/src/v86/src/lib.rs @@ -1,2 +1,7 @@ +#![feature(abi_x86_interrupt)] +#![feature(const_fn)] +#![feature(llvm_asm)] #![no_std] -pub mod monitor; \ No newline at end of file + +pub mod idt; +pub mod monitor; From d5e058ac9dc191b65d8f219d8875210298110f31 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Tue, 28 Apr 2020 12:52:56 +0100 Subject: [PATCH 05/61] Read value of pointers instead of address --- src/v86/src/lib.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/v86/src/lib.rs b/src/v86/src/lib.rs index 4c2c541d..6622b910 100644 --- a/src/v86/src/lib.rs +++ b/src/v86/src/lib.rs @@ -5,13 +5,24 @@ pub struct V86 {} impl V86 { fn gpf_handler(frame: InterruptStackFrame) { - let instruction_pointer = ((frame.cs << 4) + frame.eip) as u16; - let ivt = 0u16; - let stack = ((frame.ss << 4) + frame.esp) as u16; - let stack32 = stack as u32; + let instruction_pointer = ((frame.cs << 4) + frame.eip) as *const _ as u16; + let instructions = slice::from_raw_parts_mut(instruction_pointer, 2); - match instruction_pointer[0] { - 0xcd => match instruction_pointer[1] { + let ivt_pointer = 0 as *const _ as u16; + let ivt = slice::from_raw_parts_mut(ivt_pointer, 1024); + + let stack_pointer = ((frame.ss << 4) + frame.esp) as u16; + let stack = slice::from_raw_parts_mut(stack_pointer, 16); + + match instructions[0] { + 0xcd => match instructions[1] { + 0xff => { + match frame.eax { + 0x0 => {}, // Terminate V86 + 0x1 => {}, // Copy from 1MB buffer + _ => panic!("Invalid V86 Monitor Function") + } + }, _ => { stack -= 3; frame.esp = ((frame.esp) & 0xffff) - 6) & 0xffff; From b9fa954c188bcefafc7150724f4b175a23652d29 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Tue, 28 Apr 2020 13:36:42 +0100 Subject: [PATCH 06/61] Remove unneeded `monitor.rs` module. Implement buffer copy function --- src/v86/src/lib.rs | 63 +++++++++++++++++++++++++++++++----------- src/v86/src/monitor.rs | 0 2 files changed, 47 insertions(+), 16 deletions(-) delete mode 100644 src/v86/src/monitor.rs diff --git a/src/v86/src/lib.rs b/src/v86/src/lib.rs index 042438d5..7ff7cdf3 100644 --- a/src/v86/src/lib.rs +++ b/src/v86/src/lib.rs @@ -4,50 +4,81 @@ #![no_std] pub mod idt; -pub mod monitor; + +const EFLAG_IF: u16 = 0x00000200; +const EFLAG_VM: u16 = 0x00020000; pub struct V86 {} impl V86 { - fn gpf_handler(frame: idt::InterruptStackFrame) { + extern "x86-interrupt" fn gpf_handler(frame: &mut idt::InterruptStackFrame, error_code: u64) { + // Calculate the V86 Instruction Pointer and create a slice of it let instruction_pointer = ((frame.cs << 4) + frame.eip) as *const _ as u16; let instructions = slice::from_raw_parts_mut(instruction_pointer, 2); + // Calculate the V86 IVT pointer and create a slice of it let ivt_pointer = 0 as *const _ as u16; let ivt = slice::from_raw_parts_mut(ivt_pointer, 1024); - let stack_pointer = ((frame.ss << 4) + frame.esp) as u16; + // Calculate the V86 stack pointer and create a slice of it + let stack_pointer = ((frame.ss << 4) + frame.esp) as *const _ as u16; let stack = slice::from_raw_parts_mut(stack_pointer, 16); + // Match the first byte of the instruction match instructions[0] { + // int 0xcd => match instructions[1] { + // 0xFF (255) is our V86 monitor interrupt 0xff => { + // EAX stores the "function" to use. Similar to BIOS interrupts match frame.eax { - 0x0 => {}, // Terminate V86 - 0x1 => {}, // Copy from 1MB buffer + // Terminate V86 + 0x0 => unimplemented!(), + + // Copy data into protected mode address space + 0x1 => { + // EBX - Real mode address + // ECX - Protected mode address + // EDX - Amount of bytes to copy + let source_pointer = frame.ebx as *const _ as u32; + let destination_pointer = frame.ecx as *const _ as u32; + + let size = frame.edx; + + let source = slice::from_raw_parts(source_pointer, size); + let mut destination = slice::from_raw_parts_mut(destination_pointer, size); + + destination.clone_from_slice(source); + }, _ => panic!("Invalid V86 Monitor Function") - } + }; }, _ => { + // All other interrupt vectors are processed by the real mode handlers stack -= 3; frame.esp = ((frame.esp) & 0xffff) - 6) & 0xffff; - + + // Store the next instructions EIP and code segment onto the stack stack[0] = (frame.eip + 2) as u16; stack[1] = frame.cs; - stack[2] = frame.eflags as u16; - if current.v86_if { - stack[2] |= EFLAG_IF; - } else { - stack[2] &= ~EFLAG_IF; - } + // Store the EFlags onto the stack + stack[2] = frame.eflags as u16; - current.v86_if = false; + // Set the CS and EIP to the real mode interrupt handler frame.cs = ivt[instruction_pointer[1] * 2 + 1]; frame.eip = ivt[instruction_pointer[1] * 2]; } }, - _ => {} - } + // iret + 0xcf => { + frame.eip = stack[0]; + frame.cs = stack[1]; + frame.eflags = EFLAG_IF | EFLAG_VM | stack[2]; + + frame.esp = ((frame.esp & 0xffff) + 6) & 0xffff; + }, + _ => panic!("Unhandled GPF") + }; } } \ No newline at end of file diff --git a/src/v86/src/monitor.rs b/src/v86/src/monitor.rs deleted file mode 100644 index e69de29b..00000000 From fea81f8c0276ac71330c0e2c346185b8715b14f1 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 28 Apr 2020 14:49:41 +0200 Subject: [PATCH 07/61] Create 32-bit GDT and TSS types --- Cargo.lock | 21 +++++ src/v86/Cargo.toml | 2 + src/v86/src/gdt.rs | 230 +++++++++++++++++++++++++++++++++++++++++++++ src/v86/src/lib.rs | 1 + 4 files changed, 254 insertions(+) create mode 100644 src/v86/src/gdt.rs diff --git a/Cargo.lock b/Cargo.lock index 85faf3c3..d513678b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "bit_field" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bootsector" version = "0.1.0" @@ -19,3 +29,14 @@ dependencies = [ "shared 0.1.0", ] +[[package]] +name = "v86" +version = "0.1.0" +dependencies = [ + "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" diff --git a/src/v86/Cargo.toml b/src/v86/Cargo.toml index b2ed9448..7df91b86 100644 --- a/src/v86/Cargo.toml +++ b/src/v86/Cargo.toml @@ -5,3 +5,5 @@ authors = ["Ryland Morgan "] edition = "2018" [dependencies] +bitflags = "1.2.1" +bit_field = "0.10.0" diff --git a/src/v86/src/gdt.rs b/src/v86/src/gdt.rs new file mode 100644 index 00000000..f062852f --- /dev/null +++ b/src/v86/src/gdt.rs @@ -0,0 +1,230 @@ +use bit_field::BitField; +use bitflags::bitflags; + +#[derive(Debug, Clone)] +pub struct GlobalDescriptorTable { + table: [u64; 8], + next_free: usize, +} + +impl GlobalDescriptorTable { + /// Creates an empty GDT. + #[inline] + pub const fn new() -> GlobalDescriptorTable { + GlobalDescriptorTable { + table: [0; 8], + next_free: 1, + } + } + + /// Adds the given segment descriptor to the GDT, returning the segment selector. + /// + /// Panics if the GDT has no free entries left. + #[inline] + pub fn add_entry(&mut self, entry: Descriptor) -> u16 { + let index = self.push(entry.0); + index as u16 + } + + /// Loads the GDT in the CPU using the `lgdt` instruction. This does **not** alter any of the + /// segment registers; you **must** (re)load them yourself. + #[inline] + pub fn load(&'static self) { + use core::mem::size_of; + + /// A struct describing a pointer to a descriptor table (GDT / IDT). + /// This is in a format suitable for giving to 'lgdt' or 'lidt'. + #[derive(Debug, Clone, Copy)] + #[repr(C, packed)] + struct DescriptorTablePointer { + /// Size of the DT. + pub limit: u16, + /// Pointer to the memory region containing the DT. + pub base: u32, + } + + let ptr = DescriptorTablePointer { + base: self.table.as_ptr() as u32, + limit: (self.table.len() * size_of::() - 1) as u16, + }; + + llvm_asm!("lgdt ($0)" :: "r" (&ptr) : "memory"); + } + + #[inline] + fn push(&mut self, value: u64) -> usize { + if self.next_free < self.table.len() { + let index = self.next_free; + self.table[index] = value; + self.next_free += 1; + index + } else { + panic!("GDT full"); + } + } +} + +#[derive(Debug, Clone)] +pub struct Descriptor(u64); + +bitflags! { + /// Flags for a GDT descriptor. Not all flags are valid for all descriptor types. + pub struct DescriptorFlags: u64 { + /// For data segments, this flag sets the segment as writable. For code + /// segments, it defines whether the segment is readable. + const READABLE_WRITABLE = 1 << 41; + /// Marks a code segment as “conforming”. This influences the privilege checks that + /// occur on control transfers. + const CONFORMING = 1 << 42; + /// This flag must be set for code segments. + const EXECUTABLE = 1 << 43; + /// This flag must be set for user segments (in contrast to system segments). + const USER_SEGMENT = 1 << 44; + /// Must be set for any segment, causes a segment not present exception if not set. + const PRESENT = 1 << 47; + /// Must be set for long mode code segments. + const LONG_MODE = 1 << 53; + + /// The DPL for this descriptor is Ring 3 + const DPL_RING_3 = 3 << 45; + } +} + +impl Descriptor { + /// Creates a segment descriptor for a protected mode kernel code segment. + #[inline] + pub fn kernel_code_segment() -> Descriptor { + use self::DescriptorFlags as Flags; + + let flags = + Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::READABLE_WRITABLE; + Descriptor(flags.bits()) + } + + /// Creates a segment descriptor for a protected mode kernel data segment. + #[inline] + pub fn data_segment() -> Descriptor { + use self::DescriptorFlags as Flags; + + let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE; + Descriptor(flags.bits()) + } + + /// Creates a segment descriptor for a protected mode ring 3 data segment. + #[inline] + pub fn user_data_segment() -> Descriptor { + use self::DescriptorFlags as Flags; + + let flags = + Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::DPL_RING_3; + Descriptor(flags.bits()) + } + + /// Creates a segment descriptor for a protected mode ring 3 code segment. + #[inline] + pub fn user_code_segment() -> Descriptor { + use self::DescriptorFlags as Flags; + + let flags = Flags::USER_SEGMENT + | Flags::PRESENT + | Flags::EXECUTABLE + | Flags::DPL_RING_3 + | Flags::READABLE_WRITABLE; + Descriptor(flags.bits()) + } + + /// Creates a TSS system descriptor for the given TSS. + #[inline] + pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor { + use self::DescriptorFlags as Flags; + use core::mem::size_of; + + let ptr = tss as *const _ as u64; + + let mut val = Flags::PRESENT.bits(); + // base + val.set_bits(16..40, ptr.get_bits(0..24)); + val.set_bits(56..64, ptr.get_bits(24..32)); + // limit (the `-1` in needed since the bound is inclusive) + val.set_bits(0..16, (size_of::() - 1) as u64); + // type (0b1001 = available 32-bit tss) + val.set_bits(40..44, 0b1001); + + Descriptor(val) + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(C, packed)] +pub struct TaskStateSegment { + /// Used for hardware task switching + prev_tss: u32, + /// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2. + pub privilege_stack_table: [Stack; 3], + + cr3: u32, + eip: u32, + eflags: u32, + eax: u32, + ecx: u32, + edx: u32, + ebx: u32, + esp: u32, + ebp: u32, + esi: u32, + edi: u32, + es: u32, + cs: u32, + ss: u32, + ds: u32, + fs: u32, + gs: u32, + ldt: u32, + trap: u16, + pub iomap_base: u16, +} + +impl TaskStateSegment { + /// Creates a new TSS with zeroed privilege and interrupt stack table and a zero + /// `iomap_base`. + #[inline] + pub const fn new() -> TaskStateSegment { + TaskStateSegment { + privilege_stack_table: [Stack::zero(); 3], + iomap_base: 0, + prev_tss: 0, + cr3: 0, + eip: 0, + eflags: 0, + eax: 0, + ecx: 0, + edx: 0, + ebx: 0, + esp: 0, + ebp: 0, + esi: 0, + edi: 0, + es: 0, + cs: 0, + ss: 0, + ds: 0, + fs: 0, + gs: 0, + ldt: 0, + trap: 0, + } + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(C, packed)] +pub struct Stack { + esp: u32, + ss: u32, +} + +impl Stack { + fn zero() -> Self { + Stack { esp: 0, ss: 0 } + } +} diff --git a/src/v86/src/lib.rs b/src/v86/src/lib.rs index 7ff7cdf3..cdeaf014 100644 --- a/src/v86/src/lib.rs +++ b/src/v86/src/lib.rs @@ -3,6 +3,7 @@ #![feature(llvm_asm)] #![no_std] +pub mod gdt; pub mod idt; const EFLAG_IF: u16 = 0x00000200; From 65a024372a841921d67d100d6a529d2418701af8 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 28 Apr 2020 15:35:01 +0200 Subject: [PATCH 08/61] Add a flat limit to all GDT segments --- src/v86/src/gdt.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/v86/src/gdt.rs b/src/v86/src/gdt.rs index f062852f..94bae27e 100644 --- a/src/v86/src/gdt.rs +++ b/src/v86/src/gdt.rs @@ -87,6 +87,9 @@ bitflags! { /// The DPL for this descriptor is Ring 3 const DPL_RING_3 = 3 << 45; + + /// If set, limit is in 4k pages + const GRANULARITY = 1 << 55; } } @@ -98,7 +101,7 @@ impl Descriptor { let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::READABLE_WRITABLE; - Descriptor(flags.bits()) + Descriptor(flags.bits()).with_flat_limit() } /// Creates a segment descriptor for a protected mode kernel data segment. @@ -107,7 +110,7 @@ impl Descriptor { use self::DescriptorFlags as Flags; let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE; - Descriptor(flags.bits()) + Descriptor(flags.bits()).with_flat_limit() } /// Creates a segment descriptor for a protected mode ring 3 data segment. @@ -117,7 +120,7 @@ impl Descriptor { let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::DPL_RING_3; - Descriptor(flags.bits()) + Descriptor(flags.bits()).with_flat_limit() } /// Creates a segment descriptor for a protected mode ring 3 code segment. @@ -130,7 +133,7 @@ impl Descriptor { | Flags::EXECUTABLE | Flags::DPL_RING_3 | Flags::READABLE_WRITABLE; - Descriptor(flags.bits()) + Descriptor(flags.bits()).with_flat_limit() } /// Creates a TSS system descriptor for the given TSS. @@ -152,6 +155,17 @@ impl Descriptor { Descriptor(val) } + + fn with_flat_limit(self) -> Self { + // limit_low + self.0.set_bits(0..16, 0xffff); + // limit high + self.0.set_bits(48..52, 0xff); + // granularity + self.0 |= DescriptorFlags::GRANULARITY; + + self + } } #[derive(Debug, Clone, Copy)] From 58c8b82785274aec63a94a8f2bd692d5d2cda423 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 28 Apr 2020 15:11:32 +0200 Subject: [PATCH 09/61] Rewrite: Fix build of test and example kernel by adding them to workspace --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 36a15a34..c1baaffc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,9 @@ members = [ "src/shared", "src/v86", "src/bootsector", - "src/stage_2" + "src/stage_2", + "example-kernel", + "test-kernel", ] [profile.release] From c74eadc9e9995add2a418a9814605d7363040c19 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 28 Apr 2020 15:48:36 +0200 Subject: [PATCH 10/61] Adjust CI for rewrite --- .github/workflows/build.yml | 67 ++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6264eec..95bdb7b8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,43 +51,40 @@ jobs: name: 'Build Test Kernel' - name: 'Build Bootloader' - run: cargo xbuild --bin bootloader --features binary --release - env: - KERNEL: "test-kernel/target/x86_64-test-kernel/debug/test-kernel" - KERNEL_MANIFEST: "test-kernel/Cargo.toml" + run: cargo xbuild --release - name: 'Convert Bootloader ELF to Binary' - run: cargo objcopy -- -I elf64-x86-64 -O binary --binary-architecture=i386:x86-64 target/x86_64-bootloader/release/bootloader target/x86_64-bootloader/release/bootloader.bin - - # install QEMU - - name: Install QEMU (Linux) - run: sudo apt update && sudo apt install qemu-system-x86 - if: runner.os == 'Linux' - - name: Install QEMU (macOS) - run: brew install qemu - if: runner.os == 'macOS' - env: - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - - name: Install Scoop (Windows) - run: | - Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') - echo ::add-path::$HOME\scoop\shims - if: runner.os == 'Windows' - shell: pwsh - - name: Install QEMU (Windows) - run: scoop install qemu - if: runner.os == 'Windows' - shell: pwsh - - name: "Print QEMU Version" - run: qemu-system-x86_64 --version - - - name: 'Run Test Kernel with Bootloader' - run: | - qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootloader.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none - if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi - shell: 'bash {0}' + run: cargo objcopy -- -I elf64-i386 -O binary --binary-architecture=i386:x86-64 target/x86_64-real_mode/release/bootsector target/x86_64-real_mode/release/bootsector.bin + + # # install QEMU + # - name: Install QEMU (Linux) + # run: sudo apt update && sudo apt install qemu-system-x86 + # if: runner.os == 'Linux' + # - name: Install QEMU (macOS) + # run: brew install qemu + # if: runner.os == 'macOS' + # env: + # HOMEBREW_NO_AUTO_UPDATE: 1 + # HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1 + # HOMEBREW_NO_INSTALL_CLEANUP: 1 + # - name: Install Scoop (Windows) + # run: | + # Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') + # echo ::add-path::$HOME\scoop\shims + # if: runner.os == 'Windows' + # shell: pwsh + # - name: Install QEMU (Windows) + # run: scoop install qemu + # if: runner.os == 'Windows' + # shell: pwsh + # - name: "Print QEMU Version" + # run: qemu-system-x86_64 --version + + # - name: 'Run Test Kernel with Bootloader' + # run: | + # qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootloader.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none + # if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi + # shell: 'bash {0}' build_example_kernel: From 6f9f7c48880ec10764fcf9d78e3ef9fd1a98470d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 28 Apr 2020 15:11:32 +0200 Subject: [PATCH 11/61] Rewrite: Fix build of test and example kernel by adding them to workspace --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 36a15a34..c1baaffc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,9 @@ members = [ "src/shared", "src/v86", "src/bootsector", - "src/stage_2" + "src/stage_2", + "example-kernel", + "test-kernel", ] [profile.release] From d1a307f83bfded7eb16e761a3564e2c495d47639 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 28 Apr 2020 15:48:36 +0200 Subject: [PATCH 12/61] Adjust CI for rewrite --- .github/workflows/build.yml | 67 ++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6264eec..95bdb7b8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,43 +51,40 @@ jobs: name: 'Build Test Kernel' - name: 'Build Bootloader' - run: cargo xbuild --bin bootloader --features binary --release - env: - KERNEL: "test-kernel/target/x86_64-test-kernel/debug/test-kernel" - KERNEL_MANIFEST: "test-kernel/Cargo.toml" + run: cargo xbuild --release - name: 'Convert Bootloader ELF to Binary' - run: cargo objcopy -- -I elf64-x86-64 -O binary --binary-architecture=i386:x86-64 target/x86_64-bootloader/release/bootloader target/x86_64-bootloader/release/bootloader.bin - - # install QEMU - - name: Install QEMU (Linux) - run: sudo apt update && sudo apt install qemu-system-x86 - if: runner.os == 'Linux' - - name: Install QEMU (macOS) - run: brew install qemu - if: runner.os == 'macOS' - env: - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - - name: Install Scoop (Windows) - run: | - Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') - echo ::add-path::$HOME\scoop\shims - if: runner.os == 'Windows' - shell: pwsh - - name: Install QEMU (Windows) - run: scoop install qemu - if: runner.os == 'Windows' - shell: pwsh - - name: "Print QEMU Version" - run: qemu-system-x86_64 --version - - - name: 'Run Test Kernel with Bootloader' - run: | - qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootloader.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none - if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi - shell: 'bash {0}' + run: cargo objcopy -- -I elf64-i386 -O binary --binary-architecture=i386:x86-64 target/x86_64-real_mode/release/bootsector target/x86_64-real_mode/release/bootsector.bin + + # # install QEMU + # - name: Install QEMU (Linux) + # run: sudo apt update && sudo apt install qemu-system-x86 + # if: runner.os == 'Linux' + # - name: Install QEMU (macOS) + # run: brew install qemu + # if: runner.os == 'macOS' + # env: + # HOMEBREW_NO_AUTO_UPDATE: 1 + # HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1 + # HOMEBREW_NO_INSTALL_CLEANUP: 1 + # - name: Install Scoop (Windows) + # run: | + # Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') + # echo ::add-path::$HOME\scoop\shims + # if: runner.os == 'Windows' + # shell: pwsh + # - name: Install QEMU (Windows) + # run: scoop install qemu + # if: runner.os == 'Windows' + # shell: pwsh + # - name: "Print QEMU Version" + # run: qemu-system-x86_64 --version + + # - name: 'Run Test Kernel with Bootloader' + # run: | + # qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootloader.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none + # if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi + # shell: 'bash {0}' build_example_kernel: From df73047723e45fd02fb801ebbbe527b2c8f406f0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 28 Apr 2020 16:06:09 +0200 Subject: [PATCH 13/61] Fix build errors --- src/v86/src/gdt.rs | 10 ++++++---- src/v86/src/idt.rs | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/v86/src/gdt.rs b/src/v86/src/gdt.rs index 94bae27e..cb19e9e5 100644 --- a/src/v86/src/gdt.rs +++ b/src/v86/src/gdt.rs @@ -48,7 +48,9 @@ impl GlobalDescriptorTable { limit: (self.table.len() * size_of::() - 1) as u16, }; - llvm_asm!("lgdt ($0)" :: "r" (&ptr) : "memory"); + unsafe { + llvm_asm!("lgdt ($0)" :: "r" (&ptr) : "memory"); + } } #[inline] @@ -156,13 +158,13 @@ impl Descriptor { Descriptor(val) } - fn with_flat_limit(self) -> Self { + fn with_flat_limit(mut self) -> Self { // limit_low self.0.set_bits(0..16, 0xffff); // limit high self.0.set_bits(48..52, 0xff); // granularity - self.0 |= DescriptorFlags::GRANULARITY; + self.0 |= DescriptorFlags::GRANULARITY.bits(); self } @@ -238,7 +240,7 @@ pub struct Stack { } impl Stack { - fn zero() -> Self { + const fn zero() -> Self { Stack { esp: 0, ss: 0 } } } diff --git a/src/v86/src/idt.rs b/src/v86/src/idt.rs index 2d89cce4..b7c467e9 100644 --- a/src/v86/src/idt.rs +++ b/src/v86/src/idt.rs @@ -1,4 +1,5 @@ use core::marker::PhantomData; +use bit_field::BitField; /// An Interrupt Descriptor Table with 32 entries. #[derive(Clone)] From be52f0d9e2bedceba0cf0f467d8f2409afe47215 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Tue, 28 Apr 2020 16:17:04 +0100 Subject: [PATCH 14/61] Made a bunch of changes. Protected mode kind of works? --- Cargo.lock | 17 ++++++++ linker.ld | 25 ++++++------ src/bootsector/src/console.rs | 22 ++++++++++ src/bootsector/src/main.rs | 7 ++-- src/shared/src/console.rs | 2 +- src/shared/src/dap.rs | 2 +- src/shared/src/lib.rs | 2 +- src/shared/src/macros.rs | 6 +-- src/shared/src/utils.rs | 2 +- src/stage_2/Cargo.toml | 7 +++- src/stage_2/src/lib.rs | 77 ++++++++++++++++++++++++++++++++++- src/v86/src/gdt.rs | 8 ++-- src/v86/src/idt.rs | 8 ++-- src/v86/src/lib.rs | 68 +++++++++++++++++-------------- 14 files changed, 191 insertions(+), 62 deletions(-) create mode 100644 src/bootsector/src/console.rs diff --git a/Cargo.lock b/Cargo.lock index d513678b..8304430b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,15 +18,30 @@ dependencies = [ "stage_2 0.1.0", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "shared" version = "0.1.0" +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "stage_2" version = "0.1.0" dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "shared 0.1.0", + "v86 0.1.0", ] [[package]] @@ -40,3 +55,5 @@ dependencies = [ [metadata] "checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" diff --git a/linker.ld b/linker.ld index 0873e460..94c396cf 100644 --- a/linker.ld +++ b/linker.ld @@ -12,8 +12,6 @@ SECTIONS { *(.bootstrap) *bootsector* - - *shared* } /* We write the magic number in the linker script to cause a link-time error if the bootloader overflows the 512 byte limit */ @@ -23,22 +21,25 @@ SECTIONS { SHORT(0xaa55) } - _rest_of_bootloader_start = .; - - .stage_2 : + .rest_of_bootloader : { + _rest_of_bootloader_start = .; - _second_stage_start = .; - - *stage_2* + *(.text .text.*) + *(.data .data.*) + *(.rodata .rodata.*) + *(.bss .bss.*) - _second_stage_end = .; + . = ALIGN(512); + + _rest_of_bootloader_end = .; } - . = ALIGN(512); - - _rest_of_bootloader_end = .; _bootloader_end = .; + + _protected_mode_stack_start = .; + . += 0x1000; + _protected_mode_stack_end = .; /DISCARD/ : { diff --git a/src/bootsector/src/console.rs b/src/bootsector/src/console.rs new file mode 100644 index 00000000..8323a262 --- /dev/null +++ b/src/bootsector/src/console.rs @@ -0,0 +1,22 @@ +#[inline(never)] +pub fn println(s: &[u8]) { + print(s); + print_char(b'\n'); +} + +pub fn print(s: &[u8]) { + let mut i = 0; + + while i < s.len() { + print_char(s[i]); + i += 1; + } +} + +#[inline(always)] +pub fn print_char(c: u8) { + let ax = u16::from(c) | 0x0e00; + unsafe { + llvm_asm!("int 0x10" :: "{ax}"(ax) :: "intel" ); + } +} \ No newline at end of file diff --git a/src/bootsector/src/main.rs b/src/bootsector/src/main.rs index b1f61226..fd4c0818 100644 --- a/src/bootsector/src/main.rs +++ b/src/bootsector/src/main.rs @@ -1,4 +1,4 @@ -#![feature(asm, global_asm)] +#![feature(llvm_asm, global_asm)] #![no_std] #![no_main] @@ -6,11 +6,12 @@ #![allow(dead_code)] mod errors; +mod console; use core::panic::PanicInfo; use stage_2::second_stage; -use shared::console::println; +use self::console::println; use shared::{dap, utils, linker_symbol}; global_asm!(include_str!("bootstrap.s")); @@ -46,7 +47,7 @@ pub fn panic(_info: &PanicInfo) -> ! { pub fn check_int13h_extensions(disk_number: u16) { unsafe { - asm!(" + llvm_asm!(" int 0x13 jc no_int13h_extensions " :: "{ah}"(0x41), "{bx}"(0x55aa), "{dl}"(disk_number) :: "intel", "volatile"); diff --git a/src/shared/src/console.rs b/src/shared/src/console.rs index 7bcfac5c..8323a262 100644 --- a/src/shared/src/console.rs +++ b/src/shared/src/console.rs @@ -17,6 +17,6 @@ pub fn print(s: &[u8]) { pub fn print_char(c: u8) { let ax = u16::from(c) | 0x0e00; unsafe { - asm!("int 0x10" :: "{ax}"(ax) :: "intel" ); + llvm_asm!("int 0x10" :: "{ax}"(ax) :: "intel" ); } } \ No newline at end of file diff --git a/src/shared/src/dap.rs b/src/shared/src/dap.rs index c9ef7e34..809f57a5 100644 --- a/src/shared/src/dap.rs +++ b/src/shared/src/dap.rs @@ -31,7 +31,7 @@ impl DiskAddressPacket { #[inline(always)] pub unsafe fn perform_load(&self, disk_number: u16) { let self_addr = self as *const Self as u16; - asm!(" + llvm_asm!(" int 0x13 jc dap_load_failed " :: "{si}"(self_addr), "{ax}"(0x4200), "{dx}"(disk_number) : "bx" : "intel", "volatile"); diff --git a/src/shared/src/lib.rs b/src/shared/src/lib.rs index a7a0ed9f..96b2b887 100644 --- a/src/shared/src/lib.rs +++ b/src/shared/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(asm)] +#![feature(llvm_asm)] pub mod console; pub mod dap; diff --git a/src/shared/src/macros.rs b/src/shared/src/macros.rs index b55b2113..b3d59598 100644 --- a/src/shared/src/macros.rs +++ b/src/shared/src/macros.rs @@ -1,9 +1,9 @@ #[macro_export] macro_rules! linker_symbol { - ($symbol_name:ident) => {{ - let symbol_value: u32; + ($symbol_name:ident) => {unsafe { + let symbol_value: u32; - asm!(concat!("lea eax, ", stringify!($symbol_name)) + llvm_asm!(concat!("lea eax, ", stringify!($symbol_name)) : "={eax}"(symbol_value) ::: "intel", "volatile"); diff --git a/src/shared/src/utils.rs b/src/shared/src/utils.rs index f2cdee74..e0e82faa 100644 --- a/src/shared/src/utils.rs +++ b/src/shared/src/utils.rs @@ -1,6 +1,6 @@ #[inline(always)] pub fn hlt() { unsafe { - asm!("hlt" :::: "intel","volatile"); + llvm_asm!("hlt" :::: "intel","volatile"); } } \ No newline at end of file diff --git a/src/stage_2/Cargo.toml b/src/stage_2/Cargo.toml index ca736e07..2c128ee2 100644 --- a/src/stage_2/Cargo.toml +++ b/src/stage_2/Cargo.toml @@ -7,4 +7,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -shared = { path = "../shared" } \ No newline at end of file +shared = { path = "../shared" } +v86 = { path = "../v86" } + +[dependencies.lazy_static] +version = "1.0" +features = ["spin_no_std"] \ No newline at end of file diff --git a/src/stage_2/src/lib.rs b/src/stage_2/src/lib.rs index b1f11ba3..ffb9cf18 100644 --- a/src/stage_2/src/lib.rs +++ b/src/stage_2/src/lib.rs @@ -1,9 +1,82 @@ #![no_std] +#![feature(llvm_asm)] use shared::console::println; +use shared::linker_symbol; +use v86::gdt::{GlobalDescriptorTable, Descriptor, TaskStateSegment}; + +use lazy_static::lazy_static; + +lazy_static! { + static ref TSS: TaskStateSegment = { + let mut tss = TaskStateSegment::new(); + + tss.privilege_stack_table[0].esp = linker_symbol!(_protected_mode_stack_end); + + tss + }; + + static ref GDT: GlobalDescriptorTable = { + let mut gdt = GlobalDescriptorTable::new(); + gdt.add_entry(Descriptor::kernel_code_segment()); + gdt.add_entry(Descriptor::user_code_segment()); + gdt.add_entry(Descriptor::user_data_segment()); + + gdt.add_entry(Descriptor::tss_segment(&TSS)); + + gdt + }; +} #[no_mangle] -pub fn second_stage() -> u16 { +pub fn second_stage() { println(b"Stage 2"); - return 12345; + + println(b"Loading GDT"); + + unsafe { GDT.load(); } + + println(b"GDT Loaded!"); + + println(b"Switching to 32-bit"); + + enable_a20(); + + println(b"A20"); + + unsafe { + llvm_asm!("mov eax, cr0 + or al, 1 + mov cr0, eax + + mov bx, 0x10 + mov ds, bx + mov es, bx + + jmp protected_mode" ::: "eax", "bx" : "intel", "volatile"); + } +} + +static HELLO: &[u8] = b"Protected Mode!"; + +#[no_mangle] +extern "C" fn protected_mode() { + let vga_buffer = 0xb8000 as *mut u8; + + for (i, &byte) in HELLO.iter().enumerate() { + unsafe { + *vga_buffer.offset(i as isize * 2) = byte; + *vga_buffer.offset(i as isize * 2 + 1) = 0xb; + } + } + + loop {} } + +fn enable_a20() { + unsafe { + llvm_asm!("in al, 0x92 + or al, 2 + out 0x92, al" ::: "al" : "intel", "volatile"); + } +} \ No newline at end of file diff --git a/src/v86/src/gdt.rs b/src/v86/src/gdt.rs index f062852f..0319717d 100644 --- a/src/v86/src/gdt.rs +++ b/src/v86/src/gdt.rs @@ -29,7 +29,7 @@ impl GlobalDescriptorTable { /// Loads the GDT in the CPU using the `lgdt` instruction. This does **not** alter any of the /// segment registers; you **must** (re)load them yourself. #[inline] - pub fn load(&'static self) { + pub unsafe fn load(&'static self) { use core::mem::size_of; /// A struct describing a pointer to a descriptor table (GDT / IDT). @@ -219,12 +219,12 @@ impl TaskStateSegment { #[derive(Debug, Clone, Copy)] #[repr(C, packed)] pub struct Stack { - esp: u32, - ss: u32, + pub esp: u32, + pub ss: u32, } impl Stack { - fn zero() -> Self { + const fn zero() -> Self { Stack { esp: 0, ss: 0 } } } diff --git a/src/v86/src/idt.rs b/src/v86/src/idt.rs index 2d89cce4..2ec160fc 100644 --- a/src/v86/src/idt.rs +++ b/src/v86/src/idt.rs @@ -1,5 +1,7 @@ use core::marker::PhantomData; +use bit_field::BitField; + /// An Interrupt Descriptor Table with 32 entries. #[derive(Clone)] #[repr(C, align(16))] @@ -208,7 +210,7 @@ pub type DivergingHandlerFuncWithErrCode = #[derive(Clone)] #[repr(C)] pub struct InterruptStackFrame { - eip: u32, - cs: u32, - eflags: u32, + pub eip: u32, + pub cs: u32, + pub eflags: u32, } diff --git a/src/v86/src/lib.rs b/src/v86/src/lib.rs index cdeaf014..47e49b26 100644 --- a/src/v86/src/lib.rs +++ b/src/v86/src/lib.rs @@ -3,27 +3,31 @@ #![feature(llvm_asm)] #![no_std] +use core::slice; + pub mod gdt; pub mod idt; -const EFLAG_IF: u16 = 0x00000200; -const EFLAG_VM: u16 = 0x00020000; - +const EFLAG_IF: u32 = 0x00000200; +const EFLAG_VM: u32 = 0x00020000; +/* pub struct V86 {} impl V86 { - extern "x86-interrupt" fn gpf_handler(frame: &mut idt::InterruptStackFrame, error_code: u64) { + unsafe extern "x86-interrupt" fn gpf_handler(frame: &mut idt::InterruptStackFrame, error_code: u64) { // Calculate the V86 Instruction Pointer and create a slice of it - let instruction_pointer = ((frame.cs << 4) + frame.eip) as *const _ as u16; - let instructions = slice::from_raw_parts_mut(instruction_pointer, 2); + let instruction_pointer = ((frame.cs << 4) + frame.eip) as *const u16; + let instructions = slice::from_raw_parts(instruction_pointer, 2); // Calculate the V86 IVT pointer and create a slice of it - let ivt_pointer = 0 as *const _ as u16; - let ivt = slice::from_raw_parts_mut(ivt_pointer, 1024); + let ivt_pointer = 0 as *const u16; + let ivt = slice::from_raw_parts(ivt_pointer, 1024); // Calculate the V86 stack pointer and create a slice of it - let stack_pointer = ((frame.ss << 4) + frame.esp) as *const _ as u16; - let stack = slice::from_raw_parts_mut(stack_pointer, 16); + let mut stack_pointer = ((frame.ss << 4) + frame.esp) as *mut u16; + + let mut stack = slice::from_raw_parts_mut(stack_pointer, 16); + let mut stack32 = slice::from_raw_parts_mut(stack_pointer as *mut u32, 8); // Match the first byte of the instruction match instructions[0] { @@ -31,20 +35,24 @@ impl V86 { 0xcd => match instructions[1] { // 0xFF (255) is our V86 monitor interrupt 0xff => { - // EAX stores the "function" to use. Similar to BIOS interrupts - match frame.eax { + // Function should be pushed onto stack first + let function = stack[3]; + + match function { // Terminate V86 0x0 => unimplemented!(), + // push 0x2000 - stack[6] Size + // push 0x1000 - stack[5] Real Mode Buffer + // push 0x100000 - stack[4] Protected Mode Buffer + // push 0x1 - stack[3] Function + // Copy data into protected mode address space 0x1 => { - // EBX - Real mode address - // ECX - Protected mode address - // EDX - Amount of bytes to copy - let source_pointer = frame.ebx as *const _ as u32; - let destination_pointer = frame.ecx as *const _ as u32; - - let size = frame.edx; + // We read buffer addresses and size from + let destination_pointer = stack[4] as *mut u32; + let source_pointer = stack[5] as *const u32; + let size = stack[6]; let source = slice::from_raw_parts(source_pointer, size); let mut destination = slice::from_raw_parts_mut(destination_pointer, size); @@ -56,30 +64,30 @@ impl V86 { }, _ => { // All other interrupt vectors are processed by the real mode handlers - stack -= 3; - frame.esp = ((frame.esp) & 0xffff) - 6) & 0xffff; + stack_pointer = stack_pointer.offset(3); + frame.esp = ((frame.esp & 0xffff) - 6) & 0xffff; // Store the next instructions EIP and code segment onto the stack - stack[0] = (frame.eip + 2) as u16; - stack[1] = frame.cs; + stack[0] = (frame.eip + 2) as usize; + stack32[1] = frame.cs; // Store the EFlags onto the stack - stack[2] = frame.eflags as u16; + stack[2] = frame.eflags as usize; // Set the CS and EIP to the real mode interrupt handler - frame.cs = ivt[instruction_pointer[1] * 2 + 1]; - frame.eip = ivt[instruction_pointer[1] * 2]; + frame.cs = ivt[(instructions[1] * 2 + 1) as usize] as u32; + frame.eip = ivt[(instructions[1] * 2) as usize] as u32; } }, // iret 0xcf => { - frame.eip = stack[0]; - frame.cs = stack[1]; - frame.eflags = EFLAG_IF | EFLAG_VM | stack[2]; + frame.eip = stack32[0]; + frame.cs = stack32[1]; + frame.eflags = EFLAG_IF | EFLAG_VM | stack32[2]; frame.esp = ((frame.esp & 0xffff) + 6) & 0xffff; }, _ => panic!("Unhandled GPF") }; } -} \ No newline at end of file +}*/ \ No newline at end of file From e3c7f60044b0a45f74978596969ecd45142aa814 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Tue, 28 Apr 2020 16:19:31 +0100 Subject: [PATCH 15/61] Merge --- src/stage_2/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/stage_2/src/lib.rs b/src/stage_2/src/lib.rs index ffb9cf18..e6818605 100644 --- a/src/stage_2/src/lib.rs +++ b/src/stage_2/src/lib.rs @@ -44,6 +44,8 @@ pub fn second_stage() { println(b"A20"); + loop {} + unsafe { llvm_asm!("mov eax, cr0 or al, 1 @@ -70,7 +72,7 @@ extern "C" fn protected_mode() { } } - loop {} + loop {} } fn enable_a20() { From 6dbfaa549095ef5c01e671b7039c50e7014bc48e Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Tue, 28 Apr 2020 16:21:55 +0100 Subject: [PATCH 16/61] Fix double import --- Cargo.lock | 67 ++++++++++++++++++++++++++++++++++++++++++ src/stage_2/src/lib.rs | 4 ++- src/v86/src/idt.rs | 2 -- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8304430b..eb7593df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,18 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "array-init" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit_field" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bit_field" version = "0.10.0" @@ -18,6 +31,13 @@ dependencies = [ "stage_2 0.1.0", ] +[[package]] +name = "example-kernel" +version = "0.1.0" +dependencies = [ + "x86_64 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -26,6 +46,16 @@ dependencies = [ "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "os_bootinfo" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "shared" version = "0.1.0" @@ -44,6 +74,23 @@ dependencies = [ "v86 0.1.0", ] +[[package]] +name = "test-kernel" +version = "0.1.0" +dependencies = [ + "x86_64 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "usize_conversions" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ux" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "v86" version = "0.1.0" @@ -52,8 +99,28 @@ dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "x86_64" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] +"checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" "checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" +"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +"checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" +"checksum ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f" +"checksum x86_64 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f9258d7e2dd25008d69e8c9e9ee37865887a5e1e3d06a62f1cb3f6c209e6f177" diff --git a/src/stage_2/src/lib.rs b/src/stage_2/src/lib.rs index e6818605..fc3016d4 100644 --- a/src/stage_2/src/lib.rs +++ b/src/stage_2/src/lib.rs @@ -38,13 +38,15 @@ pub fn second_stage() { println(b"GDT Loaded!"); + loop {}; + println(b"Switching to 32-bit"); enable_a20(); println(b"A20"); - loop {} + loop {}; unsafe { llvm_asm!("mov eax, cr0 diff --git a/src/v86/src/idt.rs b/src/v86/src/idt.rs index 5ed98fe8..91ce00b3 100644 --- a/src/v86/src/idt.rs +++ b/src/v86/src/idt.rs @@ -1,8 +1,6 @@ use core::marker::PhantomData; use bit_field::BitField; -use bit_field::BitField; - /// An Interrupt Descriptor Table with 32 entries. #[derive(Clone)] #[repr(C, align(16))] From 955cd5957fbba67db70f36b61ad3159e827778ef Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Wed, 29 Apr 2020 09:46:32 +0100 Subject: [PATCH 17/61] Remove build.rs --- build.rs | 299 ------------------------------------------------------- 1 file changed, 299 deletions(-) delete mode 100644 build.rs diff --git a/build.rs b/build.rs deleted file mode 100644 index 7918c624..00000000 --- a/build.rs +++ /dev/null @@ -1,299 +0,0 @@ -#[cfg(not(feature = "binary"))] -fn main() {} - -#[cfg(feature = "binary")] -#[derive(Default)] -struct BootloaderConfig { - physical_memory_offset: Option, - kernel_stack_address: Option, - kernel_stack_size: Option, - boot_info_address: Option, -} - -#[cfg(feature = "binary")] -fn parse_aligned_addr(key: &str, value: &str) -> u64 { - let num = if value.starts_with("0x") { - u64::from_str_radix(&value[2..], 16) - } else { - u64::from_str_radix(&value, 10) - }; - - let num = num.expect(&format!( - "`{}` in the kernel manifest must be an integer (is `{}`)", - key, value - )); - - if num % 0x1000 != 0 { - panic!( - "`{}` in the kernel manifest must be aligned to 4KiB (is `{}`)", - key, value - ); - } else { - num - } -} - -#[cfg(feature = "binary")] -fn parse_to_config(cfg: &mut BootloaderConfig, table: &toml::value::Table) { - use toml::Value; - - for (key, value) in table { - match (key.as_str(), value.clone()) { - ("kernel-stack-address", Value::Integer(i)) - | ("physical-memory-offset", Value::Integer(i)) - | ("boot-info-address", Value::Integer(i)) => { - panic!( - "`{0}` in the kernel manifest must be given as a string, \ - as toml does not support unsigned 64-bit integers (try `{0} = \"{1}\"`)", - key.as_str(), - i - ); - } - ("kernel-stack-address", Value::String(s)) => { - cfg.kernel_stack_address = Some(parse_aligned_addr(key.as_str(), &s)); - } - ("boot-info-address", Value::String(s)) => { - cfg.boot_info_address = Some(parse_aligned_addr(key.as_str(), &s)); - } - #[cfg(not(feature = "map_physical_memory"))] - ("physical-memory-offset", Value::String(_)) => { - panic!( - "`physical-memory-offset` is only supported when the `map_physical_memory` \ - feature of the crate is enabled" - ); - } - #[cfg(feature = "map_physical_memory")] - ("physical-memory-offset", Value::String(s)) => { - cfg.physical_memory_offset = Some(parse_aligned_addr(key.as_str(), &s)); - } - ("kernel-stack-size", Value::Integer(i)) => { - if i <= 0 { - panic!("`kernel-stack-size` in kernel manifest must be positive"); - } else { - cfg.kernel_stack_size = Some(i as u64); - } - } - (s, _) => { - panic!( - "unknown key '{}' in kernel manifest \ - - you may need to update the bootloader crate", - s - ); - } - } - } -} - -#[cfg(feature = "binary")] -fn main() { - use std::{ - env, - fs::{self, File}, - io::Write, - path::{Path, PathBuf}, - process::{self, Command}, - }; - use toml::Value; - - let target = env::var("TARGET").expect("TARGET not set"); - if Path::new(&target) - .file_stem() - .expect("target has no file stem") - != "x86_64-bootloader" - { - panic!("The bootloader must be compiled for the `x86_64-bootloader.json` target."); - } - - let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set")); - let kernel = PathBuf::from(match env::var("KERNEL") { - Ok(kernel) => kernel, - Err(_) => { - eprintln!( - "The KERNEL environment variable must be set for building the bootloader.\n\n\ - If you use `bootimage` for building you need at least version 0.7.0. You can \ - update `bootimage` by running `cargo install bootimage --force`." - ); - process::exit(1); - } - }); - let kernel_file_name = kernel - .file_name() - .expect("KERNEL has no valid file name") - .to_str() - .expect("kernel file name not valid utf8"); - - // check that the kernel file exists - assert!( - kernel.exists(), - format!("KERNEL does not exist: {}", kernel.display()) - ); - - // get access to llvm tools shipped in the llvm-tools-preview rustup component - let llvm_tools = match llvm_tools::LlvmTools::new() { - Ok(tools) => tools, - Err(llvm_tools::Error::NotFound) => { - eprintln!("Error: llvm-tools not found"); - eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?"); - eprintln!(" Install it through: `rustup component add llvm-tools-preview`"); - process::exit(1); - } - Err(err) => { - eprintln!("Failed to retrieve llvm-tools component: {:?}", err); - process::exit(1); - } - }; - - // check that kernel executable has code in it - let llvm_size = llvm_tools - .tool(&llvm_tools::exe("llvm-size")) - .expect("llvm-size not found in llvm-tools"); - let mut cmd = Command::new(llvm_size); - cmd.arg(&kernel); - let output = cmd.output().expect("failed to run llvm-size"); - let output_str = String::from_utf8_lossy(&output.stdout); - let second_line_opt = output_str.lines().skip(1).next(); - let second_line = second_line_opt.expect("unexpected llvm-size line output"); - let text_size_opt = second_line.split_ascii_whitespace().next(); - let text_size = text_size_opt.expect("unexpected llvm-size output"); - if text_size == "0" { - panic!("Kernel executable has an empty text section. Perhaps the entry point was set incorrectly?\n\n\ - Kernel executable at `{}`\n", kernel.display()); - } - - // strip debug symbols from kernel for faster loading - let stripped_kernel_file_name = format!("kernel_stripped-{}", kernel_file_name); - let stripped_kernel = out_dir.join(&stripped_kernel_file_name); - let objcopy = llvm_tools - .tool(&llvm_tools::exe("llvm-objcopy")) - .expect("llvm-objcopy not found in llvm-tools"); - let mut cmd = Command::new(&objcopy); - cmd.arg("--strip-debug"); - cmd.arg(&kernel); - cmd.arg(&stripped_kernel); - let exit_status = cmd - .status() - .expect("failed to run objcopy to strip debug symbols"); - if !exit_status.success() { - eprintln!("Error: Stripping debug symbols failed"); - process::exit(1); - } - - // wrap the kernel executable as binary in a new ELF file - let stripped_kernel_file_name_replaced = stripped_kernel_file_name - .replace('-', "_") - .replace('.', "_"); - let kernel_bin = out_dir.join(format!("kernel_bin-{}.o", kernel_file_name)); - let kernel_archive = out_dir.join(format!("libkernel_bin-{}.a", kernel_file_name)); - let mut cmd = Command::new(&objcopy); - cmd.arg("-I").arg("binary"); - cmd.arg("-O").arg("elf64-x86-64"); - cmd.arg("--binary-architecture=i386:x86-64"); - cmd.arg("--rename-section").arg(".data=.kernel"); - cmd.arg("--redefine-sym").arg(format!( - "_binary_{}_start=_kernel_start_addr", - stripped_kernel_file_name_replaced - )); - cmd.arg("--redefine-sym").arg(format!( - "_binary_{}_end=_kernel_end_addr", - stripped_kernel_file_name_replaced - )); - cmd.arg("--redefine-sym").arg(format!( - "_binary_{}_size=_kernel_size", - stripped_kernel_file_name_replaced - )); - cmd.current_dir(&out_dir); - cmd.arg(&stripped_kernel_file_name); - cmd.arg(&kernel_bin); - let exit_status = cmd.status().expect("failed to run objcopy"); - if !exit_status.success() { - eprintln!("Error: Running objcopy failed"); - process::exit(1); - } - - // create an archive for linking - let ar = llvm_tools - .tool(&llvm_tools::exe("llvm-ar")) - .unwrap_or_else(|| { - eprintln!("Failed to retrieve llvm-ar component"); - eprint!("This component is available since nightly-2019-03-29,"); - eprintln!("so try updating your toolchain if you're using an older nightly"); - process::exit(1); - }); - let mut cmd = Command::new(ar); - cmd.arg("crs"); - cmd.arg(&kernel_archive); - cmd.arg(&kernel_bin); - let exit_status = cmd.status().expect("failed to run ar"); - if !exit_status.success() { - eprintln!("Error: Running ar failed"); - process::exit(1); - } - - // Parse the kernel's Cargo.toml which is given to us by bootimage - let mut bootloader_config = BootloaderConfig::default(); - - match env::var("KERNEL_MANIFEST") { - Err(env::VarError::NotPresent) => { - panic!("The KERNEL_MANIFEST environment variable must be set for building the bootloader.\n\n\ - If you use `bootimage` for building you need at least version 0.7.7. You can \ - update `bootimage` by running `cargo install bootimage --force`."); - } - Err(env::VarError::NotUnicode(_)) => { - panic!("The KERNEL_MANIFEST environment variable contains invalid unicode") - } - Ok(path) => { - println!("cargo:rerun-if-changed={}", path); - - let contents = fs::read_to_string(&path).expect(&format!( - "failed to read kernel manifest file (path: {})", - path - )); - - let manifest = contents - .parse::() - .expect("failed to parse kernel's Cargo.toml"); - - let table = manifest - .get("package") - .and_then(|table| table.get("metadata")) - .and_then(|table| table.get("bootloader")) - .and_then(|table| table.as_table()); - - if let Some(table) = table { - parse_to_config(&mut bootloader_config, table); - } - } - } - - // Configure constants for the bootloader - // We leave some variables as Option rather than hardcoding their defaults so that they - // can be calculated dynamically by the bootloader. - let file_path = out_dir.join("bootloader_config.rs"); - let mut file = File::create(file_path).expect("failed to create bootloader_config.rs"); - file.write_all( - format!( - "const PHYSICAL_MEMORY_OFFSET: Option = {:?}; - const KERNEL_STACK_ADDRESS: Option = {:?}; - const KERNEL_STACK_SIZE: u64 = {}; - const BOOT_INFO_ADDRESS: Option = {:?};", - bootloader_config.physical_memory_offset, - bootloader_config.kernel_stack_address, - bootloader_config.kernel_stack_size.unwrap_or(512), // size is in number of pages - bootloader_config.boot_info_address, - ) - .as_bytes(), - ) - .expect("write to bootloader_config.rs failed"); - - // pass link arguments to rustc - println!("cargo:rustc-link-search=native={}", out_dir.display()); - println!( - "cargo:rustc-link-lib=static=kernel_bin-{}", - kernel_file_name - ); - - println!("cargo:rerun-if-env-changed=KERNEL"); - println!("cargo:rerun-if-env-changed=KERNEL_MANIFEST"); - println!("cargo:rerun-if-changed={}", kernel.display()); - println!("cargo:rerun-if-changed=build.rs"); -} From 2d96758679eff92a19c2b9330e2a2224c560782b Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Wed, 29 Apr 2020 09:47:33 +0100 Subject: [PATCH 18/61] Test and example kernels stop compilation --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c1baaffc..57177d8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ members = [ "src/v86", "src/bootsector", "src/stage_2", - "example-kernel", - "test-kernel", +# "example-kernel", +# "test-kernel", ] [profile.release] From 68a6ab9ad68583fe561a39681c96084c79fdf8dd Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Wed, 29 Apr 2020 09:48:03 +0100 Subject: [PATCH 19/61] Make everything other than bootsector automatically loaded --- linker.ld | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/linker.ld b/linker.ld index 94c396cf..e19322b3 100644 --- a/linker.ld +++ b/linker.ld @@ -25,6 +25,9 @@ SECTIONS { { _rest_of_bootloader_start = .; + *stage_2* + *core* + *(.text .text.*) *(.data .data.*) *(.rodata .rodata.*) @@ -35,6 +38,9 @@ SECTIONS { _rest_of_bootloader_end = .; } + /* Cause link time error if bootloader overflows 1MB limit */ + . = 0x100000; + _bootloader_end = .; _protected_mode_stack_start = .; From a49f39e968805a175666962a8a10e9fe9b12c1a1 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Wed, 29 Apr 2020 09:48:24 +0100 Subject: [PATCH 20/61] Attempting to make println! --- src/bootsector/src/errors.rs | 2 +- src/bootsector/src/main.rs | 9 ------ src/shared/src/console.rs | 59 +++++++++++++++++++++++++++--------- src/stage_2/src/lib.rs | 34 ++++++++++----------- src/stage_2/src/panic.rs | 12 ++++++++ src/v86/src/gdt.rs | 4 +-- src/v86/src/lib.rs | 3 ++ 7 files changed, 77 insertions(+), 46 deletions(-) create mode 100644 src/stage_2/src/panic.rs diff --git a/src/bootsector/src/errors.rs b/src/bootsector/src/errors.rs index e8e5625e..7e50cb4a 100644 --- a/src/bootsector/src/errors.rs +++ b/src/bootsector/src/errors.rs @@ -1,5 +1,5 @@ use shared::utils; -use shared::console::println; +use super::console::println; #[no_mangle] pub extern "C" fn dap_load_failed() -> ! { diff --git a/src/bootsector/src/main.rs b/src/bootsector/src/main.rs index fd4c0818..89a2d817 100644 --- a/src/bootsector/src/main.rs +++ b/src/bootsector/src/main.rs @@ -8,7 +8,6 @@ mod errors; mod console; -use core::panic::PanicInfo; use stage_2::second_stage; use self::console::println; @@ -37,14 +36,6 @@ unsafe extern "C" fn rust_start(disk_number: u16) -> ! { } } -#[panic_handler] -pub fn panic(_info: &PanicInfo) -> ! { - println(b"[!] Rust Panic"); - loop { - utils::hlt() - } -} - pub fn check_int13h_extensions(disk_number: u16) { unsafe { llvm_asm!(" diff --git a/src/shared/src/console.rs b/src/shared/src/console.rs index 8323a262..8d6c386c 100644 --- a/src/shared/src/console.rs +++ b/src/shared/src/console.rs @@ -1,22 +1,51 @@ -#[inline(never)] -pub fn println(s: &[u8]) { - print(s); - print_char(b'\n'); +use core::{fmt, fmt::Write}; + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => {{ + use core::fmt::Write; + let mut writer = $crate::console::Writer {}; + let _ = writer.write_fmt(format_args!($($arg)*)); + }}; +} + +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } -pub fn print(s: &[u8]) { - let mut i = 0; +pub struct Writer {} + +impl Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + let _ = self.write_char(c); + } + + Ok(()) + } - while i < s.len() { - print_char(s[i]); - i += 1; - } + fn write_char(&mut self, c: char) -> fmt::Result { + let mut buffer = [0u8; 4]; + + c.encode_utf8(&mut buffer); + + for raw_char in &buffer { + self.write_raw_char(*raw_char); + } + + Ok(()) + } } -#[inline(always)] -pub fn print_char(c: u8) { - let ax = u16::from(c) | 0x0e00; - unsafe { - llvm_asm!("int 0x10" :: "{ax}"(ax) :: "intel" ); +impl Writer { + #[inline(always)] + fn write_raw_char(&mut self, c: u8) { + let ax = u16::from(c) | 0x0e00; + + unsafe { + llvm_asm!("int 0x10" :: "{ax}"(ax), "{bx}"(0) :: "intel", "volatile"); + } } } \ No newline at end of file diff --git a/src/stage_2/src/lib.rs b/src/stage_2/src/lib.rs index fc3016d4..2e735ac5 100644 --- a/src/stage_2/src/lib.rs +++ b/src/stage_2/src/lib.rs @@ -1,12 +1,17 @@ #![no_std] #![feature(llvm_asm)] -use shared::console::println; +// FIXME +#![allow(dead_code, unused_variables)] + +use shared::println; use shared::linker_symbol; use v86::gdt::{GlobalDescriptorTable, Descriptor, TaskStateSegment}; use lazy_static::lazy_static; +mod panic; + lazy_static! { static ref TSS: TaskStateSegment = { let mut tss = TaskStateSegment::new(); @@ -30,21 +35,23 @@ lazy_static! { #[no_mangle] pub fn second_stage() { - println(b"Stage 2"); + println!("Stage 2"); - println(b"Loading GDT"); + loop {} +} + +fn enter_protected_mode() { + println!("Loading GDT"); unsafe { GDT.load(); } - println(b"GDT Loaded!"); + println!("GDT Loaded!"); - loop {}; - - println(b"Switching to 32-bit"); + println!("Switching to 32-bit"); enable_a20(); - println(b"A20"); + println!("A20"); loop {}; @@ -61,18 +68,9 @@ pub fn second_stage() { } } -static HELLO: &[u8] = b"Protected Mode!"; - #[no_mangle] extern "C" fn protected_mode() { - let vga_buffer = 0xb8000 as *mut u8; - - for (i, &byte) in HELLO.iter().enumerate() { - unsafe { - *vga_buffer.offset(i as isize * 2) = byte; - *vga_buffer.offset(i as isize * 2 + 1) = 0xb; - } - } + println!("Protected Mode!"); loop {} } diff --git a/src/stage_2/src/panic.rs b/src/stage_2/src/panic.rs new file mode 100644 index 00000000..46a9abeb --- /dev/null +++ b/src/stage_2/src/panic.rs @@ -0,0 +1,12 @@ +use core::panic::PanicInfo; +use shared::println; +use shared::utils; + +#[panic_handler] +pub fn panic(info: &PanicInfo) -> ! { + println!("[Panic]"); + + loop { + utils::hlt() + } +} \ No newline at end of file diff --git a/src/v86/src/gdt.rs b/src/v86/src/gdt.rs index a92ce078..6b702da6 100644 --- a/src/v86/src/gdt.rs +++ b/src/v86/src/gdt.rs @@ -48,9 +48,7 @@ impl GlobalDescriptorTable { limit: (self.table.len() * size_of::() - 1) as u16, }; - unsafe { - llvm_asm!("lgdt ($0)" :: "r" (&ptr) : "memory"); - } + llvm_asm!("lgdt ($0)" :: "r" (&ptr) : "memory"); } #[inline] diff --git a/src/v86/src/lib.rs b/src/v86/src/lib.rs index 47e49b26..9e77f984 100644 --- a/src/v86/src/lib.rs +++ b/src/v86/src/lib.rs @@ -3,6 +3,9 @@ #![feature(llvm_asm)] #![no_std] +// FIXME +#![allow(dead_code, unused_imports)] + use core::slice; pub mod gdt; From f7577bd71388f730a06369f5a8f1b50ce9deaf69 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 11:07:35 +0200 Subject: [PATCH 21/61] Include .got/.got.plt in bootloader section in linker script --- linker.ld | 1 + 1 file changed, 1 insertion(+) diff --git a/linker.ld b/linker.ld index e19322b3..c7814ba0 100644 --- a/linker.ld +++ b/linker.ld @@ -32,6 +32,7 @@ SECTIONS { *(.data .data.*) *(.rodata .rodata.*) *(.bss .bss.*) + *(.got .got.plt) . = ALIGN(512); From c2c1681b6fae2e6c5eb70dfcf601e0f150363cf3 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 11:17:53 +0200 Subject: [PATCH 22/61] Fix spacing for Writer --- src/shared/src/console.rs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/shared/src/console.rs b/src/shared/src/console.rs index 8d6c386c..abce10db 100644 --- a/src/shared/src/console.rs +++ b/src/shared/src/console.rs @@ -19,20 +19,8 @@ pub struct Writer {} impl Write for Writer { fn write_str(&mut self, s: &str) -> fmt::Result { - for c in s.chars() { - let _ = self.write_char(c); - } - - Ok(()) - } - - fn write_char(&mut self, c: char) -> fmt::Result { - let mut buffer = [0u8; 4]; - - c.encode_utf8(&mut buffer); - - for raw_char in &buffer { - self.write_raw_char(*raw_char); + for &byte in s.as_bytes() { + let _ = self.write_byte(byte); } Ok(()) @@ -41,7 +29,7 @@ impl Write for Writer { impl Writer { #[inline(always)] - fn write_raw_char(&mut self, c: u8) { + fn write_byte(&mut self, c: u8) { let ax = u16::from(c) | 0x0e00; unsafe { From 8fb4f5f069ec97ee9d20dd2b58490ffdfb68397b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 11:58:51 +0200 Subject: [PATCH 23/61] Make bootsector a staticlib --- src/bootsector/Cargo.toml | 5 ++++- src/bootsector/src/console.rs | 4 ++-- src/bootsector/src/errors.rs | 12 +++++------ src/bootsector/src/{main.rs => lib.rs} | 29 +++++++++++++++++--------- 4 files changed, 31 insertions(+), 19 deletions(-) rename src/bootsector/src/{main.rs => lib.rs} (62%) diff --git a/src/bootsector/Cargo.toml b/src/bootsector/Cargo.toml index 6960247b..7dae2d7d 100644 --- a/src/bootsector/Cargo.toml +++ b/src/bootsector/Cargo.toml @@ -6,6 +6,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "bootsector" +crate-type = ["staticlib"] + [dependencies] shared = { path = "../shared" } -stage_2 = { path = "../stage_2" } \ No newline at end of file diff --git a/src/bootsector/src/console.rs b/src/bootsector/src/console.rs index 8323a262..44da8a4e 100644 --- a/src/bootsector/src/console.rs +++ b/src/bootsector/src/console.rs @@ -1,5 +1,5 @@ -#[inline(never)] -pub fn println(s: &[u8]) { +#[no_mangle] +pub fn real_mode_println(s: &[u8]) { print(s); print_char(b'\n'); } diff --git a/src/bootsector/src/errors.rs b/src/bootsector/src/errors.rs index 7e50cb4a..d2fe10b0 100644 --- a/src/bootsector/src/errors.rs +++ b/src/bootsector/src/errors.rs @@ -1,18 +1,18 @@ use shared::utils; -use super::console::println; +use super::console::real_mode_println; #[no_mangle] -pub extern "C" fn dap_load_failed() -> ! { - println(b"[!] DAP Load Failed"); +extern "C" fn dap_load_failed() -> ! { + real_mode_println(b"[!] DAP Load Failed"); loop { utils::hlt() } } #[no_mangle] -pub extern "C" fn no_int13h_extensions() -> ! { - println(b"[!] No int13h Extensions"); +extern "C" fn no_int13h_extensions() -> ! { + real_mode_println(b"[!] No int13h Extensions"); loop { utils::hlt() } -} \ No newline at end of file +} diff --git a/src/bootsector/src/main.rs b/src/bootsector/src/lib.rs similarity index 62% rename from src/bootsector/src/main.rs rename to src/bootsector/src/lib.rs index 89a2d817..2917cad5 100644 --- a/src/bootsector/src/main.rs +++ b/src/bootsector/src/lib.rs @@ -1,23 +1,23 @@ #![feature(llvm_asm, global_asm)] - #![no_std] -#![no_main] #![allow(dead_code)] mod errors; mod console; -use stage_2::second_stage; - -use self::console::println; +use self::console::real_mode_println; use shared::{dap, utils, linker_symbol}; +use core::panic::PanicInfo; +extern "C" { + fn second_stage(); +} global_asm!(include_str!("bootstrap.s")); #[no_mangle] -unsafe extern "C" fn rust_start(disk_number: u16) -> ! { - println(b"Stage 1"); +extern "C" fn rust_start(disk_number: u16) -> ! { + real_mode_println(b"Stage 1"); check_int13h_extensions(disk_number); @@ -27,20 +27,29 @@ unsafe extern "C" fn rust_start(disk_number: u16) -> ! { linker_symbol!(_rest_of_bootloader_end) - linker_symbol!(_rest_of_bootloader_start) ); - dap.perform_load(disk_number); + unsafe { dap.perform_load(disk_number) }; - second_stage(); + unsafe { second_stage() }; loop { utils::hlt(); } } -pub fn check_int13h_extensions(disk_number: u16) { +fn check_int13h_extensions(disk_number: u16) { unsafe { llvm_asm!(" int 0x13 jc no_int13h_extensions " :: "{ah}"(0x41), "{bx}"(0x55aa), "{dl}"(disk_number) :: "intel", "volatile"); } +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + real_mode_println(b"[Panic]"); + + loop { + utils::hlt() + } } \ No newline at end of file From ec5994ac6e445a65b51cb43bf668760388038c21 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 11:59:03 +0200 Subject: [PATCH 24/61] Run cargo fmt --- src/bootsector/src/console.rs | 12 ++++++------ src/bootsector/src/errors.rs | 2 +- src/bootsector/src/lib.rs | 21 ++++++++++----------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/bootsector/src/console.rs b/src/bootsector/src/console.rs index 44da8a4e..980b12a3 100644 --- a/src/bootsector/src/console.rs +++ b/src/bootsector/src/console.rs @@ -5,12 +5,12 @@ pub fn real_mode_println(s: &[u8]) { } pub fn print(s: &[u8]) { - let mut i = 0; + let mut i = 0; - while i < s.len() { - print_char(s[i]); - i += 1; - } + while i < s.len() { + print_char(s[i]); + i += 1; + } } #[inline(always)] @@ -19,4 +19,4 @@ pub fn print_char(c: u8) { unsafe { llvm_asm!("int 0x10" :: "{ax}"(ax) :: "intel" ); } -} \ No newline at end of file +} diff --git a/src/bootsector/src/errors.rs b/src/bootsector/src/errors.rs index d2fe10b0..9cc04452 100644 --- a/src/bootsector/src/errors.rs +++ b/src/bootsector/src/errors.rs @@ -1,5 +1,5 @@ -use shared::utils; use super::console::real_mode_println; +use shared::utils; #[no_mangle] extern "C" fn dap_load_failed() -> ! { diff --git a/src/bootsector/src/lib.rs b/src/bootsector/src/lib.rs index 2917cad5..346a64df 100644 --- a/src/bootsector/src/lib.rs +++ b/src/bootsector/src/lib.rs @@ -1,14 +1,13 @@ #![feature(llvm_asm, global_asm)] #![no_std] - #![allow(dead_code)] -mod errors; mod console; +mod errors; use self::console::real_mode_println; -use shared::{dap, utils, linker_symbol}; use core::panic::PanicInfo; +use shared::{dap, linker_symbol, utils}; extern "C" { fn second_stage(); @@ -22,9 +21,9 @@ extern "C" fn rust_start(disk_number: u16) -> ! { check_int13h_extensions(disk_number); let dap = dap::DiskAddressPacket::new( - linker_symbol!(_rest_of_bootloader_start) as u16, + linker_symbol!(_rest_of_bootloader_start) as u16, (linker_symbol!(_rest_of_bootloader_start) - linker_symbol!(_bootloader_start)) as u64, - linker_symbol!(_rest_of_bootloader_end) - linker_symbol!(_rest_of_bootloader_start) + linker_symbol!(_rest_of_bootloader_end) - linker_symbol!(_rest_of_bootloader_start), ); unsafe { dap.perform_load(disk_number) }; @@ -32,24 +31,24 @@ extern "C" fn rust_start(disk_number: u16) -> ! { unsafe { second_stage() }; loop { - utils::hlt(); + utils::hlt(); } } fn check_int13h_extensions(disk_number: u16) { - unsafe { - llvm_asm!(" + unsafe { + llvm_asm!(" int 0x13 jc no_int13h_extensions " :: "{ah}"(0x41), "{bx}"(0x55aa), "{dl}"(disk_number) :: "intel", "volatile"); - } + } } #[panic_handler] fn panic(_info: &PanicInfo) -> ! { - real_mode_println(b"[Panic]"); + real_mode_println(b"[Panic]"); loop { utils::hlt() } -} \ No newline at end of file +} From 26a6f85423c51ecc9dec418e21620f2737165995 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 11:59:47 +0200 Subject: [PATCH 25/61] Make stage_2 the main executable and include bootsector --- src/stage_2/Cargo.toml | 5 +++- src/stage_2/build.rs | 46 +++++++++++++++++++++++++++++ src/stage_2/src/{lib.rs => main.rs} | 3 +- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/stage_2/build.rs rename src/stage_2/src/{lib.rs => main.rs} (99%) diff --git a/src/stage_2/Cargo.toml b/src/stage_2/Cargo.toml index 2c128ee2..07511391 100644 --- a/src/stage_2/Cargo.toml +++ b/src/stage_2/Cargo.toml @@ -12,4 +12,7 @@ v86 = { path = "../v86" } [dependencies.lazy_static] version = "1.0" -features = ["spin_no_std"] \ No newline at end of file +features = ["spin_no_std"] + +[build-dependencies] +llvm-tools = "0.1.1" diff --git a/src/stage_2/build.rs b/src/stage_2/build.rs new file mode 100644 index 00000000..602346b9 --- /dev/null +++ b/src/stage_2/build.rs @@ -0,0 +1,46 @@ +use std::process::Command; +use std::env; +use std::path::Path; +use llvm_tools::{LlvmTools, exe}; + +fn main() { + let out_dir = env::var("OUT_DIR").unwrap(); + let llvm_tools = LlvmTools::new().expect("LLVM tools not found"); + let objcopy = llvm_tools.tool(&exe("llvm-objcopy")).expect("llvm-objcopy not found"); + + build_subproject(Path::new("../bootsector"), &["_start", "real_mode_println", "no_int13h_extensions", "dap_load_failed"], "x86_64-real_mode.json", &out_dir, &objcopy); +} + +fn build_subproject(dir: &Path, global_symbols: &[&str], target: &str, out_dir: &str, objcopy: &Path) { + let dir_name = dir.file_name().unwrap().to_str().unwrap(); + let manifest_path = dir.join("Cargo.toml"); + let out_path = Path::new(&out_dir); + assert!(global_symbols.len() > 0, "must have at least one global symbol"); + + // build + let mut cmd = Command::new("cargo"); + cmd.arg("xbuild").arg("--release"); + cmd.arg("--verbose"); + cmd.arg(format!("--manifest-path={}", manifest_path.display())); + cmd.arg(format!("--target={}", dir.join("../..").join(target).display())); + cmd.arg("-Z").arg("unstable-options"); + cmd.arg("--out-dir").arg(&out_dir); + cmd.arg("--target-dir").arg(out_path.join("target").join(dir_name)); + cmd.env_remove("RUSTFLAGS"); + cmd.env("XBUILD_SYSROOT_PATH", out_path.join("target").join(dir_name).join("sysroot")); + let status = cmd.status().unwrap(); + assert!(status.success()); + + // localize symbols + let mut cmd = Command::new(objcopy); + for symbol in global_symbols { + cmd.arg("-G").arg(symbol); + } + cmd.arg(out_path.join(format!("lib{}.a", dir_name))); + let status = cmd.status().unwrap(); + assert!(status.success()); + + // emit linker flags + println!("cargo:rustc-link-search=native={}", out_dir); + println!("cargo:rustc-link-lib=static={}", dir_name); +} \ No newline at end of file diff --git a/src/stage_2/src/lib.rs b/src/stage_2/src/main.rs similarity index 99% rename from src/stage_2/src/lib.rs rename to src/stage_2/src/main.rs index 2e735ac5..0b5c4929 100644 --- a/src/stage_2/src/lib.rs +++ b/src/stage_2/src/main.rs @@ -1,5 +1,6 @@ -#![no_std] #![feature(llvm_asm)] +#![no_std] +#![no_main] // FIXME #![allow(dead_code, unused_variables)] From 1f287f75e3f1016590e8fc7ca27ee7807d25f5c4 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 12:00:11 +0200 Subject: [PATCH 26/61] Run cargo fmt for stage_2 --- src/stage_2/build.rs | 49 ++++++++++++++++++++++++++++++++-------- src/stage_2/src/main.rs | 16 ++++++------- src/stage_2/src/panic.rs | 4 ++-- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/stage_2/build.rs b/src/stage_2/build.rs index 602346b9..3cd8e3c8 100644 --- a/src/stage_2/build.rs +++ b/src/stage_2/build.rs @@ -1,33 +1,62 @@ -use std::process::Command; +use llvm_tools::{exe, LlvmTools}; use std::env; use std::path::Path; -use llvm_tools::{LlvmTools, exe}; +use std::process::Command; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); let llvm_tools = LlvmTools::new().expect("LLVM tools not found"); - let objcopy = llvm_tools.tool(&exe("llvm-objcopy")).expect("llvm-objcopy not found"); + let objcopy = llvm_tools + .tool(&exe("llvm-objcopy")) + .expect("llvm-objcopy not found"); - build_subproject(Path::new("../bootsector"), &["_start", "real_mode_println", "no_int13h_extensions", "dap_load_failed"], "x86_64-real_mode.json", &out_dir, &objcopy); + build_subproject( + Path::new("../bootsector"), + &[ + "_start", + "real_mode_println", + "no_int13h_extensions", + "dap_load_failed", + ], + "x86_64-real_mode.json", + &out_dir, + &objcopy, + ); } -fn build_subproject(dir: &Path, global_symbols: &[&str], target: &str, out_dir: &str, objcopy: &Path) { +fn build_subproject( + dir: &Path, + global_symbols: &[&str], + target: &str, + out_dir: &str, + objcopy: &Path, +) { let dir_name = dir.file_name().unwrap().to_str().unwrap(); let manifest_path = dir.join("Cargo.toml"); let out_path = Path::new(&out_dir); - assert!(global_symbols.len() > 0, "must have at least one global symbol"); + assert!( + global_symbols.len() > 0, + "must have at least one global symbol" + ); // build let mut cmd = Command::new("cargo"); cmd.arg("xbuild").arg("--release"); cmd.arg("--verbose"); cmd.arg(format!("--manifest-path={}", manifest_path.display())); - cmd.arg(format!("--target={}", dir.join("../..").join(target).display())); + cmd.arg(format!( + "--target={}", + dir.join("../..").join(target).display() + )); cmd.arg("-Z").arg("unstable-options"); cmd.arg("--out-dir").arg(&out_dir); - cmd.arg("--target-dir").arg(out_path.join("target").join(dir_name)); + cmd.arg("--target-dir") + .arg(out_path.join("target").join(dir_name)); cmd.env_remove("RUSTFLAGS"); - cmd.env("XBUILD_SYSROOT_PATH", out_path.join("target").join(dir_name).join("sysroot")); + cmd.env( + "XBUILD_SYSROOT_PATH", + out_path.join("target").join(dir_name).join("sysroot"), + ); let status = cmd.status().unwrap(); assert!(status.success()); @@ -43,4 +72,4 @@ fn build_subproject(dir: &Path, global_symbols: &[&str], target: &str, out_dir: // emit linker flags println!("cargo:rustc-link-search=native={}", out_dir); println!("cargo:rustc-link-lib=static={}", dir_name); -} \ No newline at end of file +} diff --git a/src/stage_2/src/main.rs b/src/stage_2/src/main.rs index 0b5c4929..ac9aea1e 100644 --- a/src/stage_2/src/main.rs +++ b/src/stage_2/src/main.rs @@ -1,13 +1,12 @@ #![feature(llvm_asm)] #![no_std] #![no_main] - // FIXME #![allow(dead_code, unused_variables)] -use shared::println; use shared::linker_symbol; -use v86::gdt::{GlobalDescriptorTable, Descriptor, TaskStateSegment}; +use shared::println; +use v86::gdt::{Descriptor, GlobalDescriptorTable, TaskStateSegment}; use lazy_static::lazy_static; @@ -21,7 +20,6 @@ lazy_static! { tss }; - static ref GDT: GlobalDescriptorTable = { let mut gdt = GlobalDescriptorTable::new(); gdt.add_entry(Descriptor::kernel_code_segment()); @@ -44,7 +42,9 @@ pub fn second_stage() { fn enter_protected_mode() { println!("Loading GDT"); - unsafe { GDT.load(); } + unsafe { + GDT.load(); + } println!("GDT Loaded!"); @@ -54,7 +54,7 @@ fn enter_protected_mode() { println!("A20"); - loop {}; + loop {} unsafe { llvm_asm!("mov eax, cr0 @@ -73,7 +73,7 @@ fn enter_protected_mode() { extern "C" fn protected_mode() { println!("Protected Mode!"); - loop {} + loop {} } fn enable_a20() { @@ -82,4 +82,4 @@ fn enable_a20() { or al, 2 out 0x92, al" ::: "al" : "intel", "volatile"); } -} \ No newline at end of file +} diff --git a/src/stage_2/src/panic.rs b/src/stage_2/src/panic.rs index 46a9abeb..bf32b99b 100644 --- a/src/stage_2/src/panic.rs +++ b/src/stage_2/src/panic.rs @@ -4,9 +4,9 @@ use shared::utils; #[panic_handler] pub fn panic(info: &PanicInfo) -> ! { - println!("[Panic]"); + println!("[Panic]"); loop { utils::hlt() } -} \ No newline at end of file +} From bcbe295656fee84d958d120a5d5eea1ce34a20fe Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 12:00:49 +0200 Subject: [PATCH 27/61] Enable lto and overwrite release profile for bootsector --- Cargo.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 57177d8a..6b58b324 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,9 @@ members = [ [profile.release] opt-level = "z" -panic = "abort" \ No newline at end of file +panic = "abort" +lto = true + +[profile.release.package.bootsector] +opt-level = "s" +codegen-units = 1 From 51b27d00b29a1aa1becd4c4e3ab08b6e90be4e3d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 12:15:15 +0200 Subject: [PATCH 28/61] Run cargo fmt for shared --- src/shared/src/console.rs | 2 +- src/shared/src/lib.rs | 3 ++- src/shared/src/macros.rs | 2 +- src/shared/src/utils.rs | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/shared/src/console.rs b/src/shared/src/console.rs index abce10db..5eb0249c 100644 --- a/src/shared/src/console.rs +++ b/src/shared/src/console.rs @@ -36,4 +36,4 @@ impl Writer { llvm_asm!("int 0x10" :: "{ax}"(ax), "{bx}"(0) :: "intel", "volatile"); } } -} \ No newline at end of file +} diff --git a/src/shared/src/lib.rs b/src/shared/src/lib.rs index 96b2b887..7c104639 100644 --- a/src/shared/src/lib.rs +++ b/src/shared/src/lib.rs @@ -4,4 +4,5 @@ pub mod console; pub mod dap; pub mod utils; -#[macro_use] pub mod macros; \ No newline at end of file +#[macro_use] +pub mod macros; diff --git a/src/shared/src/macros.rs b/src/shared/src/macros.rs index b3d59598..98b15ba8 100644 --- a/src/shared/src/macros.rs +++ b/src/shared/src/macros.rs @@ -9,4 +9,4 @@ macro_rules! linker_symbol { symbol_value }}; -} \ No newline at end of file +} diff --git a/src/shared/src/utils.rs b/src/shared/src/utils.rs index e0e82faa..acf789f1 100644 --- a/src/shared/src/utils.rs +++ b/src/shared/src/utils.rs @@ -3,4 +3,4 @@ pub fn hlt() { unsafe { llvm_asm!("hlt" :::: "intel","volatile"); } -} \ No newline at end of file +} From 9e3e1ec2664d3d9a21bddbb32b27ec3a6c434981 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 12:15:45 +0200 Subject: [PATCH 29/61] Make example-kernel and test-kernel workspace members again --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6b58b324..65ff2c37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ members = [ "src/v86", "src/bootsector", "src/stage_2", -# "example-kernel", -# "test-kernel", + "example-kernel", + "test-kernel", ] [profile.release] From 16c36820cb314b544d3fac65b39bfe7fc9d9859c Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 12:17:20 +0200 Subject: [PATCH 30/61] Run cargo fmt for v86 and test-kernel --- src/v86/src/idt.rs | 2 +- src/v86/src/lib.rs | 9 ++++----- test-kernel/src/main.rs | 4 +++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/v86/src/idt.rs b/src/v86/src/idt.rs index 91ce00b3..6555675f 100644 --- a/src/v86/src/idt.rs +++ b/src/v86/src/idt.rs @@ -1,5 +1,5 @@ -use core::marker::PhantomData; use bit_field::BitField; +use core::marker::PhantomData; /// An Interrupt Descriptor Table with 32 entries. #[derive(Clone)] diff --git a/src/v86/src/lib.rs b/src/v86/src/lib.rs index 9e77f984..3e093157 100644 --- a/src/v86/src/lib.rs +++ b/src/v86/src/lib.rs @@ -2,7 +2,6 @@ #![feature(const_fn)] #![feature(llvm_asm)] #![no_std] - // FIXME #![allow(dead_code, unused_imports)] @@ -34,7 +33,7 @@ impl V86 { // Match the first byte of the instruction match instructions[0] { - // int + // int 0xcd => match instructions[1] { // 0xFF (255) is our V86 monitor interrupt 0xff => { @@ -52,7 +51,7 @@ impl V86 { // Copy data into protected mode address space 0x1 => { - // We read buffer addresses and size from + // We read buffer addresses and size from let destination_pointer = stack[4] as *mut u32; let source_pointer = stack[5] as *const u32; let size = stack[6]; @@ -70,7 +69,7 @@ impl V86 { stack_pointer = stack_pointer.offset(3); frame.esp = ((frame.esp & 0xffff) - 6) & 0xffff; - // Store the next instructions EIP and code segment onto the stack + // Store the next instructions EIP and code segment onto the stack stack[0] = (frame.eip + 2) as usize; stack32[1] = frame.cs; @@ -93,4 +92,4 @@ impl V86 { _ => panic!("Unhandled GPF") }; } -}*/ \ No newline at end of file +}*/ diff --git a/test-kernel/src/main.rs b/test-kernel/src/main.rs index f0352e22..24699749 100644 --- a/test-kernel/src/main.rs +++ b/test-kernel/src/main.rs @@ -15,7 +15,9 @@ pub extern "C" fn _start() -> ! { // named `_start` by default // exit QEMU (see https://os.phil-opp.com/integration-tests/#shutting-down-qemu) - unsafe { exit_qemu(); } + unsafe { + exit_qemu(); + } loop {} } From 1ed97d4248cd91b467405516bff4fdbd8a03ca67 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 12:28:58 +0200 Subject: [PATCH 31/61] Move .cargo/config into stage2 It doesn't apply to e.g. the test-kernel. --- .cargo/config | 3 --- src/stage_2/.cargo/config | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 .cargo/config create mode 100644 src/stage_2/.cargo/config diff --git a/.cargo/config b/.cargo/config deleted file mode 100644 index 02647447..00000000 --- a/.cargo/config +++ /dev/null @@ -1,3 +0,0 @@ -[build] -target = "x86_64-real_mode.json" -rustflags = ["-C", "link-arg=-Tlinker.ld"] \ No newline at end of file diff --git a/src/stage_2/.cargo/config b/src/stage_2/.cargo/config new file mode 100644 index 00000000..2ce2ea1f --- /dev/null +++ b/src/stage_2/.cargo/config @@ -0,0 +1,3 @@ +[build] +target = "../../x86_64-real_mode.json" +rustflags = ["-C", "link-arg=-Tlinker.ld"] From cc6a8789b447ce1729f0ea95b6065d3ecd3606b1 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 12:29:30 +0200 Subject: [PATCH 32/61] CI: Build bootloader in stage_2 directory --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 95bdb7b8..b579d553 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,9 +52,10 @@ jobs: - name: 'Build Bootloader' run: cargo xbuild --release + working-directory: src/stage_2 - name: 'Convert Bootloader ELF to Binary' - run: cargo objcopy -- -I elf64-i386 -O binary --binary-architecture=i386:x86-64 target/x86_64-real_mode/release/bootsector target/x86_64-real_mode/release/bootsector.bin + run: cargo objcopy -- -I elf64-i386 -O binary --binary-architecture=i386:x86-64 target/x86_64-real_mode/release/stage_2 target/x86_64-real_mode/release/image.bin # # install QEMU # - name: Install QEMU (Linux) From 0b98df9b0fb3360e6c199dde312febd681d40db6 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 12:01:04 +0200 Subject: [PATCH 33/61] Update Cargo.lock --- Cargo.lock | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index eb7593df..083f82f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,7 +28,6 @@ name = "bootsector" version = "0.1.0" dependencies = [ "shared 0.1.0", - "stage_2 0.1.0", ] [[package]] @@ -46,6 +45,11 @@ dependencies = [ "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "llvm-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "nodrop" version = "0.1.14" @@ -70,6 +74,7 @@ name = "stage_2" version = "0.1.0" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "shared 0.1.0", "v86 0.1.0", ] @@ -118,6 +123,7 @@ dependencies = [ "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" "checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" "checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" From 063085764fc1cd507cedb459c4213de39316939b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 12:35:30 +0200 Subject: [PATCH 34/61] Use `-Zbuildstd` instead of cargo-xbuild --- .cargo/config | 2 ++ .github/workflows/build.yml | 8 -------- 2 files changed, 2 insertions(+), 8 deletions(-) create mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 00000000..913ee513 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[alias] +xbuild = "build -Zbuild-std=core" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b579d553..91a3ad6f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,15 +41,9 @@ jobs: - name: "Install Rustup Components" run: rustup component add rust-src llvm-tools-preview - - name: "Install cargo-xbuild" - run: cargo install cargo-xbuild --debug - name: "Install cargo-binutils" run: cargo install cargo-binutils --version 0.1.7 --debug - - run: cargo xbuild - working-directory: test-kernel - name: 'Build Test Kernel' - - name: 'Build Bootloader' run: cargo xbuild --release working-directory: src/stage_2 @@ -109,8 +103,6 @@ jobs: if: runner.os == 'macOS' - name: "Install Rustup Components" run: rustup component add rust-src llvm-tools-preview - - name: "Install cargo-xbuild" - run: cargo install cargo-xbuild --debug - name: 'Build Example Kernel' run: cargo xbuild working-directory: example-kernel From 87673c8c49b0c4a6a480f78935bc31a5aecf9177 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 29 Apr 2020 12:57:07 +0200 Subject: [PATCH 35/61] Remove bors from this repo --- bors.toml | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 bors.toml diff --git a/bors.toml b/bors.toml deleted file mode 100644 index c8009028..00000000 --- a/bors.toml +++ /dev/null @@ -1,4 +0,0 @@ -status = [ - "Test", "Build Example Kernel", "Check Formatting" -] -delete_merged_branches = true From ef365710e4ce66fa0f939d1423f74218ac960d94 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Wed, 29 Apr 2020 13:18:12 +0100 Subject: [PATCH 36/61] Refactoring arch-specific subcrates --- .cargo/config | 4 +- Cargo.lock | 120 +----------------- Cargo.toml | 25 ++-- src/stage_2/build.rs => build.rs | 21 ++- src/lib.rs | 2 + src/{ => protected}/v86/Cargo.toml | 2 - src/{ => protected}/v86/src/lib.rs | 7 +- src/real/Cargo.lock | 58 +++++++++ src/real/Cargo.toml | 14 ++ src/{ => real}/bootsector/Cargo.toml | 2 +- src/{ => real}/bootsector/src/bootstrap.s | 0 src/{ => real}/bootsector/src/console.rs | 0 src/{ => real}/bootsector/src/errors.rs | 0 src/{ => real}/bootsector/src/lib.rs | 0 linker.ld => src/real/linker.ld | 0 src/{ => real}/stage_2/Cargo.toml | 7 +- .../src/main.rs => real/stage_2/src/lib.rs} | 2 +- src/{ => real}/stage_2/src/panic.rs | 0 .../real/x86_64-real_mode.json | 7 +- src/shared/Cargo.toml | 2 + src/shared/src/lib.rs | 5 +- src/{v86/src => shared/src/structures}/gdt.rs | 0 src/{v86/src => shared/src/structures}/idt.rs | 0 src/shared/src/structures/mod.rs | 2 + src/stage_2/.cargo/config | 3 - 25 files changed, 126 insertions(+), 157 deletions(-) rename src/stage_2/build.rs => build.rs (83%) create mode 100644 src/lib.rs rename src/{ => protected}/v86/Cargo.toml (75%) rename src/{ => protected}/v86/src/lib.rs (97%) create mode 100644 src/real/Cargo.lock create mode 100644 src/real/Cargo.toml rename src/{ => real}/bootsector/Cargo.toml (89%) rename src/{ => real}/bootsector/src/bootstrap.s (100%) rename src/{ => real}/bootsector/src/console.rs (100%) rename src/{ => real}/bootsector/src/errors.rs (100%) rename src/{ => real}/bootsector/src/lib.rs (100%) rename linker.ld => src/real/linker.ld (100%) rename src/{ => real}/stage_2/Cargo.toml (79%) rename src/{stage_2/src/main.rs => real/stage_2/src/lib.rs} (99%) rename src/{ => real}/stage_2/src/panic.rs (100%) rename x86_64-real_mode.json => src/real/x86_64-real_mode.json (83%) rename src/{v86/src => shared/src/structures}/gdt.rs (100%) rename src/{v86/src => shared/src/structures}/idt.rs (100%) create mode 100644 src/shared/src/structures/mod.rs delete mode 100644 src/stage_2/.cargo/config diff --git a/.cargo/config b/.cargo/config index 913ee513..607a7a45 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,2 +1,2 @@ -[alias] -xbuild = "build -Zbuild-std=core" +[build] +target = "src/real/x86_64-real_mode.json" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 083f82f8..0972c6e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,48 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "array-init" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bit_field" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bit_field" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bootsector" -version = "0.1.0" -dependencies = [ - "shared 0.1.0", -] - -[[package]] -name = "example-kernel" +name = "bootloader" version = "0.1.0" dependencies = [ - "x86_64 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -50,83 +12,5 @@ name = "llvm-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "os_bootinfo" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "shared" -version = "0.1.0" - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "stage_2" -version = "0.1.0" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "shared 0.1.0", - "v86 0.1.0", -] - -[[package]] -name = "test-kernel" -version = "0.1.0" -dependencies = [ - "x86_64 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "usize_conversions" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ux" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "v86" -version = "0.1.0" -dependencies = [ - "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "x86_64" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [metadata] -"checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" -"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" -"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" -"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a" -"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" -"checksum ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f" -"checksum x86_64 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f9258d7e2dd25008d69e8c9e9ee37865887a5e1e3d06a62f1cb3f6c209e6f177" diff --git a/Cargo.toml b/Cargo.toml index 65ff2c37..b67f005f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,11 @@ -[workspace] -members = [ - "src/shared", - "src/v86", - "src/bootsector", - "src/stage_2", - "example-kernel", - "test-kernel", -] +[package] +name = "bootloader" +version = "0.1.0" +authors = ["Ryland Morgan "] +edition = "2018" +build = "build.rs" -[profile.release] -opt-level = "z" -panic = "abort" -lto = true +[dependencies] -[profile.release.package.bootsector] -opt-level = "s" -codegen-units = 1 +[build-dependencies] +llvm-tools = "0.1" \ No newline at end of file diff --git a/src/stage_2/build.rs b/build.rs similarity index 83% rename from src/stage_2/build.rs rename to build.rs index 3cd8e3c8..ae78e46c 100644 --- a/src/stage_2/build.rs +++ b/build.rs @@ -11,17 +11,28 @@ fn main() { .expect("llvm-objcopy not found"); build_subproject( - Path::new("../bootsector"), + Path::new("src/real/bootsector"), &[ "_start", "real_mode_println", "no_int13h_extensions", "dap_load_failed", ], - "x86_64-real_mode.json", + "real/x86_64-real_mode.json", &out_dir, &objcopy, ); + + build_subproject( + Path::new("src/real/stage2"), + &[ + "second_stage", + ], + "real/x86_64-real_mode.json", + &out_dir, + &objcopy, + ); + } fn build_subproject( @@ -42,11 +53,13 @@ fn build_subproject( // build let mut cmd = Command::new("cargo"); cmd.arg("xbuild").arg("--release"); + cmd.arg("--verbose"); + cmd.arg(format!("--manifest-path={}", manifest_path.display())); cmd.arg(format!( "--target={}", - dir.join("../..").join(target).display() + dir.join("..").join("..").join(target).display() )); cmd.arg("-Z").arg("unstable-options"); cmd.arg("--out-dir").arg(&out_dir); @@ -58,7 +71,7 @@ fn build_subproject( out_path.join("target").join(dir_name).join("sysroot"), ); let status = cmd.status().unwrap(); - assert!(status.success()); + assert!(status.success(), "Subcrate build failed!"); // localize symbols let mut cmd = Command::new(objcopy); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..e4449ceb --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +#![no_std] +#![no_main] \ No newline at end of file diff --git a/src/v86/Cargo.toml b/src/protected/v86/Cargo.toml similarity index 75% rename from src/v86/Cargo.toml rename to src/protected/v86/Cargo.toml index 7df91b86..b2ed9448 100644 --- a/src/v86/Cargo.toml +++ b/src/protected/v86/Cargo.toml @@ -5,5 +5,3 @@ authors = ["Ryland Morgan "] edition = "2018" [dependencies] -bitflags = "1.2.1" -bit_field = "0.10.0" diff --git a/src/v86/src/lib.rs b/src/protected/v86/src/lib.rs similarity index 97% rename from src/v86/src/lib.rs rename to src/protected/v86/src/lib.rs index 3e093157..ec90b0b9 100644 --- a/src/v86/src/lib.rs +++ b/src/protected/v86/src/lib.rs @@ -1,15 +1,10 @@ -#![feature(abi_x86_interrupt)] -#![feature(const_fn)] -#![feature(llvm_asm)] #![no_std] + // FIXME #![allow(dead_code, unused_imports)] use core::slice; -pub mod gdt; -pub mod idt; - const EFLAG_IF: u32 = 0x00000200; const EFLAG_VM: u32 = 0x00020000; /* diff --git a/src/real/Cargo.lock b/src/real/Cargo.lock new file mode 100644 index 00000000..f3bd2eb8 --- /dev/null +++ b/src/real/Cargo.lock @@ -0,0 +1,58 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bit_field" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bootsector" +version = "0.1.0" +dependencies = [ + "shared", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "llvm-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" + +[[package]] +name = "shared" +version = "0.1.0" +dependencies = [ + "bit_field", + "bitflags", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "stage_2" +version = "0.1.0" +dependencies = [ + "lazy_static", + "llvm-tools", + "shared", +] diff --git a/src/real/Cargo.toml b/src/real/Cargo.toml new file mode 100644 index 00000000..c77dbd09 --- /dev/null +++ b/src/real/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +members = [ + "bootsector", + "stage_2" +] + +[profile.release] +opt-level = "z" +panic = "abort" +lto = true + +[profile.release.package.bootsector] +opt-level = "s" +codegen-units = 1 diff --git a/src/bootsector/Cargo.toml b/src/real/bootsector/Cargo.toml similarity index 89% rename from src/bootsector/Cargo.toml rename to src/real/bootsector/Cargo.toml index 7dae2d7d..d109ed04 100644 --- a/src/bootsector/Cargo.toml +++ b/src/real/bootsector/Cargo.toml @@ -11,4 +11,4 @@ name = "bootsector" crate-type = ["staticlib"] [dependencies] -shared = { path = "../shared" } +shared = { path = "../../shared" } diff --git a/src/bootsector/src/bootstrap.s b/src/real/bootsector/src/bootstrap.s similarity index 100% rename from src/bootsector/src/bootstrap.s rename to src/real/bootsector/src/bootstrap.s diff --git a/src/bootsector/src/console.rs b/src/real/bootsector/src/console.rs similarity index 100% rename from src/bootsector/src/console.rs rename to src/real/bootsector/src/console.rs diff --git a/src/bootsector/src/errors.rs b/src/real/bootsector/src/errors.rs similarity index 100% rename from src/bootsector/src/errors.rs rename to src/real/bootsector/src/errors.rs diff --git a/src/bootsector/src/lib.rs b/src/real/bootsector/src/lib.rs similarity index 100% rename from src/bootsector/src/lib.rs rename to src/real/bootsector/src/lib.rs diff --git a/linker.ld b/src/real/linker.ld similarity index 100% rename from linker.ld rename to src/real/linker.ld diff --git a/src/stage_2/Cargo.toml b/src/real/stage_2/Cargo.toml similarity index 79% rename from src/stage_2/Cargo.toml rename to src/real/stage_2/Cargo.toml index 07511391..6a9a1f62 100644 --- a/src/stage_2/Cargo.toml +++ b/src/real/stage_2/Cargo.toml @@ -6,9 +6,12 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "stage_2" +crate-type = ["staticlib"] + [dependencies] -shared = { path = "../shared" } -v86 = { path = "../v86" } +shared = { path = "../../shared" } [dependencies.lazy_static] version = "1.0" diff --git a/src/stage_2/src/main.rs b/src/real/stage_2/src/lib.rs similarity index 99% rename from src/stage_2/src/main.rs rename to src/real/stage_2/src/lib.rs index ac9aea1e..b9b7ce02 100644 --- a/src/stage_2/src/main.rs +++ b/src/real/stage_2/src/lib.rs @@ -1,6 +1,6 @@ #![feature(llvm_asm)] #![no_std] -#![no_main] + // FIXME #![allow(dead_code, unused_variables)] diff --git a/src/stage_2/src/panic.rs b/src/real/stage_2/src/panic.rs similarity index 100% rename from src/stage_2/src/panic.rs rename to src/real/stage_2/src/panic.rs diff --git a/x86_64-real_mode.json b/src/real/x86_64-real_mode.json similarity index 83% rename from x86_64-real_mode.json rename to src/real/x86_64-real_mode.json index 35bd2784..68e357f2 100644 --- a/x86_64-real_mode.json +++ b/src/real/x86_64-real_mode.json @@ -17,5 +17,10 @@ "os": "none", "vendor": "unknown", "relocation_model": "static", - "eliminate_frame_pointer": true + "eliminate_frame_pointer": true, + "pre-link-args": { + "ld.lld": [ + "--script=real/linker.ld" + ] + } } \ No newline at end of file diff --git a/src/shared/Cargo.toml b/src/shared/Cargo.toml index d9c9c70e..3f26483d 100644 --- a/src/shared/Cargo.toml +++ b/src/shared/Cargo.toml @@ -5,3 +5,5 @@ authors = ["Ryland Morgan "] edition = "2018" [dependencies] +bitflags = "1.2.1" +bit_field = "0.10.0" diff --git a/src/shared/src/lib.rs b/src/shared/src/lib.rs index 7c104639..46645305 100644 --- a/src/shared/src/lib.rs +++ b/src/shared/src/lib.rs @@ -1,8 +1,11 @@ -#![no_std] +#![feature(abi_x86_interrupt)] +#![feature(const_fn)] #![feature(llvm_asm)] +#![no_std] pub mod console; pub mod dap; pub mod utils; #[macro_use] pub mod macros; +pub mod structures; \ No newline at end of file diff --git a/src/v86/src/gdt.rs b/src/shared/src/structures/gdt.rs similarity index 100% rename from src/v86/src/gdt.rs rename to src/shared/src/structures/gdt.rs diff --git a/src/v86/src/idt.rs b/src/shared/src/structures/idt.rs similarity index 100% rename from src/v86/src/idt.rs rename to src/shared/src/structures/idt.rs diff --git a/src/shared/src/structures/mod.rs b/src/shared/src/structures/mod.rs new file mode 100644 index 00000000..6aed3686 --- /dev/null +++ b/src/shared/src/structures/mod.rs @@ -0,0 +1,2 @@ +pub mod gdt; +pub mod idt; \ No newline at end of file diff --git a/src/stage_2/.cargo/config b/src/stage_2/.cargo/config deleted file mode 100644 index 2ce2ea1f..00000000 --- a/src/stage_2/.cargo/config +++ /dev/null @@ -1,3 +0,0 @@ -[build] -target = "../../x86_64-real_mode.json" -rustflags = ["-C", "link-arg=-Tlinker.ld"] From 7ad20279b75195c10f439920c5baa7eb896f08d7 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Wed, 29 Apr 2020 13:41:04 +0100 Subject: [PATCH 37/61] Typo. --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index ae78e46c..4e87a7c1 100644 --- a/build.rs +++ b/build.rs @@ -24,7 +24,7 @@ fn main() { ); build_subproject( - Path::new("src/real/stage2"), + Path::new("src/real/stage_2"), &[ "second_stage", ], From 9802c7597b8497d14044636d41ecd4fbc89b5fb3 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Wed, 29 Apr 2020 14:43:07 +0100 Subject: [PATCH 38/61] Trying to fix --- .cargo/config | 2 +- build.rs | 18 ++++++------- .../x86_64-bootsector.json} | 0 src/real/stage_2/src/lib.rs | 2 +- src/real/stage_2/x86_64-stage_2.json | 26 +++++++++++++++++++ x86_64-real_mode.json | 26 +++++++++++++++++++ 6 files changed, 63 insertions(+), 11 deletions(-) rename src/real/{x86_64-real_mode.json => bootsector/x86_64-bootsector.json} (100%) create mode 100644 src/real/stage_2/x86_64-stage_2.json create mode 100644 x86_64-real_mode.json diff --git a/.cargo/config b/.cargo/config index 607a7a45..8f58fe10 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,2 +1,2 @@ [build] -target = "src/real/x86_64-real_mode.json" \ No newline at end of file +target = "x86_64-real_mode.json" \ No newline at end of file diff --git a/build.rs b/build.rs index 4e87a7c1..469fc4d8 100644 --- a/build.rs +++ b/build.rs @@ -10,7 +10,7 @@ fn main() { .tool(&exe("llvm-objcopy")) .expect("llvm-objcopy not found"); - build_subproject( + /*build_subproject( Path::new("src/real/bootsector"), &[ "_start", @@ -18,17 +18,17 @@ fn main() { "no_int13h_extensions", "dap_load_failed", ], - "real/x86_64-real_mode.json", + "x86_64-bootsector.json", &out_dir, &objcopy, - ); + );*/ build_subproject( Path::new("src/real/stage_2"), &[ "second_stage", ], - "real/x86_64-real_mode.json", + "x86_64-stage_2.json", &out_dir, &objcopy, ); @@ -43,7 +43,6 @@ fn build_subproject( objcopy: &Path, ) { let dir_name = dir.file_name().unwrap().to_str().unwrap(); - let manifest_path = dir.join("Cargo.toml"); let out_path = Path::new(&out_dir); assert!( global_symbols.len() > 0, @@ -52,14 +51,15 @@ fn build_subproject( // build let mut cmd = Command::new("cargo"); - cmd.arg("xbuild").arg("--release"); - + cmd.current_dir(&dir); + + cmd.arg("xbuild").arg("--release"); cmd.arg("--verbose"); - cmd.arg(format!("--manifest-path={}", manifest_path.display())); cmd.arg(format!( "--target={}", - dir.join("..").join("..").join(target).display() + //dir.join(target).display() + target )); cmd.arg("-Z").arg("unstable-options"); cmd.arg("--out-dir").arg(&out_dir); diff --git a/src/real/x86_64-real_mode.json b/src/real/bootsector/x86_64-bootsector.json similarity index 100% rename from src/real/x86_64-real_mode.json rename to src/real/bootsector/x86_64-bootsector.json diff --git a/src/real/stage_2/src/lib.rs b/src/real/stage_2/src/lib.rs index b9b7ce02..5b824340 100644 --- a/src/real/stage_2/src/lib.rs +++ b/src/real/stage_2/src/lib.rs @@ -6,7 +6,7 @@ use shared::linker_symbol; use shared::println; -use v86::gdt::{Descriptor, GlobalDescriptorTable, TaskStateSegment}; +use shared::structures::gdt::{Descriptor, GlobalDescriptorTable, TaskStateSegment}; use lazy_static::lazy_static; diff --git a/src/real/stage_2/x86_64-stage_2.json b/src/real/stage_2/x86_64-stage_2.json new file mode 100644 index 00000000..68e357f2 --- /dev/null +++ b/src/real/stage_2/x86_64-stage_2.json @@ -0,0 +1,26 @@ +{ + "arch": "x86", + "cpu": "i386", + "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", + "dynamic-linking": false, + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "llvm-target": "i386-unknown-none-code16", + "max-atomic-width": 64, + "position-independent-executables": false, + "disable-redzone": true, + "target-c-int-width": "32", + "target-pointer-width": "32", + "target-endian": "little", + "panic-strategy": "abort", + "os": "none", + "vendor": "unknown", + "relocation_model": "static", + "eliminate_frame_pointer": true, + "pre-link-args": { + "ld.lld": [ + "--script=real/linker.ld" + ] + } +} \ No newline at end of file diff --git a/x86_64-real_mode.json b/x86_64-real_mode.json new file mode 100644 index 00000000..68e357f2 --- /dev/null +++ b/x86_64-real_mode.json @@ -0,0 +1,26 @@ +{ + "arch": "x86", + "cpu": "i386", + "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", + "dynamic-linking": false, + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "llvm-target": "i386-unknown-none-code16", + "max-atomic-width": 64, + "position-independent-executables": false, + "disable-redzone": true, + "target-c-int-width": "32", + "target-pointer-width": "32", + "target-endian": "little", + "panic-strategy": "abort", + "os": "none", + "vendor": "unknown", + "relocation_model": "static", + "eliminate_frame_pointer": true, + "pre-link-args": { + "ld.lld": [ + "--script=real/linker.ld" + ] + } +} \ No newline at end of file From 5ceab3163000b2288dbc06eba6a4454aa2b67bce Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Wed, 29 Apr 2020 15:07:56 +0100 Subject: [PATCH 39/61] Still trying --- .cargo/config | 5 ++++- build.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.cargo/config b/.cargo/config index 8f58fe10..faec9701 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,2 +1,5 @@ [build] -target = "x86_64-real_mode.json" \ No newline at end of file +target = "x86_64-real_mode.json" + +[alias] +xbuild = "build -Zbuild-std=core" \ No newline at end of file diff --git a/build.rs b/build.rs index 469fc4d8..32ab71bb 100644 --- a/build.rs +++ b/build.rs @@ -53,7 +53,7 @@ fn build_subproject( let mut cmd = Command::new("cargo"); cmd.current_dir(&dir); - cmd.arg("xbuild").arg("--release"); + cmd.arg("build").arg("--release").arg("-Zbuild-std=core"); cmd.arg("--verbose"); cmd.arg(format!( From 23991b34c71216437aac15e6c041b46552d80ace Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Wed, 29 Apr 2020 15:15:40 +0100 Subject: [PATCH 40/61] Fix GH workflows --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 91a3ad6f..d5d0dfb1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,6 @@ jobs: - name: 'Build Bootloader' run: cargo xbuild --release - working-directory: src/stage_2 - name: 'Convert Bootloader ELF to Binary' run: cargo objcopy -- -I elf64-i386 -O binary --binary-architecture=i386:x86-64 target/x86_64-real_mode/release/stage_2 target/x86_64-real_mode/release/image.bin From 5d4d99ead984380eb75e15b62e1713ca31f2ed4d Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Wed, 29 Apr 2020 16:48:16 +0100 Subject: [PATCH 41/61] Update build.rs --- build.rs | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/build.rs b/build.rs index 32ab71bb..8e485c04 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,11 @@ +// TODO - Make this code less awful use llvm_tools::{exe, LlvmTools}; use std::env; use std::path::Path; use std::process::Command; fn main() { + println!("cargo:warning=Compiling..."); let out_dir = env::var("OUT_DIR").unwrap(); let llvm_tools = LlvmTools::new().expect("LLVM tools not found"); let objcopy = llvm_tools @@ -23,6 +25,8 @@ fn main() { &objcopy, );*/ + println!("cargo:warning=Compiling stage2..."); + build_subproject( Path::new("src/real/stage_2"), &[ @@ -36,14 +40,17 @@ fn main() { } fn build_subproject( - dir: &Path, + subproject_dir: &Path, global_symbols: &[&str], target: &str, out_dir: &str, objcopy: &Path, ) { - let dir_name = dir.file_name().unwrap().to_str().unwrap(); - let out_path = Path::new(&out_dir); + let dir_name = subproject_dir.file_name().unwrap().to_str().unwrap(); + let out_dir_path = Path::new(&out_dir); + + let target_dir = out_dir_path.join("..").join("..").join("..").join("..").join(".."); + assert!( global_symbols.len() > 0, "must have at least one global symbol" @@ -51,25 +58,31 @@ fn build_subproject( // build let mut cmd = Command::new("cargo"); - cmd.current_dir(&dir); + cmd.current_dir(&subproject_dir); cmd.arg("build").arg("--release").arg("-Zbuild-std=core"); cmd.arg("--verbose"); cmd.arg(format!( "--target={}", - //dir.join(target).display() target )); - cmd.arg("-Z").arg("unstable-options"); + + cmd.arg("-Zunstable-options"); cmd.arg("--out-dir").arg(&out_dir); cmd.arg("--target-dir") - .arg(out_path.join("target").join(dir_name)); + .arg(&target_dir); cmd.env_remove("RUSTFLAGS"); cmd.env( "XBUILD_SYSROOT_PATH", - out_path.join("target").join(dir_name).join("sysroot"), + out_dir_path.join("target").join(dir_name).join("sysroot"), ); + + println!("cargo:warning=Out Dir - {}", &out_dir_path.display()); + println!("cargo:warning=Subproject Dir - {}", &subproject_dir.display()); + println!("cargo:warning=Dir Name - {}", &dir_name); + println!("cargo:warning=Target Dir - {}", &target_dir.display()); + let status = cmd.status().unwrap(); assert!(status.success(), "Subcrate build failed!"); @@ -78,9 +91,9 @@ fn build_subproject( for symbol in global_symbols { cmd.arg("-G").arg(symbol); } - cmd.arg(out_path.join(format!("lib{}.a", dir_name))); + cmd.arg(target_dir.join(format!("lib{}.a", dir_name))); let status = cmd.status().unwrap(); - assert!(status.success()); + assert!(status.success(), "Objcopy failed!"); // emit linker flags println!("cargo:rustc-link-search=native={}", out_dir); From 604b374df8f27b4dd929c397adbf16088f0d90d0 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Thu, 30 Apr 2020 12:07:19 +0100 Subject: [PATCH 42/61] Fixed it --- .cargo/config | 2 +- Cargo.lock | 6 +- build.rs | 124 ++++++++++-------- ...4-bootsector.json => i8086-bootloader.json | 0 .../i8086-bootsector.json} | 0 .../real/stage_2/i8086-stage_2.json | 0 6 files changed, 73 insertions(+), 59 deletions(-) rename src/real/bootsector/x86_64-bootsector.json => i8086-bootloader.json (100%) rename src/real/{stage_2/x86_64-stage_2.json => bootsector/i8086-bootsector.json} (100%) rename x86_64-real_mode.json => src/real/stage_2/i8086-stage_2.json (100%) diff --git a/.cargo/config b/.cargo/config index faec9701..126b7170 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,5 +1,5 @@ [build] -target = "x86_64-real_mode.json" +target = "i8086-bootloader.json" [alias] xbuild = "build -Zbuild-std=core" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 0972c6e5..fd402fe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,13 +4,11 @@ name = "bootloader" version = "0.1.0" dependencies = [ - "llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "llvm-tools", ] [[package]] name = "llvm-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" +checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" diff --git a/build.rs b/build.rs index 8e485c04..73a40af8 100644 --- a/build.rs +++ b/build.rs @@ -1,18 +1,30 @@ -// TODO - Make this code less awful +// TODO - Reuse compilation artifacts so core isn't compiled so many times use llvm_tools::{exe, LlvmTools}; use std::env; use std::path::Path; use std::process::Command; fn main() { - println!("cargo:warning=Compiling..."); - let out_dir = env::var("OUT_DIR").unwrap(); + // Read environment variables set by cargo + let cargo_path = env::var("CARGO").expect("Missing CARGO environment variable"); + let cargo = Path::new(&cargo_path); + + let manifest_dir_path = env::var("CARGO_MANIFEST_DIR").expect("Missing CARGO_MANIFEST_DIR environment variable"); + let manifest_dir = Path::new(&manifest_dir_path); + + // Calculate target directory + let current_dir = env::current_dir().expect("Couldn't get current directory"); + let target_dir_rel = manifest_dir.join("target"); + let target_dir = current_dir.join(target_dir_rel); + + // Find the objcopy binary let llvm_tools = LlvmTools::new().expect("LLVM tools not found"); let objcopy = llvm_tools .tool(&exe("llvm-objcopy")) .expect("llvm-objcopy not found"); - /*build_subproject( + // Build the bootsector + build_subproject( Path::new("src/real/bootsector"), &[ "_start", @@ -20,82 +32,86 @@ fn main() { "no_int13h_extensions", "dap_load_failed", ], - "x86_64-bootsector.json", - &out_dir, + "i8086-bootsector.json", + &target_dir, &objcopy, - );*/ - - println!("cargo:warning=Compiling stage2..."); + &cargo, + ); + // Build stage 2 build_subproject( Path::new("src/real/stage_2"), &[ "second_stage", ], - "x86_64-stage_2.json", - &out_dir, + "i8086-stage_2.json", + &target_dir, &objcopy, + &cargo, ); - } fn build_subproject( subproject_dir: &Path, global_symbols: &[&str], - target: &str, - out_dir: &str, + target_file_path: &str, + target_dir: &Path, objcopy: &Path, + cargo: &Path, ) { - let dir_name = subproject_dir.file_name().unwrap().to_str().unwrap(); - let out_dir_path = Path::new(&out_dir); - - let target_dir = out_dir_path.join("..").join("..").join("..").join("..").join(".."); + let subproject_name = subproject_dir.file_stem().expect("Couldn't get subproject name").to_str().expect("Subproject Name is not valid UTF-8"); + let target_file = Path::new(&target_file_path).file_stem().expect("Couldn't get target file stem"); + // We have to export at least 1 symbol assert!( global_symbols.len() > 0, "must have at least one global symbol" ); - // build - let mut cmd = Command::new("cargo"); - cmd.current_dir(&subproject_dir); - - cmd.arg("build").arg("--release").arg("-Zbuild-std=core"); - cmd.arg("--verbose"); - - cmd.arg(format!( - "--target={}", - target - )); - - cmd.arg("-Zunstable-options"); - cmd.arg("--out-dir").arg(&out_dir); - cmd.arg("--target-dir") - .arg(&target_dir); - cmd.env_remove("RUSTFLAGS"); - cmd.env( - "XBUILD_SYSROOT_PATH", - out_dir_path.join("target").join(dir_name).join("sysroot"), - ); + // Use cargo in CARGO environment variable (set on build) + let mut build_cmd = Command::new(cargo); - println!("cargo:warning=Out Dir - {}", &out_dir_path.display()); - println!("cargo:warning=Subproject Dir - {}", &subproject_dir.display()); - println!("cargo:warning=Dir Name - {}", &dir_name); - println!("cargo:warning=Target Dir - {}", &target_dir.display()); + // Build inside the subproject + build_cmd.current_dir(&subproject_dir); - let status = cmd.status().unwrap(); - assert!(status.success(), "Subcrate build failed!"); + // Build in release mode + build_cmd.arg("build").arg("--release"); - // localize symbols - let mut cmd = Command::new(objcopy); + // Cross-compile core (cargo-xbuild no longer needed) + build_cmd.arg("-Zbuild-std=core"); + + // Use root package target directory + build_cmd.arg(format!("--target-dir={}", &target_dir.join(&subproject_name).display())); + + // Use the passed target + build_cmd.arg("--target").arg(target_file_path); + + // Run the command and make sure it succeeds + let build_status = build_cmd.status().expect("Subcrate build failed!"); + assert!(build_status.success(), "Subcrate build failed!"); + + // Compute the path to the binary + let binary_dir = target_dir.join(&subproject_name).join(&target_file).join("release"); + let binary_path = binary_dir.join(format!("lib{}.a", &subproject_name)); + + // Use passed objcopy + let mut objcopy_cmd = Command::new(objcopy); + + // Localize all symbols except those passed for symbol in global_symbols { - cmd.arg("-G").arg(symbol); + objcopy_cmd.arg("-G").arg(symbol); } - cmd.arg(target_dir.join(format!("lib{}.a", dir_name))); - let status = cmd.status().unwrap(); - assert!(status.success(), "Objcopy failed!"); - // emit linker flags - println!("cargo:rustc-link-search=native={}", out_dir); - println!("cargo:rustc-link-lib=static={}", dir_name); + // Pass the binary as argument + objcopy_cmd.arg(binary_path); + + // Run the command and make sure it succeeds + let objcopy_status = objcopy_cmd.status().expect("Objcopy failed!"); + assert!(objcopy_status.success(), "Objcopy failed!"); + + // Emit flags to the linker + // + // Staticlibs can't be used as normal dependencies, they have to be linked by a build script + println!("cargo:rustc-link-search=native={}", &binary_dir.display()); + println!("cargo:rustc-link-lib=static={}", &subproject_name); } diff --git a/src/real/bootsector/x86_64-bootsector.json b/i8086-bootloader.json similarity index 100% rename from src/real/bootsector/x86_64-bootsector.json rename to i8086-bootloader.json diff --git a/src/real/stage_2/x86_64-stage_2.json b/src/real/bootsector/i8086-bootsector.json similarity index 100% rename from src/real/stage_2/x86_64-stage_2.json rename to src/real/bootsector/i8086-bootsector.json diff --git a/x86_64-real_mode.json b/src/real/stage_2/i8086-stage_2.json similarity index 100% rename from x86_64-real_mode.json rename to src/real/stage_2/i8086-stage_2.json From 58fe2cf282bb89519581a2212530b8135f8af80a Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Thu, 30 Apr 2020 12:16:14 +0100 Subject: [PATCH 43/61] Add a small documentation comment --- build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.rs b/build.rs index 73a40af8..a43258dd 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,5 @@ +// This build script compiles our bootloader. Because of architecture differences we can't use the standard Rust dependency resolution. To get around this (and add some more seperation between different areas) we compile all of the subcrates as static libraries and link them like we would a C dependency + // TODO - Reuse compilation artifacts so core isn't compiled so many times use llvm_tools::{exe, LlvmTools}; use std::env; From 58f9ed245f390f313b8e695f6be4162ad69ca1dc Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Thu, 30 Apr 2020 12:37:33 +0100 Subject: [PATCH 44/61] It boots! --- Cargo.lock | 21 +++++++++++++++++++++ Cargo.toml | 1 + i8086-bootloader.json | 2 +- src/lib.rs | 2 -- src/main.rs | 7 +++++++ src/panic.rs | 12 ++++++++++++ src/protected/v86/src/lib.rs | 1 + src/real/linker.ld | 1 + 8 files changed, 44 insertions(+), 3 deletions(-) delete mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/panic.rs diff --git a/Cargo.lock b/Cargo.lock index fd402fe0..d7071791 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,10 +1,23 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "bit_field" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + [[package]] name = "bootloader" version = "0.1.0" dependencies = [ "llvm-tools", + "shared", ] [[package]] @@ -12,3 +25,11 @@ name = "llvm-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" + +[[package]] +name = "shared" +version = "0.1.0" +dependencies = [ + "bit_field", + "bitflags", +] diff --git a/Cargo.toml b/Cargo.toml index b67f005f..6247b4fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" build = "build.rs" [dependencies] +shared = { path = "src/shared" } [build-dependencies] llvm-tools = "0.1" \ No newline at end of file diff --git a/i8086-bootloader.json b/i8086-bootloader.json index 68e357f2..be7172b1 100644 --- a/i8086-bootloader.json +++ b/i8086-bootloader.json @@ -20,7 +20,7 @@ "eliminate_frame_pointer": true, "pre-link-args": { "ld.lld": [ - "--script=real/linker.ld" + "--script=src/real/linker.ld" ] } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index e4449ceb..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -#![no_std] -#![no_main] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..b2fb61ff --- /dev/null +++ b/src/main.rs @@ -0,0 +1,7 @@ +#![no_std] +#![no_main] + +mod panic; + +#[no_mangle] +fn bootloader_no_optimize() {} \ No newline at end of file diff --git a/src/panic.rs b/src/panic.rs new file mode 100644 index 00000000..bf32b99b --- /dev/null +++ b/src/panic.rs @@ -0,0 +1,12 @@ +use core::panic::PanicInfo; +use shared::println; +use shared::utils; + +#[panic_handler] +pub fn panic(info: &PanicInfo) -> ! { + println!("[Panic]"); + + loop { + utils::hlt() + } +} diff --git a/src/protected/v86/src/lib.rs b/src/protected/v86/src/lib.rs index ec90b0b9..39e3da51 100644 --- a/src/protected/v86/src/lib.rs +++ b/src/protected/v86/src/lib.rs @@ -7,6 +7,7 @@ use core::slice; const EFLAG_IF: u32 = 0x00000200; const EFLAG_VM: u32 = 0x00020000; + /* pub struct V86 {} diff --git a/src/real/linker.ld b/src/real/linker.ld index c7814ba0..4ea6cc0b 100644 --- a/src/real/linker.ld +++ b/src/real/linker.ld @@ -12,6 +12,7 @@ SECTIONS { *(.bootstrap) *bootsector* + *core* } /* We write the magic number in the linker script to cause a link-time error if the bootloader overflows the 512 byte limit */ From 9a568dba796c428abcee4f54a6311d1c6c92271c Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Thu, 30 Apr 2020 17:27:50 +0100 Subject: [PATCH 45/61] Fix @bjorn3's nitpick --- .cargo/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cargo/config b/.cargo/config index 126b7170..d0601978 100644 --- a/.cargo/config +++ b/.cargo/config @@ -2,4 +2,4 @@ target = "i8086-bootloader.json" [alias] -xbuild = "build -Zbuild-std=core" \ No newline at end of file +xbuild = "build -Zbuild-std=core" From 1ed3171f4429a27457c7e6d5db2a1f95d505cea4 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Thu, 30 Apr 2020 17:28:16 +0100 Subject: [PATCH 46/61] Finish build script --- build.rs | 25 ++++----- linker.ld | 55 +++++++++++++++++++ ...6-bootsector.json => i8086-real_mode.json} | 7 +-- src/real/stage_2/i8086-stage_2.json | 26 --------- 4 files changed, 68 insertions(+), 45 deletions(-) create mode 100644 linker.ld rename src/real/{bootsector/i8086-bootsector.json => i8086-real_mode.json} (83%) delete mode 100644 src/real/stage_2/i8086-stage_2.json diff --git a/build.rs b/build.rs index a43258dd..1bb9a5c0 100644 --- a/build.rs +++ b/build.rs @@ -8,17 +8,15 @@ use std::process::Command; fn main() { // Read environment variables set by cargo + let out_dir_path = env::var("OUT_DIR").expect("Missing OUT_DIR environment variable"); + let out_dir = Path::new(&out_dir_path); + let cargo_path = env::var("CARGO").expect("Missing CARGO environment variable"); let cargo = Path::new(&cargo_path); let manifest_dir_path = env::var("CARGO_MANIFEST_DIR").expect("Missing CARGO_MANIFEST_DIR environment variable"); let manifest_dir = Path::new(&manifest_dir_path); - // Calculate target directory - let current_dir = env::current_dir().expect("Couldn't get current directory"); - let target_dir_rel = manifest_dir.join("target"); - let target_dir = current_dir.join(target_dir_rel); - // Find the objcopy binary let llvm_tools = LlvmTools::new().expect("LLVM tools not found"); let objcopy = llvm_tools @@ -34,8 +32,8 @@ fn main() { "no_int13h_extensions", "dap_load_failed", ], - "i8086-bootsector.json", - &target_dir, + "../i8086-real_mode.json", + &out_dir, &objcopy, &cargo, ); @@ -46,8 +44,8 @@ fn main() { &[ "second_stage", ], - "i8086-stage_2.json", - &target_dir, + "../i8086-real_mode.json", + &out_dir, &objcopy, &cargo, ); @@ -57,12 +55,13 @@ fn build_subproject( subproject_dir: &Path, global_symbols: &[&str], target_file_path: &str, - target_dir: &Path, + root_out_dir: &Path, objcopy: &Path, cargo: &Path, ) { let subproject_name = subproject_dir.file_stem().expect("Couldn't get subproject name").to_str().expect("Subproject Name is not valid UTF-8"); let target_file = Path::new(&target_file_path).file_stem().expect("Couldn't get target file stem"); + let target_dir = root_out_dir.join("target").join(&subproject_name); // We have to export at least 1 symbol assert!( @@ -82,8 +81,8 @@ fn build_subproject( // Cross-compile core (cargo-xbuild no longer needed) build_cmd.arg("-Zbuild-std=core"); - // Use root package target directory - build_cmd.arg(format!("--target-dir={}", &target_dir.join(&subproject_name).display())); + // Use calculated target directory + build_cmd.arg(format!("--target-dir={}", &target_dir.display())); // Use the passed target build_cmd.arg("--target").arg(target_file_path); @@ -93,7 +92,7 @@ fn build_subproject( assert!(build_status.success(), "Subcrate build failed!"); // Compute the path to the binary - let binary_dir = target_dir.join(&subproject_name).join(&target_file).join("release"); + let binary_dir = target_dir.join(&target_file).join("release"); let binary_path = binary_dir.join(format!("lib{}.a", &subproject_name)); // Use passed objcopy diff --git a/linker.ld b/linker.ld new file mode 100644 index 00000000..c7814ba0 --- /dev/null +++ b/linker.ld @@ -0,0 +1,55 @@ +ENTRY(_start) + +SECTIONS { + . = 0x500; + _stack_start = .; + . = 0x7c00; + _stack_end = .; + + .bootsector : + { + _bootloader_start = .; + + *(.bootstrap) + *bootsector* + } + + /* We write the magic number in the linker script to cause a link-time error if the bootloader overflows the 512 byte limit */ + . = 0x7c00 + 510; + .magic : + { + SHORT(0xaa55) + } + + .rest_of_bootloader : + { + _rest_of_bootloader_start = .; + + *stage_2* + *core* + + *(.text .text.*) + *(.data .data.*) + *(.rodata .rodata.*) + *(.bss .bss.*) + *(.got .got.plt) + + . = ALIGN(512); + + _rest_of_bootloader_end = .; + } + + /* Cause link time error if bootloader overflows 1MB limit */ + . = 0x100000; + + _bootloader_end = .; + + _protected_mode_stack_start = .; + . += 0x1000; + _protected_mode_stack_end = .; + + /DISCARD/ : + { + *(.eh_frame) + } +} \ No newline at end of file diff --git a/src/real/bootsector/i8086-bootsector.json b/src/real/i8086-real_mode.json similarity index 83% rename from src/real/bootsector/i8086-bootsector.json rename to src/real/i8086-real_mode.json index 68e357f2..35bd2784 100644 --- a/src/real/bootsector/i8086-bootsector.json +++ b/src/real/i8086-real_mode.json @@ -17,10 +17,5 @@ "os": "none", "vendor": "unknown", "relocation_model": "static", - "eliminate_frame_pointer": true, - "pre-link-args": { - "ld.lld": [ - "--script=real/linker.ld" - ] - } + "eliminate_frame_pointer": true } \ No newline at end of file diff --git a/src/real/stage_2/i8086-stage_2.json b/src/real/stage_2/i8086-stage_2.json deleted file mode 100644 index 68e357f2..00000000 --- a/src/real/stage_2/i8086-stage_2.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "arch": "x86", - "cpu": "i386", - "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", - "dynamic-linking": false, - "executables": true, - "linker-flavor": "ld.lld", - "linker": "rust-lld", - "llvm-target": "i386-unknown-none-code16", - "max-atomic-width": 64, - "position-independent-executables": false, - "disable-redzone": true, - "target-c-int-width": "32", - "target-pointer-width": "32", - "target-endian": "little", - "panic-strategy": "abort", - "os": "none", - "vendor": "unknown", - "relocation_model": "static", - "eliminate_frame_pointer": true, - "pre-link-args": { - "ld.lld": [ - "--script=real/linker.ld" - ] - } -} \ No newline at end of file From 318abe2e5668562bdb7a96ae42fb7db3d3dc56b4 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Thu, 30 Apr 2020 17:44:57 +0100 Subject: [PATCH 47/61] Fix workflows to work with new build system --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d5d0dfb1..3d094b83 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,7 +48,7 @@ jobs: run: cargo xbuild --release - name: 'Convert Bootloader ELF to Binary' - run: cargo objcopy -- -I elf64-i386 -O binary --binary-architecture=i386:x86-64 target/x86_64-real_mode/release/stage_2 target/x86_64-real_mode/release/image.bin + run: cargo objcopy -- -I elf64-i386 -O binary --binary-architecture=i386:x86-64 target/i8086-bootloader/release/bootloader target/i8086-bootloader/release/image.bin # # install QEMU # - name: Install QEMU (Linux) From 4be20822c6b1336f2f6bb621eb69aa3f1aba47b2 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Thu, 30 Apr 2020 17:49:25 +0100 Subject: [PATCH 48/61] Run rustfmt --- build.rs | 21 +++++++++++++-------- src/main.rs | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/build.rs b/build.rs index 1bb9a5c0..fbc25e03 100644 --- a/build.rs +++ b/build.rs @@ -14,7 +14,8 @@ fn main() { let cargo_path = env::var("CARGO").expect("Missing CARGO environment variable"); let cargo = Path::new(&cargo_path); - let manifest_dir_path = env::var("CARGO_MANIFEST_DIR").expect("Missing CARGO_MANIFEST_DIR environment variable"); + let manifest_dir_path = + env::var("CARGO_MANIFEST_DIR").expect("Missing CARGO_MANIFEST_DIR environment variable"); let manifest_dir = Path::new(&manifest_dir_path); // Find the objcopy binary @@ -41,9 +42,7 @@ fn main() { // Build stage 2 build_subproject( Path::new("src/real/stage_2"), - &[ - "second_stage", - ], + &["second_stage"], "../i8086-real_mode.json", &out_dir, &objcopy, @@ -59,8 +58,14 @@ fn build_subproject( objcopy: &Path, cargo: &Path, ) { - let subproject_name = subproject_dir.file_stem().expect("Couldn't get subproject name").to_str().expect("Subproject Name is not valid UTF-8"); - let target_file = Path::new(&target_file_path).file_stem().expect("Couldn't get target file stem"); + let subproject_name = subproject_dir + .file_stem() + .expect("Couldn't get subproject name") + .to_str() + .expect("Subproject Name is not valid UTF-8"); + let target_file = Path::new(&target_file_path) + .file_stem() + .expect("Couldn't get target file stem"); let target_dir = root_out_dir.join("target").join(&subproject_name); // We have to export at least 1 symbol @@ -98,9 +103,9 @@ fn build_subproject( // Use passed objcopy let mut objcopy_cmd = Command::new(objcopy); - // Localize all symbols except those passed + // Localize all symbols except those passed for symbol in global_symbols { - objcopy_cmd.arg("-G").arg(symbol); + objcopy_cmd.arg("-G").arg(symbol); } // Pass the binary as argument diff --git a/src/main.rs b/src/main.rs index b2fb61ff..606b78e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,4 +4,4 @@ mod panic; #[no_mangle] -fn bootloader_no_optimize() {} \ No newline at end of file +fn bootloader_no_optimize() {} From 21acd1ba60bc302f5f286e3d24981e995010d3bd Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Thu, 30 Apr 2020 19:08:50 +0100 Subject: [PATCH 49/61] Revert OUT_DIR output --- build.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/build.rs b/build.rs index fbc25e03..2d04808b 100644 --- a/build.rs +++ b/build.rs @@ -8,9 +8,6 @@ use std::process::Command; fn main() { // Read environment variables set by cargo - let out_dir_path = env::var("OUT_DIR").expect("Missing OUT_DIR environment variable"); - let out_dir = Path::new(&out_dir_path); - let cargo_path = env::var("CARGO").expect("Missing CARGO environment variable"); let cargo = Path::new(&cargo_path); @@ -18,6 +15,11 @@ fn main() { env::var("CARGO_MANIFEST_DIR").expect("Missing CARGO_MANIFEST_DIR environment variable"); let manifest_dir = Path::new(&manifest_dir_path); + // Find the root project target dir + let current_dir = env::current_dir().expect("Couldn't get current directory"); + let target_dir_rel = manifest_dir.join("target"); + let target_dir = current_dir.join(target_dir_rel); + // Find the objcopy binary let llvm_tools = LlvmTools::new().expect("LLVM tools not found"); let objcopy = llvm_tools @@ -34,7 +36,7 @@ fn main() { "dap_load_failed", ], "../i8086-real_mode.json", - &out_dir, + &target_dir, &objcopy, &cargo, ); @@ -44,7 +46,7 @@ fn main() { Path::new("src/real/stage_2"), &["second_stage"], "../i8086-real_mode.json", - &out_dir, + &target_dir, &objcopy, &cargo, ); @@ -54,7 +56,7 @@ fn build_subproject( subproject_dir: &Path, global_symbols: &[&str], target_file_path: &str, - root_out_dir: &Path, + root_target_dir: &Path, objcopy: &Path, cargo: &Path, ) { @@ -66,7 +68,8 @@ fn build_subproject( let target_file = Path::new(&target_file_path) .file_stem() .expect("Couldn't get target file stem"); - let target_dir = root_out_dir.join("target").join(&subproject_name); + + let target_dir = root_target_dir.join(&subproject_name); // We have to export at least 1 symbol assert!( @@ -83,6 +86,9 @@ fn build_subproject( // Build in release mode build_cmd.arg("build").arg("--release"); + // Very verbose (build script output only shows if you use `-vv` or it fails anyway) + build_cmd.arg("-vv"); + // Cross-compile core (cargo-xbuild no longer needed) build_cmd.arg("-Zbuild-std=core"); From 2d787464584d3416024b2e716c44a1b1b6eca290 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Thu, 30 Apr 2020 19:40:29 +0100 Subject: [PATCH 50/61] Inform cargo to rerun on source changes --- build.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/build.rs b/build.rs index 2d04808b..d3dad55a 100644 --- a/build.rs +++ b/build.rs @@ -50,6 +50,9 @@ fn main() { &objcopy, &cargo, ); + + // Inform cargo that we should rerun this on linker script changes + println!("cargo:rerun-if-changed=linker.ld"); } fn build_subproject( @@ -126,4 +129,10 @@ fn build_subproject( // Staticlibs can't be used as normal dependencies, they have to be linked by a build script println!("cargo:rustc-link-search=native={}", &binary_dir.display()); println!("cargo:rustc-link-lib=static={}", &subproject_name); + + // Inform cargo to rerun on source changes + // + // Cargo doesn't understand that the subcrates are part of the project because of how we build them, we have to tell it ourselves + println!("cargo:rerun-if-changed={}", &target_file_path); + println!("cargo:rerun-if-changed={}", &subproject_dir.display()); } From 155ec067b0e59cb97d3d9b324e804d4da4808bd1 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Thu, 30 Apr 2020 19:41:29 +0100 Subject: [PATCH 51/61] Fix panic handler --- src/real/stage_2/src/panic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/real/stage_2/src/panic.rs b/src/real/stage_2/src/panic.rs index bf32b99b..96c4c9a9 100644 --- a/src/real/stage_2/src/panic.rs +++ b/src/real/stage_2/src/panic.rs @@ -4,7 +4,7 @@ use shared::utils; #[panic_handler] pub fn panic(info: &PanicInfo) -> ! { - println!("[Panic]"); + println!("[Panic] {}", info); loop { utils::hlt() From b891b3a04c88f21046638a7b72405dadf48b7fe3 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Fri, 1 May 2020 14:13:15 +0100 Subject: [PATCH 52/61] Protected Mode --- .cargo/config | 2 +- Cargo.lock | 31 ++++ Cargo.toml | 2 + build.rs | 15 +- i8086-bootloader.json | 2 +- linker.ld | 5 +- src/main.rs | 2 + src/panic.rs | 2 +- src/protected/Cargo.lock | 56 ++++++ src/protected/Cargo.toml | 10 + src/protected/i386-unknown-none.json | 21 +++ src/protected/stage_3/Cargo.toml | 14 ++ src/protected/stage_3/src/lib.rs | 17 ++ src/protected/stage_3/src/panic.rs | 12 ++ src/real/Cargo.lock | 16 +- ...ode.json => i386-unknown-none-code16.json} | 0 src/real/linker.ld | 56 ------ src/real/stage_2/Cargo.toml | 3 - src/real/stage_2/src/lib.rs | 36 ++-- src/shared/Cargo.toml | 6 + .../src/{console.rs => console/bios.rs} | 0 src/shared/src/console/mod.rs | 18 ++ src/shared/src/console/vga.rs | 171 ++++++++++++++++++ src/shared/src/structures/gdt.rs | 13 +- 24 files changed, 412 insertions(+), 98 deletions(-) create mode 100644 src/protected/Cargo.lock create mode 100644 src/protected/Cargo.toml create mode 100644 src/protected/i386-unknown-none.json create mode 100644 src/protected/stage_3/Cargo.toml create mode 100644 src/protected/stage_3/src/lib.rs create mode 100644 src/protected/stage_3/src/panic.rs rename src/real/{i8086-real_mode.json => i386-unknown-none-code16.json} (100%) delete mode 100644 src/real/linker.ld rename src/shared/src/{console.rs => console/bios.rs} (100%) create mode 100644 src/shared/src/console/mod.rs create mode 100644 src/shared/src/console/vga.rs diff --git a/.cargo/config b/.cargo/config index d0601978..5aad35a7 100644 --- a/.cargo/config +++ b/.cargo/config @@ -2,4 +2,4 @@ target = "i8086-bootloader.json" [alias] -xbuild = "build -Zbuild-std=core" +bbuild = "build -Zbuild-std=core" diff --git a/Cargo.lock b/Cargo.lock index d7071791..c317483b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,19 +17,50 @@ name = "bootloader" version = "0.1.0" dependencies = [ "llvm-tools", + "rlibc", "shared", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + [[package]] name = "llvm-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" +[[package]] +name = "rlibc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" + [[package]] name = "shared" version = "0.1.0" dependencies = [ "bit_field", "bitflags", + "lazy_static", + "spin", + "volatile", ] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "volatile" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29" diff --git a/Cargo.toml b/Cargo.toml index 6247b4fd..4b488a40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,12 @@ name = "bootloader" version = "0.1.0" authors = ["Ryland Morgan "] edition = "2018" +description = "An experimental bootloader, written in Rust" build = "build.rs" [dependencies] shared = { path = "src/shared" } +rlibc = "1" [build-dependencies] llvm-tools = "0.1" \ No newline at end of file diff --git a/build.rs b/build.rs index d3dad55a..00a850a8 100644 --- a/build.rs +++ b/build.rs @@ -26,6 +26,17 @@ fn main() { .tool(&exe("llvm-objcopy")) .expect("llvm-objcopy not found"); + // Build stage 3 + build_subproject( + Path::new("src/protected/stage_3"), + &["third_stage"], + "../i386-unknown-none.json", + &target_dir, + &objcopy, + &cargo, + ); + + // Build the bootsector build_subproject( Path::new("src/real/bootsector"), @@ -35,7 +46,7 @@ fn main() { "no_int13h_extensions", "dap_load_failed", ], - "../i8086-real_mode.json", + "../i386-unknown-none-code16.json", &target_dir, &objcopy, &cargo, @@ -45,7 +56,7 @@ fn main() { build_subproject( Path::new("src/real/stage_2"), &["second_stage"], - "../i8086-real_mode.json", + "../i386-unknown-none-code16.json", &target_dir, &objcopy, &cargo, diff --git a/i8086-bootloader.json b/i8086-bootloader.json index be7172b1..b818ae42 100644 --- a/i8086-bootloader.json +++ b/i8086-bootloader.json @@ -20,7 +20,7 @@ "eliminate_frame_pointer": true, "pre-link-args": { "ld.lld": [ - "--script=src/real/linker.ld" + "--script=linker.ld" ] } } \ No newline at end of file diff --git a/linker.ld b/linker.ld index c7814ba0..3e10fc34 100644 --- a/linker.ld +++ b/linker.ld @@ -24,10 +24,7 @@ SECTIONS { .rest_of_bootloader : { _rest_of_bootloader_start = .; - - *stage_2* - *core* - + *(.text .text.*) *(.data .data.*) *(.rodata .rodata.*) diff --git a/src/main.rs b/src/main.rs index 606b78e3..395e7d2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,5 +3,7 @@ mod panic; +extern crate rlibc; + #[no_mangle] fn bootloader_no_optimize() {} diff --git a/src/panic.rs b/src/panic.rs index bf32b99b..96c4c9a9 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -4,7 +4,7 @@ use shared::utils; #[panic_handler] pub fn panic(info: &PanicInfo) -> ! { - println!("[Panic]"); + println!("[Panic] {}", info); loop { utils::hlt() diff --git a/src/protected/Cargo.lock b/src/protected/Cargo.lock new file mode 100644 index 00000000..c62b4662 --- /dev/null +++ b/src/protected/Cargo.lock @@ -0,0 +1,56 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bit_field" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "shared" +version = "0.1.0" +dependencies = [ + "bit_field", + "bitflags", + "lazy_static", + "spin", + "volatile", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "stage_3" +version = "0.1.0" +dependencies = [ + "shared", +] + +[[package]] +name = "v86" +version = "0.1.0" + +[[package]] +name = "volatile" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29" diff --git a/src/protected/Cargo.toml b/src/protected/Cargo.toml new file mode 100644 index 00000000..53289326 --- /dev/null +++ b/src/protected/Cargo.toml @@ -0,0 +1,10 @@ +[workspace] +members = [ + "v86", + "stage_3" +] + +[profile.release] +opt-level = "z" +panic = "abort" +lto = true \ No newline at end of file diff --git a/src/protected/i386-unknown-none.json b/src/protected/i386-unknown-none.json new file mode 100644 index 00000000..38796c83 --- /dev/null +++ b/src/protected/i386-unknown-none.json @@ -0,0 +1,21 @@ +{ + "arch": "x86", + "cpu": "i386", + "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", + "dynamic-linking": false, + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "llvm-target": "i386-unknown-none", + "max-atomic-width": 64, + "position-independent-executables": false, + "disable-redzone": true, + "target-c-int-width": "32", + "target-pointer-width": "32", + "target-endian": "little", + "panic-strategy": "abort", + "os": "none", + "vendor": "unknown", + "relocation_model": "static", + "eliminate_frame_pointer": true +} \ No newline at end of file diff --git a/src/protected/stage_3/Cargo.toml b/src/protected/stage_3/Cargo.toml new file mode 100644 index 00000000..d82b3370 --- /dev/null +++ b/src/protected/stage_3/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "stage_3" +version = "0.1.0" +authors = ["Ryland Morgan "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "stage_3" +crate-type = ["staticlib"] + +[dependencies] +shared = { path = "../../shared" } \ No newline at end of file diff --git a/src/protected/stage_3/src/lib.rs b/src/protected/stage_3/src/lib.rs new file mode 100644 index 00000000..fff5039f --- /dev/null +++ b/src/protected/stage_3/src/lib.rs @@ -0,0 +1,17 @@ +#![no_std] +#![feature(llvm_asm)] + +use shared::println; + +mod panic; + +#[no_mangle] +pub extern "C" fn third_stage() { + unsafe { + llvm_asm!("mov bx, 0x0 + mov ds, bx + mov es, bx" ::: "bx" : "intel", "volatile"); + } + + println!("Stage 3"); +} \ No newline at end of file diff --git a/src/protected/stage_3/src/panic.rs b/src/protected/stage_3/src/panic.rs new file mode 100644 index 00000000..96c4c9a9 --- /dev/null +++ b/src/protected/stage_3/src/panic.rs @@ -0,0 +1,12 @@ +use core::panic::PanicInfo; +use shared::println; +use shared::utils; + +#[panic_handler] +pub fn panic(info: &PanicInfo) -> ! { + println!("[Panic] {}", info); + + loop { + utils::hlt() + } +} diff --git a/src/real/Cargo.lock b/src/real/Cargo.lock index f3bd2eb8..f4e0e87c 100644 --- a/src/real/Cargo.lock +++ b/src/real/Cargo.lock @@ -28,18 +28,15 @@ dependencies = [ "spin", ] -[[package]] -name = "llvm-tools" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" - [[package]] name = "shared" version = "0.1.0" dependencies = [ "bit_field", "bitflags", + "lazy_static", + "spin", + "volatile", ] [[package]] @@ -53,6 +50,11 @@ name = "stage_2" version = "0.1.0" dependencies = [ "lazy_static", - "llvm-tools", "shared", ] + +[[package]] +name = "volatile" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29" diff --git a/src/real/i8086-real_mode.json b/src/real/i386-unknown-none-code16.json similarity index 100% rename from src/real/i8086-real_mode.json rename to src/real/i386-unknown-none-code16.json diff --git a/src/real/linker.ld b/src/real/linker.ld deleted file mode 100644 index 4ea6cc0b..00000000 --- a/src/real/linker.ld +++ /dev/null @@ -1,56 +0,0 @@ -ENTRY(_start) - -SECTIONS { - . = 0x500; - _stack_start = .; - . = 0x7c00; - _stack_end = .; - - .bootsector : - { - _bootloader_start = .; - - *(.bootstrap) - *bootsector* - *core* - } - - /* We write the magic number in the linker script to cause a link-time error if the bootloader overflows the 512 byte limit */ - . = 0x7c00 + 510; - .magic : - { - SHORT(0xaa55) - } - - .rest_of_bootloader : - { - _rest_of_bootloader_start = .; - - *stage_2* - *core* - - *(.text .text.*) - *(.data .data.*) - *(.rodata .rodata.*) - *(.bss .bss.*) - *(.got .got.plt) - - . = ALIGN(512); - - _rest_of_bootloader_end = .; - } - - /* Cause link time error if bootloader overflows 1MB limit */ - . = 0x100000; - - _bootloader_end = .; - - _protected_mode_stack_start = .; - . += 0x1000; - _protected_mode_stack_end = .; - - /DISCARD/ : - { - *(.eh_frame) - } -} \ No newline at end of file diff --git a/src/real/stage_2/Cargo.toml b/src/real/stage_2/Cargo.toml index 6a9a1f62..a897522a 100644 --- a/src/real/stage_2/Cargo.toml +++ b/src/real/stage_2/Cargo.toml @@ -16,6 +16,3 @@ shared = { path = "../../shared" } [dependencies.lazy_static] version = "1.0" features = ["spin_no_std"] - -[build-dependencies] -llvm-tools = "0.1.1" diff --git a/src/real/stage_2/src/lib.rs b/src/real/stage_2/src/lib.rs index 5b824340..c579423e 100644 --- a/src/real/stage_2/src/lib.rs +++ b/src/real/stage_2/src/lib.rs @@ -12,6 +12,10 @@ use lazy_static::lazy_static; mod panic; +extern "C" { + fn third_stage(); +} + lazy_static! { static ref TSS: TaskStateSegment = { let mut tss = TaskStateSegment::new(); @@ -23,6 +27,7 @@ lazy_static! { static ref GDT: GlobalDescriptorTable = { let mut gdt = GlobalDescriptorTable::new(); gdt.add_entry(Descriptor::kernel_code_segment()); + gdt.add_entry(Descriptor::kernel_data_segment()); gdt.add_entry(Descriptor::user_code_segment()); gdt.add_entry(Descriptor::user_data_segment()); @@ -36,44 +41,33 @@ lazy_static! { pub fn second_stage() { println!("Stage 2"); - loop {} + enter_protected_mode(); + + loop {}; } fn enter_protected_mode() { - println!("Loading GDT"); - unsafe { GDT.load(); } - println!("GDT Loaded!"); - - println!("Switching to 32-bit"); + println!("Switching to Protected Mode"); enable_a20(); - println!("A20"); - - loop {} + println!("A20 On"); unsafe { - llvm_asm!("mov eax, cr0 + llvm_asm!("cli + + mov eax, cr0 or al, 1 mov cr0, eax - mov bx, 0x10 - mov ds, bx - mov es, bx - - jmp protected_mode" ::: "eax", "bx" : "intel", "volatile"); + jmp third_stage" ::: "eax" : "intel", "volatile"); } -} - -#[no_mangle] -extern "C" fn protected_mode() { - println!("Protected Mode!"); - loop {} + unreachable!(); } fn enable_a20() { diff --git a/src/shared/Cargo.toml b/src/shared/Cargo.toml index 3f26483d..c5629353 100644 --- a/src/shared/Cargo.toml +++ b/src/shared/Cargo.toml @@ -7,3 +7,9 @@ edition = "2018" [dependencies] bitflags = "1.2.1" bit_field = "0.10.0" +volatile = "0.2" +spin = "0.5" + +[dependencies.lazy_static] +version = "1" +features = ["spin_no_std"] \ No newline at end of file diff --git a/src/shared/src/console.rs b/src/shared/src/console/bios.rs similarity index 100% rename from src/shared/src/console.rs rename to src/shared/src/console/bios.rs diff --git a/src/shared/src/console/mod.rs b/src/shared/src/console/mod.rs new file mode 100644 index 00000000..7d16639d --- /dev/null +++ b/src/shared/src/console/mod.rs @@ -0,0 +1,18 @@ +// BIOS Interrupts + +/* +#[cfg(target_triple = "i386-unknown-none-code16")] +#[cfg_attr(target_triple = "i386-unknown-none-code16", macro_use)] +pub mod bios; + +#[cfg(target_triple = "i386-unknown-none-code16")] +pub use self::bios::*; +*/ + +// VGA Buffer +//#[cfg(target_triple = "i386-unknown-none-code16")] +//#[cfg_attr(target_triple = "i386-unknown-none-code16", macro_use)] +pub mod vga; + +//#[cfg(target_triple = "i386-unknown-none")] +pub use self::vga::*; \ No newline at end of file diff --git a/src/shared/src/console/vga.rs b/src/shared/src/console/vga.rs new file mode 100644 index 00000000..96ccdbb4 --- /dev/null +++ b/src/shared/src/console/vga.rs @@ -0,0 +1,171 @@ +use core::fmt; +use lazy_static::lazy_static; +use spin::Mutex; +use volatile::Volatile; + +lazy_static! { + /// A global `Writer` instance that can be used for printing to the VGA text buffer. + /// + /// Used by the `print!` and `println!` macros. + pub static ref WRITER: Mutex = Mutex::new(Writer { + column_position: 0, + color_code: ColorCode::new(Color::Yellow, Color::Black), + buffer: unsafe { &mut *(0xb8000 as *mut Buffer) }, + }); +} + +/// The standard color palette in VGA text mode. +#[allow(dead_code)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum Color { + Black = 0, + Blue = 1, + Green = 2, + Cyan = 3, + Red = 4, + Magenta = 5, + Brown = 6, + LightGray = 7, + DarkGray = 8, + LightBlue = 9, + LightGreen = 10, + LightCyan = 11, + LightRed = 12, + Pink = 13, + Yellow = 14, + White = 15, +} + +/// A combination of a foreground and a background color. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +struct ColorCode(u8); + +impl ColorCode { + /// Create a new `ColorCode` with the given foreground and background colors. + fn new(foreground: Color, background: Color) -> ColorCode { + ColorCode((background as u8) << 4 | (foreground as u8)) + } +} + +/// A screen character in the VGA text buffer, consisting of an ASCII character and a `ColorCode`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +struct ScreenChar { + ascii_character: u8, + color_code: ColorCode, +} + +/// The height of the text buffer (normally 25 lines). +const BUFFER_HEIGHT: usize = 25; +/// The width of the text buffer (normally 80 columns). +const BUFFER_WIDTH: usize = 80; + +/// A structure representing the VGA text buffer. +#[repr(transparent)] +struct Buffer { + chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT], +} + +/// A writer type that allows writing ASCII bytes and strings to an underlying `Buffer`. +/// +/// Wraps lines at `BUFFER_WIDTH`. Supports newline characters and implements the +/// `core::fmt::Write` trait. +pub struct Writer { + column_position: usize, + color_code: ColorCode, + buffer: &'static mut Buffer, +} + +impl Writer { + /// Writes an ASCII byte to the buffer. + /// + /// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. + pub fn write_byte(&mut self, byte: u8) { + match byte { + b'\n' => self.new_line(), + byte => { + if self.column_position >= BUFFER_WIDTH { + self.new_line(); + } + + let row = BUFFER_HEIGHT - 1; + let col = self.column_position; + + let color_code = self.color_code; + self.buffer.chars[row][col].write(ScreenChar { + ascii_character: byte, + color_code, + }); + self.column_position += 1; + } + } + } + + /// Writes the given ASCII string to the buffer. + /// + /// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. Does **not** + /// support strings with non-ASCII characters, since they can't be printed in the VGA text + /// mode. + fn write_string(&mut self, s: &str) { + for byte in s.bytes() { + match byte { + // printable ASCII byte or newline + 0x20..=0x7e | b'\n' => self.write_byte(byte), + // not part of printable ASCII range + _ => self.write_byte(0xfe), + } + } + } + + /// Shifts all lines one line up and clears the last row. + fn new_line(&mut self) { + for row in 1..BUFFER_HEIGHT { + for col in 0..BUFFER_WIDTH { + let character = self.buffer.chars[row][col].read(); + self.buffer.chars[row - 1][col].write(character); + } + } + self.clear_row(BUFFER_HEIGHT - 1); + self.column_position = 0; + } + + /// Clears a row by overwriting it with blank characters. + fn clear_row(&mut self, row: usize) { + let blank = ScreenChar { + ascii_character: b' ', + color_code: self.color_code, + }; + for col in 0..BUFFER_WIDTH { + self.buffer.chars[row][col].write(blank); + } + } +} + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_string(s); + Ok(()) + } +} + +/// Like the `print!` macro in the standard library, but prints to the VGA text buffer. +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::console::vga::_print(format_args!($($arg)*))); +} + +/// Like the `println!` macro in the standard library, but prints to the VGA text buffer. +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); +} + +/// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance. +#[doc(hidden)] +pub fn _print(args: fmt::Arguments) { + use core::fmt::Write; + WRITER.lock().write_fmt(args).unwrap(); +} \ No newline at end of file diff --git a/src/shared/src/structures/gdt.rs b/src/shared/src/structures/gdt.rs index 6b702da6..b3d2d0e9 100644 --- a/src/shared/src/structures/gdt.rs +++ b/src/shared/src/structures/gdt.rs @@ -23,6 +23,7 @@ impl GlobalDescriptorTable { #[inline] pub fn add_entry(&mut self, entry: Descriptor) -> u16 { let index = self.push(entry.0); + index as u16 } @@ -57,6 +58,7 @@ impl GlobalDescriptorTable { let index = self.next_free; self.table[index] = value; self.next_free += 1; + index } else { panic!("GDT full"); @@ -101,12 +103,13 @@ impl Descriptor { let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::READABLE_WRITABLE; + Descriptor(flags.bits()).with_flat_limit() } /// Creates a segment descriptor for a protected mode kernel data segment. #[inline] - pub fn data_segment() -> Descriptor { + pub fn kernel_data_segment() -> Descriptor { use self::DescriptorFlags as Flags; let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE; @@ -159,8 +162,14 @@ impl Descriptor { fn with_flat_limit(mut self) -> Self { // limit_low self.0.set_bits(0..16, 0xffff); + // limit high - self.0.set_bits(48..52, 0xff); + // self.0.set_bits(48..52, 0xff); + self.0.set_bit(48, true); + self.0.set_bit(49, true); + self.0.set_bit(50, true); + self.0.set_bit(51, true); + // granularity self.0 |= DescriptorFlags::GRANULARITY.bits(); From 0f410d5cc416ab88663bb2ac67662707c415a699 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Fri, 1 May 2020 14:14:59 +0100 Subject: [PATCH 53/61] Fix console output --- src/shared/src/{console/vga.rs => console.rs} | 0 src/shared/src/console/bios.rs | 39 ------------------- src/shared/src/console/mod.rs | 18 --------- 3 files changed, 57 deletions(-) rename src/shared/src/{console/vga.rs => console.rs} (100%) delete mode 100644 src/shared/src/console/bios.rs delete mode 100644 src/shared/src/console/mod.rs diff --git a/src/shared/src/console/vga.rs b/src/shared/src/console.rs similarity index 100% rename from src/shared/src/console/vga.rs rename to src/shared/src/console.rs diff --git a/src/shared/src/console/bios.rs b/src/shared/src/console/bios.rs deleted file mode 100644 index 5eb0249c..00000000 --- a/src/shared/src/console/bios.rs +++ /dev/null @@ -1,39 +0,0 @@ -use core::{fmt, fmt::Write}; - -#[macro_export] -macro_rules! print { - ($($arg:tt)*) => {{ - use core::fmt::Write; - let mut writer = $crate::console::Writer {}; - let _ = writer.write_fmt(format_args!($($arg)*)); - }}; -} - -#[macro_export] -macro_rules! println { - () => ($crate::print!("\n")); - ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); -} - -pub struct Writer {} - -impl Write for Writer { - fn write_str(&mut self, s: &str) -> fmt::Result { - for &byte in s.as_bytes() { - let _ = self.write_byte(byte); - } - - Ok(()) - } -} - -impl Writer { - #[inline(always)] - fn write_byte(&mut self, c: u8) { - let ax = u16::from(c) | 0x0e00; - - unsafe { - llvm_asm!("int 0x10" :: "{ax}"(ax), "{bx}"(0) :: "intel", "volatile"); - } - } -} diff --git a/src/shared/src/console/mod.rs b/src/shared/src/console/mod.rs deleted file mode 100644 index 7d16639d..00000000 --- a/src/shared/src/console/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// BIOS Interrupts - -/* -#[cfg(target_triple = "i386-unknown-none-code16")] -#[cfg_attr(target_triple = "i386-unknown-none-code16", macro_use)] -pub mod bios; - -#[cfg(target_triple = "i386-unknown-none-code16")] -pub use self::bios::*; -*/ - -// VGA Buffer -//#[cfg(target_triple = "i386-unknown-none-code16")] -//#[cfg_attr(target_triple = "i386-unknown-none-code16", macro_use)] -pub mod vga; - -//#[cfg(target_triple = "i386-unknown-none")] -pub use self::vga::*; \ No newline at end of file From a91fa97df6ff89951b9395ab1a8582bd41c37671 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Sun, 3 May 2020 20:55:49 +0100 Subject: [PATCH 54/61] Update tree --- .cargo/config | 2 +- src/protected/Cargo.lock | 4 -- src/protected/Cargo.toml | 1 - src/protected/stage_3/src/lib.rs | 10 +-- src/protected/v86/Cargo.toml | 7 --- src/protected/v86/src/lib.rs | 91 --------------------------- src/real/bootsector/src/lib.rs | 13 ++-- src/real/stage_2/src/lib.rs | 55 +++++++++++++--- src/real/stage_2/src/protected_mode.s | 50 +++++++++++++++ src/shared/src/console.rs | 4 +- src/shared/src/structures/gdt.rs | 6 ++ 11 files changed, 112 insertions(+), 131 deletions(-) delete mode 100644 src/protected/v86/Cargo.toml delete mode 100644 src/protected/v86/src/lib.rs create mode 100644 src/real/stage_2/src/protected_mode.s diff --git a/.cargo/config b/.cargo/config index 5aad35a7..d0601978 100644 --- a/.cargo/config +++ b/.cargo/config @@ -2,4 +2,4 @@ target = "i8086-bootloader.json" [alias] -bbuild = "build -Zbuild-std=core" +xbuild = "build -Zbuild-std=core" diff --git a/src/protected/Cargo.lock b/src/protected/Cargo.lock index c62b4662..be62379c 100644 --- a/src/protected/Cargo.lock +++ b/src/protected/Cargo.lock @@ -45,10 +45,6 @@ dependencies = [ "shared", ] -[[package]] -name = "v86" -version = "0.1.0" - [[package]] name = "volatile" version = "0.2.6" diff --git a/src/protected/Cargo.toml b/src/protected/Cargo.toml index 53289326..df8df32c 100644 --- a/src/protected/Cargo.toml +++ b/src/protected/Cargo.toml @@ -1,6 +1,5 @@ [workspace] members = [ - "v86", "stage_3" ] diff --git a/src/protected/stage_3/src/lib.rs b/src/protected/stage_3/src/lib.rs index fff5039f..3212ec44 100644 --- a/src/protected/stage_3/src/lib.rs +++ b/src/protected/stage_3/src/lib.rs @@ -6,12 +6,8 @@ use shared::println; mod panic; #[no_mangle] -pub extern "C" fn third_stage() { - unsafe { - llvm_asm!("mov bx, 0x0 - mov ds, bx - mov es, bx" ::: "bx" : "intel", "volatile"); - } +pub extern "C" fn third_stage() -> ! { + println!("X"); - println!("Stage 3"); + loop {} } \ No newline at end of file diff --git a/src/protected/v86/Cargo.toml b/src/protected/v86/Cargo.toml deleted file mode 100644 index b2ed9448..00000000 --- a/src/protected/v86/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "v86" -version = "0.1.0" -authors = ["Ryland Morgan "] -edition = "2018" - -[dependencies] diff --git a/src/protected/v86/src/lib.rs b/src/protected/v86/src/lib.rs deleted file mode 100644 index 39e3da51..00000000 --- a/src/protected/v86/src/lib.rs +++ /dev/null @@ -1,91 +0,0 @@ -#![no_std] - -// FIXME -#![allow(dead_code, unused_imports)] - -use core::slice; - -const EFLAG_IF: u32 = 0x00000200; -const EFLAG_VM: u32 = 0x00020000; - -/* -pub struct V86 {} - -impl V86 { - unsafe extern "x86-interrupt" fn gpf_handler(frame: &mut idt::InterruptStackFrame, error_code: u64) { - // Calculate the V86 Instruction Pointer and create a slice of it - let instruction_pointer = ((frame.cs << 4) + frame.eip) as *const u16; - let instructions = slice::from_raw_parts(instruction_pointer, 2); - - // Calculate the V86 IVT pointer and create a slice of it - let ivt_pointer = 0 as *const u16; - let ivt = slice::from_raw_parts(ivt_pointer, 1024); - - // Calculate the V86 stack pointer and create a slice of it - let mut stack_pointer = ((frame.ss << 4) + frame.esp) as *mut u16; - - let mut stack = slice::from_raw_parts_mut(stack_pointer, 16); - let mut stack32 = slice::from_raw_parts_mut(stack_pointer as *mut u32, 8); - - // Match the first byte of the instruction - match instructions[0] { - // int - 0xcd => match instructions[1] { - // 0xFF (255) is our V86 monitor interrupt - 0xff => { - // Function should be pushed onto stack first - let function = stack[3]; - - match function { - // Terminate V86 - 0x0 => unimplemented!(), - - // push 0x2000 - stack[6] Size - // push 0x1000 - stack[5] Real Mode Buffer - // push 0x100000 - stack[4] Protected Mode Buffer - // push 0x1 - stack[3] Function - - // Copy data into protected mode address space - 0x1 => { - // We read buffer addresses and size from - let destination_pointer = stack[4] as *mut u32; - let source_pointer = stack[5] as *const u32; - let size = stack[6]; - - let source = slice::from_raw_parts(source_pointer, size); - let mut destination = slice::from_raw_parts_mut(destination_pointer, size); - - destination.clone_from_slice(source); - }, - _ => panic!("Invalid V86 Monitor Function") - }; - }, - _ => { - // All other interrupt vectors are processed by the real mode handlers - stack_pointer = stack_pointer.offset(3); - frame.esp = ((frame.esp & 0xffff) - 6) & 0xffff; - - // Store the next instructions EIP and code segment onto the stack - stack[0] = (frame.eip + 2) as usize; - stack32[1] = frame.cs; - - // Store the EFlags onto the stack - stack[2] = frame.eflags as usize; - - // Set the CS and EIP to the real mode interrupt handler - frame.cs = ivt[(instructions[1] * 2 + 1) as usize] as u32; - frame.eip = ivt[(instructions[1] * 2) as usize] as u32; - } - }, - // iret - 0xcf => { - frame.eip = stack32[0]; - frame.cs = stack32[1]; - frame.eflags = EFLAG_IF | EFLAG_VM | stack32[2]; - - frame.esp = ((frame.esp & 0xffff) + 6) & 0xffff; - }, - _ => panic!("Unhandled GPF") - }; - } -}*/ diff --git a/src/real/bootsector/src/lib.rs b/src/real/bootsector/src/lib.rs index 346a64df..c4d2f9c6 100644 --- a/src/real/bootsector/src/lib.rs +++ b/src/real/bootsector/src/lib.rs @@ -10,7 +10,7 @@ use core::panic::PanicInfo; use shared::{dap, linker_symbol, utils}; extern "C" { - fn second_stage(); + fn second_stage() -> !; } global_asm!(include_str!("bootstrap.s")); @@ -26,13 +26,10 @@ extern "C" fn rust_start(disk_number: u16) -> ! { linker_symbol!(_rest_of_bootloader_end) - linker_symbol!(_rest_of_bootloader_start), ); - unsafe { dap.perform_load(disk_number) }; - - unsafe { second_stage() }; - - loop { - utils::hlt(); - } + unsafe { + dap.perform_load(disk_number); + second_stage(); + }; } fn check_int13h_extensions(disk_number: u16) { diff --git a/src/real/stage_2/src/lib.rs b/src/real/stage_2/src/lib.rs index c579423e..26917c67 100644 --- a/src/real/stage_2/src/lib.rs +++ b/src/real/stage_2/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(llvm_asm)] +#![feature(global_asm, llvm_asm)] #![no_std] // FIXME @@ -13,9 +13,11 @@ use lazy_static::lazy_static; mod panic; extern "C" { - fn third_stage(); + fn protected_mode_switch() -> !; } +global_asm!(include_str!("protected_mode.s")); + lazy_static! { static ref TSS: TaskStateSegment = { let mut tss = TaskStateSegment::new(); @@ -26,6 +28,7 @@ lazy_static! { }; static ref GDT: GlobalDescriptorTable = { let mut gdt = GlobalDescriptorTable::new(); + gdt.add_entry(Descriptor::kernel_code_segment()); gdt.add_entry(Descriptor::kernel_data_segment()); gdt.add_entry(Descriptor::user_code_segment()); @@ -38,15 +41,19 @@ lazy_static! { } #[no_mangle] -pub fn second_stage() { +pub fn second_stage() -> ! { println!("Stage 2"); - enter_protected_mode(); - - loop {}; + unsafe { + //GDT.load(); + + println!("Switching to Protected Mode"); + + protected_mode_switch(); + } } -fn enter_protected_mode() { +fn enter_protected_mode() -> ! { unsafe { GDT.load(); } @@ -57,14 +64,42 @@ fn enter_protected_mode() { println!("A20 On"); + unsafe { - llvm_asm!("cli + llvm_asm!("cli" :::: "intel", "volatile"); + } + + println!("Interrupts off"); - mov eax, cr0 + let ds: u16; + let es: u16; + + unsafe { + llvm_asm!("mov ax, ds + mov bx, es" + : "={ax}"(ds), "={bx}"(es) + ::: "intel", "volatile"); + } + + println!("Segments stored"); + + unsafe { + llvm_asm!("mov bx, 0x0 + mov ds, bx + mov es, bx" ::: "bx" : "intel", "volatile"); + } + + println!("Segments set"); + + unsafe { + llvm_asm!("mov eax, cr0 or al, 1 mov cr0, eax - jmp third_stage" ::: "eax" : "intel", "volatile"); + push dx + push cx + + jmp third_stage" :: "{dx}"(ds), "{cx}"(es) :: "intel", "volatile"); } unreachable!(); diff --git a/src/real/stage_2/src/protected_mode.s b/src/real/stage_2/src/protected_mode.s new file mode 100644 index 00000000..d11eb8b6 --- /dev/null +++ b/src/real/stage_2/src/protected_mode.s @@ -0,0 +1,50 @@ +.intel_syntax noprefix +.code16 + +protected_mode_switch: + cli + + lgdt [gdt32info] + + mov eax, cr0 + or al, 1 + mov cr0, eax + + push 0x8 + lea eax, [protected_mode] + push eax + retf + +protected_mode: + mov bx, 0x10 + mov ds, bx + mov es, bx + + jmp third_stage + +gdt32info: + .word gdt32_end - gdt32 - 1 # last byte in table + .word gdt32 # start of table + +gdt32: + # entry 0 is always unused + .quad 0 +codedesc: + .byte 0xff + .byte 0xff + .byte 0 + .byte 0 + .byte 0 + .byte 0x9a + .byte 0xcc + .byte 0 +datadesc: + .byte 0xff + .byte 0xff + .byte 0 + .byte 0 + .byte 0 + .byte 0x92 + .byte 0xcc + .byte 0 +gdt32_end: \ No newline at end of file diff --git a/src/shared/src/console.rs b/src/shared/src/console.rs index 96ccdbb4..5df146cb 100644 --- a/src/shared/src/console.rs +++ b/src/shared/src/console.rs @@ -9,7 +9,7 @@ lazy_static! { /// Used by the `print!` and `println!` macros. pub static ref WRITER: Mutex = Mutex::new(Writer { column_position: 0, - color_code: ColorCode::new(Color::Yellow, Color::Black), + color_code: ColorCode::new(Color::Red, Color::Black), buffer: unsafe { &mut *(0xb8000 as *mut Buffer) }, }); } @@ -153,7 +153,7 @@ impl fmt::Write for Writer { /// Like the `print!` macro in the standard library, but prints to the VGA text buffer. #[macro_export] macro_rules! print { - ($($arg:tt)*) => ($crate::console::vga::_print(format_args!($($arg)*))); + ($($arg:tt)*) => ($crate::console::_print(format_args!($($arg)*))); } /// Like the `println!` macro in the standard library, but prints to the VGA text buffer. diff --git a/src/shared/src/structures/gdt.rs b/src/shared/src/structures/gdt.rs index b3d2d0e9..174aa226 100644 --- a/src/shared/src/structures/gdt.rs +++ b/src/shared/src/structures/gdt.rs @@ -96,6 +96,12 @@ bitflags! { } impl Descriptor { + /// Creates a null descriptor + #[inline] + pub fn null_descriptor() -> Descriptor { + Descriptor(0) + } + /// Creates a segment descriptor for a protected mode kernel code segment. #[inline] pub fn kernel_code_segment() -> Descriptor { From 1ec9e6c94e1650bd2d652066614d976e02a08a38 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Mon, 15 Jun 2020 18:20:53 +0100 Subject: [PATCH 55/61] Fix GDT, among other things Currently in a bootable state. I haven't worked on this project in a while, so many of the fixes in this commit I have forgotten. Sorry! --- src/protected/Cargo.toml | 3 +- src/protected/stage_3/src/lib.rs | 2 +- src/real/Cargo.toml | 3 ++ src/real/bootsector/src/lib.rs | 2 +- src/real/stage_2/src/lib.rs | 69 +++----------------------- src/real/stage_2/src/protected_mode.s | 33 ++---------- src/shared/Cargo.toml | 8 ++- src/shared/src/console.rs | 2 +- src/shared/src/instructions/assembly.s | 3 ++ src/shared/src/instructions/mod.rs | 5 ++ src/shared/src/lib.rs | 5 +- src/shared/src/structures/gdt.rs | 15 ++++-- 12 files changed, 46 insertions(+), 104 deletions(-) create mode 100644 src/shared/src/instructions/assembly.s create mode 100644 src/shared/src/instructions/mod.rs diff --git a/src/protected/Cargo.toml b/src/protected/Cargo.toml index df8df32c..3b674689 100644 --- a/src/protected/Cargo.toml +++ b/src/protected/Cargo.toml @@ -6,4 +6,5 @@ members = [ [profile.release] opt-level = "z" panic = "abort" -lto = true \ No newline at end of file +lto = true +debug = true \ No newline at end of file diff --git a/src/protected/stage_3/src/lib.rs b/src/protected/stage_3/src/lib.rs index 3212ec44..0a1e2615 100644 --- a/src/protected/stage_3/src/lib.rs +++ b/src/protected/stage_3/src/lib.rs @@ -7,7 +7,7 @@ mod panic; #[no_mangle] pub extern "C" fn third_stage() -> ! { - println!("X"); + println!("[Bootloader] [32] Stage 3"); loop {} } \ No newline at end of file diff --git a/src/real/Cargo.toml b/src/real/Cargo.toml index c77dbd09..1fc43c67 100644 --- a/src/real/Cargo.toml +++ b/src/real/Cargo.toml @@ -12,3 +12,6 @@ lto = true [profile.release.package.bootsector] opt-level = "s" codegen-units = 1 + +[profile.release.package.stage_2] +debug = true \ No newline at end of file diff --git a/src/real/bootsector/src/lib.rs b/src/real/bootsector/src/lib.rs index c4d2f9c6..74208cf1 100644 --- a/src/real/bootsector/src/lib.rs +++ b/src/real/bootsector/src/lib.rs @@ -16,7 +16,7 @@ global_asm!(include_str!("bootstrap.s")); #[no_mangle] extern "C" fn rust_start(disk_number: u16) -> ! { - real_mode_println(b"Stage 1"); + real_mode_println(b"[Bootloader] [16] Bootsector"); check_int13h_extensions(disk_number); diff --git a/src/real/stage_2/src/lib.rs b/src/real/stage_2/src/lib.rs index 26917c67..b5525e36 100644 --- a/src/real/stage_2/src/lib.rs +++ b/src/real/stage_2/src/lib.rs @@ -1,13 +1,9 @@ #![feature(global_asm, llvm_asm)] #![no_std] -// FIXME -#![allow(dead_code, unused_variables)] - use shared::linker_symbol; use shared::println; use shared::structures::gdt::{Descriptor, GlobalDescriptorTable, TaskStateSegment}; - use lazy_static::lazy_static; mod panic; @@ -16,8 +12,6 @@ extern "C" { fn protected_mode_switch() -> !; } -global_asm!(include_str!("protected_mode.s")); - lazy_static! { static ref TSS: TaskStateSegment = { let mut tss = TaskStateSegment::new(); @@ -26,13 +20,12 @@ lazy_static! { tss }; + static ref GDT: GlobalDescriptorTable = { let mut gdt = GlobalDescriptorTable::new(); gdt.add_entry(Descriptor::kernel_code_segment()); gdt.add_entry(Descriptor::kernel_data_segment()); - gdt.add_entry(Descriptor::user_code_segment()); - gdt.add_entry(Descriptor::user_data_segment()); gdt.add_entry(Descriptor::tss_segment(&TSS)); @@ -40,66 +33,18 @@ lazy_static! { }; } +global_asm!(include_str!("protected_mode.s")); + #[no_mangle] pub fn second_stage() -> ! { - println!("Stage 2"); - - unsafe { - //GDT.load(); - - println!("Switching to Protected Mode"); - - protected_mode_switch(); - } -} - -fn enter_protected_mode() -> ! { - unsafe { - GDT.load(); - } - - println!("Switching to Protected Mode"); + println!("[Bootloader] [16] Stage 2"); enable_a20(); - println!("A20 On"); - - - unsafe { - llvm_asm!("cli" :::: "intel", "volatile"); - } - - println!("Interrupts off"); - - let ds: u16; - let es: u16; - - unsafe { - llvm_asm!("mov ax, ds - mov bx, es" - : "={ax}"(ds), "={bx}"(es) - ::: "intel", "volatile"); - } - - println!("Segments stored"); - unsafe { - llvm_asm!("mov bx, 0x0 - mov ds, bx - mov es, bx" ::: "bx" : "intel", "volatile"); - } - - println!("Segments set"); - - unsafe { - llvm_asm!("mov eax, cr0 - or al, 1 - mov cr0, eax - - push dx - push cx - - jmp third_stage" :: "{dx}"(ds), "{cx}"(es) :: "intel", "volatile"); + GDT.load(); + + protected_mode_switch(); } unreachable!(); diff --git a/src/real/stage_2/src/protected_mode.s b/src/real/stage_2/src/protected_mode.s index d11eb8b6..45d89f39 100644 --- a/src/real/stage_2/src/protected_mode.s +++ b/src/real/stage_2/src/protected_mode.s @@ -4,8 +4,6 @@ protected_mode_switch: cli - lgdt [gdt32info] - mov eax, cr0 or al, 1 mov cr0, eax @@ -15,36 +13,11 @@ protected_mode_switch: push eax retf +.code32 protected_mode: mov bx, 0x10 + mov ds, bx mov es, bx - jmp third_stage - -gdt32info: - .word gdt32_end - gdt32 - 1 # last byte in table - .word gdt32 # start of table - -gdt32: - # entry 0 is always unused - .quad 0 -codedesc: - .byte 0xff - .byte 0xff - .byte 0 - .byte 0 - .byte 0 - .byte 0x9a - .byte 0xcc - .byte 0 -datadesc: - .byte 0xff - .byte 0xff - .byte 0 - .byte 0 - .byte 0 - .byte 0x92 - .byte 0xcc - .byte 0 -gdt32_end: \ No newline at end of file + jmp third_stage \ No newline at end of file diff --git a/src/shared/Cargo.toml b/src/shared/Cargo.toml index c5629353..b82dbe60 100644 --- a/src/shared/Cargo.toml +++ b/src/shared/Cargo.toml @@ -12,4 +12,10 @@ spin = "0.5" [dependencies.lazy_static] version = "1" -features = ["spin_no_std"] \ No newline at end of file +features = ["spin_no_std"] + +[profile.release] +debug = true +opt-level = "z" +panic = "abort" +lto = true diff --git a/src/shared/src/console.rs b/src/shared/src/console.rs index 5df146cb..530d48ea 100644 --- a/src/shared/src/console.rs +++ b/src/shared/src/console.rs @@ -9,7 +9,7 @@ lazy_static! { /// Used by the `print!` and `println!` macros. pub static ref WRITER: Mutex = Mutex::new(Writer { column_position: 0, - color_code: ColorCode::new(Color::Red, Color::Black), + color_code: ColorCode::new(Color::LightGray, Color::Black), buffer: unsafe { &mut *(0xb8000 as *mut Buffer) }, }); } diff --git a/src/shared/src/instructions/assembly.s b/src/shared/src/instructions/assembly.s new file mode 100644 index 00000000..46639caf --- /dev/null +++ b/src/shared/src/instructions/assembly.s @@ -0,0 +1,3 @@ +.intel_syntax noprefix + +retf: retf \ No newline at end of file diff --git a/src/shared/src/instructions/mod.rs b/src/shared/src/instructions/mod.rs new file mode 100644 index 00000000..774ce9d9 --- /dev/null +++ b/src/shared/src/instructions/mod.rs @@ -0,0 +1,5 @@ +global_asm!(include_str!("assembly.s")); + +extern "C" { + pub fn retf(cs: u32, eip: u32); +} \ No newline at end of file diff --git a/src/shared/src/lib.rs b/src/shared/src/lib.rs index 46645305..ce897264 100644 --- a/src/shared/src/lib.rs +++ b/src/shared/src/lib.rs @@ -1,6 +1,6 @@ #![feature(abi_x86_interrupt)] #![feature(const_fn)] -#![feature(llvm_asm)] +#![feature(llvm_asm, global_asm)] #![no_std] pub mod console; @@ -8,4 +8,5 @@ pub mod dap; pub mod utils; #[macro_use] pub mod macros; -pub mod structures; \ No newline at end of file +pub mod structures; +pub mod instructions; \ No newline at end of file diff --git a/src/shared/src/structures/gdt.rs b/src/shared/src/structures/gdt.rs index 174aa226..8ac4696f 100644 --- a/src/shared/src/structures/gdt.rs +++ b/src/shared/src/structures/gdt.rs @@ -1,9 +1,11 @@ use bit_field::BitField; use bitflags::bitflags; +use crate::println; + #[derive(Debug, Clone)] pub struct GlobalDescriptorTable { - table: [u64; 8], + pub table: [u64; 8], next_free: usize, } @@ -30,7 +32,7 @@ impl GlobalDescriptorTable { /// Loads the GDT in the CPU using the `lgdt` instruction. This does **not** alter any of the /// segment registers; you **must** (re)load them yourself. #[inline] - pub unsafe fn load(&'static self) { + pub unsafe fn load(&self) { use core::mem::size_of; /// A struct describing a pointer to a descriptor table (GDT / IDT). @@ -90,6 +92,9 @@ bitflags! { /// The DPL for this descriptor is Ring 3 const DPL_RING_3 = 3 << 45; + /// If set, this page is a 32 bit descriptor + const SIZE = 1 << 54; + /// If set, limit is in 4k pages const GRANULARITY = 1 << 55; } @@ -108,7 +113,7 @@ impl Descriptor { use self::DescriptorFlags as Flags; let flags = - Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::READABLE_WRITABLE; + Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::READABLE_WRITABLE | Flags::SIZE; Descriptor(flags.bits()).with_flat_limit() } @@ -118,7 +123,7 @@ impl Descriptor { pub fn kernel_data_segment() -> Descriptor { use self::DescriptorFlags as Flags; - let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE; + let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::SIZE; Descriptor(flags.bits()).with_flat_limit() } @@ -147,7 +152,7 @@ impl Descriptor { /// Creates a TSS system descriptor for the given TSS. #[inline] - pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor { + pub fn tss_segment(tss: &TaskStateSegment) -> Descriptor { use self::DescriptorFlags as Flags; use core::mem::size_of; From 3ad17ea931d1c6ec58a71f3a5ab6771e2be8d986 Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Tue, 16 Jun 2020 11:59:45 +0100 Subject: [PATCH 56/61] Update to use the new `asm!` macro It's much easier to understand and has a simpler syntax. This breaks compatibility with nightlies older than 08/06/2020 (dd/mm/yyyy) --- src/protected/stage_3/src/lib.rs | 8 +++++++- src/real/bootsector/src/console.rs | 5 ++++- src/real/bootsector/src/lib.rs | 13 ++++++++----- src/real/stage_2/src/lib.rs | 11 +++++++---- src/shared/src/dap.rs | 8 +++++--- src/shared/src/lib.rs | 2 +- src/shared/src/macros.rs | 7 ++++--- src/shared/src/structures/gdt.rs | 5 ++++- src/shared/src/structures/idt.rs | 11 +++++++++-- src/shared/src/utils.rs | 2 +- 10 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/protected/stage_3/src/lib.rs b/src/protected/stage_3/src/lib.rs index 0a1e2615..c3eb1ae8 100644 --- a/src/protected/stage_3/src/lib.rs +++ b/src/protected/stage_3/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(llvm_asm)] use shared::println; @@ -9,5 +8,12 @@ mod panic; pub extern "C" fn third_stage() -> ! { println!("[Bootloader] [32] Stage 3"); + unsafe { + let ptr = 0x110000 as *mut u32; + *ptr = 0xdeadbeef; + } + + println!("[Bootloader] [32] > 1MB"); + loop {} } \ No newline at end of file diff --git a/src/real/bootsector/src/console.rs b/src/real/bootsector/src/console.rs index 980b12a3..3a2e8c67 100644 --- a/src/real/bootsector/src/console.rs +++ b/src/real/bootsector/src/console.rs @@ -17,6 +17,9 @@ pub fn print(s: &[u8]) { pub fn print_char(c: u8) { let ax = u16::from(c) | 0x0e00; unsafe { - llvm_asm!("int 0x10" :: "{ax}"(ax) :: "intel" ); + asm!("int 0x10", + in("ax") ax, + options(nostack) + ); } } diff --git a/src/real/bootsector/src/lib.rs b/src/real/bootsector/src/lib.rs index 74208cf1..fbfc13aa 100644 --- a/src/real/bootsector/src/lib.rs +++ b/src/real/bootsector/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(llvm_asm, global_asm)] +#![feature(asm, global_asm)] #![no_std] #![allow(dead_code)] @@ -34,10 +34,13 @@ extern "C" fn rust_start(disk_number: u16) -> ! { fn check_int13h_extensions(disk_number: u16) { unsafe { - llvm_asm!(" - int 0x13 - jc no_int13h_extensions - " :: "{ah}"(0x41), "{bx}"(0x55aa), "{dl}"(disk_number) :: "intel", "volatile"); + asm!(" + int 0x13 + jc no_int13h_extensions", + + in("ax") 0x41, in("bx") 0x55aa, in("dx") disk_number, + options(nostack) + ) } } diff --git a/src/real/stage_2/src/lib.rs b/src/real/stage_2/src/lib.rs index b5525e36..2d2bd377 100644 --- a/src/real/stage_2/src/lib.rs +++ b/src/real/stage_2/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(global_asm, llvm_asm)] +#![feature(global_asm, asm)] #![no_std] use shared::linker_symbol; @@ -52,8 +52,11 @@ pub fn second_stage() -> ! { fn enable_a20() { unsafe { - llvm_asm!("in al, 0x92 - or al, 2 - out 0x92, al" ::: "al" : "intel", "volatile"); + asm!("in {0}, 0x92 + or {0}, 2 + out 0x92, {0}", + out(reg) _, + options(nostack) + ); } } diff --git a/src/shared/src/dap.rs b/src/shared/src/dap.rs index 809f57a5..e529b367 100644 --- a/src/shared/src/dap.rs +++ b/src/shared/src/dap.rs @@ -31,9 +31,11 @@ impl DiskAddressPacket { #[inline(always)] pub unsafe fn perform_load(&self, disk_number: u16) { let self_addr = self as *const Self as u16; - llvm_asm!(" + asm!(" int 0x13 - jc dap_load_failed - " :: "{si}"(self_addr), "{ax}"(0x4200), "{dx}"(disk_number) : "bx" : "intel", "volatile"); + jc dap_load_failed", + in("si") self_addr, in("ax") 0x4200, in("dx") disk_number, out("bx") _, + options(nostack) + ); } } diff --git a/src/shared/src/lib.rs b/src/shared/src/lib.rs index ce897264..63bfcc63 100644 --- a/src/shared/src/lib.rs +++ b/src/shared/src/lib.rs @@ -1,6 +1,6 @@ #![feature(abi_x86_interrupt)] #![feature(const_fn)] -#![feature(llvm_asm, global_asm)] +#![feature(asm, global_asm)] #![no_std] pub mod console; diff --git a/src/shared/src/macros.rs b/src/shared/src/macros.rs index 98b15ba8..1b0f53bf 100644 --- a/src/shared/src/macros.rs +++ b/src/shared/src/macros.rs @@ -3,9 +3,10 @@ macro_rules! linker_symbol { ($symbol_name:ident) => {unsafe { let symbol_value: u32; - llvm_asm!(concat!("lea eax, ", stringify!($symbol_name)) - : "={eax}"(symbol_value) - ::: "intel", "volatile"); + asm!( + concat!("lea {}, ", stringify!($symbol_name)), + out(reg) symbol_value + ); symbol_value }}; diff --git a/src/shared/src/structures/gdt.rs b/src/shared/src/structures/gdt.rs index 8ac4696f..6e13ad34 100644 --- a/src/shared/src/structures/gdt.rs +++ b/src/shared/src/structures/gdt.rs @@ -51,7 +51,10 @@ impl GlobalDescriptorTable { limit: (self.table.len() * size_of::() - 1) as u16, }; - llvm_asm!("lgdt ($0)" :: "r" (&ptr) : "memory"); + asm!("lgdt [{}]", + in(reg) &ptr, + options(nostack) + ); } #[inline] diff --git a/src/shared/src/structures/idt.rs b/src/shared/src/structures/idt.rs index 6555675f..65c92d58 100644 --- a/src/shared/src/structures/idt.rs +++ b/src/shared/src/structures/idt.rs @@ -86,7 +86,10 @@ impl InterruptDescriptorTable { limit: (size_of::() - 1) as u16, }; - llvm_asm!("lidt ($0)" :: "r" (&ptr) : "memory"); + asm!("lidt [{}]", + in(reg) &ptr, + options(nostack) + ); } } @@ -142,7 +145,11 @@ impl Entry { self.offset_high = (addr >> 16) as u16; let segment: u16; - unsafe { llvm_asm!("mov %cs, $0" : "=r" (segment) ) }; + + unsafe { asm!("mov {:x}, cs", + out(reg) segment, + options(nostack, nomem) + ) }; self.gdt_selector = segment; diff --git a/src/shared/src/utils.rs b/src/shared/src/utils.rs index acf789f1..9ad5edc9 100644 --- a/src/shared/src/utils.rs +++ b/src/shared/src/utils.rs @@ -1,6 +1,6 @@ #[inline(always)] pub fn hlt() { unsafe { - llvm_asm!("hlt" :::: "intel","volatile"); + asm!("hlt"); } } From 1e839c9ad6015a36c8856aaeb87f10f1df817c7d Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Sun, 28 Jun 2020 09:40:04 +0100 Subject: [PATCH 57/61] Successfully load TSS --- build.rs | 2 ++ src/panic.rs | 5 ++--- src/protected/stage_3/src/lib.rs | 8 +++++++ src/protected/stage_3/src/panic.rs | 4 ++-- src/real/bootsector/src/errors.rs | 6 ++--- src/real/bootsector/src/lib.rs | 4 ++-- src/real/stage_2/src/lib.rs | 12 +++++++--- src/real/stage_2/src/panic.rs | 4 ++-- src/shared/src/instructions.rs | 31 ++++++++++++++++++++++++++ src/shared/src/instructions/assembly.s | 3 --- src/shared/src/instructions/mod.rs | 5 ----- src/shared/src/lib.rs | 1 - src/shared/src/structures/gdt.rs | 13 ++++++----- src/shared/src/utils.rs | 6 ----- 14 files changed, 68 insertions(+), 36 deletions(-) create mode 100644 src/shared/src/instructions.rs delete mode 100644 src/shared/src/instructions/assembly.s delete mode 100644 src/shared/src/instructions/mod.rs delete mode 100644 src/shared/src/utils.rs diff --git a/build.rs b/build.rs index 00a850a8..cbaca11a 100644 --- a/build.rs +++ b/build.rs @@ -63,6 +63,8 @@ fn main() { ); // Inform cargo that we should rerun this on linker script changes + // + // This is NOT performed by default println!("cargo:rerun-if-changed=linker.ld"); } diff --git a/src/panic.rs b/src/panic.rs index 96c4c9a9..d72cd0d1 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -1,12 +1,11 @@ use core::panic::PanicInfo; -use shared::println; -use shared::utils; +use shared::{println, instructions}; #[panic_handler] pub fn panic(info: &PanicInfo) -> ! { println!("[Panic] {}", info); loop { - utils::hlt() + instructions::hlt() } } diff --git a/src/protected/stage_3/src/lib.rs b/src/protected/stage_3/src/lib.rs index c3eb1ae8..94f091b1 100644 --- a/src/protected/stage_3/src/lib.rs +++ b/src/protected/stage_3/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] use shared::println; +use shared::instructions; mod panic; @@ -15,5 +16,12 @@ pub extern "C" fn third_stage() -> ! { println!("[Bootloader] [32] > 1MB"); + // Load the TSS + unsafe { + instructions::ltr(0x2B) + }; + + println!("[Bootloader] [32] Loaded TSS"); + loop {} } \ No newline at end of file diff --git a/src/protected/stage_3/src/panic.rs b/src/protected/stage_3/src/panic.rs index 96c4c9a9..8e85d28d 100644 --- a/src/protected/stage_3/src/panic.rs +++ b/src/protected/stage_3/src/panic.rs @@ -1,12 +1,12 @@ use core::panic::PanicInfo; use shared::println; -use shared::utils; +use shared::instructions; #[panic_handler] pub fn panic(info: &PanicInfo) -> ! { println!("[Panic] {}", info); loop { - utils::hlt() + instructions::hlt() } } diff --git a/src/real/bootsector/src/errors.rs b/src/real/bootsector/src/errors.rs index 9cc04452..0218239a 100644 --- a/src/real/bootsector/src/errors.rs +++ b/src/real/bootsector/src/errors.rs @@ -1,11 +1,11 @@ use super::console::real_mode_println; -use shared::utils; +use shared::instructions; #[no_mangle] extern "C" fn dap_load_failed() -> ! { real_mode_println(b"[!] DAP Load Failed"); loop { - utils::hlt() + instructions::hlt() } } @@ -13,6 +13,6 @@ extern "C" fn dap_load_failed() -> ! { extern "C" fn no_int13h_extensions() -> ! { real_mode_println(b"[!] No int13h Extensions"); loop { - utils::hlt() + instructions::hlt() } } diff --git a/src/real/bootsector/src/lib.rs b/src/real/bootsector/src/lib.rs index fbfc13aa..017b42fe 100644 --- a/src/real/bootsector/src/lib.rs +++ b/src/real/bootsector/src/lib.rs @@ -7,7 +7,7 @@ mod errors; use self::console::real_mode_println; use core::panic::PanicInfo; -use shared::{dap, linker_symbol, utils}; +use shared::{dap, linker_symbol, instructions}; extern "C" { fn second_stage() -> !; @@ -49,6 +49,6 @@ fn panic(_info: &PanicInfo) -> ! { real_mode_println(b"[Panic]"); loop { - utils::hlt() + instructions::hlt() } } diff --git a/src/real/stage_2/src/lib.rs b/src/real/stage_2/src/lib.rs index 2d2bd377..c0f492c9 100644 --- a/src/real/stage_2/src/lib.rs +++ b/src/real/stage_2/src/lib.rs @@ -24,11 +24,17 @@ lazy_static! { static ref GDT: GlobalDescriptorTable = { let mut gdt = GlobalDescriptorTable::new(); + // Set up kernel segments gdt.add_entry(Descriptor::kernel_code_segment()); gdt.add_entry(Descriptor::kernel_data_segment()); - gdt.add_entry(Descriptor::tss_segment(&TSS)); + // Set up user segments + gdt.add_entry(Descriptor::user_code_segment()); + gdt.add_entry(Descriptor::user_data_segment()); + // Set up the TSS + gdt.add_entry(Descriptor::tss_segment(&*TSS)); + gdt }; } @@ -43,11 +49,11 @@ pub fn second_stage() -> ! { unsafe { GDT.load(); + + println!("[Bootloader] [16] Loaded GDT"); protected_mode_switch(); } - - unreachable!(); } fn enable_a20() { diff --git a/src/real/stage_2/src/panic.rs b/src/real/stage_2/src/panic.rs index 96c4c9a9..8e85d28d 100644 --- a/src/real/stage_2/src/panic.rs +++ b/src/real/stage_2/src/panic.rs @@ -1,12 +1,12 @@ use core::panic::PanicInfo; use shared::println; -use shared::utils; +use shared::instructions; #[panic_handler] pub fn panic(info: &PanicInfo) -> ! { println!("[Panic] {}", info); loop { - utils::hlt() + instructions::hlt() } } diff --git a/src/shared/src/instructions.rs b/src/shared/src/instructions.rs new file mode 100644 index 00000000..cc6a85df --- /dev/null +++ b/src/shared/src/instructions.rs @@ -0,0 +1,31 @@ +/// Performs a retf instruction, jumping to cs:eip +/// +/// # Unsafety +/// We make no guarantees that the cs and eip are valid, nor that they contain executable code +#[inline(always)] +pub unsafe fn retf(cs: u16, eip: u32) { + asm!("push {0:x} + push {1} + retf", + in(reg) cs, in(reg) eip); +} + +/// Loads a new value into the task state register +/// +/// # Unsafety +/// A bad value will cause undefined behaviour +#[inline(always)] +pub unsafe fn ltr(task_state: u16) { + asm!("ltr {0:x}", + in(reg) task_state, + options(nostack) + ); +} + +/// Halts the processor +#[inline(always)] +pub fn hlt() { + unsafe { + asm!("hlt", options(nostack, nomem)); + } +} \ No newline at end of file diff --git a/src/shared/src/instructions/assembly.s b/src/shared/src/instructions/assembly.s deleted file mode 100644 index 46639caf..00000000 --- a/src/shared/src/instructions/assembly.s +++ /dev/null @@ -1,3 +0,0 @@ -.intel_syntax noprefix - -retf: retf \ No newline at end of file diff --git a/src/shared/src/instructions/mod.rs b/src/shared/src/instructions/mod.rs deleted file mode 100644 index 774ce9d9..00000000 --- a/src/shared/src/instructions/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -global_asm!(include_str!("assembly.s")); - -extern "C" { - pub fn retf(cs: u32, eip: u32); -} \ No newline at end of file diff --git a/src/shared/src/lib.rs b/src/shared/src/lib.rs index 63bfcc63..d7d6c960 100644 --- a/src/shared/src/lib.rs +++ b/src/shared/src/lib.rs @@ -5,7 +5,6 @@ pub mod console; pub mod dap; -pub mod utils; #[macro_use] pub mod macros; pub mod structures; diff --git a/src/shared/src/structures/gdt.rs b/src/shared/src/structures/gdt.rs index 6e13ad34..5f48876c 100644 --- a/src/shared/src/structures/gdt.rs +++ b/src/shared/src/structures/gdt.rs @@ -1,8 +1,6 @@ use bit_field::BitField; use bitflags::bitflags; -use crate::println; - #[derive(Debug, Clone)] pub struct GlobalDescriptorTable { pub table: [u64; 8], @@ -77,6 +75,8 @@ pub struct Descriptor(u64); bitflags! { /// Flags for a GDT descriptor. Not all flags are valid for all descriptor types. pub struct DescriptorFlags: u64 { + /// The CPU sets this value to one when the segment is accessed + const ACCESSED = 1 << 40; /// For data segments, this flag sets the segment as writable. For code /// segments, it defines whether the segment is readable. const READABLE_WRITABLE = 1 << 41; @@ -161,14 +161,15 @@ impl Descriptor { let ptr = tss as *const _ as u64; - let mut val = Flags::PRESENT.bits(); + + let mut val: u64 = (Flags::PRESENT | Flags::EXECUTABLE | Flags::ACCESSED | Flags::SIZE).bits(); + // base val.set_bits(16..40, ptr.get_bits(0..24)); val.set_bits(56..64, ptr.get_bits(24..32)); + // limit (the `-1` in needed since the bound is inclusive) - val.set_bits(0..16, (size_of::() - 1) as u64); - // type (0b1001 = available 32-bit tss) - val.set_bits(40..44, 0b1001); + val.set_bits(0..16, ((size_of::() - 1) as u64).get_bits(0..16)); Descriptor(val) } diff --git a/src/shared/src/utils.rs b/src/shared/src/utils.rs deleted file mode 100644 index 9ad5edc9..00000000 --- a/src/shared/src/utils.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[inline(always)] -pub fn hlt() { - unsafe { - asm!("hlt"); - } -} From b287d3f6900686dc05f8659a614f0b10e19e500f Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Sat, 25 Jul 2020 21:26:25 +0100 Subject: [PATCH 58/61] IDT --- linker.ld | 4 +++ src/protected/Cargo.lock | 1 + src/protected/stage_3/Cargo.toml | 6 +++- src/protected/stage_3/src/interrupts.rs | 41 +++++++++++++++++++++++++ src/protected/stage_3/src/lib.rs | 12 +++++++- src/real/stage_2/src/lib.rs | 1 + src/shared/src/structures/idt.rs | 16 +++++----- 7 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 src/protected/stage_3/src/interrupts.rs diff --git a/linker.ld b/linker.ld index 3e10fc34..63515052 100644 --- a/linker.ld +++ b/linker.ld @@ -47,6 +47,10 @@ SECTIONS { /DISCARD/ : { + /* + Weird section that always puts itself at the start and causes the binary to be non-bootable + Only seems to appear on some platforms, and we can find no use of it in our circumstances + */ *(.eh_frame) } } \ No newline at end of file diff --git a/src/protected/Cargo.lock b/src/protected/Cargo.lock index be62379c..e36ab8d9 100644 --- a/src/protected/Cargo.lock +++ b/src/protected/Cargo.lock @@ -42,6 +42,7 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" name = "stage_3" version = "0.1.0" dependencies = [ + "lazy_static", "shared", ] diff --git a/src/protected/stage_3/Cargo.toml b/src/protected/stage_3/Cargo.toml index d82b3370..fbc5784e 100644 --- a/src/protected/stage_3/Cargo.toml +++ b/src/protected/stage_3/Cargo.toml @@ -11,4 +11,8 @@ name = "stage_3" crate-type = ["staticlib"] [dependencies] -shared = { path = "../../shared" } \ No newline at end of file +shared = { path = "../../shared" } + +[dependencies.lazy_static] +version = "1.0" +features = ["spin_no_std"] diff --git a/src/protected/stage_3/src/interrupts.rs b/src/protected/stage_3/src/interrupts.rs new file mode 100644 index 00000000..4751b57d --- /dev/null +++ b/src/protected/stage_3/src/interrupts.rs @@ -0,0 +1,41 @@ +use shared::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; +use crate::println; +use lazy_static::lazy_static; + +lazy_static! { + static ref IDT: InterruptDescriptorTable = { + let mut idt = InterruptDescriptorTable::new(); + + idt.divide_error.set_handler_fn(divide_handler); + idt.breakpoint.set_handler_fn(breakpoint_handler); + //idt.double_fault.set_handler_fn(double_fault_handler); + + idt + }; +} + + +pub fn init_idt() { + // Seems like we have to manually initialize it first for some reason, otherwise it crashes + ::lazy_static::initialize(&IDT); + IDT.load(); +} + +extern "x86-interrupt" fn divide_handler( + stack_frame: &mut InterruptStackFrame) +{ + println!("[Bootloader] [IDT] Divide Exception"); +} + +extern "x86-interrupt" fn breakpoint_handler( + stack_frame: &mut InterruptStackFrame) +{ + println!("[Bootloader] [IDT] Breakpoint Hit"); +} + +/*extern "x86-interrupt" fn double_fault_handler( + stack_frame: &mut InterruptStackFrame, _error_code: u64) -> ! +{ + panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); + loop {}; +}*/ \ No newline at end of file diff --git a/src/protected/stage_3/src/lib.rs b/src/protected/stage_3/src/lib.rs index 94f091b1..60a22e9a 100644 --- a/src/protected/stage_3/src/lib.rs +++ b/src/protected/stage_3/src/lib.rs @@ -1,8 +1,10 @@ #![no_std] +#![feature(abi_x86_interrupt, asm)] use shared::println; use shared::instructions; +mod interrupts; mod panic; #[no_mangle] @@ -23,5 +25,13 @@ pub extern "C" fn third_stage() -> ! { println!("[Bootloader] [32] Loaded TSS"); - loop {} + interrupts::init_idt(); + + println!("[Bootloader] [32] Loaded IDT"); + + unsafe { asm!("int 3") }; + + println!("[Bootloader] [32] It didn't crash!"); + + loop {}; } \ No newline at end of file diff --git a/src/real/stage_2/src/lib.rs b/src/real/stage_2/src/lib.rs index c0f492c9..2075df94 100644 --- a/src/real/stage_2/src/lib.rs +++ b/src/real/stage_2/src/lib.rs @@ -17,6 +17,7 @@ lazy_static! { let mut tss = TaskStateSegment::new(); tss.privilege_stack_table[0].esp = linker_symbol!(_protected_mode_stack_end); + tss.privilege_stack_table[0].ss = 2 * 8; // Kernel data segment is 2nd segment (null, code, data) tss }; diff --git a/src/shared/src/structures/idt.rs b/src/shared/src/structures/idt.rs index 65c92d58..d6228b53 100644 --- a/src/shared/src/structures/idt.rs +++ b/src/shared/src/structures/idt.rs @@ -34,8 +34,8 @@ pub struct InterruptDescriptorTable { impl InterruptDescriptorTable { /// Creates a new IDT filled with non-present entries. #[inline] - pub const fn new() -> InterruptDescriptorTable { - InterruptDescriptorTable { + pub fn new() -> InterruptDescriptorTable { + let idt = InterruptDescriptorTable { divide_error: Entry::missing(), debug: Entry::missing(), non_maskable_interrupt: Entry::missing(), @@ -60,7 +60,9 @@ impl InterruptDescriptorTable { reserved_2: [Entry::missing(); 9], security_exception: Entry::missing(), reserved_3: Entry::missing(), - } + }; + + idt } /// Loads the IDT in the CPU using the `lidt` command. @@ -122,7 +124,7 @@ pub struct Entry { impl Entry { /// Creates a non-present IDT entry (but sets the must-be-one bits). #[inline] - pub const fn missing() -> Self { + pub fn missing() -> Self { Entry { gdt_selector: 0, offset_low: 0, @@ -140,7 +142,7 @@ impl Entry { /// The function returns a mutable reference to the entry's options that allows /// further customization. #[inline] - fn set_handler_addr(&mut self, addr: u32) -> &mut EntryOptions { + pub fn set_handler_addr(&mut self, addr: u32) -> &mut EntryOptions { self.offset_low = addr as u16; self.offset_high = (addr >> 16) as u16; @@ -196,7 +198,7 @@ impl EntryOptions { /// Set or reset the preset bit. #[inline] pub fn set_present(&mut self, present: bool) -> &mut Self { - self.0.set_bit(15, present); + self.0.set_bit(7, present); self } } @@ -213,7 +215,7 @@ pub type DivergingHandlerFuncWithErrCode = extern "x86-interrupt" fn(&mut InterruptStackFrame, error_code: u64) -> !; /// Represents the interrupt stack frame pushed by the CPU on interrupt or exception entry. -#[derive(Clone)] +#[derive(Clone, Debug)] #[repr(C)] pub struct InterruptStackFrame { pub eip: u32, From 0dc16ef554645881b96dd30a4648290cf912753f Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Sat, 25 Jul 2020 21:38:34 +0100 Subject: [PATCH 59/61] Fix error codes (double fault handler added) --- src/protected/stage_3/src/interrupts.rs | 12 ++++++------ src/protected/stage_3/src/lib.rs | 4 ---- src/shared/src/structures/idt.rs | 4 ++-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/protected/stage_3/src/interrupts.rs b/src/protected/stage_3/src/interrupts.rs index 4751b57d..4d16bbe7 100644 --- a/src/protected/stage_3/src/interrupts.rs +++ b/src/protected/stage_3/src/interrupts.rs @@ -8,7 +8,8 @@ lazy_static! { idt.divide_error.set_handler_fn(divide_handler); idt.breakpoint.set_handler_fn(breakpoint_handler); - //idt.double_fault.set_handler_fn(double_fault_handler); + + idt.double_fault.set_handler_fn(double_fault_handler); idt }; @@ -33,9 +34,8 @@ extern "x86-interrupt" fn breakpoint_handler( println!("[Bootloader] [IDT] Breakpoint Hit"); } -/*extern "x86-interrupt" fn double_fault_handler( - stack_frame: &mut InterruptStackFrame, _error_code: u64) -> ! +extern "x86-interrupt" fn double_fault_handler( + stack_frame: &mut InterruptStackFrame, _error_code: u32) -> ! { - panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); - loop {}; -}*/ \ No newline at end of file + panic!("[Bootloader] [IDT] Double Fault!"); +} \ No newline at end of file diff --git a/src/protected/stage_3/src/lib.rs b/src/protected/stage_3/src/lib.rs index 60a22e9a..a931f040 100644 --- a/src/protected/stage_3/src/lib.rs +++ b/src/protected/stage_3/src/lib.rs @@ -29,9 +29,5 @@ pub extern "C" fn third_stage() -> ! { println!("[Bootloader] [32] Loaded IDT"); - unsafe { asm!("int 3") }; - - println!("[Bootloader] [32] It didn't crash!"); - loop {}; } \ No newline at end of file diff --git a/src/shared/src/structures/idt.rs b/src/shared/src/structures/idt.rs index d6228b53..c540b6d2 100644 --- a/src/shared/src/structures/idt.rs +++ b/src/shared/src/structures/idt.rs @@ -207,12 +207,12 @@ impl EntryOptions { pub type HandlerFunc = extern "x86-interrupt" fn(&mut InterruptStackFrame); /// A handler function for an exception that pushes an error code. pub type HandlerFuncWithErrCode = - extern "x86-interrupt" fn(&mut InterruptStackFrame, error_code: u64); + extern "x86-interrupt" fn(&mut InterruptStackFrame, error_code: u32); /// A handler function that must not return, e.g. for a machine check exception. pub type DivergingHandlerFunc = extern "x86-interrupt" fn(&mut InterruptStackFrame) -> !; /// A handler function with an error code that must not return, e.g. for a double fault exception. pub type DivergingHandlerFuncWithErrCode = - extern "x86-interrupt" fn(&mut InterruptStackFrame, error_code: u64) -> !; + extern "x86-interrupt" fn(&mut InterruptStackFrame, error_code: u32) -> !; /// Represents the interrupt stack frame pushed by the CPU on interrupt or exception entry. #[derive(Clone, Debug)] From 5b091a6ceffc836a8018b55579b2ef10768a153e Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Tue, 25 Aug 2020 15:28:52 +0100 Subject: [PATCH 60/61] User Mode working --- build.rs | 4 +-- src/protected/stage_3/src/interrupts.rs | 8 +++++ src/protected/stage_3/src/iret.s | 18 +++++++++++ src/protected/stage_3/src/lib.rs | 34 +++++++++++++++++--- src/real/stage_2/src/lib.rs | 10 ++++++ src/shared/src/instructions.rs | 41 +++++++++++++++++++++++++ src/shared/src/instructions.s | 11 +++++++ src/shared/src/structures/gdt.rs | 30 ++++++++++++------ 8 files changed, 140 insertions(+), 16 deletions(-) create mode 100644 src/protected/stage_3/src/iret.s create mode 100644 src/shared/src/instructions.s diff --git a/build.rs b/build.rs index cbaca11a..79a0929a 100644 --- a/build.rs +++ b/build.rs @@ -29,7 +29,7 @@ fn main() { // Build stage 3 build_subproject( Path::new("src/protected/stage_3"), - &["third_stage"], + &["third_stage", "iret_test"], "../i386-unknown-none.json", &target_dir, &objcopy, @@ -55,7 +55,7 @@ fn main() { // Build stage 2 build_subproject( Path::new("src/real/stage_2"), - &["second_stage"], + &["second_stage", "v8086_test"], "../i386-unknown-none-code16.json", &target_dir, &objcopy, diff --git a/src/protected/stage_3/src/interrupts.rs b/src/protected/stage_3/src/interrupts.rs index 4d16bbe7..c55c15d7 100644 --- a/src/protected/stage_3/src/interrupts.rs +++ b/src/protected/stage_3/src/interrupts.rs @@ -9,6 +9,7 @@ lazy_static! { idt.divide_error.set_handler_fn(divide_handler); idt.breakpoint.set_handler_fn(breakpoint_handler); + idt.general_protection_fault.set_handler_fn(general_protection_fault_handler); idt.double_fault.set_handler_fn(double_fault_handler); idt @@ -38,4 +39,11 @@ extern "x86-interrupt" fn double_fault_handler( stack_frame: &mut InterruptStackFrame, _error_code: u32) -> ! { panic!("[Bootloader] [IDT] Double Fault!"); +} + +extern "x86-interrupt" fn general_protection_fault_handler( + stack_frame: &mut InterruptStackFrame, error_code: u32) +{ + println!("[Bootloader] [IDT] GPF {} ({})", stack_frame.eip, error_code); + loop {}; } \ No newline at end of file diff --git a/src/protected/stage_3/src/iret.s b/src/protected/stage_3/src/iret.s new file mode 100644 index 00000000..e04a0d6d --- /dev/null +++ b/src/protected/stage_3/src/iret.s @@ -0,0 +1,18 @@ +.att_syntax prefix +iret_asm_test: + mov $0x23, %eax + mov %eax, %ds + mov %eax, %es + mov %eax, %fs + mov %eax, %gs + mov %esp, %eax + + push $0x23 + push %eax + + pushf + + push $0x1b + pushl $iret_test + + iret \ No newline at end of file diff --git a/src/protected/stage_3/src/lib.rs b/src/protected/stage_3/src/lib.rs index a931f040..1e50aad4 100644 --- a/src/protected/stage_3/src/lib.rs +++ b/src/protected/stage_3/src/lib.rs @@ -1,19 +1,27 @@ #![no_std] -#![feature(abi_x86_interrupt, asm)] +#![feature(abi_x86_interrupt, asm, global_asm)] use shared::println; use shared::instructions; +use shared::linker_symbol; mod interrupts; mod panic; +global_asm!(include_str!("iret.s")); + +extern "C" { + fn v8086_test(); + fn iret_asm_test(); +} + #[no_mangle] pub extern "C" fn third_stage() -> ! { - println!("[Bootloader] [32] Stage 3"); + println!("[Bootloader] [32] Stage 3"); - unsafe { - let ptr = 0x110000 as *mut u32; - *ptr = 0xdeadbeef; + unsafe { + let ptr = 0x110000 as *mut u32; + *ptr = 0xdeadbeef; } println!("[Bootloader] [32] > 1MB"); @@ -29,5 +37,21 @@ pub extern "C" fn third_stage() -> ! { println!("[Bootloader] [32] Loaded IDT"); + unsafe { + let eflags = instructions::read_eflags() ;//| (1 << 17); + let fn_addr = &iret_test as *const _ as u32; + + println!("fn @ {}", fn_addr); + + iret_asm_test(); + } + + println!("User mode returned"); + loop {}; +} + +#[no_mangle] +pub extern "C" fn iret_test() { + println!("User mode"); } \ No newline at end of file diff --git a/src/real/stage_2/src/lib.rs b/src/real/stage_2/src/lib.rs index 2075df94..ef8e3abb 100644 --- a/src/real/stage_2/src/lib.rs +++ b/src/real/stage_2/src/lib.rs @@ -8,16 +8,26 @@ use lazy_static::lazy_static; mod panic; +#[no_mangle] +pub fn v8086_test() { + loop {}; + println!("v8086 mode! Yayyyy"); + loop {}; +} + extern "C" { fn protected_mode_switch() -> !; } lazy_static! { static ref TSS: TaskStateSegment = { + use core::mem::size_of; + let mut tss = TaskStateSegment::new(); tss.privilege_stack_table[0].esp = linker_symbol!(_protected_mode_stack_end); tss.privilege_stack_table[0].ss = 2 * 8; // Kernel data segment is 2nd segment (null, code, data) + tss.iomap_base = size_of::() as u16; tss }; diff --git a/src/shared/src/instructions.rs b/src/shared/src/instructions.rs index cc6a85df..ad72227d 100644 --- a/src/shared/src/instructions.rs +++ b/src/shared/src/instructions.rs @@ -1,3 +1,5 @@ +global_asm!(include_str!("instructions.s")); + /// Performs a retf instruction, jumping to cs:eip /// /// # Unsafety @@ -10,6 +12,45 @@ pub unsafe fn retf(cs: u16, eip: u32) { in(reg) cs, in(reg) eip); } +/// Performs an iret instruction, jumping to cs:eip (as well as setting the stack to ss:esp and setting eflags) +/// +/// # Unsafety +/// We make no guarantees that any of the parameters are valid +extern "C" { + pub fn iret(ss: u32, esp: u32, cs: u32, eip: u32, eflags: u32); +} + +/* +#[inline(always)] +pub unsafe fn iret(ss: u32, esp: u32, cs: u32, eip: u32, eflags: u32) { + use crate::println; + println!("ss - {} esp - {} cs - {} eip - {} eflags - {}", ss, esp, cs, eip, eflags); + asm!("push {ss:e} + push {esp:e} + push {eflags:e} + push {cs:e} + push {eip:e} + iret", + ss = in(reg) ss, esp = in(reg) esp, cs = in(reg) cs, eip = in(reg) eip, eflags = in(reg) eflags + ); +}*/ + +/// Reads EFlags +#[inline] +pub fn read_eflags() -> u32 { + let mut eflags: u32; + + unsafe { + asm!( + "pushfd + pop {}", + out(reg) eflags, options(nomem, preserves_flags) + ) + }; + + eflags +} + /// Loads a new value into the task state register /// /// # Unsafety diff --git a/src/shared/src/instructions.s b/src/shared/src/instructions.s new file mode 100644 index 00000000..6b8b8228 --- /dev/null +++ b/src/shared/src/instructions.s @@ -0,0 +1,11 @@ +.intel_syntax noprefix + +iret: + mov ebp, esp + + push dword PTR [ebp+4] + push dword PTR [ebp+8] + push dword PTR [ebp+12] + push dword PTR [ebp+16] + push dword PTR [ebp+20] + iret \ No newline at end of file diff --git a/src/shared/src/structures/gdt.rs b/src/shared/src/structures/gdt.rs index 5f48876c..59409cca 100644 --- a/src/shared/src/structures/gdt.rs +++ b/src/shared/src/structures/gdt.rs @@ -49,6 +49,17 @@ impl GlobalDescriptorTable { limit: (self.table.len() * size_of::() - 1) as u16, }; + use crate::println; + println!("GDT -"); + println!(" {:#08x}", self.table[0]); + println!(" {:#08x}", self.table[1]); + println!(" {:#08x}", self.table[2]); + println!(" {:#08x}", self.table[3]); + println!(" {:#08x}", self.table[4]); + println!(" {:#08x}", self.table[5]); + println!(" {:#08x}", self.table[6]); + println!(" {:#08x}", self.table[7]); + asm!("lgdt [{}]", in(reg) &ptr, options(nostack) @@ -95,6 +106,9 @@ bitflags! { /// The DPL for this descriptor is Ring 3 const DPL_RING_3 = 3 << 45; + /// Is this segment available for use + const AVAILABLE = 1 << 52; + /// If set, this page is a 32 bit descriptor const SIZE = 1 << 54; @@ -116,7 +130,7 @@ impl Descriptor { use self::DescriptorFlags as Flags; let flags = - Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::READABLE_WRITABLE | Flags::SIZE; + Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::ACCESSED | Flags::SIZE | Flags::EXECUTABLE; Descriptor(flags.bits()).with_flat_limit() } @@ -126,7 +140,8 @@ impl Descriptor { pub fn kernel_data_segment() -> Descriptor { use self::DescriptorFlags as Flags; - let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::SIZE; + let flags = + Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::ACCESSED | Flags::SIZE; Descriptor(flags.bits()).with_flat_limit() } @@ -136,7 +151,7 @@ impl Descriptor { use self::DescriptorFlags as Flags; let flags = - Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::DPL_RING_3; + Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::ACCESSED | Flags::SIZE | Flags::DPL_RING_3; Descriptor(flags.bits()).with_flat_limit() } @@ -145,11 +160,8 @@ impl Descriptor { pub fn user_code_segment() -> Descriptor { use self::DescriptorFlags as Flags; - let flags = Flags::USER_SEGMENT - | Flags::PRESENT - | Flags::EXECUTABLE - | Flags::DPL_RING_3 - | Flags::READABLE_WRITABLE; + let flags = + Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::ACCESSED | Flags::SIZE | Flags::EXECUTABLE | Flags::DPL_RING_3; Descriptor(flags.bits()).with_flat_limit() } @@ -162,7 +174,7 @@ impl Descriptor { let ptr = tss as *const _ as u64; - let mut val: u64 = (Flags::PRESENT | Flags::EXECUTABLE | Flags::ACCESSED | Flags::SIZE).bits(); + let mut val: u64 = (Flags::PRESENT | Flags::EXECUTABLE | Flags::ACCESSED | Flags::SIZE | Flags::DPL_RING_3).bits(); // base val.set_bits(16..40, ptr.get_bits(0..24)); From 76daf838143827ce57a2fc87f3cb42a4db83203c Mon Sep 17 00:00:00 2001 From: Ryland Morgan Date: Sat, 20 Feb 2021 16:12:26 +0000 Subject: [PATCH 61/61] Working on V86 mode; messy code rn --- .cargo/{config => config.toml} | 4 +- Cargo.lock | 15 +- Cargo.toml | 1 - build.rs | 11 +- linker.ld | 10 +- src/main.rs | 2 - src/protected/Cargo.toml | 5 + src/protected/stage_3/src/interrupts.rs | 24 +- src/protected/stage_3/src/iret.s | 18 - src/protected/stage_3/src/ivt.rs | 13 + src/protected/stage_3/src/lib.rs | 26 +- src/protected/stage_3/src/v8086/enter_v8086.s | 33 ++ src/protected/stage_3/src/v8086/macros.rs | 4 + src/protected/stage_3/src/v8086/mod.rs | 314 ++++++++++++++++++ src/protected/stage_3/src/v8086_old.rs | 212 ++++++++++++ src/real/Cargo.toml | 12 +- src/real/stage_2/src/lib.rs | 10 +- src/real/stage_2/src/v8086_code.rs | 34 ++ src/shared/Cargo.toml | 7 +- src/shared/src/lib.rs | 4 +- src/shared/src/memory_operations.rs | 53 +++ src/shared/src/structures/gdt.rs | 12 +- 22 files changed, 739 insertions(+), 85 deletions(-) rename .cargo/{config => config.toml} (50%) delete mode 100644 src/protected/stage_3/src/iret.s create mode 100644 src/protected/stage_3/src/ivt.rs create mode 100644 src/protected/stage_3/src/v8086/enter_v8086.s create mode 100644 src/protected/stage_3/src/v8086/macros.rs create mode 100644 src/protected/stage_3/src/v8086/mod.rs create mode 100644 src/protected/stage_3/src/v8086_old.rs create mode 100644 src/real/stage_2/src/v8086_code.rs create mode 100644 src/shared/src/memory_operations.rs diff --git a/.cargo/config b/.cargo/config.toml similarity index 50% rename from .cargo/config rename to .cargo/config.toml index d0601978..a03fb80d 100644 --- a/.cargo/config +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ [build] target = "i8086-bootloader.json" -[alias] -xbuild = "build -Zbuild-std=core" +[unstable] +build-std = ["core"] \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c317483b..5a9f7ea9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "bit_field" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" [[package]] name = "bitflags" @@ -17,7 +17,6 @@ name = "bootloader" version = "0.1.0" dependencies = [ "llvm-tools", - "rlibc", "shared", ] @@ -36,12 +35,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" -[[package]] -name = "rlibc" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" - [[package]] name = "shared" version = "0.1.0" @@ -61,6 +54,6 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "volatile" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29" +checksum = "f6b06ad3ed06fef1713569d547cdbdb439eafed76341820fb0e0344f29a41945" diff --git a/Cargo.toml b/Cargo.toml index 4b488a40..dc78aac1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ build = "build.rs" [dependencies] shared = { path = "src/shared" } -rlibc = "1" [build-dependencies] llvm-tools = "0.1" \ No newline at end of file diff --git a/build.rs b/build.rs index 79a0929a..dcec196e 100644 --- a/build.rs +++ b/build.rs @@ -29,7 +29,7 @@ fn main() { // Build stage 3 build_subproject( Path::new("src/protected/stage_3"), - &["third_stage", "iret_test"], + &["third_stage"], "../i386-unknown-none.json", &target_dir, &objcopy, @@ -98,9 +98,14 @@ fn build_subproject( // Build inside the subproject build_cmd.current_dir(&subproject_dir); + build_cmd.arg("build"); - // Build in release mode - build_cmd.arg("build").arg("--release"); + // Build in release mode if we're built in release mode + let build_profile = env::var("PROFILE").expect("Couldn't get cargo build profile"); + + if build_profile == "release" { + build_cmd.arg("--release"); + } // Very verbose (build script output only shows if you use `-vv` or it fails anyway) build_cmd.arg("-vv"); diff --git a/linker.ld b/linker.ld index 63515052..44fbb9d1 100644 --- a/linker.ld +++ b/linker.ld @@ -24,7 +24,7 @@ SECTIONS { .rest_of_bootloader : { _rest_of_bootloader_start = .; - + *(.text .text.*) *(.data .data.*) *(.rodata .rodata.*) @@ -32,7 +32,7 @@ SECTIONS { *(.got .got.plt) . = ALIGN(512); - + _rest_of_bootloader_end = .; } @@ -44,13 +44,13 @@ SECTIONS { _protected_mode_stack_start = .; . += 0x1000; _protected_mode_stack_end = .; - + /DISCARD/ : { - /* + /* Weird section that always puts itself at the start and causes the binary to be non-bootable Only seems to appear on some platforms, and we can find no use of it in our circumstances */ - *(.eh_frame) + *(.eh_frame*) } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 395e7d2e..606b78e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,5 @@ mod panic; -extern crate rlibc; - #[no_mangle] fn bootloader_no_optimize() {} diff --git a/src/protected/Cargo.toml b/src/protected/Cargo.toml index 3b674689..426d6ae9 100644 --- a/src/protected/Cargo.toml +++ b/src/protected/Cargo.toml @@ -7,4 +7,9 @@ members = [ opt-level = "z" panic = "abort" lto = true + +[profile.dev] +opt-level = "z" +panic = "abort" +lto = true debug = true \ No newline at end of file diff --git a/src/protected/stage_3/src/interrupts.rs b/src/protected/stage_3/src/interrupts.rs index c55c15d7..b0655828 100644 --- a/src/protected/stage_3/src/interrupts.rs +++ b/src/protected/stage_3/src/interrupts.rs @@ -6,7 +6,7 @@ lazy_static! { static ref IDT: InterruptDescriptorTable = { let mut idt = InterruptDescriptorTable::new(); - idt.divide_error.set_handler_fn(divide_handler); + //idt.segment_not_present.set_handler_fn(segment_not_present_handler); idt.breakpoint.set_handler_fn(breakpoint_handler); idt.general_protection_fault.set_handler_fn(general_protection_fault_handler); @@ -23,16 +23,10 @@ pub fn init_idt() { IDT.load(); } -extern "x86-interrupt" fn divide_handler( - stack_frame: &mut InterruptStackFrame) -{ - println!("[Bootloader] [IDT] Divide Exception"); -} - extern "x86-interrupt" fn breakpoint_handler( stack_frame: &mut InterruptStackFrame) { - println!("[Bootloader] [IDT] Breakpoint Hit"); + println!("[Bootloader] [IDT] Breakpoint Hit @ {}:{}", stack_frame.cs, stack_frame.eip); } extern "x86-interrupt" fn double_fault_handler( @@ -41,9 +35,23 @@ extern "x86-interrupt" fn double_fault_handler( panic!("[Bootloader] [IDT] Double Fault!"); } +extern "x86-interrupt" fn segment_not_present_handler( + stack_frame: &mut InterruptStackFrame, error_code: u32) +{ + println!("[Bootloader] [IDT] #NP {} ({})", stack_frame.eip, error_code); + loop {}; +} + extern "x86-interrupt" fn general_protection_fault_handler( stack_frame: &mut InterruptStackFrame, error_code: u32) { + println!("{:?}", stack_frame); + // VM Bit + if stack_frame.eflags & (1 << 17) == (1 << 17) { +// loop {}; +// v8086_handler(stack_frame); + println!("VM Bit Set"); + } println!("[Bootloader] [IDT] GPF {} ({})", stack_frame.eip, error_code); loop {}; } \ No newline at end of file diff --git a/src/protected/stage_3/src/iret.s b/src/protected/stage_3/src/iret.s deleted file mode 100644 index e04a0d6d..00000000 --- a/src/protected/stage_3/src/iret.s +++ /dev/null @@ -1,18 +0,0 @@ -.att_syntax prefix -iret_asm_test: - mov $0x23, %eax - mov %eax, %ds - mov %eax, %es - mov %eax, %fs - mov %eax, %gs - mov %esp, %eax - - push $0x23 - push %eax - - pushf - - push $0x1b - pushl $iret_test - - iret \ No newline at end of file diff --git a/src/protected/stage_3/src/ivt.rs b/src/protected/stage_3/src/ivt.rs new file mode 100644 index 00000000..470ee16e --- /dev/null +++ b/src/protected/stage_3/src/ivt.rs @@ -0,0 +1,13 @@ +#[derive(Debug, Clone, Copy)] +#[repr(C, packed)] +pub struct InterruptVectorTable { + // Goes up to 255 + pub entries: [Entry; 32], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C, packed)] +pub struct Entry { + pub segment: u32, + pub offset: u32, +} \ No newline at end of file diff --git a/src/protected/stage_3/src/lib.rs b/src/protected/stage_3/src/lib.rs index 1e50aad4..36286f69 100644 --- a/src/protected/stage_3/src/lib.rs +++ b/src/protected/stage_3/src/lib.rs @@ -7,13 +7,10 @@ use shared::linker_symbol; mod interrupts; mod panic; +mod ivt; +mod v8086; -global_asm!(include_str!("iret.s")); - -extern "C" { - fn v8086_test(); - fn iret_asm_test(); -} +use v8086::{Monitor, Stack}; #[no_mangle] pub extern "C" fn third_stage() -> ! { @@ -37,21 +34,18 @@ pub extern "C" fn third_stage() -> ! { println!("[Bootloader] [32] Loaded IDT"); - unsafe { - let eflags = instructions::read_eflags() ;//| (1 << 17); - let fn_addr = &iret_test as *const _ as u32; + let stack = Stack::new(linker_symbol!(_stack_start), 0x2B); + let monitor = Monitor::new(stack); + let function_address = linker_symbol!(v8086_test); - println!("fn @ {}", fn_addr); + println!("Entering V8086"); - iret_asm_test(); + unsafe { + //enter_v8086(); + monitor.start(function_address); } println!("User mode returned"); loop {}; -} - -#[no_mangle] -pub extern "C" fn iret_test() { - println!("User mode"); } \ No newline at end of file diff --git a/src/protected/stage_3/src/v8086/enter_v8086.s b/src/protected/stage_3/src/v8086/enter_v8086.s new file mode 100644 index 00000000..775f99fc --- /dev/null +++ b/src/protected/stage_3/src/v8086/enter_v8086.s @@ -0,0 +1,33 @@ +.intel_syntax noprefix +.code32 +/* _enter_v8086(eip: u32) */ +_enter_v8086: + # Store EIP + pop eax + + # gs, fs, ds, es + xor ebx, ebx + + push ebx + push ebx + push ebx + push ebx + + # ss, esp + push ebx + push ebx + + # eflags + mov ebx, ((1 << 17) | (1 << 1)) + push ebx + + # cs, eip + xor ebx, ebx + + push ebx + push eax + + iret + +_spin: + jmp _spin \ No newline at end of file diff --git a/src/protected/stage_3/src/v8086/macros.rs b/src/protected/stage_3/src/v8086/macros.rs new file mode 100644 index 00000000..f7219f93 --- /dev/null +++ b/src/protected/stage_3/src/v8086/macros.rs @@ -0,0 +1,4 @@ +/// Converts a segment:offset pair to a logical address +macro_rules! seg_off_to_log { + ($segment:ident, $offset:ident) => {($segment & 0xFFFF) * 16 + $offset}; +} diff --git a/src/protected/stage_3/src/v8086/mod.rs b/src/protected/stage_3/src/v8086/mod.rs new file mode 100644 index 00000000..e3e314c3 --- /dev/null +++ b/src/protected/stage_3/src/v8086/mod.rs @@ -0,0 +1,314 @@ +//! Virtual 8086 Mode monitor and entry code +use core::mem; + +#[macro_use] +mod macros; + +const PFX_ES: u16 = 0x001; +const PFX_CS: u16 = 0x002; +const PFX_SS: u16 = 0x004; +const PFX_DS: u16 = 0x008; +const PFX_FS: u16 = 0x010; +const PFX_GS: u16 = 0x020; + +const PFX_OP32: u16 = 0x040; +const PFX_ADDR32: u16 = 0x080; +const PFX_LOCK: u16 = 0x100; +const PFX_REPNE: u16 = 0x200; +const PFX_REP: u16 = 0x400; + +// V8086 Entry Code +global_asm!(include_str!("enter_v8086.s")); + +extern "C" { + fn _enter_v8086(eip: u32); +} + +/// Trait for integers usable as peek / poke / pop / push / etc. +trait IntegerValue: Copy {} +impl IntegerValue for u8 {} +impl IntegerValue for u16 {} +impl IntegerValue for u32 {} + +/// Stack info +#[derive(Debug)] +pub struct Stack { + segment: u32, + offset: u32 +} + +impl Stack { + pub fn new(segment: u32, offset: u32) -> Stack { + Stack { + segment: segment, + offset: offset + } + } +} + +/// Registers +#[derive(Debug)] +#[repr(C)] +#[repr(packed)] +pub struct Registers { + edi: u32, + esi: u32, + ebp: u32, + esp: u32, + ebx: u32, + edx: u32, + ecx: u32, + eax: u32, + + ds: u32, + es: u32, + fs: u32, + gs: u32, + + eip: u32, + cs: u32, + eflags: u32, + user_esp: u32, + user_ss: u32, + + v_es: u32, + v_ds: u32, + v_fs: u32, + v_gs: u32 +} + +impl Registers { + /// Creates a new set of registers (all zero) + pub fn new() -> Registers { + Registers { + edi: 0, + esi: 0, + ebp: 0, + esp: 0, + ebx: 0, + edx: 0, + ecx: 0, + eax: 0, + + ds: 0, + es: 0, + fs: 0, + gs: 0, + + eip: 0, + cs: 0, + eflags: 0, + user_esp: 0, + user_ss: 0, + + v_es: 0, + v_ds: 0, + v_fs: 0, + v_gs: 0, + } + } + + /// Saves the current state of CPU registers into ourselves + pub unsafe extern "C" fn save(&mut self) { + // TODO + + } +} + +#[repr(C)] +#[repr(packed)] +struct V86Registers { + pub gs: u32, + pub fs: u32, + pub ds: u32, + pub es: u32, + + pub ss: u32, + pub esp: u32, + + pub eflags: u32, + + pub cs: u32, + pub eip: u32 +} + +/// The V8086 Monitor itself +#[derive(Debug)] +pub struct Monitor { + pub stack: Stack, + pub registers: Registers +} + +impl Monitor { + /// Creates a new Monitor + pub fn new(stack: Stack) -> Monitor { + Monitor { + stack: stack, + registers: Registers::new() + } + } + + /// Enters V86 mode at the specified address + pub unsafe fn start(&self, address: u32) { + let mut registers = V86Registers { + gs: 8 * 4, + fs: 8 * 4, + ds: 8 * 4, + es: 8 * 4, + ss: 8 * 4, + esp: self.stack.offset, + eflags: 0b01000000000011110100000000000000, + cs: 8 * 3, + eip: address + }; + + crate::println!("Address {}", address); + + _enter_v8086(address); + } + + /// Reads the memory offset at the provided seg:addr + pub unsafe fn peek(&self, segment: u32, offset: u32) -> T { + let logical = seg_off_to_log!(segment, offset); + + let ptr = logical as *const T; + let value = *ptr; + value + } + + /// Writes to the memory offset at the provided seg:addr + pub unsafe fn poke(&self, segment: u32, offset: u32, value: T) { + let logical = seg_off_to_log!(segment, offset); + + let ptr = logical as *mut T; + *ptr = value; + } + + /// Reads one byte from the current EIP and increments it + pub unsafe fn fetch(&mut self) -> u8 { + let value = self.peek(self.registers.cs, self.registers.eip); + self.registers.eip = (self.registers.eip + 1) & 0xFFFF; + + value + } + + /// Pops a value from the v86 stack + pub unsafe fn pop(&mut self) -> T { + let value = self.peek(self.stack.segment, self.stack.offset); + self.stack.offset = (self.stack.offset + (mem::size_of_val(&value) as u32)) & 0xFFFF; + + value + } + + /// Pushes a value to the v86 stack + pub unsafe fn push(&mut self, value: T) { + self.stack.offset = (self.stack.offset - (mem::size_of_val(&value) as u32)) & 0xFFFF; + self.poke(self.stack.segment, self.stack.offset, value); + } + + /// Handles an interrupt using the BIOS IVT + pub unsafe fn handle_interrupt(&mut self, int_number: u32) { + // Push return IP, CS and EFLAGS onto the V86 stack + self.push(self.registers.eflags); + self.push(self.registers.cs); + self.push(self.registers.eip); + + // Disable Interrupts + self.registers.eflags &= !0x200; + + // Load new CS and IP from the IVT + let ivt_offset = int_number * 4; + self.registers.eip = (self.registers.eip & !0xFFFF) | self.peek::(0, ivt_offset); + self.registers.cs = self.peek(0, ivt_offset + 2); + } + + /// Executes an instruction + pub unsafe fn emulate(&mut self) { + let inital_eip = self.registers.eip; + let mut prefix = 0; + let mut instruction = 0; + + loop { + instruction = self.fetch(); + + match instruction { + // Segment prefixes + 0x26 => prefix |= PFX_ES, + 0x2e => prefix |= PFX_CS, + 0x36 => prefix |= PFX_SS, + 0x3e => prefix |= PFX_DS, + 0x64 => prefix |= PFX_FS, + 0x65 => prefix |= PFX_GS, + + 0x66 => prefix |= PFX_OP32, + 0x67 => prefix |= PFX_ADDR32, + 0xF0 => prefix |= PFX_LOCK, + 0xF2 => prefix |= PFX_REPNE, + 0xF3 => prefix |= PFX_REP, + _ => break, + } + }; + + match instruction { + // PUSHF + 0x9C => { + if (prefix & PFX_OP32) == PFX_OP32 { + self.push(self.registers.eflags); + } else { + self.push(self.registers.eflags as u16); + } + }, + + // POPF + 0x9D => { + if (prefix & PFX_OP32) == PFX_OP32 { + if self.registers.esp > 0xFFFC { + return; + } + + self.registers.eflags = self.pop(); + } else { + if self.registers.esp > 0xFFFE { + return; + } + + self.registers.eflags = (self.registers.eflags & 0xFFFF0000) | self.pop::(); + } + }, + + // INT nn + 0xCD => { + let interrupt_number = self.fetch() as u32; + self.handle_interrupt(interrupt_number); + }, + + // IRET + 0xCF => { + if (prefix & PFX_OP32) == PFX_OP32 { + if self.registers.esp > 0xFFF4 { + return; + } + + self.registers.eip = self.pop(); + self.registers.cs = self.pop(); + self.registers.eflags = self.pop(); + } else { + if self.registers.esp > 0xFFFA { + return; + } + + self.registers.eip = self.pop::() as u32; + self.registers.cs = self.pop::() as u32; + self.registers.eflags = self.pop::() as u32; + } + }, + + // CLI & STI + 0xFA => self.registers.eflags &= !0x200, + 0xFB => self.registers.eflags |= 0x200, + + // Other + _ => panic!("Unimplemented V8086 Instruction") + } + } +} \ No newline at end of file diff --git a/src/protected/stage_3/src/v8086_old.rs b/src/protected/stage_3/src/v8086_old.rs new file mode 100644 index 00000000..2a23f3cc --- /dev/null +++ b/src/protected/stage_3/src/v8086_old.rs @@ -0,0 +1,212 @@ +pub const PREFIX_ES: u8 = 0x001; +pub const PREFIX_CS: u8 = 0x002; +pub const PREFIX_SS: u8 = 0x004; +pub const PREFIX_DS: u8 = 0x008; +pub const PREFIX_FS: u8 = 0x010; +pub const PREFIX_GS: u8 = 0x020; + +pub const PREFIX_OP32: u8 = 0x040; +pub const PREFIX_ADDR32: u8 = 0x080; +pub const PREFIX_LOCK: u8 = 0x100; +pub const PREFIX_REPNE: u8 = 0x200; +pub const PREFIX_REP: u8 = 0x400; + +#[derive(Clone, Debug)] +#[repr(C, packed)] +pub struct Registers { + pub edi: u32, + pub esi: u32, + pub ebp: u32, + pub esp: u32, + pub ebx: u32, + pub ecx: u32, + pub edx: u32, + pub ds : u32, + pub es : u32, + pub fs : u32, + pub gs : u32, + pub eip: u32, + pub cs : u32, + pub eflags: u32, + pub user_esp: u32, + pub user_ss: u32, + pub v_es: u32, + pub v_ds: u32, + pub v_fs: u32, + pub v_gs: u32 +} + +/// Converts a segment:offset address into a linear one +pub fn segmented_to_linear(segment: u16, offset: u16) -> u32 { + (segment & 0xFFFF) as u32 * 16 + (offset as u32) +} + +/// Reads a byte at segment:offset in memory +pub unsafe fn peekb(segment: u16, offset: u16) -> u8 { + let ptr = segmented_to_linear(segment, offset) as *const u8; + *ptr +} + +/// Reads a word at segment:offset in memory +pub unsafe fn peekw(segment: u16, offset: u16) -> u16 { + let ptr = segmented_to_linear(segment, offset) as *const u16; + *ptr +} + +/// Reads a long at segment:offset in memory +pub unsafe fn peekl(segment: u16, offset: u16) -> u32 { + let ptr = segmented_to_linear(segment, offset) as *const u32; + *ptr +} + +/// Writes a byte at segment:offset in memory +pub unsafe fn pokeb(segment: u16, offset: u16, value: u8) { + let ptr = segmented_to_linear(segment, offset) as *mut u8; + *ptr = value; +} + +/// Writes a word at segment:offset in memory +pub unsafe fn pokew(segment: u16, offset: u16, value: u16) { + let ptr = segmented_to_linear(segment, offset) as *mut u16; + *ptr = value; +} + +/// Writes a long at segment:offset in memory +pub unsafe fn pokel(segment: u16, offset: u16, value: u32) { + let ptr = segmented_to_linear(segment, offset) as *mut u32; + *ptr = value; +} + +/// Fetches one byte from v86 memory at the IP and advances the instruction pointer +pub unsafe fn fetchb(registers: &mut Registers) -> u8 { + let byte = peekb(registers.cs as u16, registers.eip as u16); + registers.eip = (registers.eip + 1) & 0xFFFF; + + byte +} + +/// Pushes a word onto the stack +pub unsafe fn pushw(registers: &mut Registers, value: u16) { + registers.user_esp = (registers.user_esp - 2) & 0xFFFF; + pokew(registers.user_ss as u16, registers.user_esp as u16, value); +} + +/// Pops a word from the stack +pub unsafe fn popw(registers: &mut Registers) -> u16 { + let ret = peekw(registers.user_ss as u16, registers.user_esp as u16); + registers.user_esp = (registers.user_esp + 2) & 0xFFFF; + + ret +} + +/// Pushes a long onto the stack +pub unsafe fn pushl(registers: &mut Registers, value: u32) { + registers.user_esp = (registers.user_esp - 2) & 0xFFFF; + pokel(registers.user_ss as u16, registers.user_esp as u16, value); +} + +/// Pops a long from the stack +pub unsafe fn popl(registers: &mut Registers) -> u32 { + let ret = peekl(registers.user_ss as u16, registers.user_esp as u16); + registers.user_esp = (registers.user_esp + 2) & 0xFFFF; + + ret +} + +/// Handles an interrupt in V86 mode +pub unsafe fn int(registers: &mut Registers, int_number: u16) { + // Push return IP, CS and FLAGS onto V86 stack + pushw(registers, registers.eflags as u16); + pushw(registers, registers.cs as u16); + pushw(registers, registers.eip as u16); + + // Disable interrupts + registers.eflags &= !0x200; + + // Load new CS and IP from IVT + registers.eip = ((registers.eip & !0xFFFF) as u16 | peekw(0, int_number * 4)) as u32; + registers.cs = peekw(0, (int_number * 4) + 2) as u32; +} + +pub unsafe fn emulate(registers: &mut Registers) { + let init_eip = registers.eip; + let mut prefix = 0; + let mut instruction = fetchb(registers); + + loop { + match instruction { + 0x26 => prefix |= PREFIX_ES, + 0x2E => prefix |= PREFIX_CS, + 0x36 => prefix |= PREFIX_SS, + 0x3E => prefix |= PREFIX_DS, + 0x64 => prefix |= PREFIX_ES, + 0x65 => prefix |= PREFIX_GS, + + 0x66 => prefix |= PREFIX_OP32, + 0x67 => prefix |= PREFIX_ADDR32, + 0xF0 => prefix |= PREFIX_LOCK, + 0xF2 => prefix |= PREFIX_REPNE, + 0xF3 => prefix |= PREFIX_REP, + + _ => break, + }; + }; + + match instruction { + // PUSHF + 0x9C => { + if (prefix & PREFIX_OP32) == PREFIX_OP32 { + pushl(registers, registers.eflags); + } else { + pushw(registers, registers.eflags as u16); + } + }, + + // POPF + 0x9D => { + if (prefix & PREFIX_OP32) == PREFIX_OP32 { + if registers.user_esp > 0xFFFC { + panic!("[V8086] Invalid Stack"); + } else { + registers.eflags = popl(registers); + } + } else { + if registers.user_esp > 0xFFFE { + panic!("[V8086] Invalid Stack"); + } else { + registers.eflags = ((registers.eflags & 0xFFFF0000) as u16 | popw(registers)) as u32; + } + } + }, + + // INT nn + 0xCD => { + let interrupt_id = fetchb(registers); + int(registers, interrupt_id as u16); + }, + + // IRET + 0xCF => { + if (prefix & PREFIX_OP32) == PREFIX_OP32 { + if registers.user_esp > 0xFFF4 { + panic!("[V8086] Invalid Stack"); + } else { + registers.eip = popl(registers); + registers.cs = popl(registers); + registers.eflags = (registers.eflags & 0xFFFF0000) | popl(registers); + } + } else { + if registers.user_esp > 0xFFFA { + panic!("[V8086] Invalid Stack"); + } else { + registers.eip = popw(registers) as u32; + registers.cs = popw(registers) as u32; + registers.eflags = ((registers.eflags & 0xFFFF0000) as u16 | popw(registers)) as u32; + } + } + }, + + 0xE4 | 0xE6 | 0xE5 | 0xE7 | 0x6C | 0x6E | 0xEC | 0xEE | 0x6D | 0x6F | 0xED | 0xEF | 0xFA | 0xFB => panic!("I/O Operation Performed"), + _ => panic!("Invalid instruction in v86 mode") + }; +} diff --git a/src/real/Cargo.toml b/src/real/Cargo.toml index 1fc43c67..ce659165 100644 --- a/src/real/Cargo.toml +++ b/src/real/Cargo.toml @@ -9,9 +9,13 @@ opt-level = "z" panic = "abort" lto = true +[profile.dev] +opt-level = "z" +panic = "abort" +lto = true +debug = true + [profile.release.package.bootsector] opt-level = "s" -codegen-units = 1 - -[profile.release.package.stage_2] -debug = true \ No newline at end of file +debug = false +codegen-units = 1 \ No newline at end of file diff --git a/src/real/stage_2/src/lib.rs b/src/real/stage_2/src/lib.rs index ef8e3abb..f728cb74 100644 --- a/src/real/stage_2/src/lib.rs +++ b/src/real/stage_2/src/lib.rs @@ -6,15 +6,9 @@ use shared::println; use shared::structures::gdt::{Descriptor, GlobalDescriptorTable, TaskStateSegment}; use lazy_static::lazy_static; +mod v8086_code; mod panic; -#[no_mangle] -pub fn v8086_test() { - loop {}; - println!("v8086 mode! Yayyyy"); - loop {}; -} - extern "C" { fn protected_mode_switch() -> !; } @@ -26,7 +20,7 @@ lazy_static! { let mut tss = TaskStateSegment::new(); tss.privilege_stack_table[0].esp = linker_symbol!(_protected_mode_stack_end); - tss.privilege_stack_table[0].ss = 2 * 8; // Kernel data segment is 2nd segment (null, code, data) + tss.privilege_stack_table[0].ss = 2 * 8; // Kernel data segment is 3rd segment (null, code, data) tss.iomap_base = size_of::() as u16; tss diff --git a/src/real/stage_2/src/v8086_code.rs b/src/real/stage_2/src/v8086_code.rs new file mode 100644 index 00000000..ecdeaaf2 --- /dev/null +++ b/src/real/stage_2/src/v8086_code.rs @@ -0,0 +1,34 @@ +//! V86 code (has to compiled for real mode) +pub fn println(s: &[u8]) { + print(s); + print_char(b'\n'); +} + +pub fn print(s: &[u8]) { + let mut i = 0; + + while i < s.len() { + print_char(s[i]); + i += 1; + } +} + +#[inline(always)] +pub fn print_char(c: u8) { + let ax = u16::from(c) | 0x0e00; + unsafe { + asm!("int 0x10", + in("ax") ax, + options(nostack) + ); + } +} + +#[no_mangle] +pub extern "C" fn v8086_test() { + unsafe { asm!("mov bx, 0xcafe"); } + unsafe { + // asm!("int 0x10", in("ax") 0x41 | 0x0e00, options(nostack)); + } + loop {}; +} \ No newline at end of file diff --git a/src/shared/Cargo.toml b/src/shared/Cargo.toml index b82dbe60..794151f3 100644 --- a/src/shared/Cargo.toml +++ b/src/shared/Cargo.toml @@ -15,7 +15,12 @@ version = "1" features = ["spin_no_std"] [profile.release] -debug = true opt-level = "z" panic = "abort" lto = true + +[profile.dev] +opt-level = "z" +panic = "abort" +lto = true +debug = true \ No newline at end of file diff --git a/src/shared/src/lib.rs b/src/shared/src/lib.rs index d7d6c960..21602c06 100644 --- a/src/shared/src/lib.rs +++ b/src/shared/src/lib.rs @@ -8,4 +8,6 @@ pub mod dap; #[macro_use] pub mod macros; pub mod structures; -pub mod instructions; \ No newline at end of file +pub mod instructions; + +pub mod memory_operations; \ No newline at end of file diff --git a/src/shared/src/memory_operations.rs b/src/shared/src/memory_operations.rs new file mode 100644 index 00000000..5c637d4a --- /dev/null +++ b/src/shared/src/memory_operations.rs @@ -0,0 +1,53 @@ +#[no_mangle] +pub unsafe extern fn memcpy(dest: *mut u8, src: *const u8, + n: usize) -> *mut u8 { + let mut i = 0; + while i < n { + *dest.offset(i as isize) = *src.offset(i as isize); + i += 1; + } + return dest; +} + +#[no_mangle] +pub unsafe extern fn memmove(dest: *mut u8, src: *const u8, + n: usize) -> *mut u8 { + if src < dest as *const u8 { // copy from end + let mut i = n; + while i != 0 { + i -= 1; + *dest.offset(i as isize) = *src.offset(i as isize); + } + } else { // copy from beginning + let mut i = 0; + while i < n { + *dest.offset(i as isize) = *src.offset(i as isize); + i += 1; + } + } + return dest; +} + +#[no_mangle] +pub unsafe extern fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8 { + let mut i = 0; + while i < n { + *s.offset(i as isize) = c as u8; + i += 1; + } + return s; +} + +#[no_mangle] +pub unsafe extern fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + let mut i = 0; + while i < n { + let a = *s1.offset(i as isize); + let b = *s2.offset(i as isize); + if a != b { + return a as i32 - b as i32 + } + i += 1; + } + return 0; +} \ No newline at end of file diff --git a/src/shared/src/structures/gdt.rs b/src/shared/src/structures/gdt.rs index 59409cca..89494371 100644 --- a/src/shared/src/structures/gdt.rs +++ b/src/shared/src/structures/gdt.rs @@ -23,7 +23,7 @@ impl GlobalDescriptorTable { #[inline] pub fn add_entry(&mut self, entry: Descriptor) -> u16 { let index = self.push(entry.0); - + index as u16 } @@ -140,7 +140,7 @@ impl Descriptor { pub fn kernel_data_segment() -> Descriptor { use self::DescriptorFlags as Flags; - let flags = + let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::ACCESSED | Flags::SIZE; Descriptor(flags.bits()).with_flat_limit() } @@ -151,7 +151,8 @@ impl Descriptor { use self::DescriptorFlags as Flags; let flags = - Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::ACCESSED | Flags::SIZE | Flags::DPL_RING_3; + Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::ACCESSED | Flags::DPL_RING_3; + Descriptor(flags.bits()).with_flat_limit() } @@ -161,7 +162,8 @@ impl Descriptor { use self::DescriptorFlags as Flags; let flags = - Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::ACCESSED | Flags::SIZE | Flags::EXECUTABLE | Flags::DPL_RING_3; + Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::ACCESSED | Flags::EXECUTABLE | Flags::DPL_RING_3; + Descriptor(flags.bits()).with_flat_limit() } @@ -196,7 +198,7 @@ impl Descriptor { self.0.set_bit(49, true); self.0.set_bit(50, true); self.0.set_bit(51, true); - + // granularity self.0 |= DescriptorFlags::GRANULARITY.bits();