stage1: implement bios disk access
This commit is contained in:
parent
68f56352ad
commit
4c8a00abc4
6 changed files with 170 additions and 7 deletions
|
@ -15,7 +15,7 @@ repository = "https://git.bsd.gay/fef/bussy"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
opt-level = 0
|
opt-level = "s"
|
||||||
lto = false
|
lto = false
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
debug = true
|
debug = true
|
||||||
|
|
19
stage1/src/asm/disk.s
Normal file
19
stage1/src/asm/disk.s
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
|
||||||
|
.align 8
|
||||||
|
GLOBL dap
|
||||||
|
.skip 0x10
|
||||||
|
END dap
|
||||||
|
|
||||||
|
.align 8
|
||||||
|
GLOBL drive_parms
|
||||||
|
.skip 0x1a
|
||||||
|
END drive_parms
|
||||||
|
/*
|
||||||
|
* According to the GRUB:
|
||||||
|
* <http://git.savannah.gnu.org/cgit/grub.git/tree/include/grub/i386/pc/biosdisk.h?h=grub-2.06#n76>
|
||||||
|
* the ThinkPad X20 BIOS ignores the `size` member and writes
|
||||||
|
* garbage data out of bounds on the drive parameter structure
|
||||||
|
*/
|
||||||
|
.skip 0x10
|
|
@ -1,4 +1,4 @@
|
||||||
use core::ptr::write_volatile;
|
use core::ptr::{read, write_volatile};
|
||||||
|
|
||||||
// XXX This doesn't emit proper debug symbols for assembly sources
|
// XXX This doesn't emit proper debug symbols for assembly sources
|
||||||
|
|
||||||
|
@ -21,6 +21,18 @@ include_asm!("header.s");
|
||||||
|
|
||||||
// main bootstrap routine that calls the rust entry point
|
// main bootstrap routine that calls the rust entry point
|
||||||
include_asm!("boot.s");
|
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
|
// switch between real and protected mode
|
||||||
include_asm!("pmode.s");
|
include_asm!("pmode.s");
|
||||||
|
@ -78,3 +90,12 @@ pub unsafe fn do_bios_int(number: u8, mut regs: BiosIntRegs) -> BiosResult {
|
||||||
Err(regs)
|
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;
|
||||||
|
}
|
||||||
|
|
107
stage1/src/disk.rs
Normal file
107
stage1/src/disk.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
use core::mem::size_of;
|
||||||
|
use core::ptr::{read_volatile, write_volatile};
|
||||||
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
|
|
||||||
|
use crate::asm::{dap, do_bios_int, drive_parms, BiosIntRegs};
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct DriveParams {
|
||||||
|
size: u16,
|
||||||
|
flags: u16,
|
||||||
|
cylinders: u32,
|
||||||
|
heads: u32,
|
||||||
|
sectors_per_track: u32,
|
||||||
|
total_sectors: u64,
|
||||||
|
sector_size: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_sector_size(drive_number: u8) -> Option<u16> {
|
||||||
|
// rustc whines about unaligned members due to repr(packed)
|
||||||
|
// but we know FOR SURE that everything is aligned properly
|
||||||
|
unsafe {
|
||||||
|
let drive_params_ptr = (&mut drive_parms) as *mut DriveParams;
|
||||||
|
write_volatile(
|
||||||
|
drive_params_ptr as *mut u16,
|
||||||
|
size_of::<DriveParams>() as u16,
|
||||||
|
);
|
||||||
|
|
||||||
|
let regs = do_bios_int(
|
||||||
|
0x13,
|
||||||
|
BiosIntRegs {
|
||||||
|
eax: 0x4800,
|
||||||
|
edx: drive_number as u32,
|
||||||
|
esi: drive_params_ptr as u32,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if regs.eax & 0xff00 == 0 {
|
||||||
|
Some(read_volatile(
|
||||||
|
(drive_params_ptr as *const u8).add(16) as *const u16
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct DiskAddressPacket {
|
||||||
|
size: u8,
|
||||||
|
_rsvd: u8,
|
||||||
|
sector_count: u16,
|
||||||
|
buffer_offset: u16,
|
||||||
|
buffer_segment: u16,
|
||||||
|
lba: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read one logical sector from a disk using BIOS int13/42.
|
||||||
|
///
|
||||||
|
/// ## Safety
|
||||||
|
///
|
||||||
|
/// Since this uses a BIOS call, there is no real safety here.
|
||||||
|
/// The best approximation to safety requires the following:
|
||||||
|
///
|
||||||
|
/// - `dest` MUST have capacity for at least the size of a sector.
|
||||||
|
/// - If the sector size is a power of 2, `dest` MUST be aligned to a sector size.
|
||||||
|
/// - If the sector size is not a power of 2, `dest` MUST be aligned to the next
|
||||||
|
/// power of 2 greater than the sector size.
|
||||||
|
/// - `dest` SHOULD be aligned to at least 64 K.
|
||||||
|
/// - `dest` MUST be in low memory (i.e. below 1 M).
|
||||||
|
/// - `drive_number` MUST be a valid BIOS drive number.
|
||||||
|
/// - `lba` SHOULD fit into 48 bits.
|
||||||
|
pub unsafe fn read_lba(dest: *mut u8, drive_number: u8, lba: u64) -> Result<(), u8> {
|
||||||
|
debug_assert!((dest as usize) >= (1 << 16));
|
||||||
|
debug_assert!((dest as usize) < (1 << 20));
|
||||||
|
|
||||||
|
let dap_ptr = (&mut dap) as *mut DiskAddressPacket;
|
||||||
|
dap = DiskAddressPacket {
|
||||||
|
size: size_of::<DiskAddressPacket>() as u8,
|
||||||
|
_rsvd: 0,
|
||||||
|
sector_count: 1,
|
||||||
|
buffer_offset: ((dest as usize) & 0xf) as u16,
|
||||||
|
buffer_segment: ((dest as usize) >> 4) as u16,
|
||||||
|
lba,
|
||||||
|
};
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
let ah = do_bios_int(
|
||||||
|
0x13,
|
||||||
|
BiosIntRegs {
|
||||||
|
eax: 0x4200,
|
||||||
|
edx: drive_number as u32,
|
||||||
|
esi: dap_ptr as u32,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map(|regs| (regs.eax >> 8) as u8)
|
||||||
|
.map_err(|regs| (regs.eax >> 8) as u8)?;
|
||||||
|
|
||||||
|
if ah == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ah)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,15 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use core::arch::asm;
|
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
/// Collection of all assembly sources and symbols they expose
|
/// Collection of all assembly sources and symbols they expose
|
||||||
pub mod asm;
|
mod asm;
|
||||||
|
/// BIOS disk access utilities
|
||||||
|
mod disk;
|
||||||
|
|
||||||
|
use crate::disk::{get_sector_size, read_lba};
|
||||||
|
use asm::{do_bios_int, get_boot_drive, get_boot_lba, BiosIntRegs};
|
||||||
|
|
||||||
use asm::{do_bios_int, BiosIntRegs};
|
use asm::{do_bios_int, BiosIntRegs};
|
||||||
|
|
||||||
|
@ -18,9 +22,10 @@ pub extern "C" fn main() -> ! {
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
pub fn rust_panic(_info: &PanicInfo) -> ! {
|
pub fn rust_panic(_info: &PanicInfo) -> ! {
|
||||||
|
print("stage1 panicked, system halted.\r\n");
|
||||||
loop {
|
loop {
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!("cli", "hlt");
|
core::arch::asm!("cli", "hlt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,8 +37,8 @@ pub fn print(s: &str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let regs = BiosIntRegs {
|
let regs = BiosIntRegs {
|
||||||
ax: 0x0e00 | b as u16,
|
eax: 0x0e00 | b as u32,
|
||||||
bx: 0x0001,
|
ebx: 0x0001,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
OUTPUT_FORMAT(elf32-i386)
|
OUTPUT_FORMAT(elf32-i386)
|
||||||
ENTRY(_start)
|
ENTRY(_start)
|
||||||
|
|
||||||
|
SEGMENT_SIZE = 0x10000;
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
. = 0x0500;
|
. = 0x0500;
|
||||||
|
|
||||||
|
@ -29,4 +31,13 @@ SECTIONS {
|
||||||
*(.bss .bss.*)
|
*(.bss .bss.*)
|
||||||
_bss_end = .;
|
_bss_end = .;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.iomem(NOLOAD) : ALIGN(SEGMENT_SIZE) {
|
||||||
|
_disk_buf = .;
|
||||||
|
. += SEGMENT_SIZE;
|
||||||
|
_disk_buf_end = .;
|
||||||
|
_mem_map = .;
|
||||||
|
. += SEGMENT_SIZE;
|
||||||
|
_mem_map_end = .;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue