stage1: fix more mode switching bugs

BIOS calls from Rust should work now.
This commit is contained in:
anna 2023-05-30 00:35:53 +02:00
parent 2ec426677f
commit bbe3375b8a
Signed by: fef
GPG key ID: 2585C2DC6D79B485
6 changed files with 79 additions and 62 deletions

View file

@ -15,7 +15,7 @@ repository = "https://git.bsd.gay/fef/bussy"
[profile.dev] [profile.dev]
panic = "abort" panic = "abort"
opt-level = "s" opt-level = 0
lto = false lto = false
codegen-units = 1 codegen-units = 1
debug = true debug = true

View file

@ -77,14 +77,18 @@ GLOBL _boot
a20_enabled: a20_enabled:
/* int13/00 RESET DISK SYSTEM */ /* int13/00 RESET DISK SYSTEM */
mov $0, %ah mov $0, %ah
mov boot_drive, %dl
int $0x13 int $0x13
/* make absolutely sure that %sp is aligned */ /* make absolutely sure that %sp is aligned */
and $0xfffc, %sp and $0xfffc, %esp
/* make real_to_prot return directly to rust */ call real_to_prot
pushw $main .code32
jmp real_to_prot /* initialize an empty stack frame */
xor %ebp, %ebp
jmp main
.code16
err_no_a20: err_no_a20:
mov $msg_err_no_a20, %ax mov $msg_err_no_a20, %ax

View file

@ -7,12 +7,17 @@
* goes back to Protected Mode again. Furthermore, the interrupt * goes back to Protected Mode again. Furthermore, the interrupt
* number is written ahead of time by the callee. * number is written ahead of time by the callee.
*/ */
/* fn __do_bios_int(regs: &mut BiosIntRegs) -> u8 */
GLOBL __do_bios_int GLOBL __do_bios_int
push %ebp /* '1 v */ pushal // '1 v
mov %esp, %ebp
pushal /* '2 v */
mov 8(%ebp), %ebp /*
* Move the `regs` parameter into %ebp. `call` pushes one longword
* (4 bytes) for the return address, then the `pushal` above pushes
* 8 longwords (32 bytes), so the pointer is at offset 36.
*/
mov 36(%esp), %ebp
/* now load all registers with their desired values */
mov (%ebp), %ax mov (%ebp), %ax
mov 2(%ebp), %cx mov 2(%ebp), %cx
mov 4(%ebp), %dx mov 4(%ebp), %dx
@ -20,30 +25,32 @@ GLOBL __do_bios_int
mov 8(%ebp), %si mov 8(%ebp), %si
mov 10(%ebp), %di mov 10(%ebp), %di
push %ebp /* '3 v */ push %ebp // '2 v
push %eax /* '4 v */ push %eax // '3 v
push %edx /* '5 v */ push %edx // '4 v
call prot_to_real call prot_to_real
.code16 .code16
pop %edx /* '5 ^ */ pop %edx // '4 ^
pop %eax /* '4 ^ */ pop %eax // '3 ^
/* call that polymorphism! */ /* call that polymorphism! */
.byte 0xcd /* opcode for `int imm8` */ .byte 0xcd /* opcode for `int imm8` */
GLOBL __bios_int_number, object GLOBL __bios_int_number, object
.byte 0x18 /* this is the imm8 */ .byte 0x18 /* this is the imm8 (overwritten by caller) */
push %eax /* '4 v */ /* be careful not to touch EFLAGS now because that's our return value */
push %edx /* '5 v */
pushfl /* '6 v */ push %eax // '3 v
push %edx // '4 v
pushfl // '5 v
call real_to_prot call real_to_prot
.code32 .code32
popfl /* '6 ^ */ popfl // '5 ^
pop %edx /* '5 ^ */ pop %edx // '4 ^
pop %eax /* '4 ^ */ pop %eax // '3 ^
pop %ebp /* '3 ^ */ pop %ebp // '2 ^
mov %ax, (%ebp) mov %ax, (%ebp)
mov %cx, 2(%ebp) mov %cx, 2(%ebp)
@ -51,11 +58,10 @@ GLOBL __bios_int_number, object
mov %bx, 6(%ebp) mov %bx, 6(%ebp)
mov %si, 8(%ebp) mov %si, 8(%ebp)
mov %di, 10(%ebp) mov %di, 10(%ebp)
mov %al, 12(%ebp)
popal /* '2 ^ */ popal // '1 ^
setc %al setc %al
/* this is not really required, but just in case */
movzbl %al, %eax movzbl %al, %eax
pop %ebx /* '1 ^ */
ret ret
END __do_bios_int END __do_bios_int

