stage1: add bios call hook

This commit is contained in:
anna 2023-05-29 04:18:55 +02:00
parent ce19f70440
commit 2ec426677f
Signed by: fef
GPG key ID: 2585C2DC6D79B485
2 changed files with 110 additions and 0 deletions

61
stage1/src/asm/int.s Normal file
View 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

View file

@ -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)
}
}