Skip to content

Commit

Permalink
Switch to 32bit protected mode
Browse files Browse the repository at this point in the history
  • Loading branch information
mrgian committed Mar 20, 2023
1 parent 21973ad commit 2521a5c
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 138 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ jobs:
- name: Build bootloader
run: cargo build --release --target=x86_16-felix.json --package=felix-bootloader
- name: Build kernel
run: cargo build --release --target=x86_16-felix.json --package=felix-kernel
run: cargo build --release --target=x86_32-felix.json --package=felix-kernel
- name: Make build directory
run: mkdir build
- name: Objcopy boot
run: objcopy -I elf32-i386 -O binary target/x86_16-felix/release/felix-boot build/boot.bin
- name: Objcopy bootloader
run: objcopy -I elf32-i386 -O binary target/x86_16-felix/release/felix-bootloader build/bootloader.bin
- name: Objcopy kernel
run: objcopy -I elf32-i386 -O binary target/x86_16-felix/release/felix-kernel build/kernel.bin
run: objcopy -I elf32-i386 -O binary target/x86_32-felix/release/felix-kernel build/kernel.bin
- name: Create empty disk
run: dd if=/dev/zero of=build/disk.img bs=64MiB count=1
- name: Partition disk
Expand All @@ -44,16 +44,16 @@ jobs:
run: dd if=build/disk.img of=build/partition.img bs=512 skip=4096
- name: Format main partition
run: mkfs.fat -F 16 build/partition.img
- name: Copy kernel
run: sudo mcopy -i build/partition.img build/kernel.bin "::kernel.bin"
- name: Copy test1.txt
run: sudo mcopy -i build/partition.img test1.txt "::test1.txt"
run: mcopy -i build/partition.img test1.txt "::test1.txt"
- name: Copy test2.txt
run: sudo mcopy -i build/partition.img test2.txt "::test2.txt"
run: mcopy -i build/partition.img test2.txt "::test2.txt"
- name: Put main partition
run: dd if=build/partition.img of=build/disk.img bs=512 seek=4096 conv=notrunc
- name: Put bootloader
run: dd if=build/bootloader.bin of=build/disk.img bs=512 seek=2048 conv=notrunc
- name: Put kernel
run: dd if=build/bootloader.bin of=build/disk.img bs=512 seek=2080 conv=notrunc
- name: Remove temp partition file
run: rm -rf build/partition.img
- name: Get current date
Expand Down
7 changes: 4 additions & 3 deletions boot/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod disk;
use disk::DiskReader;

const BOOTLOADER_LBA: u64 = 2048; //bootloader location logical block address
const BOOTLOADER_SIZE: u16 = 64; //bootloader size in sectors
const BOOTLOADER_SIZE: u16 = 32; //bootloader size in sectors