View file

@ -1,7 +1,11 @@
use core::ptr::write_volatile;
// XXX This doesn't emit proper debug symbols for assembly sources
macro_rules! include_asm { macro_rules! include_asm {
($name:literal) => { ($name:literal) => {
::core::arch::global_asm!( ::core::arch::global_asm!(
::core::concat!(".file \"", $name, "\""), // for better debugging ::core::concat!(".file \"", $name, "\""),
::core::include_str!($name), ::core::include_str!($name),
::core::concat!(".file \"", ::core::file!(), "\""), ::core::concat!(".file \"", ::core::file!(), "\""),
options(raw, att_syntax), options(raw, att_syntax),
@ -9,7 +13,7 @@ macro_rules! include_asm {
}; };
} }
// common macros for the other assembly files, keep at the beginning // common macros for the other assembly files, keep this at the beginning
include_asm!("common.s"); include_asm!("common.s");
// stage1 header containing its magic, size, checksum, and entry point // stage1 header containing its magic, size, checksum, and entry point
@ -20,15 +24,17 @@ extern "C" {
pub static _stage1_csum: u32; pub static _stage1_csum: u32;
} }
// main bootstrap routine that calls the rust entry point
include_asm!("boot.s"); include_asm!("boot.s");
// switch between real and protected mode
include_asm!("pmode.s"); include_asm!("pmode.s");
// do bios interrupts from protected mode
include_asm!("int.s"); include_asm!("int.s");
extern "C" { extern "C" {
/// Temporarily return to Real Mode and do a BIOS interrupt with the /// Temporarily return to Real Mode and do a BIOS interrupt with the
/// specified register values. Returns the value of the carry flag /// specified register values. Returns the value of the carry flag.
/// (`true` if CF=1, `false` if CF=0).
/// ///
/// ## Safety /// ## Safety
/// ///
@ -62,9 +68,13 @@ pub type BiosResult = Result<BiosIntRegs, BiosIntRegs>;
/// clear after the interrupt, and `Err` if it was set. In either /// clear after the interrupt, and `Err` if it was set. In either
/// case, the wrapped value contains the state of the registers after /// case, the wrapped value contains the state of the registers after
/// the interrupt. /// 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 { pub unsafe fn do_bios_int(number: u8, mut regs: BiosIntRegs) -> BiosResult {
// this might be mildly unsafe // this might be mildly unsafe
__bios_int_number = number; write_volatile(&mut __bios_int_number, number);
let cf = __do_bios_int(&mut regs); let cf = __do_bios_int(&mut regs);
if cf == 0 { if cf == 0 {
Ok(regs) Ok(regs)

View file

@ -6,13 +6,17 @@
* Intel(R) 64 and IA-32 Architectures Software Developer's Manual * Intel(R) 64 and IA-32 Architectures Software Developer's Manual
* vol. 3A, chapter 9.9.1. Lines prefixed with '>' are direct quotes. * vol. 3A, chapter 9.9.1. Lines prefixed with '>' are direct quotes.
* Annotations within those lines are enclosed in [square brackets]. * Annotations within those lines are enclosed in [square brackets].
* Must be called in Real Mode, returns in Protected Mode.
* *
* ATTENTION: This messes with the stack! Before calling this, you * ATTENTION: This messes with the stack! Before calling this function,
* MUST ensure that the stack pointer is aligned to 4 bytes (that is, * you MUST ensure that %sp is aligned to 4 bytes. The stack pointer
* *before* the call to this subroutine pushes the return address). * will be automatically translated to its linear address equivalent
* (i.e. %esp = %ss * 0x10 + %sp), however, it DOES NOT touch %ebp.
* *
* ATTENTION: This trashes registers %ax and %dx, as well as EFLAGS. * ATTENTION: You MUST call this from real mode (.code16), and any code
* following the call MUST be for 32-bit protected mode (.code32).
*
* ATTENTION: This trashes %eax, %edx, eflags, and ALL segment registers.
* Furthermore, it clears the IF flag (i.e. it disables interrupts).
* *
> Intel 64 and IA-32 processors have slightly different requirements > Intel 64 and IA-32 processors have slightly different requirements
> for switching to protected mode. To ensure upwards and downwards > for switching to protected mode. To ensure upwards and downwards
@ -64,11 +68,10 @@ GLOBL real_to_prot
> jump or call to the next instruction in the instruction stram.) > jump or call to the next instruction in the instruction stram.)
*/ */
ljmpl $0x08, $1f ljmpl $0x08, $1f
1: .code32
.code32
/* recompute the (now linear) stack address */ /* recompute the (now linear) stack address */
1: xor %eax, %eax xor %eax, %eax
mov %ss, %ax mov %ss, %ax
shl $4, %eax shl $4, %eax
add %eax, %esp add %eax, %esp
@ -87,20 +90,11 @@ GLOBL real_to_prot
> 7. If a local descriptor table is going to be used [...] > 7. If a local descriptor table is going to be used [...]
* *
* No. * No.
*/
/*
> 8. Execute the LTR instruction to load the task register with
> a segment selector to the initial protected-mode task or to
> a writable area of memory that can be used to store TSS
> information on a task switch.
* *
* If you insist ... > 8. Execute the LTR instruction [...]
*/ *
//mov $0x28, %ax * We don't do tasks here
//ltr %ax /* offset 0x28 into GDT, 32-bit TSS */ *
/*
> 9. After entering protected mode, the segment registers continue > 9. After entering protected mode, the segment registers continue
> to hold the contents they had in real-address mode. > to hold the contents they had in real-address mode.
> The JMP or CALL instruction in step 4 resets the CS register. > The JMP or CALL instruction in step 4 resets the CS register.
@ -252,8 +246,9 @@ GLOBL prot_to_real
out %al, $0x70 out %al, $0x70
in $0x71, %al in $0x71, %al
jmpw *%dx jmp *%dx
END prot_to_real END prot_to_real
.code32
.data .data
@ -265,7 +260,8 @@ END gdtr
/* /*
* This figure is based on the Intel SDM vol. 3A, fig. 5-1. * This figure is based on the Intel SDM vol. 3A, fig. 5-1.
* Reordered so that the structure matches the definition below. * Reordered so that the structure matches the definition below, i.e.
* little endian byte order (bits WITHIN the bytes are big endian).
* *
* | 0 1 | 2 3 | * | 0 1 | 2 3 |
* +-------------------------------+-------------------------------+ * +-------------------------------+-------------------------------+
@ -322,23 +318,18 @@ END gdt
.section .bss .section .bss
/* dummy IDT Register value (0 entries) */ /* dummy IDT Register value (0 entries) */
LOCAL prot_idtr LOCAL prot_idtr
.word 0 .word 0
.long 0 .long 0
END prot_idtr END prot_idtr
/* this is where we store the Real Mode IDT for later BIOS calls */ /* this is where we store the Real Mode IDT for later BIOS calls */
LOCAL real_idtr LOCAL real_idtr
.word 0 .word 0
.long 0 .long 0
END real_idtr END real_idtr
/* stores whether real mode sp was aligned */
LOCAL real_sp_offset
.word 0
END real_sp_offset
LOCAL real_ss LOCAL real_ss
.word 0 .word 0
END real_ss END real_ss

View file

@ -1,11 +1,13 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use crate::asm::{do_bios_int, BiosIntRegs};
use core::arch::asm; use core::arch::asm;
use core::panic::PanicInfo; use core::panic::PanicInfo;
mod asm; /// Collection of all assembly sources and symbols they expose
pub mod asm;
use asm::{do_bios_int, BiosIntRegs};
#[no_mangle] #[no_mangle]
pub extern "C" fn main() -> ! { pub extern "C" fn main() -> ! {
@ -25,6 +27,10 @@ pub fn rust_panic(_info: &PanicInfo) -> ! {
pub fn print(s: &str) { pub fn print(s: &str) {
for b in s.bytes() { for b in s.bytes() {
if b == b'\0' {
break;
}
let regs = BiosIntRegs { let regs = BiosIntRegs {
ax: 0x0e00 | b as u16, ax: 0x0e00 | b as u16,
bx: 0x0001, bx: 0x0001,