2020-07-12 22:55:13 -07:00
|
|
|
#![no_std]
|
|
|
|
#![no_main]
|
|
|
|
#![feature(abi_efiapi)]
|
2020-07-16 01:17:45 -07:00
|
|
|
#![feature(alloc_error_handler)]
|
2020-07-13 17:39:19 -07:00
|
|
|
#![feature(asm)]
|
2020-07-17 16:51:24 -07:00
|
|
|
#![feature(abi_x86_interrupt)]
|
2020-07-18 00:50:26 -07:00
|
|
|
#![feature(generic_associated_types)]
|
2020-07-16 01:17:45 -07:00
|
|
|
extern crate alloc;
|
2020-07-12 22:55:13 -07:00
|
|
|
|
2020-07-16 01:17:45 -07:00
|
|
|
mod allocator;
|
2020-07-16 22:31:03 -07:00
|
|
|
#[macro_use]
|
2020-07-16 01:17:45 -07:00
|
|
|
mod graphics;
|
2020-07-17 16:51:24 -07:00
|
|
|
mod arch;
|
2020-07-15 14:06:15 -07:00
|
|
|
|
2020-07-13 17:39:19 -07:00
|
|
|
use core::mem;
|
|
|
|
use core::slice;
|
2020-07-16 22:31:03 -07:00
|
|
|
use crate::allocator::{Allocator, ALLOCATOR};
|
|
|
|
use crate::graphics::tty::{Tty, STDOUT, STDERR};
|
|
|
|
use crate::graphics::tty::serial::SerialTty;
|
2020-07-12 22:55:13 -07:00
|
|
|
use uefi::prelude::*;
|
2020-07-13 17:39:19 -07:00
|
|
|
use uefi::table::boot::{AllocateType, MemoryDescriptor, MemoryType};
|
|
|
|
|
2020-07-12 22:55:13 -07:00
|
|
|
#[entry]
|
2020-07-13 17:39:19 -07:00
|
|
|
fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
|
2020-07-18 00:50:26 -07:00
|
|
|
// Although serial ports don't physically exist on modern devices,
|
|
|
|
// they're still supposed by emulators (for QEMU you can set `-serial stdio`),
|
|
|
|
// and they're extremely useful for debugging
|
|
|
|
// because they don't require any setup and are trivial to use.
|
2020-07-12 22:55:13 -07:00
|
|
|
unsafe {
|
2020-07-16 22:31:03 -07:00
|
|
|
STDOUT = Some(SerialTty::new(0x3F8));
|
|
|
|
STDERR = Some(SerialTty::new(0x3F8));
|
2020-07-12 22:55:13 -07:00
|
|
|
}
|
2020-07-13 17:39:19 -07:00
|
|
|
|
2020-07-18 00:50:26 -07:00
|
|
|
// Our first task is to exit the UEFI boot services.
|
|
|
|
// UEFI provides a whole bunch of useful device drivers,
|
|
|
|
// but they're unusable after you exit the boot services,
|
|
|
|
// and you absolutely *have* to exit boot services to do most OS-related things.
|
|
|
|
|
|
|
|
// When we exit boot services, UEFI provides us with a memory map,
|
|
|
|
// which describes where a bunch of important stuff lies in memory
|
|
|
|
// (e.g. memory-mapped devices and the ACPI tables),
|
|
|
|
// and what memory is available for us to use safely.
|
|
|
|
let (st, mmap) = {
|
|
|
|
// We must provide a buffer (mmap_buf) for UEFI to write the memory map to.
|
2020-07-13 17:39:19 -07:00
|
|
|
let bs = st_boot.boot_services();
|
|
|
|
|
|
|
|
// More allocations can happen between the allocation of the buffer and the buffer being filled,
|
2020-07-18 00:50:26 -07:00
|
|
|
// so add space for 8 more memory descriptors (an arbitrary number) just to make sure there's enough.
|
2020-07-13 17:39:19 -07:00
|
|
|
let mmap_buf_size = bs.memory_map_size() + 8 * mem::size_of::<MemoryDescriptor>();
|
2020-07-18 00:50:26 -07:00
|
|
|
|
|
|
|
// In the memory map, the OS's own code and data is included as LOADER_CODE and LOADER_DATA.
|
|
|
|
// This is good to know so we don't accidentally write over it!
|
|
|
|
// We allocate the mmap_buf as LOADER_DATA for this purpose.
|
|
|
|
let mmap_buf = bs.allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, mmap_buf_size)
|
|
|
|
.expect_success("Failed to allocate memory for UEFI memory map buffer.");
|
|
|
|
|
|
|
|
// allocate_pages returns a u64 address, but we need a `&mut [u8]`.
|
2020-07-13 17:39:19 -07:00
|
|
|
let mmap_buf_slice = unsafe { slice::from_raw_parts_mut(mmap_buf as *mut u8, mmap_buf_size) };
|
2020-07-18 00:50:26 -07:00
|
|
|
|
|
|
|
// Finally, we actually exit the UEFI boot services.
|
2020-07-13 17:39:19 -07:00
|
|
|
st_boot.exit_boot_services(handle, mmap_buf_slice).expect_success("Failed to exit the UEFI boot services.")
|
|
|
|
};
|
|
|
|
|
2020-07-18 00:50:26 -07:00
|
|
|
// Set up the stuff I need to handle interrupts, which is necessary to write drivers for most devices.
|
2020-07-17 16:51:24 -07:00
|
|
|
use x86_64::instructions::interrupts;
|
|
|
|
use crate::arch::x86_64::{gdt, idt};
|
|
|
|
|
|
|
|
interrupts::disable();
|
2020-07-18 00:50:26 -07:00
|
|
|
// TODO: I've actually found that resetting the GDT isn't necessary in the emulator.
|
|
|
|
// However, I'm not sure if that's true in general, and at worst it seems harmless, so it stays for now.
|
|
|
|
// That said, further research is needed.
|
2020-07-17 16:51:24 -07:00
|
|
|
gdt::load();
|
|
|
|
idt::load();
|
|
|
|
interrupts::enable();
|
|
|
|
|
2020-07-18 00:50:26 -07:00
|
|
|
// Everything up to this point has been setting up the CPU state, drivers, etc.
|
|
|
|
// Now we begin running actual programs
|
|
|
|
// (or in this case, since we don't support actual programs yet, whatever debug stuff I want to run).
|
|
|
|
main(st, mmap)
|
|
|
|
}
|
2020-07-13 17:39:19 -07:00
|
|
|
|
2020-07-18 00:50:26 -07:00
|
|
|
fn main(_st: SystemTable<uefi::table::Runtime>, _mmap: uefi::table::boot::MemoryMapIter) -> ! {
|
|
|
|
// Put whatever code you want for debugging/testing purposes here...
|
|
|
|
arch::x86_64::breakpoint();
|
2020-07-13 17:39:19 -07:00
|
|
|
|
2020-07-18 00:50:26 -07:00
|
|
|
// There's nothing left for us to do at this point, because there are no meaningful programs to run.
|
|
|
|
// Instead, we'll just spin forever until the computer is turned off.
|
|
|
|
// We do *not* disable interrupts to allow for testing the interrupt handlers.
|
|
|
|
loop { x86_64::instructions::hlt(); }
|
2020-07-13 17:39:19 -07:00
|
|
|
}
|