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