stage1: add bios call hook
This commit is contained in:
parent
ce19f70440
commit
2ec426677f
2 changed files with 110 additions and 0 deletions
61
stage1/src/asm/int.s
Normal file
61
stage1/src/asm/int.s
Normal file
|
@ -0,0 +1,61 @@
|
|||
.text
|
||||
.code32
|
||||
|
||||
/*
|
||||
* This has got to be one of the most cursed routines within stage1.
|
||||
* Temporarily returns to Real Mode, does a BIOS interrupt, and then
|
||||
* goes back to Protected Mode again. Furthermore, the interrupt
|
||||
* number is written ahead of time by the callee.
|
||||
*/
|
||||
GLOBL __do_bios_int
|
||||
push %ebp /* '1 v */
|
||||
mov %esp, %ebp
|
||||
pushal /* '2 v */
|
||||
|
||||
mov 8(%ebp), %ebp
|
||||
mov (%ebp), %ax
|
||||
mov 2(%ebp), %cx
|
||||
mov 4(%ebp), %dx
|
||||
mov 6(%ebp), %bx
|
||||
mov 8(%ebp), %si
|
||||
mov 10(%ebp), %di
|
||||
|
||||
push %ebp /* '3 v */
|
||||
|
||||
push %eax /* '4 v */
|
||||
push %edx /* '5 v */
|
||||
call prot_to_real
|
||||
.code16
|
||||
pop %edx /* '5 ^ */
|
||||
pop %eax /* '4 ^ */
|
||||
|
||||
/* call that polymorphism! */
|
||||
.byte 0xcd /* opcode for `int imm8` */
|
||||
GLOBL __bios_int_number, object
|
||||
.byte 0x18 /* this is the imm8 */
|
||||
|
||||
push %eax /* '4 v */
|
||||
push %edx /* '5 v */
|
||||
pushfl /* '6 v */
|
||||
call real_to_prot
|
||||
.code32
|
||||
popfl /* '6 ^ */
|
||||
pop %edx /* '5 ^ */
|
||||
pop %eax /* '4 ^ */
|
||||
|
||||
pop %ebp /* '3 ^ */
|
||||
|
||||
mov %ax, (%ebp)
|
||||
mov %cx, 2(%ebp)
|
||||
mov %dx, 4(%ebp)
|
||||
mov %bx, 6(%ebp)
|
||||
mov %si, 8(%ebp)
|
||||
mov %di, 10(%ebp)
|
||||
mov %al, 12(%ebp)
|
||||
|
||||
popal /* '2 ^ */
|
||||
setc %al
|
||||
movzbl %al, %eax
|
||||
pop %ebx /* '1 ^ */
|
||||
ret
|
||||
END __do_bios_int
|
|
@ -23,3 +23,52 @@ extern "C" {
|
|||
include_asm!("boot.s");
|
||||
|
||||
include_asm!("pmode.s");
|
||||
|
||||
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
|
||||
/// (`true` if CF=1, `false` if CF=0).
|
||||
///
|
||||
/// ## 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 ax: u16,
|
||||
pub cx: u16,
|
||||
pub dx: u16,
|
||||
pub bx: u16,
|
||||
pub si: u16,
|
||||
pub di: 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.
|
||||
pub unsafe fn do_bios_int(number: u8, mut regs: BiosIntRegs) -> BiosResult {
|
||||
// this might be mildly unsafe
|
||||
__bios_int_number = number;
|
||||
let cf = __do_bios_int(&mut regs);
|
||||
if cf == 0 {
|
||||
Ok(regs)
|
||||
} else {
|
||||
Err(regs)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue