You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
3.0 KiB
Rust
102 lines
3.0 KiB
Rust
use core::ptr::{read, write_volatile};
|
|
|
|
// XXX This doesn't emit proper debug symbols for assembly sources
|
|
|
|
macro_rules! include_asm {
|
|
($name:literal) => {
|
|
::core::arch::global_asm!(
|
|
::core::concat!(".file \"", $name, "\""),
|
|
::core::include_str!($name),
|
|
::core::concat!(".file \"", ::core::file!(), "\""),
|
|
options(raw, att_syntax),
|
|
);
|
|
};
|
|
}
|
|
|
|
// common macros for the other assembly files, keep this at the beginning
|
|
include_asm!("common.s");
|
|
|
|
// stage1 header containing its magic, size, checksum, and entry point
|
|
include_asm!("header.s");
|
|
|
|
// main bootstrap routine that calls the rust entry point
|
|
include_asm!("boot.s");
|
|
extern "C" {
|
|
static boot_drive: u8;
|
|
static boot_lba: u64;
|
|
}
|
|
|
|
pub fn get_boot_drive() -> u8 {
|
|
unsafe { read(&boot_drive) }
|
|
}
|
|
|
|
pub fn get_boot_lba() -> u64 {
|
|
unsafe { read(&boot_lba) }
|
|
}
|
|
|
|
// switch between real and protected mode
|
|
include_asm!("pmode.s");
|
|
|
|
// do bios interrupts from protected mode
|
|
include_asm!("int.s");
|
|
extern "C" {
|
|
/// Temporarily return to Real Mode and do a BIOS interrupt with the
|
|
/// specified register values. Returns the value of the carry flag.
|
|
///
|
|
/// ## Safety
|
|
///
|
|
/// This is horrendously unsafe and there is nothing you can do about it.
|
|
/// Before calling, you MUST set [`__bios_int_number`] to the interrupt
|
|
/// number you wish to invoke. Cache shouldn't be an issue because going
|
|
/// from protected to real mode flushes the pipeline and prefetch cache
|
|
/// anyway, but don't quote me on that.
|
|
fn __do_bios_int(regs: &mut BiosIntRegs) -> u8;
|
|
|
|
/// This is located within [`__do_bios_int`] and writing to it modifies
|
|
/// the interrupt number that is invoked when calling the function.
|
|
static mut __bios_int_number: u8;
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone, Default)]
|
|
pub struct BiosIntRegs {
|
|
pub eax: u32,
|
|
pub ecx: u32,
|
|
pub edx: u32,
|
|
pub ebx: u32,
|
|
pub esi: u32,
|
|
pub edi: u32,
|
|
pub ds: u16,
|
|
pub es: u16,
|
|
}
|
|
|
|
pub type BiosResult = Result<BiosIntRegs, BiosIntRegs>;
|
|
|
|
/// Temporarily return to Real Mode and do a BIOS interrupt with the
|
|
/// specified register values. Returns `Ok` if the carry flag was clear
|
|
/// after the interrupt, and `Err` if it was set. In either case, the
|
|
/// wrapped value contains the state of the registers after the interrupt.
|
|
///
|
|
/// ## Safety
|
|
///
|
|
/// This is horrendously unsafe and there is nothing you can do about it.
|
|
pub unsafe fn do_bios_int(number: u8, mut regs: BiosIntRegs) -> BiosResult {
|
|
// this might be mildly unsafe
|
|
write_volatile(&mut __bios_int_number, number);
|
|
let cf = __do_bios_int(&mut regs);
|
|
if cf == 0 {
|
|
Ok(regs)
|
|
} else {
|
|
Err(regs)
|
|
}
|
|
}
|
|
|
|
// Rust doesn't support aligning global symbols directly; it has to be
|
|
// defined in the type. However, we can't put align(8) on the type
|
|
// either because that would destroy the layout of the data structure.
|
|
include_asm!("disk.s");
|
|
extern "C" {
|
|
pub static mut drive_parms: crate::disk::DriveParams;
|
|
pub static mut dap: crate::disk::DiskAddressPacket;
|
|
}
|