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.
108 lines
2.9 KiB
Rust
108 lines
2.9 KiB
Rust
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)
|
|
}
|
|
}
|