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

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;
}