//set data segments to zero and setup stack
global_asm!(include_str!("boot.asm"));
Expand All @@ -22,7 +22,8 @@ extern "C" {
pub extern "C" fn main() {
clear();

print("Loading bootloader...\r\n\0");
print("[!] Starting Felix...\r\n\0");
print("[!] Loading bootloader...\r\n\0");

//get bootloader address from linker
let bootloader_start: *const u16 = unsafe { &_bootloader_start };
Expand Down Expand Up @@ -72,7 +73,7 @@ fn jump(address: *const u16) {

#[no_mangle]
pub extern "C" fn fail() -> ! {
print("Failed loading bootloader!");
print("[!] Failed loading bootloader!");

loop {}
}
Expand Down
6 changes: 4 additions & 2 deletions bootloader/linker.ld
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ SECTIONS {
*(.eh_frame_hdr .eh_frame_hdr.*)
}

/* in this way the bootloader size is exactly 32768 bytes (64 sectors) */
. = _bootloader_start + 0x8000 - 2;
/* in this way the bootloader size is exactly 16384 bytes (32 sectors) */
. = _bootloader_start + 0x4000 - 2;
.end_marker :
{
SHORT(0xdead)
}

_kernel_start = .;
}
83 changes: 83 additions & 0 deletions bootloader/src/gdt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use core::{arch::asm, mem::size_of};

const GDT_ENTRIES: usize = 3;

#[repr(C)]
pub struct GlobalDescriptorTable {
zero: u64,
pub code: u64,
data: u64,
}

//global descriptor table for flat memory model
impl GlobalDescriptorTable {
//left shifts are used to set bit from specified position
pub fn new() -> Self {
//segment lenght (0xffff means all 32bit memory)
let limit = {
let limit_low = 0xffff << 0;
let limit_high = 0xf << 48;

limit_low | limit_high
};

//base address
let base = {
let base_low = 0x0000 << 16;
let base_high = 0x00 << 56;

base_low | base_high
};

//access byte
let access = {
let p = 0b1 << 47; //present bit (1 for any segment)
let dpl = 0b00 << 46; //descriptor privilege level (ring, 0 for highest privilege, 3 for lowest)
let s = 0b1 << 44; //descriptor type bit
let e = 0b0 << 43; //executable bit
let dc = 0b0 << 42; //direction bit/conforming bit
let rw = 0b1 << 41; //readable bit/writable bit
let a = 0b0 << 40; //accessed bit

p | dpl | s | e | dc | rw | a
};

//flags
let flags = {
let g = 0b1 << 55; //granularity flag
let db = 0b1 << 54; //size flag
let l = 0b0 << 53; //long mode flag
let r = 0b0 << 52; //reserved

g | db | l | r
};

let executable = 0b1 << 43; //set only executable flag again, instead of setting all values again

//first entry is always zero
//second entry is code segment (default + executable)
//third entry is data segment (default)
Self {
zero: 0,
code: limit | base | access | flags | executable,
data: limit | base | access | flags ,
}
}

pub fn load(&self) {
let descriptor = GdtDescriptor {
base: self,
limit: (GDT_ENTRIES * size_of::<u64>() - 1) as u16, //calculate size of gdt
};

unsafe {
asm!("cli", "lgdt [{}]", in(reg) &descriptor, options(readonly, nostack, preserves_flags));
}
}
}

#[repr(C, packed(2))]
pub struct GdtDescriptor {
pub limit: u16, //gdt size
pub base: *const GlobalDescriptorTable, //pointer to gdt
}
59 changes: 49 additions & 10 deletions bootloader/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
#![no_std]
#![no_main]

//use core::arch::asm;
use core::arch::asm;
use core::panic::PanicInfo;

#[macro_use]
mod print;

mod fat;
use fat::FatDriver;
mod disk;
use disk::DiskReader;

mod gdt;
use gdt::GlobalDescriptorTable;

//const VERSION: &str = env!("CARGO_PKG_VERSION");
const KERNEL_LBA: u64 = 2048 + 32; //kernel location logical block address (bootloader lba + bootloader size)
const KERNEL_SIZE: u16 = 32; //kernel size in sectors
const KERNEL_TARGET: u16 = 0xbe00; //where to put kernel in memory

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
Expand All @@ -23,23 +29,56 @@ fn panic(info: &PanicInfo) -> ! {
#[no_mangle]
#[link_section = ".start"]
pub extern "C" fn _start() -> ! {
println!("Bootloader loaded!");
println!("[!] Loading kernel...");

let mut disk = DiskReader::new(KERNEL_LBA, KERNEL_TARGET);
disk.read_sectors(KERNEL_SIZE);

println!("[!] Loading Global Descriptor Table...");
let gdt = GlobalDescriptorTable::new();

gdt.load();

println!("[!] Switching to 32bit protected mode and jumping to kernel...");

unsafe {
//enable protected mode in cr0 register
asm!("mov eax, cr0", "or al, 1", "mov cr0, eax");

//push kernel address
asm!(
"push {0:e}",
in(reg) KERNEL_TARGET as u32,
);

//jump to protected mode
asm!("ljmp $0x8, $2f", "2:", options(att_syntax));

let driver = FatDriver::new();
driver.load_header();
//protected mode start
asm!(
".code32",

println!("{:?}", driver.header);
//setup segment registers
"mov {0}, 0x10",
"mov ds, {0}",
"mov es, {0}",
"mov ss, {0}",

driver.load_entries();
//jump to kernel
"pop {1}",
"call {1:e}",

driver.list_entries();
out(reg) _,
in(reg) KERNEL_TARGET as u32,
);
}

loop {}
}

#[no_mangle]
pub extern "C" fn fail() -> ! {
println!("Read fail!");
println!("[!] Read fail!");

loop {}
}
8 changes: 5 additions & 3 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ rm -rf build
echo "Building Felix..."
cargo build --target=x86_16-felix.json --package=felix-boot
cargo build --target=x86_16-felix.json --package=felix-bootloader
cargo build --target=x86_16-felix.json --package=felix-kernel
cargo build --target=x86_32-felix.json --package=felix-kernel

echo "Making build directory..."
mkdir build

echo "Objcopy..."
objcopy -I elf32-i386 -O binary target/x86_16-felix/debug/felix-boot build/boot.bin
objcopy -I elf32-i386 -O binary target/x86_16-felix/debug/felix-bootloader build/bootloader.bin
objcopy -I elf32-i386 -O binary target/x86_16-felix/debug/felix-kernel build/kernel.bin
objcopy -I elf32-i386 -O binary target/x86_32-felix/debug/felix-kernel build/kernel.bin

echo "Making empty disk image..."
dd if=/dev/zero of=build/disk.img bs=64MiB count=1
Expand All @@ -34,7 +34,6 @@ echo "Formatting main partition..."
mkfs.fat -F 16 build/partition.img

echo "Copying kernel and data to main partition..."
mcopy -i build/partition.img build/kernel.bin "::kernel.bin"
mcopy -i build/partition.img test1.txt "::test1.txt"
mcopy -i build/partition.img test2.txt "::test2.txt"

Expand All @@ -47,4 +46,7 @@ rm -rf build/partition.img
echo "Putting bootloader..."
dd if=build/bootloader.bin of=build/disk.img bs=512 seek=2048 conv=notrunc

echo "Putting kernel..."
dd if=build/kernel.bin of=build/disk.img bs=512 seek=2080 conv=notrunc

echo "Felix has been successfully built!"
7 changes: 1 addition & 6 deletions kernel/linker.ld
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ENTRY(_start)

SECTIONS {
. = 0x7c00 + 512;
. = 0x7e00 + 16384;

.start : {
*(.start)
Expand All @@ -25,11 +25,6 @@ SECTIONS {
*(.eh_frame_hdr .eh_frame_hdr.*)
}

. = ALIGN(512);

_second_stage_end = .;

/*. = 0x0007FFFF - 2;*/
.end_marker :
{
SHORT(0xdead)
Expand Down
4 changes: 1 addition & 3 deletions bootloader/src/fat.rs → kernel/src/fat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ use core::ptr;
const ENTRY_COUNT: u16 = 512;
const FAT_START: u16 = 4096;

//Link to bible: https://wiki.osdev.org/FAT#Implementation_Details

//FAT12 header
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
Expand Down Expand Up @@ -159,7 +157,7 @@ impl FatDriver {

println!("Name Size");

//NOTE: if i scan to 512 it doesn't work, maybe stack is too small to contain all entries
//NOTE: if i scan to 512 it doesn't work, maybe stack is too small to contain all entries

for i in 0..64 {
if self.entries[i].name[0] != 0 {
Expand Down
19 changes: 1 addition & 18 deletions kernel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,15 @@
use core::arch::asm;
use core::panic::PanicInfo;

mod print;

const VERSION: &str = env!("CARGO_PKG_VERSION");

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("{}", info);
wait_key_and_reboot();

loop {}
}

#[no_mangle]
#[link_section = ".start"]
pub extern "C" fn _start() -> ! {
println!("Loaded! Welcome to Felix {}", VERSION);
unsafe { asm!("mov eax, 0xdeadbeef") }

loop {}
}

//TODO: Fix, it's not working
#[allow(overflowing_literals)]
fn wait_key_and_reboot() {
println!("Press any key to reboot...");

unsafe {
asm!("mov ah, 0", "int 0x16", "jmp {0:x}", in(reg) 0x7c00 as u16);
}
}
Loading

0 comments on commit 2521a5c

Please sign in to comment.