initial commit of project, can be booted by UEFI firmware into an infinite loop, next step is to handover machine to an OS
This commit is contained in:
commit
f1827e6b23
|
@ -0,0 +1,5 @@
|
||||||
|
[build]
|
||||||
|
target = "x86_64-unknown-uefi"
|
||||||
|
|
||||||
|
[unstable]
|
||||||
|
build-std = ["core", "compiler_builtins", "alloc"]
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uefi-os-loader"
|
||||||
|
version = "0.1.0"
|
|
@ -0,0 +1,26 @@
|
||||||
|
[package]
|
||||||
|
name = "uefi-os-loader"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "uefi_os_loader"
|
||||||
|
path = "src/uefi_os_loader.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
std = []
|
||||||
|
no_std = []
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
codegen-units = 1
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
|
targets = ["x86_64-unknown-uefi"]
|
|
@ -0,0 +1,75 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
echo "Error occurred, cleaning up."
|
||||||
|
sudo umount -v "${mount_directory}" || true # Unmount if mounted
|
||||||
|
sudo rm -rv "${mount_directory}" || true # Safely remove mount directory if it exists
|
||||||
|
sudo losetup -d "${loop_device}" || true # Detach loop device if attached
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup ERR
|
||||||
|
|
||||||
|
# Build the project
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
# Check if build was successful
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
# If successful, run the emulator script
|
||||||
|
echo "Building disk.img"
|
||||||
|
else
|
||||||
|
echo "Build failed, aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
script_directory=$(dirname "${BASH_SOURCE[0]}")
|
||||||
|
script_directory=$(realpath "$script_directory")
|
||||||
|
disk_image="$HOME/tmpfs/disk.img"
|
||||||
|
block_size="1M"
|
||||||
|
count="100"
|
||||||
|
disk_size="100MiB"
|
||||||
|
bootloader="${script_directory}/../target/x86_64-unknown-uefi/release/uefi_os_loader.efi"
|
||||||
|
|
||||||
|
# Get the size of the bootloader in bytes
|
||||||
|
bootloader_size=$(stat -c%s "$bootloader")
|
||||||
|
|
||||||
|
# Calculate the required disk image size with overhead
|
||||||
|
# Adding 20% overhead for FAT32 filesystem
|
||||||
|
overhead_percentage=20
|
||||||
|
overhead_size=$((bootloader_size * overhead_percentage / 100))
|
||||||
|
total_size=$((bootloader_size + overhead_size))
|
||||||
|
|
||||||
|
# Ensure the size is at least 4MB
|
||||||
|
minimum_size=$((4 * 1024 * 1024))
|
||||||
|
|
||||||
|
# Calculate the final size, which should be the maximum of total_size or minimum_size
|
||||||
|
final_size=$(( total_size > minimum_size ? total_size : minimum_size ))
|
||||||
|
|
||||||
|
# Ensure the size is aligned to a 1MB boundary
|
||||||
|
block_size=$((1024 * 1024))
|
||||||
|
count=$(( (final_size + block_size - 1) / block_size )) # Round up to nearest MB
|
||||||
|
|
||||||
|
echo "Creating disk image with size $((count * block_size)) bytes ($count MB)"
|
||||||
|
|
||||||
|
# Create the disk image
|
||||||
|
dd if=/dev/zero of="$disk_image" bs="$block_size" count="$count" status=progress
|
||||||
|
|
||||||
|
loop_device=$(sudo losetup -fP "$disk_image" --show)
|
||||||
|
echo "Using loop device "$loop_device"."
|
||||||
|
sudo parted --script "$loop_device" mklabel gpt
|
||||||
|
sudo parted --script "$loop_device" mkpart ESP fat32 1MiB 100%
|
||||||
|
sudo parted --script "$loop_device" set 1 esp on
|
||||||
|
sudo mkfs.fat -F 32 "${loop_device}p1" # Ensure the partition number is correct
|
||||||
|
|
||||||
|
mount_directory=$(mktemp --tmpdir="$script_directory" -d)
|
||||||
|
sudo mount -v "${loop_device}p1" "$mount_directory"
|
||||||
|
sudo mkdir -pv "${mount_directory}/EFI/BOOT/"
|
||||||
|
sudo cp -vf "$bootloader" "${mount_directory}/EFI/BOOT/BOOTX64.EFI"
|
||||||
|
sudo umount -v "${mount_directory}"
|
||||||
|
|
||||||
|
echo "Partition and formatting ✓"
|
||||||
|
sudo rm -rv "$mount_directory"
|
||||||
|
sudo losetup -d "$loop_device"
|
||||||
|
|
||||||
|
qemu-system-x86_64 -enable-kvm -nodefaults -vga std -monitor vc:1024x768 -machine q35,accel=kvm -m 1024 -cpu host -drive if=pflash,format=raw,readonly=on,file="${script_directory}/../docs/OVMF_CODE.fd" -drive if=pflash,format=raw,file="${script_directory}/../docs/OVMF_VARS.fd" -drive file="$disk_image",format=raw,index=0,media=disk -boot order=c
|
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// type efi_main = fn(i32) -> i32;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Step 2: Obtain the Function Pointer
|
||||||
|
|
||||||
|
Assuming you're given a pointer to a table that contains function pointers, you'll first need to safely access this table and the specific function pointer. This is often done by casting the provided pointer to a Rust struct that mirrors the layout of the UEFI table. Here's a simplified example:
|
||||||
|
|
||||||
|
rust
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct UefiTable {
|
||||||
|
example_function: *const ExampleFunction,
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Assume this is the function signature provided by UEFI.
|
||||||
|
type GetTime = unsafe extern "efiapi" fn(/* parameters */) -> /* return type */;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct RuntimeServices {
|
||||||
|
// Other fields omitted
|
||||||
|
get_time: *const GetTime,
|
||||||
|
// Other fields omitted
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let runtime_services: *const RuntimeServices = /* provided by UEFI */;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if let Some(services) = runtime_services.as_ref() {
|
||||||
|
if let Some(get_time) = services.get_time.as_ref() {
|
||||||
|
get_time(/* arguments */);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::uefi_structs::{MemoryDescriptor, Status, TPL};
|
||||||
|
|
||||||
|
// Task Priority Services
|
||||||
|
pub type raise_tpl = fn(efi_tpl: TPL) -> Status;
|
||||||
|
pub type restore_tpl = fn() -> ();
|
||||||
|
|
||||||
|
// Memory Services
|
||||||
|
pub type allocate_pages = fn() -> ();
|
||||||
|
pub type free_pages = fn() -> ();
|
||||||
|
pub type allocate_pool = fn() -> ();
|
||||||
|
pub type free_pool = fn() -> ();
|
||||||
|
|
||||||
|
pub type get_memory_map = extern "efiapi" fn(
|
||||||
|
memory_map_size: *mut usize,
|
||||||
|
memory_map: *mut MemoryDescriptor,
|
||||||
|
map_key: *mut usize,
|
||||||
|
descriptor_size: *mut usize,
|
||||||
|
descriptor_version: *mut u32,
|
||||||
|
) -> Status;
|
|
@ -0,0 +1,27 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
mod uefi_functions;
|
||||||
|
mod uefi_structs;
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
use uefi_structs::{Handle, SystemTable};
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
#[export_name = "efi_main"]
|
||||||
|
extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut SystemTable) -> usize {
|
||||||
|
unsafe {
|
||||||
|
let system_table_ref: &SystemTable = &*system_table;
|
||||||
|
let console_out: *mut uefi_structs::SimpleTextOutputProtocol = system_table_ref.console_out;
|
||||||
|
}
|
||||||
|
loop {};
|
||||||
|
0 // EFI_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
use crate::uefi_functions::raise_tpl;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ImageEntryPoint {
|
||||||
|
image_handle: *const i32, // The firmware allocated handle for the UEFI image.
|
||||||
|
system_table_pointer: *const i32, // A pointer to the EFI System Table.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct TableSignatures;
|
||||||
|
|
||||||
|
impl TableSignatures {
|
||||||
|
pub const SYSTEM_TABLE_SIGNATURE: u64 = 0x5453595320494249;
|
||||||
|
pub const BOOT_TABLE_SIGNATURE: u64 = 0x56524553544f4f42;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SystemTableRevisions;
|
||||||
|
|
||||||
|
impl SystemTableRevisions {
|
||||||
|
pub const EFI_2_90_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (90));
|
||||||
|
pub const EFI_2_80_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (80));
|
||||||
|
pub const EFI_2_70_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (70));
|
||||||
|
pub const EFI_2_60_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (60));
|
||||||
|
pub const EFI_2_50_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (50));
|
||||||
|
pub const EFI_2_40_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (40));
|
||||||
|
pub const EFI_2_31_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (31));
|
||||||
|
pub const EFI_2_30_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (30));
|
||||||
|
pub const EFI_2_20_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (20));
|
||||||
|
pub const EFI_2_10_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (10));
|
||||||
|
pub const EFI_2_00_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (00));
|
||||||
|
pub const EFI_1_10_SYSTEM_TABLE_REVISION: u32 = ((1 << 16) | (10));
|
||||||
|
pub const EFI_1_02_SYSTEM_TABLE_REVISION: u32 = ((1 << 16) | (02));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
UEFI uses the EFI System Table, which contains pointers to the runtime and boot services tables. The
|
||||||
|
definition for this table is shown in the following code fragments. Except for the table header, all
|
||||||
|
elements in the service tables are pointers to functions as defined in Section 7 and Section 8. Prior to a
|
||||||
|
call to EFI_BOOT_SERVICES.ExitBootServices(), all of the fields of the EFI System Table are valid.
|
||||||
|
After an operating system has taken control of the platform with a call to ExitBootServices(), only
|
||||||
|
the Hdr, FirmwareVendor, FirmwareRevision, RuntimeServices, NumberOfTableEntries,
|
||||||
|
and ConfigurationTable fields are valid
|
||||||
|
**/
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SystemTable {
|
||||||
|
/// The table header for the EFI System Table. This header contains the EFI_SYSTEM_TABLE_SIGNATURE and EFI_SYSTEM_TABLE_REVISION values along with the size of the EFI_SYSTEM_TABLE structure and a 32-bit CRC to verify that the contents of the EFI System Table are valid.
|
||||||
|
pub efi_table_header: TableHeader,
|
||||||
|
/// A pointer to a null terminated string that identifies the vendor that produces the system firmware for the platform.
|
||||||
|
pub firmware_vendor: *mut u16,
|
||||||
|
/// A firmware vendor specific value that identifies the revision of the system firmware for the platform.
|
||||||
|
pub firmware_revision: SystemTableRevisions,
|
||||||
|
/// The handle for the active console input device. This handle must support EFI_SIMPLE_TEXT_INPUT_PROTOCOL and EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. If there is no active console, these protocols must still be present.
|
||||||
|
pub console_in_handle: Handle,
|
||||||
|
/// A pointer to the EFI_SIMPLE_TEXT_INPUT_PROTOCOL interface that is associated with ConsoleInHandle.
|
||||||
|
pub console_in: *mut SimpleTextInputProtocol,
|
||||||
|
/// The handle for the active console output device. This handle must support the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. If there is no active console, this protocol must still be present.
|
||||||
|
pub console_out_handle: Handle,
|
||||||
|
/// A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL interface that is associated with ConsoleOutHandle.
|
||||||
|
pub console_out: *mut SimpleTextOutputProtocol,
|
||||||
|
/// The handle for the active standard error console device. This handle must support the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. If there is no active console, this protocol must still be present.
|
||||||
|
pub standard_error_handle: Handle,
|
||||||
|
/// A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL interface that is associated with StandardErrorHandle.
|
||||||
|
pub standard_error: *mut SimpleTextOutputProtocol,
|
||||||
|
/// A pointer to the EFI Runtime Services Table.
|
||||||
|
pub runtime_services_table_pointer: *mut RuntimeServicesTable,
|
||||||
|
/// A pointer to the EFI Boot Services Table.
|
||||||
|
pub boot_services_table_pointer: *mut BootServicesTable,
|
||||||
|
/// The number of system configuration tables in the buffer ConfigurationTable.
|
||||||
|
pub number_of_table_entries: usize,
|
||||||
|
/// A pointer to the system configuration tables. The number of entries in the table is NumberOfTableEntries.
|
||||||
|
pub configuration_table: ConfigurationTable,
|
||||||
|
// ACPIConfigurationTablePointer: *mut ACPIConfigurationTable,
|
||||||
|
// SMBIOSConfigurationTable: *mut SMBIOSConfigurationTable,
|
||||||
|
// SALSystemTable: *mut SALSystemTable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
UEFI uses the EFI Boot Services Table, which contains a table header and pointers to all of the boot
|
||||||
|
services. The definition for this table is shown in the following code fragments. Except for the table
|
||||||
|
header, all elements in the EFI Boot Services Tables are prototypes of function pointers to functions as
|
||||||
|
defined in Section 7. The function pointers in this table are not valid after the operating system has taken
|
||||||
|
control of the platform with a call to EFI_BOOT_SERVICES.ExitBootServices().
|
||||||
|
**/
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct BootServicesTable {
|
||||||
|
efi_table_header: TableHeader,
|
||||||
|
load_image: extern "efiapi" fn(boot_policy: bool, parent_image_hanle: Handle),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RuntimeServicesTable {
|
||||||
|
efi_table_header: TableHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ACPIConfigurationTable {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SMBIOSConfigurationTable {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SALSystemTable {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ConfigurationTable {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Handle {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SimpleTextInputProtocol {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SimpleTextOutputProtocol {}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
/*
|
||||||
|
Signature A 64-bit signature that identifies the type of table that follows.
|
||||||
|
Unique signatures have been generated for the EFI System Table, the
|
||||||
|
EFI Boot Services Table, and the EFI Runtime Services Table.
|
||||||
|
Revision The revision of the EFI Specification to which this table conforms.
|
||||||
|
The upper 16 bits of this field contain the major revision value, and
|
||||||
|
the lower 16 bits contain the minor revision value. The minor
|
||||||
|
revision values are binary coded decimals and are limited to the
|
||||||
|
range of 00..99.
|
||||||
|
When printed or displayed UEFI spec revision is referred as (Major
|
||||||
|
revision).(Minor revision upper decimal).(Minor revision lower
|
||||||
|
decimal) or (Major revision).(Minor revision upper decimal) in case
|
||||||
|
Minor revision lower decimal is set to 0. For example:
|
||||||
|
A specification with the revision value ((2<<16) | (30)) would be
|
||||||
|
referred as 2.3;
|
||||||
|
A specification with the revision value ((2<<16) | (31)) would be
|
||||||
|
referred as 2.3.1
|
||||||
|
HeaderSize The size, in bytes, of the entire table including the
|
||||||
|
EFI_TABLE_HEADER.
|
||||||
|
CRC32 The 32-bit CRC for the entire table. This value is computed by setting
|
||||||
|
this field to 0, and computing the 32-bit CRC for HeaderSize bytes.
|
||||||
|
Reserved Reserved field that must be set to 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct TableHeader {
|
||||||
|
signature: u64,
|
||||||
|
revision: u32,
|
||||||
|
header_size: u32,
|
||||||
|
crc32: u32,
|
||||||
|
reserved: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub enum Status {
|
||||||
|
Success,
|
||||||
|
LoadError,
|
||||||
|
InvalidParameter,
|
||||||
|
Unsupported,
|
||||||
|
BadBufferSize,
|
||||||
|
BufferTooSmall,
|
||||||
|
NotReady,
|
||||||
|
DeviceError,
|
||||||
|
WriteProtected,
|
||||||
|
OutOfResources,
|
||||||
|
VolumeCorrupted,
|
||||||
|
VolumeFull,
|
||||||
|
NoMedia,
|
||||||
|
MediaChanged,
|
||||||
|
NotFound,
|
||||||
|
AccessDenied,
|
||||||
|
NoResponce,
|
||||||
|
NoMapping,
|
||||||
|
Timeout,
|
||||||
|
NotStarted,
|
||||||
|
AlreadyStarted,
|
||||||
|
Aborted,
|
||||||
|
IcmpError,
|
||||||
|
TftpError,
|
||||||
|
ProtocolError,
|
||||||
|
IncompatibleVersion,
|
||||||
|
SecurityViolation,
|
||||||
|
CrcError,
|
||||||
|
EndOfMedia,
|
||||||
|
EndOfFile,
|
||||||
|
InvalidLanguage,
|
||||||
|
CompromisedData,
|
||||||
|
IpAddressConflict,
|
||||||
|
HttpError,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct TPL {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct EFIGUID {
|
||||||
|
data1: u32,
|
||||||
|
data2: u16,
|
||||||
|
data3: u16,
|
||||||
|
data4: [u8; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MemoryDescriptor {
|
||||||
|
pub type_: u32,
|
||||||
|
pub physical_start: u64,
|
||||||
|
pub virtual_start: u64,
|
||||||
|
pub number_of_pages: u64,
|
||||||
|
pub attribute: u64,
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
use core;
|
||||||
|
pub type Handle = *mut core::ffi::c_void;
|
||||||
|
pub type PhysicalAddress = u64;
|
Loading…
Reference in New Issue