rust-osdev / bootloader

An experimental pure-Rust x86 bootloader
Apache License 2.0
1.39k stars 212 forks source link

Adding a way to calls some BIOS/UEFI functions before entering protected mode #380

Closed interstellarfrog closed 1 year ago

interstellarfrog commented 1 year ago

it would be nice if in the bootloader builder crate we could somehow add some externel assembly code or maybe call a function from the crate to add a line of asm code and another function to add specific register values to a custom section of the boot_info struct to call some BIOS/UEFI functions and get the values instead of having to switch modes after booting to call them.

example use:

// Builder code
let mut boot_config = BootConfig::default();

// These will be added to the code in order somehow probably better to use functions instead of lists

boot_config.custom_assembly[0] = Some("set an arg in a register for the BIOS/UEFI function in assembly code"); // step 1

boot_config.custom_assembly[1] = Some("Call A bios function in assembly code here that gives a return value in a register"); // step 2

boot_config.custom_value[0] = Some("rax"); // step 3 Select a register to get a value from after calling function

// OS code

let function_return_value = boot_info.custom_value[0];

// Use the value

Please correct me if I am wrong but I think this could be implemented in some way like this I believe you could either implement set values and make an enum of all the BIOS/UEFI functions and do something like this:

boot_config.bios_function(Bootloader::Bootconfig::BiosFunctions::function);

or

boot_config.uefi_function(Bootloader::Bootconfig::UefiFunctions::function);

and have the returned values put into the boot_info struct somewhere

or you could extract the assembly code from the before example and directly add it to the source code of the bootloader before building (im not sure if this code would require more space in the bootloader) which would be more complex and likely to cause errors.

Do you think this could be a good solution or would you recommend to just switch modes then call the functions?

edit(phil-opp): formatting

phil-opp commented 1 year ago

Thanks for the suggestion! Could you give us some concrete examples for register values/assembly operations you are interested in?

interstellarfrog commented 1 year ago

For example I believe we can use 0x11 BIOS interrupt to get the systems equipment list which I believe could simplify the later process of initializing certain hardware:

boot_config.custom_assembly[0] = Some("INT 0x11");  // Query System Equipment List

boot_config.custom_value[0] = Some("AX");  // Get System Equipment List

Or we could just do a simple read system date which I believe looks something like this:

boot_config.custom_assembly[0] = Some("mov ah 0x04");

boot_config.custom_assembly[1] = Some("INT 0x1A");

// All in Binary Coded Decimal (BCD) format

boot_config.custom_value[0] = Some("CH"); // Centuary

boot_config.custom_value[1] = Some("CL"); // Year

boot_config.custom_value[2] = Some("DH"); // Month

boot_config.custom_value[3] = Some("DL"); // Day

or any other low level operations that can/needs to be done before entering protected mode

Im sure this feature is not needed and these operations can be done in other ways from inside of the operating system but something like this could provide more flexibility

Totally up to you just an idea

bjorn3 commented 1 year ago

Based on the description at http://www.ablmcc.edu.hk/~scy/CIT/8086_bios_and_dos_interrupts.htm INT 11h doesn't seen very useful nowadays. It only lists a couple of legacy things like the amount of floppy disks. For detecting all hardware you will need to do PCIe enumeration and parsing the ACPI tables. Instead of INT 1Ah you can read directly from the IO port of the RTC according to https://wiki.osdev.org/CMOS#Getting_Current_Date_and_Time_from_RTC.