Integrate EFI bootloader

This commit is contained in:
Jeremy Soller
2022-02-08 14:17:09 -07:00
parent 6936228dff
commit 8c7bb57ef3
26 changed files with 2121 additions and 73 deletions
Generated
+60
View File
@@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "bit_field"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -47,15 +53,33 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "orbclient"
version = "0.3.21"
source = "git+https://gitlab.redox-os.org/redox-os/orbclient.git?branch=no_std#0cf93f23b88fdeff6561a4f9b4b91798c3c3a4a1"
[[package]]
name = "raw-cpuid"
version = "10.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "929f54e29691d4e6a9cc558479de70db7aa3d98cd6fe7ab86d7507aa2886b9d2"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_bootloader"
version = "0.1.0"
dependencies = [
"linked_list_allocator",
"log",
"orbclient",
"redox_syscall",
"redox_uefi",
"redox_uefi_std",
"redoxfs",
"spin",
"x86",
]
[[package]]
@@ -67,6 +91,31 @@ dependencies = [
"bitflags",
]
[[package]]
name = "redox_uefi"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c63a3180c5aba47178029b21c1615fbdf87d2bf682669708ea15e9c71eb8935"
[[package]]
name = "redox_uefi_alloc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524f31a58708b6d0ba2d4e734c1dcf14d287307b09118ff07ef25dd504773c7c"
dependencies = [
"redox_uefi",
]
[[package]]
name = "redox_uefi_std"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a520781d9bb97c9802dd196e7a550fafaeea62d94b2351422c3424981d3a2d"
dependencies = [
"redox_uefi",
"redox_uefi_alloc",
]
[[package]]
name = "redoxfs"
version = "0.4.4"
@@ -107,3 +156,14 @@ name = "uuid"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
[[package]]
name = "x86"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e85eb056bbe47f56d75dc0ccc5fe9c12211ed141292f4d7485d2a7c3dedda09"
dependencies = [
"bit_field",
"bitflags",
"raw-cpuid",
]
+19 -1
View File
@@ -3,9 +3,15 @@ name = "redox_bootloader"
version = "0.1.0"
edition = "2018"
# UEFI uses bin target
[[bin]]
name = "bootloader"
path = "src/main.rs"
# BIOS uses lib target
[lib]
name = "bootloader"
path = "src/lib.rs"
path = "src/main.rs"
crate-type = ["staticlib"]
[dependencies]
@@ -14,3 +20,15 @@ log = "0.4.14"
redox_syscall = "0.2.10"
redoxfs = { version = "0.4.4", default-features = false }
spin = "0.9.2"
[target.'cfg(target_os = "uefi")'.dependencies]
redox_uefi = "0.1.2"
redox_uefi_std = "0.1.5"
[target.'cfg(target_os = "uefi")'.dependencies.orbclient]
git = "https://gitlab.redox-os.org/redox-os/orbclient.git"
branch = "no_std"
features = ["no_std"]
[target."x86_64-unknown-uefi".dependencies]
x86 = "0.43.0"
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 Redox OS
Copyright (c) 2017-2022 Redox OS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+2 -57
View File
@@ -1,40 +1,12 @@
TARGET?=x86-unknown-none
TARGET?=x86_64-unknown-uefi
BUILD=build/$(TARGET)
export RUST_TARGET_PATH=$(CURDIR)/targets
ifeq ($(TARGET),x86-unknown-none)
export LD=ld -m elf_i386
export OBJCOPY=objcopy
export PARTED=parted
export QEMU=qemu-system-x86_64
all: $(BUILD)/bootloader.bin
else
all:
$(error target $(TARGET) not supported by bootloader yet)
endif
include mk/$(TARGET).mk
clean:
rm -rf build
$(BUILD)/libbootloader.a: Cargo.lock Cargo.toml $(shell find src -type f)
mkdir -p $(BUILD)
cargo rustc --lib --target $(TARGET) --release -- -C soft-float -C debuginfo=2 --emit link=$@
$(BUILD)/bootloader.elf: linkers/$(TARGET).ld $(BUILD)/libbootloader.a
mkdir -p $(BUILD)
$(LD) --gc-sections -z max-page-size=0x1000 -T $< -o $@ $(BUILD)/libbootloader.a && \
$(OBJCOPY) --only-keep-debug $@ $@.sym && \
$(OBJCOPY) --strip-debug $@
$(BUILD)/bootloader.bin: $(BUILD)/bootloader.elf $(shell find asm/$(TARGET) -type f)
mkdir -p $(BUILD)
nasm -f bin -o $@ -l $@.lst -D STAGE3=$< -iasm/$(TARGET) asm/$(TARGET)/bootloader.asm
$(BUILD)/filesystem:
mkdir -p $(BUILD)
rm -f $@.partial
@@ -42,36 +14,9 @@ $(BUILD)/filesystem:
fallocate -l 1MiB $@.partial/kernel
mv $@.partial $@
$(BUILD)/filesystem.bin: $(BUILD)/filesystem
mkdir -p $(BUILD)
rm -f $@.partial
fallocate -l 255MiB $@.partial
redoxfs-ar $@.partial $<
mv $@.partial $@
$(BUILD)/harddrive.bin: $(BUILD)/bootloader.bin $(BUILD)/filesystem.bin
mkdir -p $(BUILD)
rm -f $@.partial
fallocate -l 256MiB $@.partial
$(PARTED) -s -a minimal $@.partial mklabel msdos
$(PARTED) -s -a minimal $@.partial mkpart primary 1MiB 100%
dd if=$< of=$@.partial bs=1 count=446 conv=notrunc
dd if=$< of=$@.partial bs=512 skip=1 seek=1 conv=notrunc
dd if=$(BUILD)/filesystem.bin of=$@.partial bs=1MiB seek=1 conv=notrunc
mv $@.partial $@
qemu: $(BUILD)/harddrive.bin
$(QEMU) \
-d cpu_reset \
-d guest_errors \
-no-reboot \
-smp 4 -m 2048 \
-chardev stdio,id=debug,signal=off,mux=on \
-serial chardev:debug \
-mon chardev=debug \
-machine q35 \
-net none \
-enable-kvm \
-cpu host \
-drive file=$<,format=raw
+46
View File
@@ -0,0 +1,46 @@
export LD?=ld
export OBJCOPY?=objcopy
export PARTED?=parted
export QEMU?=qemu-system-x86_64
all: $(BUILD)/bootloader.bin
$(BUILD)/libbootloader.a: Cargo.lock Cargo.toml $(shell find src -type f)
mkdir -p $(BUILD)
cargo rustc --lib --target $(TARGET) --release -- -C soft-float -C debuginfo=2 --emit link=$@
$(BUILD)/bootloader.elf: linkers/$(TARGET).ld $(BUILD)/libbootloader.a
mkdir -p $(BUILD)
$(LD) -m elf_i386 --gc-sections -z max-page-size=0x1000 -T $< -o $@ $(BUILD)/libbootloader.a && \
$(OBJCOPY) --only-keep-debug $@ $@.sym && \
$(OBJCOPY) --strip-debug $@
$(BUILD)/bootloader.bin: $(BUILD)/bootloader.elf $(shell find asm/$(TARGET) -type f)
mkdir -p $(BUILD)
nasm -f bin -o $@ -l $@.lst -D STAGE3=$< -iasm/$(TARGET) asm/$(TARGET)/bootloader.asm
$(BUILD)/harddrive.bin: $(BUILD)/bootloader.bin $(BUILD)/filesystem.bin
mkdir -p $(BUILD)
rm -f $@.partial
fallocate -l 256MiB $@.partial
$(PARTED) -s -a minimal $@.partial mklabel msdos
$(PARTED) -s -a minimal $@.partial mkpart primary 1MiB 100%
dd if=$< of=$@.partial bs=1 count=446 conv=notrunc
dd if=$< of=$@.partial bs=512 skip=1 seek=1 conv=notrunc
dd if=$(BUILD)/filesystem.bin of=$@.partial bs=1MiB seek=1 conv=notrunc
mv $@.partial $@
qemu: $(BUILD)/harddrive.bin
$(QEMU) \
-d cpu_reset \
-d guest_errors \
-no-reboot \
-smp 4 -m 2048 \
-chardev stdio,id=debug,signal=off,mux=on \
-serial chardev:debug \
-mon chardev=debug \
-machine q35 \
-net none \
-enable-kvm \
-cpu host \
-drive file=$<,format=raw
+59
View File
@@ -0,0 +1,59 @@
export LD?=ld
export OBJCOPY?=objcopy
export PARTED?=parted
export QEMU?=qemu-system-x86_64
all: $(BUILD)/bootloader.efi
$(BUILD)/bootloader.efi: Cargo.lock Cargo.toml $(shell find src -type f)
mkdir -p $(BUILD)
cargo rustc \
-Z build-std=core,alloc \
-Z build-std-features=compiler-builtins-mem \
--target $(TARGET) \
--bin bootloader \
--release \
-- \
-C soft-float \
--emit link=$@
$(BUILD)/esp.bin: $(BUILD)/bootloader.efi
mkdir -p $(BUILD)
rm -f $@.partial
fallocate -l 64MiB $@.partial
mkfs.vfat -F 32 $@.partial
mmd -i $@.partial efi
mmd -i $@.partial efi/boot
mcopy -i $@.partial $< ::efi/boot/bootx64.efi
mv $@.partial $@
$(BUILD)/harddrive.bin: $(BUILD)/esp.bin $(BUILD)/filesystem.bin
mkdir -p $(BUILD)
rm -f $@.partial
fallocate -l 320MiB $@.partial
$(PARTED) -s -a minimal $@.partial mklabel gpt
$(PARTED) -s -a minimal $@.partial mkpart ESP FAT32 1MiB 65MiB
$(PARTED) -s -a minimal $@.partial mkpart REDOXFS 65MiB 100%
$(PARTED) -s -a minimal $@.partial toggle 1 boot
dd if=$(BUILD)/esp.bin of=$@.partial bs=1MiB seek=1 conv=notrunc
dd if=$(BUILD)/filesystem.bin of=$@.partial bs=1MiB seek=65 conv=notrunc
mv $@.partial $@
$(BUILD)/firmware.rom:
cp /usr/share/OVMF/OVMF_CODE.fd $@
qemu: $(BUILD)/harddrive.bin $(BUILD)/firmware.rom
$(QEMU) \
-d cpu_reset \
-d guest_errors \
-no-reboot \
-smp 4 -m 2048 \
-chardev stdio,id=debug,signal=off,mux=on \
-serial chardev:debug \
-mon chardev=debug \
-machine q35 \
-net none \
-enable-kvm \
-cpu host \
-bios $(BUILD)/firmware.rom \
-drive file=$<,format=raw
-1
View File
@@ -1,4 +1,3 @@
pub use self::paging::paging_create;
mod paging;
mod panic;
-13
View File
@@ -1,13 +0,0 @@
#![no_std]
#![feature(asm)]
#![feature(lang_items)]
#![feature(llvm_asm)]
#[macro_use]
extern crate alloc;
#[macro_use]
mod os;
mod arch;
mod logger;
+23
View File
@@ -0,0 +1,23 @@
#![no_std]
#![feature(asm)]
#![feature(lang_items)]
#![feature(llvm_asm)]
#![cfg_attr(
target_os = "uefi",
no_main,
feature(control_flow_enum),
feature(try_trait_v2),
)]
#[cfg_attr(target_os = "none", macro_use)]
extern crate alloc;
#[cfg(target_os = "uefi")]
#[macro_use]
extern crate uefi_std as std;
#[macro_use]
mod os;
mod arch;
mod logger;
+6
View File
@@ -27,6 +27,7 @@ mod macros;
mod disk;
mod memory_map;
mod panic;
mod thunk;
mod vbe;
mod vga;
@@ -331,6 +332,11 @@ pub unsafe extern "C" fn kstart(
}
println!("\rKernel: {}/{} MiB", i / 1024 / 1024, size / 1024 / 1024);
let magic = &kernel[..4];
if magic != b"\x7FELF" {
panic!("Kernel has invalid magic number {:#X?}", magic);
}
kernel
};
+20
View File
@@ -0,0 +1,20 @@
use uefi::memory::MemoryDescriptor;
pub unsafe fn memory_map() -> usize {
let uefi = std::system_table();
let mut map: [u8; 65536] = [0; 65536];
let mut map_size = map.len();
let mut map_key = 0;
let mut descriptor_size = 0;
let mut descriptor_version = 0;
let _ = (uefi.BootServices.GetMemoryMap)(
&mut map_size,
map.as_mut_ptr() as *mut MemoryDescriptor,
&mut map_key,
&mut descriptor_size,
&mut descriptor_version
);
map_key
}
+269
View File
@@ -0,0 +1,269 @@
use core::{mem, ptr};
use orbclient::{Color, Renderer};
use std::fs::find;
use std::proto::Protocol;
use uefi::guid::Guid;
use uefi::status::{Error, Result};
use crate::display::{Display, ScaledDisplay, Output};
use crate::image::{self, Image};
use crate::key::{key, Key};
use crate::redoxfs;
use crate::text::TextDisplay;
use self::memory_map::memory_map;
use self::paging::paging;
mod memory_map;
mod paging;
mod partitions;
static KERNEL: &'static str = concat!("\\", env!("BASEDIR"), "\\kernel");
static SPLASHBMP: &'static [u8] = include_bytes!("../../../res/splash.bmp");
static KERNEL_OFFSET: u64 = 0xFFFF_FF00_0000_0000;
static KERNEL_PHYSICAL: u64 = 0x4000_0000;
static mut KERNEL_SIZE: u64 = 0;
static mut KERNEL_ENTRY: u64 = 0;
static mut DTB_PHYSICAL: u64 = 0;
#[no_mangle]
pub extern "C" fn __chkstk() {
//TODO
}
unsafe fn exit_boot_services(key: usize) {
let handle = std::handle();
let uefi = std::system_table();
let _ = (uefi.BootServices.ExitBootServices)(handle, key);
}
unsafe fn enter() -> ! {
let entry_fn: extern "C" fn(dtb: u64) -> ! = mem::transmute((
KERNEL_PHYSICAL + KERNEL_ENTRY - KERNEL_OFFSET
));
entry_fn(DTB_PHYSICAL);
}
fn get_correct_block_io() -> Result<redoxfs::Disk> {
// Get all BlockIo handles.
let mut handles = vec! [uefi::Handle(0); 128];
let mut size = handles.len() * mem::size_of::<uefi::Handle>();
(std::system_table().BootServices.LocateHandle)(uefi::boot::LocateSearchType::ByProtocol, &uefi::guid::BLOCK_IO_GUID, 0, &mut size, handles.as_mut_ptr())?;
let max_size = size / mem::size_of::<uefi::Handle>();
let actual_size = std::cmp::min(handles.len(), max_size);
// Return the handle that seems bootable.
for handle in handles.into_iter().take(actual_size) {
let block_io = redoxfs::Disk::handle_protocol(handle)?;
if !block_io.0.Media.LogicalPartition {
continue;
}
let part = partitions::PartitionProto::handle_protocol(handle)?.0;
if part.sys == 1 {
continue;
}
assert_eq!({part.rev}, partitions::PARTITION_INFO_PROTOCOL_REVISION);
if part.ty == partitions::PartitionProtoDataTy::Gpt as u32 {
let gpt = unsafe { part.info.gpt };
assert_ne!(gpt.part_ty_guid, partitions::ESP_GUID, "detected esp partition again");
if gpt.part_ty_guid == partitions::REDOX_FS_GUID || gpt.part_ty_guid == partitions::LINUX_FS_GUID {
return Ok(block_io);
}
} else if part.ty == partitions::PartitionProtoDataTy::Mbr as u32 {
let mbr = unsafe { part.info.mbr };
if mbr.ty == 0x83 {
return Ok(block_io);
}
} else {
continue;
}
}
panic!("Couldn't find handle for partition");
}
static DTB_GUID: Guid = Guid(0xb1b621d5, 0xf19c, 0x41a5, [0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0]);
fn find_dtb() -> Result<()> {
let cfg_tables = std::system_table().config_tables();
for cfg_table in cfg_tables.iter() {
if cfg_table.VendorGuid == DTB_GUID {
unsafe {
DTB_PHYSICAL = cfg_table.VendorTable as u64;
println!("DTB: {:X}", DTB_PHYSICAL);
}
return Ok(());
}
}
println!("Failed to find DTB");
Err(Error::NotFound)
}
fn redoxfs() -> Result<redoxfs::FileSystem> {
// TODO: Scan multiple partitions for a kernel.
redoxfs::FileSystem::open(get_correct_block_io()?)
}
const MB: usize = 1024 * 1024;
fn inner() -> Result<()> {
find_dtb()?;
{
println!("Loading Kernel...");
let (kernel, mut env): (Vec<u8>, String) = {
let (_i, mut kernel_file) = find(KERNEL)?;
let info = kernel_file.info()?;
let len = info.FileSize;
let mut kernel = Vec::with_capacity(len as usize);
let mut buf = vec![0; 4 * MB];
loop {
let percent = kernel.len() as u64 * 100 / len;
print!("\r{}% - {} MB", percent, kernel.len() / MB);
let count = kernel_file.read(&mut buf)?;
if count == 0 {
break;
}
kernel.extend(&buf[.. count]);
}
println!("");
(kernel, String::new())
};
println!("Copying Kernel...");
unsafe {
KERNEL_SIZE = kernel.len() as u64;
println!("Size: {}", KERNEL_SIZE);
KERNEL_ENTRY = *(kernel.as_ptr().offset(0x18) as *const u64);
println!("Entry: {:X}", KERNEL_ENTRY);
ptr::copy(kernel.as_ptr(), KERNEL_PHYSICAL as *mut u8, kernel.len());
}
println!("Done!");
}
unsafe {
let key = memory_map();
exit_boot_services(key);
}
unsafe {
asm!("msr daifset, #2");
paging();
}
unsafe {
enter();
}
}
fn select_mode(output: &mut Output) -> Result<u32> {
loop {
for i in 0..output.0.Mode.MaxMode {
let mut mode_ptr = ::core::ptr::null_mut();
let mut mode_size = 0;
(output.0.QueryMode)(output.0, i, &mut mode_size, &mut mode_ptr)?;
let mode = unsafe { &mut *mode_ptr };
let w = mode.HorizontalResolution;
let h = mode.VerticalResolution;
print!("\r{}x{}: Is this OK? (y)es/(n)o", w, h);
if key(true)? == Key::Character('y') {
println!("");
return Ok(i);
}
}
}
}
fn pretty_pipe<T, F: FnMut() -> Result<T>>(splash: &Image, f: F) -> Result<T> {
let mut display = Display::new(Output::one()?);
let mut display = ScaledDisplay::new(&mut display);
{
let bg = Color::rgb(0x4a, 0xa3, 0xfd);
display.set(bg);
{
let x = (display.width() as i32 - splash.width() as i32)/2;
let y = 16;
splash.draw(&mut display, x, y);
}
{
let prompt = format!(
"Redox Bootloader {} {}",
env!("CARGO_PKG_VERSION"),
env!("TARGET").split('-').next().unwrap_or("")
);
let mut x = (display.width() as i32 - prompt.len() as i32 * 8)/2;
let y = display.height() as i32 - 32;
for c in prompt.chars() {
display.char(x, y, c, Color::rgb(0xff, 0xff, 0xff));
x += 8;
}
}
display.sync();
}
{
let cols = 80;
let off_x = (display.width() as i32 - cols as i32 * 8)/2;
let off_y = 16 + splash.height() as i32 + 16;
let rows = (display.height() as i32 - 64 - off_y - 1) as usize/16;
display.rect(off_x, off_y, cols as u32 * 8, rows as u32 * 16, Color::rgb(0, 0, 0));
display.sync();
let mut text = TextDisplay::new(display);
text.off_x = off_x;
text.off_y = off_y;
text.cols = cols;
text.rows = rows;
text.pipe(f)
}
}
pub fn main() -> Result<()> {
inner()?;
/* TODO
if let Ok(mut output) = Output::one() {
let mut splash = Image::new(0, 0);
{
println!("Loading Splash...");
if let Ok(image) = image::bmp::parse(&SPLASHBMP) {
splash = image;
}
println!(" Done");
}
/* TODO
let mode = pretty_pipe(&splash, || {
select_mode(&mut output)
})?;
(output.0.SetMode)(output.0, mode)?;
*/
pretty_pipe(&splash, inner)?;
} else {
inner()?;
}
*/
Ok(())
}
+10
View File
@@ -0,0 +1,10 @@
pub unsafe fn paging() {
// Disable MMU
asm!(
"mrs x0, sctlr_el1",
"bic x0, x0, 1",
"msr sctlr_el1, x0",
"isb",
lateout("x0") _
);
}
+63
View File
@@ -0,0 +1,63 @@
use std::proto::Protocol;
#[repr(packed)]
#[derive(Clone, Copy, Debug)]
pub struct PartitionProtoInfoMbr {
pub boot: u8,
pub chs_start: [u8; 3],
pub ty: u8,
pub chs_end: [u8; 3],
pub start_lba: u32,
pub lba_size: u32,
}
#[repr(packed)]
#[derive(Clone, Copy)]
pub struct PartitionProtoInfoGpt {
pub part_ty_guid: [u8; 16],
pub uniq_guid: [u8; 16],
pub start_lba: u64,
pub end_lba: u64,
pub attrs: u64,
pub name: [u16; 36],
// reserved until end of block
}
#[repr(packed)]
#[derive(Clone, Copy)]
pub union PartitionProtoDataInfo {
pub mbr: PartitionProtoInfoMbr,
pub gpt: PartitionProtoInfoGpt,
}
#[repr(packed)]
pub struct PartitionProtoData {
pub rev: u32,
pub ty: u32,
pub sys: u8,
pub resv: [u8; 7],
pub info: PartitionProtoDataInfo,
}
pub const PARTITION_INFO_PROTOCOL_REVISION: u32 = 0x1000;
pub const ESP_GUID: [u8; 16] = [0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11, 0xba, 0x4b, 0x0, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b]; // c12a7328-f81f-11d2-bA4b-00a0c93ec93b
pub const LINUX_FS_GUID: [u8; 16] = [0xaf, 0x3d, 0xc6, 0xf, 0x83, 0x84, 0x72, 0x47, 0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4]; // 0fc63daf-8483-4772-8e79-3d69d8477de4
pub const REDOX_FS_GUID: [u8; 16] = [0xfd, 0x98, 0x78, 0x52, 0xe3, 0xff, 0xc2, 0x42, 0xe3, 0x96, 0x10, 0x5b, 0xa6, 0x3f, 0x5a, 0xbf]; // 527898fd-ffe3-42c2-96e3-bf5a3fa65b10
#[repr(u32)]
pub enum PartitionProtoDataTy {
Other = 0,
Mbr = 1,
Gpt = 2,
}
pub struct PartitionProto(pub &'static mut PartitionProtoData);
impl Protocol<PartitionProtoData> for PartitionProto {
fn guid() -> uefi::guid::Guid {
uefi::guid::Guid(0x8cf2f62c, 0xbc9b, 0x4821, [0x80, 0x8d, 0xec, 0x9e, 0xc4, 0x21, 0xa1, 0xa0])
}
fn new(inner: &'static mut PartitionProtoData) -> Self {
Self(inner)
}
}
+9
View File
@@ -0,0 +1,9 @@
#[cfg(target_arch = "aarch64")]
mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use self::aarch64::*;
#[cfg(target_arch = "x86_64")]
mod x86_64;
#[cfg(target_arch = "x86_64")]
pub use self::x86_64::*;
+80
View File
@@ -0,0 +1,80 @@
use core::{mem, ptr};
use uefi::memory::{MemoryDescriptor, MemoryType};
static MM_BASE: u64 = 0x500;
static MM_SIZE: u64 = 0x4B00;
/// Memory does not exist
pub const MEMORY_AREA_NULL: u32 = 0;
/// Memory is free to use
pub const MEMORY_AREA_FREE: u32 = 1;
/// Memory is reserved
pub const MEMORY_AREA_RESERVED: u32 = 2;
/// Memory is used by ACPI, and can be reclaimed
pub const MEMORY_AREA_ACPI: u32 = 3;
/// A memory map area
#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
pub struct MemoryArea {
pub base_addr: u64,
pub length: u64,
pub _type: u32,
pub acpi: u32
}
pub unsafe fn memory_map() -> usize {
let uefi = std::system_table();
ptr::write_bytes(MM_BASE as *mut u8, 0, MM_SIZE as usize);
let mut map: [u8; 65536] = [0; 65536];
let mut map_size = map.len();
let mut map_key = 0;
let mut descriptor_size = 0;
let mut descriptor_version = 0;
let _ = (uefi.BootServices.GetMemoryMap)(
&mut map_size,
map.as_mut_ptr() as *mut MemoryDescriptor,
&mut map_key,
&mut descriptor_size,
&mut descriptor_version
);
if descriptor_size >= mem::size_of::<MemoryDescriptor>() {
for i in 0..map_size/descriptor_size {
let descriptor_ptr = map.as_ptr().offset((i * descriptor_size) as isize);
let descriptor = & *(descriptor_ptr as *const MemoryDescriptor);
let descriptor_type: MemoryType = mem::transmute(descriptor.Type);
let bios_type = match descriptor_type {
MemoryType::EfiLoaderCode |
MemoryType::EfiLoaderData |
MemoryType::EfiBootServicesCode |
MemoryType::EfiBootServicesData |
MemoryType::EfiConventionalMemory => {
MEMORY_AREA_FREE
},
_ => {
MEMORY_AREA_RESERVED
}
};
let bios_area = MemoryArea {
base_addr: descriptor.PhysicalStart.0,
length: descriptor.NumberOfPages * 4096,
_type: bios_type,
acpi: 0,
};
ptr::write((MM_BASE as *mut MemoryArea).offset(i as isize), bios_area);
}
} else {
println!("Unknown memory descriptor size: {}", descriptor_size);
}
map_key
}
+530
View File
@@ -0,0 +1,530 @@
use core::{cmp, mem, ptr, slice};
use orbclient::{Color, Renderer};
use std::fs::find;
use std::proto::Protocol;
use std::string::String;
use std::vec::Vec;
use uefi::status::{Error, Result};
use uefi::guid::GuidKind;
use uefi::memory::MemoryType;
use super::super::{
disk::DiskEfi,
display::{Display, ScaledDisplay, Output},
key::{key, Key},
text::TextDisplay,
};
use self::memory_map::memory_map;
use self::paging::{paging_create, paging_enter};
mod memory_map;
mod paging;
mod partitions;
static PHYS_OFFSET: u64 = 0xFFFF800000000000;
static mut KERNEL_PHYS: u64 = 0;
static mut KERNEL_SIZE: u64 = 0;
static mut KERNEL_ENTRY: u64 = 0;
static mut STACK_PHYS: u64 = 0;
static STACK_SIZE: u64 = 0x20000;
static mut ENV_PHYS: u64 = 0;
static mut ENV_SIZE: u64 = 0;
static mut RSDPS_AREA: Option<Vec<u8>> = None;
#[repr(packed)]
pub struct KernelArgs {
kernel_base: u64,
kernel_size: u64,
stack_base: u64,
stack_size: u64,
env_base: u64,
env_size: u64,
acpi_rsdps_base: u64,
acpi_rsdps_size: u64,
}
unsafe fn allocate_zero_pages(pages: usize) -> Result<usize> {
let uefi = std::system_table();
let mut ptr = 0;
(uefi.BootServices.AllocatePages)(
0, // AllocateAnyPages
MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list
pages,
&mut ptr
)?;
ptr::write_bytes(ptr as *mut u8, 0, 4096);
Ok(ptr)
}
unsafe fn exit_boot_services(key: usize) {
let handle = std::handle();
let uefi = std::system_table();
let _ = (uefi.BootServices.ExitBootServices)(handle, key);
}
unsafe fn enter() -> ! {
let args = KernelArgs {
kernel_base: KERNEL_PHYS,
kernel_size: KERNEL_SIZE,
stack_base: STACK_PHYS,
stack_size: STACK_SIZE,
env_base: ENV_PHYS,
env_size: ENV_SIZE,
acpi_rsdps_base: RSDPS_AREA.as_ref().map(Vec::as_ptr).unwrap_or(core::ptr::null()) as usize as u64 + PHYS_OFFSET,
acpi_rsdps_size: RSDPS_AREA.as_ref().map(Vec::len).unwrap_or(0) as u64,
};
let entry_fn: extern "sysv64" fn(args_ptr: *const KernelArgs) -> ! = mem::transmute(KERNEL_ENTRY);
entry_fn(&args);
}
fn get_correct_block_io() -> Result<DiskEfi> {
// Get all BlockIo handles.
let mut handles = vec! [uefi::Handle(0); 128];
let mut size = handles.len() * mem::size_of::<uefi::Handle>();
(std::system_table().BootServices.LocateHandle)(uefi::boot::LocateSearchType::ByProtocol, &uefi::guid::BLOCK_IO_GUID, 0, &mut size, handles.as_mut_ptr())?;
let max_size = size / mem::size_of::<uefi::Handle>();
let actual_size = std::cmp::min(handles.len(), max_size);
// Return the handle that seems bootable.
for handle in handles.into_iter().take(actual_size) {
let block_io = DiskEfi::handle_protocol(handle)?;
if !block_io.0.Media.LogicalPartition {
continue;
}
let part = partitions::PartitionProto::handle_protocol(handle)?.0;
if part.sys == 1 {
continue;
}
assert_eq!({part.rev}, partitions::PARTITION_INFO_PROTOCOL_REVISION);
if part.ty == partitions::PartitionProtoDataTy::Gpt as u32 {
let gpt = unsafe { part.info.gpt };
assert_ne!(gpt.part_ty_guid, partitions::ESP_GUID, "detected esp partition again");
if gpt.part_ty_guid == partitions::REDOX_FS_GUID || gpt.part_ty_guid == partitions::LINUX_FS_GUID {
return Ok(block_io);
}
} else if part.ty == partitions::PartitionProtoDataTy::Mbr as u32 {
let mbr = unsafe { part.info.mbr };
if mbr.ty == 0x83 {
return Ok(block_io);
}
} else {
continue;
}
}
panic!("Couldn't find handle for partition");
}
struct Invalid;
fn validate_rsdp(address: usize, v2: bool) -> core::result::Result<usize, Invalid> {
#[repr(packed)]
#[derive(Clone, Copy, Debug)]
struct Rsdp {
signature: [u8; 8], // b"RSD PTR "
chksum: u8,
oem_id: [u8; 6],
revision: u8,
rsdt_addr: u32,
// the following fields are only available for ACPI 2.0, and are reserved otherwise
length: u32,
xsdt_addr: u64,
extended_chksum: u8,
_rsvd: [u8; 3],
}
// paging is not enabled at this stage; we can just read the physical address here.
let rsdp_bytes = unsafe { core::slice::from_raw_parts(address as *const u8, core::mem::size_of::<Rsdp>()) };
let rsdp = unsafe { (rsdp_bytes.as_ptr() as *const Rsdp).as_ref::<'static>().unwrap() };
println!("RSDP: {:?}", rsdp);
if rsdp.signature != *b"RSD PTR " {
return Err(Invalid);
}
let mut base_sum = 0u8;
for base_byte in &rsdp_bytes[..20] {
base_sum = base_sum.wrapping_add(*base_byte);
}
if base_sum != 0 {
return Err(Invalid);
}
if rsdp.revision == 2 {
let mut extended_sum = 0u8;
for byte in rsdp_bytes {
extended_sum = extended_sum.wrapping_add(*byte);
}
if extended_sum != 0 {
return Err(Invalid);
}
}
let length = if rsdp.revision == 2 { rsdp.length as usize } else { core::mem::size_of::<Rsdp>() };
Ok(length)
}
fn find_acpi_table_pointers() -> Result<()> {
let rsdps_area = unsafe {
RSDPS_AREA = Some(Vec::new());
RSDPS_AREA.as_mut().unwrap()
};
let cfg_tables = std::system_table().config_tables();
for (address, v2) in cfg_tables.iter().find_map(|cfg_table| if cfg_table.VendorGuid.kind() == GuidKind::Acpi { Some((cfg_table.VendorTable, false)) } else if cfg_table.VendorGuid.kind() == GuidKind::Acpi2 { Some((cfg_table.VendorTable, true)) } else { None }) {
match validate_rsdp(address, v2) {
Ok(length) => {
let align = 8;
rsdps_area.extend(&u32::to_ne_bytes(length as u32));
rsdps_area.extend(unsafe { core::slice::from_raw_parts(address as *const u8, length) });
rsdps_area.resize(((rsdps_area.len() + (align - 1)) / align) * align, 0u8);
}
Err(_) => println!("Found RSDP that wasn't valid at {:p}", address as *const u8),
}
}
Ok(())
}
fn redoxfs() -> Result<redoxfs::FileSystem<DiskEfi>> {
// TODO: Scan multiple partitions for a kernel.
// TODO: pass block_opt for performance reasons
redoxfs::FileSystem::open(get_correct_block_io()?, None).map_err(|_| Error::DeviceError)
}
const MB: usize = 1024 * 1024;
fn inner() -> Result<()> {
//TODO: detect page size?
let page_size = 4096;
{
let mut env = String::new();
if let Ok(output) = Output::one() {
let mode = &output.0.Mode;
env.push_str(&format!("FRAMEBUFFER_ADDR={:016x}\n", mode.FrameBufferBase));
env.push_str(&format!("FRAMEBUFFER_WIDTH={:016x}\n", mode.Info.HorizontalResolution));
env.push_str(&format!("FRAMEBUFFER_HEIGHT={:016x}\n", mode.Info.VerticalResolution));
}
println!("Loading Kernel...");
let kernel = {
let mut fs = redoxfs()?;
let root = fs.header.1.root;
let node = fs.find_node("kernel", root).map_err(|_| Error::DeviceError)?;
let len = fs.node_len(node.0).map_err(|_| Error::DeviceError)?;
let kernel = unsafe {
let ptr = allocate_zero_pages((len as usize + page_size - 1) / page_size)?;
println!("{:X}", ptr);
slice::from_raw_parts_mut(
ptr as *mut u8,
len as usize
)
};
let mut i = 0;
for mut chunk in kernel.chunks_mut(4 * MB) {
print!("\r{}% - {} MB", i as u64 * 100 / len, i / MB);
let count = fs.read_node(node.0, i as u64, &mut chunk, 0, 0).map_err(|_| Error::DeviceError)?;
if count == 0 {
break;
}
//TODO: return error instead of assert
assert_eq!(count, chunk.len());
i += count;
}
println!("\r{}% - {} MB", i as u64 * 100 / len, i / MB);
env.push_str(&format!("REDOXFS_BLOCK={:016x}\n", fs.block));
env.push_str("REDOXFS_UUID=");
for i in 0..fs.header.1.uuid.len() {
if i == 4 || i == 6 || i == 8 || i == 10 {
env.push('-');
}
env.push_str(&format!("{:>02x}", fs.header.1.uuid[i]));
}
kernel
};
unsafe {
KERNEL_PHYS = kernel.as_ptr() as u64;
KERNEL_SIZE = kernel.len() as u64;
KERNEL_ENTRY = *(kernel.as_ptr().offset(0x18) as *const u64);
println!("Kernel {:X}:{:X} entry {:X}", KERNEL_PHYS, KERNEL_SIZE, KERNEL_ENTRY);
}
println!("Allocating stack {:X}", STACK_SIZE);
unsafe {
STACK_PHYS = allocate_zero_pages(STACK_SIZE as usize / page_size)? as u64;
println!("Stack {:X}:{:X}", STACK_PHYS, STACK_SIZE);
}
println!("Allocating env {:X}", env.len());
unsafe {
ENV_PHYS = allocate_zero_pages((env.len() + page_size - 1) / page_size)? as u64;
ENV_SIZE = env.len() as u64;
ptr::copy(env.as_ptr(), ENV_PHYS as *mut u8, env.len());
println!("Env {:X}:{:X}", ENV_PHYS, ENV_SIZE);
}
println!("Parsing and writing ACPI RSDP structures.");
find_acpi_table_pointers();
println!("Done!");
}
println!("Creating page tables");
let page_phys = unsafe {
paging_create(KERNEL_PHYS)?
};
println!("Entering kernel");
unsafe {
let key = memory_map();
exit_boot_services(key);
}
unsafe {
llvm_asm!("cli" : : : "memory" : "intel", "volatile");
paging_enter(page_phys);
}
unsafe {
llvm_asm!("mov rsp, $0" : : "r"(STACK_PHYS + PHYS_OFFSET + STACK_SIZE) : "memory" : "intel", "volatile");
enter();
}
}
fn draw_text(display: &mut ScaledDisplay, mut x: i32, y: i32, text: &str, color: Color) {
for c in text.chars() {
display.char(x, y, c, color);
x += 8;
}
}
fn draw_background(display: &mut ScaledDisplay) {
let bg = Color::rgb(0x4a, 0xa3, 0xfd);
display.set(bg);
{
let prompt = format!(
"Redox Bootloader {} {}",
env!("CARGO_PKG_VERSION"),
env!("TARGET").split('-').next().unwrap_or("")
);
let x = (display.width() as i32 - prompt.len() as i32 * 8)/2;
let y = display.height() as i32 - 32;
draw_text(display, x, y, &prompt, Color::rgb(0xff, 0xff, 0xff));
}
}
fn select_mode(output: &mut Output) -> Result<()> {
// Read all available modes
let mut modes = Vec::new();
for i in 0..output.0.Mode.MaxMode {
let mut mode_ptr = ::core::ptr::null_mut();
let mut mode_size = 0;
(output.0.QueryMode)(output.0, i, &mut mode_size, &mut mode_ptr)?;
let mode = unsafe { &mut *mode_ptr };
let w = mode.HorizontalResolution;
let h = mode.VerticalResolution;
let mut aspect_w = w;
let mut aspect_h = h;
for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) {
while aspect_w % i == 0 && aspect_h % i == 0 {
aspect_w /= i;
aspect_h /= i;
}
}
//TODO: support resolutions that are not perfect multiples of 4
if w % 4 != 0 {
continue;
}
modes.push((i, w, h, format!("{:>4}x{:<4} {:>3}:{:<3}", w, h, aspect_w, aspect_h)));
}
// Sort modes by pixel area, reversed
modes.sort_by(|a, b| (b.1 * b.2).cmp(&(a.1 * a.2)));
// Find current mode index
let mut selected = output.0.Mode.Mode;
// If there are no modes from querymode, don't change mode
if modes.is_empty() {
return Ok(());
}
let white = Color::rgb(0xff, 0xff, 0xff);
let black = Color::rgb(0x00, 0x00, 0x00);
let rows = 12;
loop {
{
// Create a scaled display
let mut display = Display::new(output);
let mut display = ScaledDisplay::new(&mut display);
draw_background(&mut display);
let off_x = (display.width() as i32 - 60 * 8)/2;
let mut off_y = 16;
draw_text(
&mut display,
off_x, off_y,
"Arrow keys and enter select mode",
white
);
off_y += 24;
let mut row = 0;
let mut col = 0;
for (i, w, h, text) in modes.iter() {
if row >= rows as i32 {
col += 1;
row = 0;
}
let x = off_x + col * 20 * 8;
let y = off_y + row * 16;
let fg = if *i == selected {
display.rect(x - 8, y, text.len() as u32 * 8 + 16, 16, white);
black
} else {
white
};
draw_text(&mut display, x, y, text, fg);
row += 1;
}
display.sync();
}
match key(true)? {
Key::Left => {
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
if mode_i < rows {
while mode_i < modes.len() {
mode_i += rows;
}
}
mode_i -= rows;
if let Some(new) = modes.get(mode_i) {
selected = new.0;
}
}
},
Key::Right => {
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
mode_i += rows;
if mode_i >= modes.len() {
mode_i = mode_i % rows;
}
if let Some(new) = modes.get(mode_i) {
selected = new.0;
}
}
},
Key::Up => {
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
if mode_i % rows == 0 {
mode_i += rows;
if mode_i > modes.len() {
mode_i = modes.len();
}
}
mode_i -= 1;
if let Some(new) = modes.get(mode_i) {
selected = new.0;
}
}
},
Key::Down => {
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
mode_i += 1;
if mode_i % rows == 0 {
mode_i -= rows;
}
if mode_i >= modes.len() {
mode_i = mode_i - mode_i % rows;
}
if let Some(new) = modes.get(mode_i) {
selected = new.0;
}
}
},
Key::Enter => {
(output.0.SetMode)(output.0, selected)?;
return Ok(());
},
_ => (),
}
}
}
fn pretty_pipe<T, F: FnMut() -> Result<T>>(output: &mut Output, f: F) -> Result<T> {
let mut display = Display::new(output);
let mut display = ScaledDisplay::new(&mut display);
draw_background(&mut display);
display.sync();
{
let cols = 80;
let off_x = (display.width() as i32 - cols as i32 * 8)/2;
let off_y = 16;
let rows = (display.height() as i32 - 64 - off_y - 1) as usize/16;
display.rect(off_x, off_y, cols as u32 * 8, rows as u32 * 16, Color::rgb(0, 0, 0));
display.sync();
let mut text = TextDisplay::new(display);
text.off_x = off_x;
text.off_y = off_y;
text.cols = cols;
text.rows = rows;
text.pipe(f)
}
}
pub fn main() -> Result<()> {
if let Ok(mut output) = Output::one() {
select_mode(&mut output)?;
pretty_pipe(&mut output, inner)?;
} else {
inner()?;
}
Ok(())
}
+101
View File
@@ -0,0 +1,101 @@
use core::slice;
use x86::{
controlregs::{self, Cr0, Cr4},
msr,
};
use uefi::status::Result;
unsafe fn paging_allocate() -> Result<&'static mut [u64]> {
let ptr = super::allocate_zero_pages(1)?;
Ok(slice::from_raw_parts_mut(
ptr as *mut u64,
512 // page size divided by u64 size
))
}
pub unsafe fn paging_create(kernel_phys: u64) -> Result<u64> {
// Create PML4
let pml4 = paging_allocate()?;
// Recursive mapping for compatibility
pml4[511] = pml4.as_ptr() as u64 | 1 << 1 | 1;
{
// Create PDP for identity mapping
let pdp = paging_allocate()?;
// Link first user and first kernel PML4 entry to PDP
pml4[0] = pdp.as_ptr() as u64 | 1 << 1 | 1;
pml4[256] = pdp.as_ptr() as u64 | 1 << 1 | 1;
// Identity map 8 GiB pages
for pdp_i in 0..8 {
let pd = paging_allocate()?;
pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1;
for pd_i in 0..pd.len() {
let pt = paging_allocate()?;
pd[pd_i] = pt.as_ptr() as u64 | 1 << 1 | 1;
for pt_i in 0..pt.len() {
let addr =
pdp_i as u64 * 0x4000_0000 +
pd_i as u64 * 0x20_0000 +
pt_i as u64 * 0x1000;
pt[pt_i] = addr | 1 << 1 | 1;
}
}
}
}
{
// Create PDP for kernel mapping
let pdp = paging_allocate()?;
// Link second to last PML4 entry to PDP
pml4[510] = pdp.as_ptr() as u64 | 1 << 1 | 1;
// Map 1 GiB at kernel offset
for pdp_i in 0..1 {
let pd = paging_allocate()?;
pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1;
for pd_i in 0..pd.len() {
let pt = paging_allocate()?;
pd[pd_i] = pt.as_ptr() as u64 | 1 << 1 | 1;
for pt_i in 0..pt.len() {
let addr =
pdp_i as u64 * 0x4000_0000 +
pd_i as u64 * 0x20_0000 +
pt_i as u64 * 0x1000 +
kernel_phys;
pt[pt_i] = addr | 1 << 1 | 1;
}
}
}
}
Ok(pml4.as_ptr() as u64)
}
pub unsafe fn paging_enter(page_phys: u64) {
// Enable OSXSAVE, FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension
let mut cr4 = controlregs::cr4();
cr4 |= Cr4::CR4_ENABLE_OS_XSAVE
| Cr4::CR4_ENABLE_SSE
| Cr4::CR4_ENABLE_GLOBAL_PAGES
| Cr4::CR4_ENABLE_PAE
| Cr4::CR4_ENABLE_PSE;
controlregs::cr4_write(cr4);
// Enable Long mode and NX bit
let mut efer = msr::rdmsr(msr::IA32_EFER);
efer |= 1 << 11 | 1 << 8;
msr::wrmsr(msr::IA32_EFER, efer);
// Set new page map
controlregs::cr3_write(page_phys);
// Enable paging, write protect kernel, protected mode
let mut cr0 = controlregs::cr0();
cr0 |= Cr0::CR0_ENABLE_PAGING | Cr0::CR0_WRITE_PROTECT | Cr0::CR0_PROTECTED_MODE;
controlregs::cr0_write(cr0);
}
+63
View File
@@ -0,0 +1,63 @@
use std::proto::Protocol;
#[repr(packed)]
#[derive(Clone, Copy, Debug)]
pub struct PartitionProtoInfoMbr {
pub boot: u8,
pub chs_start: [u8; 3],
pub ty: u8,
pub chs_end: [u8; 3],
pub start_lba: u32,
pub lba_size: u32,
}
#[repr(packed)]
#[derive(Clone, Copy)]
pub struct PartitionProtoInfoGpt {
pub part_ty_guid: [u8; 16],
pub uniq_guid: [u8; 16],
pub start_lba: u64,
pub end_lba: u64,
pub attrs: u64,
pub name: [u16; 36],
// reserved until end of block
}
#[repr(packed)]
#[derive(Clone, Copy)]
pub union PartitionProtoDataInfo {
pub mbr: PartitionProtoInfoMbr,
pub gpt: PartitionProtoInfoGpt,
}
#[repr(packed)]
pub struct PartitionProtoData {
pub rev: u32,
pub ty: u32,
pub sys: u8,
pub resv: [u8; 7],
pub info: PartitionProtoDataInfo,
}
pub const PARTITION_INFO_PROTOCOL_REVISION: u32 = 0x1000;
pub const ESP_GUID: [u8; 16] = [0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11, 0xba, 0x4b, 0x0, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b]; // c12a7328-f81f-11d2-bA4b-00a0c93ec93b
pub const LINUX_FS_GUID: [u8; 16] = [0xaf, 0x3d, 0xc6, 0xf, 0x83, 0x84, 0x72, 0x47, 0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4]; // 0fc63daf-8483-4772-8e79-3d69d8477de4
pub const REDOX_FS_GUID: [u8; 16] = [0xfd, 0x98, 0x78, 0x52, 0xe3, 0xff, 0xc2, 0x42, 0xe3, 0x96, 0x10, 0x5b, 0xa6, 0x3f, 0x5a, 0xbf]; // 527898fd-ffe3-42c2-96e3-bf5a3fa65b10
#[repr(u32)]
pub enum PartitionProtoDataTy {
Other = 0,
Mbr = 1,
Gpt = 2,
}
pub struct PartitionProto(pub &'static mut PartitionProtoData);
impl Protocol<PartitionProtoData> for PartitionProto {
fn guid() -> uefi::guid::Guid {
uefi::guid::Guid(0x8cf2f62c, 0xbc9b, 0x4821, [0x80, 0x8d, 0xec, 0x9e, 0xc4, 0x21, 0xa1, 0xa0])
}
fn new(inner: &'static mut PartitionProtoData) -> Self {
Self(inner)
}
}
+44
View File
@@ -0,0 +1,44 @@
use core::ops::{ControlFlow, Try};
use redoxfs::{BLOCK_SIZE, Disk};
use syscall::{EIO, Error, Result};
use std::proto::Protocol;
use uefi::guid::{Guid, BLOCK_IO_GUID};
use uefi::block_io::BlockIo as UefiBlockIo;
pub struct DiskEfi(pub &'static mut UefiBlockIo);
impl Protocol<UefiBlockIo> for DiskEfi {
fn guid() -> Guid {
BLOCK_IO_GUID
}
fn new(inner: &'static mut UefiBlockIo) -> Self {
Self(inner)
}
}
impl Disk for DiskEfi {
fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
let block_size = self.0.Media.BlockSize as u64;
let lba = block * BLOCK_SIZE / block_size;
match (self.0.ReadBlocks)(self.0, self.0.Media.MediaId, lba, buffer.len(), buffer.as_mut_ptr()).branch() {
ControlFlow::Continue(_) => Ok(buffer.len()),
ControlFlow::Break(err) => {
println!("DiskEfi::read_at 0x{:X} failed: {:?}", block, err);
Err(Error::new(EIO))
}
}
}
fn write_at(&mut self, block: u64, _buffer: &[u8]) -> Result<usize> {
println!("DiskEfi::write_at 0x{:X} not implemented", block);
Err(Error::new(EIO))
}
fn size(&mut self) -> Result<u64> {
println!("DiskEfi::size not implemented");
Err(Error::new(EIO))
}
}
+188
View File
@@ -0,0 +1,188 @@
use core::cell::Cell;
use core::ops::Try;
use core::ptr;
use orbclient::{Color, Mode, Renderer};
use std::boxed::Box;
use std::proto::Protocol;
use uefi::graphics::{GraphicsOutput, GraphicsBltOp, GraphicsBltPixel};
use uefi::guid::{Guid, GRAPHICS_OUTPUT_PROTOCOL_GUID};
pub struct Output(pub &'static mut GraphicsOutput);
impl Protocol<GraphicsOutput> for Output {
fn guid() -> Guid {
GRAPHICS_OUTPUT_PROTOCOL_GUID
}
fn new(inner: &'static mut GraphicsOutput) -> Self {
Output(inner)
}
}
pub struct Display<'a> {
output: &'a mut Output,
w: u32,
h: u32,
data: Box<[Color]>,
mode: Cell<Mode>,
}
impl<'a> Display<'a> {
pub fn new(output: &'a mut Output) -> Self {
let w = output.0.Mode.Info.HorizontalResolution;
let h = output.0.Mode.Info.VerticalResolution;
Self {
output: output,
w: w,
h: h,
data: vec![Color::rgb(0, 0, 0); w as usize * h as usize].into_boxed_slice(),
mode: Cell::new(Mode::Blend),
}
}
pub fn blit(&mut self, x: i32, y: i32, w: u32, h: u32) -> bool {
let status = (self.output.0.Blt)(
self.output.0,
self.data.as_mut_ptr() as *mut GraphicsBltPixel,
GraphicsBltOp::BufferToVideo,
x as usize,
y as usize,
x as usize,
y as usize,
w as usize,
h as usize,
0
);
status.branch().is_continue()
}
pub fn scroll(&mut self, rows: usize, color: Color) {
let width = self.w as usize;
let height = self.h as usize;
if rows > 0 && rows < height {
let off1 = rows * width;
let off2 = height * width - off1;
unsafe {
let data_ptr = self.data.as_mut_ptr() as *mut u32;
ptr::copy(data_ptr.offset(off1 as isize), data_ptr, off2 as usize);
for i in off2..off2 + off1 {
*data_ptr.add(i) = color.data;
}
}
}
}
}
impl<'a> Renderer for Display<'a> {
fn width(&self) -> u32 {
self.w
}
fn height(&self) -> u32 {
self.h
}
fn data(&self) -> &[Color] {
&self.data
}
fn data_mut(&mut self) -> &mut [Color] {
&mut self.data
}
fn sync(&mut self) -> bool {
let w = self.width();
let h = self.height();
self.blit(0, 0, w, h)
}
fn mode(&self) -> &Cell<Mode> {
&self.mode
}
}
pub struct ScaledDisplay<'a> {
display: &'a mut Display<'a>,
scale: u32,
}
impl<'a> ScaledDisplay<'a> {
pub fn new(display: &'a mut Display<'a>) -> Self {
let scale = if display.height() > 1440 {
2
} else {
1
};
Self {
display,
scale,
}
}
pub fn scale(&self) -> u32 {
self.scale
}
pub fn scroll(&mut self, rows: usize, color: Color) {
let scale = self.scale as usize;
self.display.scroll(rows * scale, color);
}
pub fn blit(&mut self, x: i32, y: i32, w: u32, h: u32) -> bool {
let scale = self.scale;
self.display.blit(
x * scale as i32,
y * scale as i32,
w * scale,
h * scale
)
}
}
impl<'a> Renderer for ScaledDisplay<'a> {
fn width(&self) -> u32 {
self.display.width()/self.scale
}
fn height(&self) -> u32 {
self.display.height()/self.scale
}
fn data(&self) -> &[Color] {
unreachable!()
}
fn data_mut(&mut self) -> &mut [Color] {
unreachable!()
}
fn sync(&mut self) -> bool {
self.display.sync()
}
fn pixel(&mut self, x: i32, y: i32, color: Color) {
self.rect(x, y, 1, 1, color);
}
fn rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) {
let scale = self.scale;
self.display.rect(
x * scale as i32,
y * scale as i32,
w * scale,
h * scale,
color
);
}
fn set(&mut self, color: Color) {
let w = self.width();
let h = self.height();
self.rect(0, 0, w, h, color);
}
fn mode(&self) -> &Cell<Mode> {
self.display.mode()
}
}
+95
View File
@@ -0,0 +1,95 @@
use core::char;
use uefi::status::Result;
use uefi::text::TextInputKey;
#[derive(Debug, PartialEq)]
pub enum Key {
Backspace,
Tab,
Enter,
Character(char),
Up,
Down,
Right,
Left,
Home,
End,
Insert,
Delete,
PageUp,
PageDown,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Escape,
Scancode(u16),
}
impl From<TextInputKey> for Key {
fn from(raw_key: TextInputKey) -> Self {
match raw_key.ScanCode {
0 => match unsafe { char::from_u32_unchecked(raw_key.UnicodeChar as u32) } {
'\u{8}' => Key::Backspace,
'\t' => Key::Tab,
'\r' => Key::Enter,
c => Key::Character(c),
},
1 => Key::Up,
2 => Key::Down,
3 => Key::Right,
4 => Key::Left,
5 => Key::Home,
6 => Key::End,
7 => Key::Insert,
8 => Key::Delete,
9 => Key::PageUp,
10 => Key::PageDown,
11 => Key::F1,
12 => Key::F2,
13 => Key::F3,
14 => Key::F4,
15 => Key::F5,
16 => Key::F6,
17 => Key::F7,
18 => Key::F8,
19 => Key::F9,
20 => Key::F10,
21 => Key::F11,
22 => Key::F12,
23 => Key::Escape,
scancode => Key::Scancode(scancode),
}
}
}
pub fn raw_key(wait: bool) -> Result<TextInputKey> {
let uefi = std::system_table();
if wait {
let mut index = 0;
(uefi.BootServices.WaitForEvent)(1, &uefi.ConsoleIn.WaitForKey, &mut index)?;
}
let mut key = TextInputKey {
ScanCode: 0,
UnicodeChar: 0
};
(uefi.ConsoleIn.ReadKeyStroke)(uefi.ConsoleIn, &mut key)?;
Ok(key)
}
pub fn key(wait: bool) -> Result<Key> {
let raw_key = raw_key(wait)?;
Ok(Key::from(raw_key))
}
+53
View File
@@ -0,0 +1,53 @@
use core::ops::Try;
use core::ptr;
use uefi::reset::ResetType;
use uefi::status::{Result, Status};
mod arch;
mod disk;
mod display;
mod key;
pub mod null;
pub mod text;
fn set_max_mode(output: &uefi::text::TextOutput) -> Result<()> {
let mut max_i = None;
let mut max_w = 0;
let mut max_h = 0;
for i in 0..output.Mode.MaxMode as usize {
let mut w = 0;
let mut h = 0;
if (output.QueryMode)(output, i, &mut w, &mut h).branch().is_continue() {
if w >= max_w && h >= max_h {
max_i = Some(i);
max_w = w;
max_h = h;
}
}
}
if let Some(i) = max_i {
(output.SetMode)(output, i)?;
}
Ok(())
}
#[no_mangle]
pub extern "C" fn main() -> Status {
let uefi = std::system_table();
let _ = (uefi.BootServices.SetWatchdogTimer)(0, 0, 0, ptr::null());
if let Err(err) = set_max_mode(uefi.ConsoleOut) {
println!("Failed to set max mode: {:?}", err);
}
if let Err(err) = arch::main() {
println!("App error: {:?}", err);
let _ = key::key(true);
}
(uefi.RuntimeServices.ResetSystem)(ResetType::Cold, Status(0), 0, ptr::null());
}
+128
View File
@@ -0,0 +1,128 @@
use core::mem;
use core::ops::Deref;
use std::boxed::Box;
use uefi::Handle;
use uefi::boot::InterfaceType;
use uefi::guid::SIMPLE_TEXT_OUTPUT_GUID;
use uefi::status::{Result, Status};
use uefi::text::TextOutputMode;
#[repr(C)]
#[allow(non_snake_case)]
pub struct NullDisplay {
pub Reset: extern "win64" fn(&mut NullDisplay, bool) -> Status,
pub OutputString: extern "win64" fn(&mut NullDisplay, *const u16) -> Status,
pub TestString: extern "win64" fn(&mut NullDisplay, *const u16) -> Status,
pub QueryMode: extern "win64" fn(&mut NullDisplay, usize, &mut usize, &mut usize) -> Status,
pub SetMode: extern "win64" fn(&mut NullDisplay, usize) -> Status,
pub SetAttribute: extern "win64" fn(&mut NullDisplay, usize) -> Status,
pub ClearScreen: extern "win64" fn(&mut NullDisplay) -> Status,
pub SetCursorPosition: extern "win64" fn(&mut NullDisplay, usize, usize) -> Status,
pub EnableCursor: extern "win64" fn(&mut NullDisplay, bool) -> Status,
pub Mode: &'static TextOutputMode,
pub mode: Box<TextOutputMode>,
}
extern "win64" fn reset(_output: &mut NullDisplay, _extra: bool) -> Status {
Status(0)
}
extern "win64" fn output_string(_output: &mut NullDisplay, _string: *const u16) -> Status {
Status(0)
}
extern "win64" fn test_string(_output: &mut NullDisplay, _string: *const u16) -> Status {
Status(0)
}
extern "win64" fn query_mode(_output: &mut NullDisplay, _mode: usize, columns: &mut usize, rows: &mut usize) -> Status {
*columns = 80;
*rows = 30;
Status(0)
}
extern "win64" fn set_mode(_output: &mut NullDisplay, _mode: usize) -> Status {
Status(0)
}
extern "win64" fn set_attribute(output: &mut NullDisplay, attribute: usize) -> Status {
output.mode.Attribute = attribute as i32;
Status(0)
}
extern "win64" fn clear_screen(_output: &mut NullDisplay) -> Status {
Status(0)
}
extern "win64" fn set_cursor_position(output: &mut NullDisplay, column: usize, row: usize) -> Status {
output.mode.CursorColumn = column as i32;
output.mode.CursorRow = row as i32;
Status(0)
}
extern "win64" fn enable_cursor(output: &mut NullDisplay, enable: bool) -> Status {
output.mode.CursorVisible = enable;
Status(0)
}
impl NullDisplay {
pub fn new() -> NullDisplay {
let mode = Box::new(TextOutputMode {
MaxMode: 0,
Mode: 0,
Attribute: 0,
CursorColumn: 0,
CursorRow: 0,
CursorVisible: false,
});
NullDisplay {
Reset: reset,
OutputString: output_string,
TestString: test_string,
QueryMode: query_mode,
SetMode: set_mode,
SetAttribute: set_attribute,
ClearScreen: clear_screen,
SetCursorPosition: set_cursor_position,
EnableCursor: enable_cursor,
Mode: unsafe { mem::transmute(&*mode.deref()) },
mode: mode
}
}
pub fn pipe<T, F: FnMut() -> Result<T>>(&mut self, mut f: F) -> Result<T> {
let uefi = unsafe { std::system_table_mut() };
let stdout = self as *mut _;
let mut stdout_handle = Handle(0);
(uefi.BootServices.InstallProtocolInterface)(&mut stdout_handle, &SIMPLE_TEXT_OUTPUT_GUID, InterfaceType::Native, stdout as usize)?;
let old_stdout_handle = uefi.ConsoleOutHandle;
let old_stdout = uefi.ConsoleOut as *mut _;
let old_stderr_handle = uefi.ConsoleErrorHandle;
let old_stderr = uefi.ConsoleError as *mut _;
uefi.ConsoleOutHandle = stdout_handle;
uefi.ConsoleOut = unsafe { mem::transmute(&mut *stdout) };
uefi.ConsoleErrorHandle = stdout_handle;
uefi.ConsoleError = unsafe { mem::transmute(&mut *stdout) };
let res = f();
uefi.ConsoleOutHandle = old_stdout_handle;
uefi.ConsoleOut = unsafe { mem::transmute(&mut *old_stdout) };
uefi.ConsoleErrorHandle = old_stderr_handle;
uefi.ConsoleError = unsafe { mem::transmute(&mut *old_stderr) };
let _ = (uefi.BootServices.UninstallProtocolInterface)(stdout_handle, &SIMPLE_TEXT_OUTPUT_GUID, stdout as usize);
res
}
}
pub fn pipe<T, F: FnMut() -> Result<T>>(f: F) -> Result<T> {
NullDisplay::new().pipe(f)
}
+252
View File
@@ -0,0 +1,252 @@
use core::{char, mem, ptr};
use core::ops::Deref;
use orbclient::{Color, Renderer};
use std::boxed::Box;
use std::proto::Protocol;
use uefi::Handle;
use uefi::boot::InterfaceType;
use uefi::guid::SIMPLE_TEXT_OUTPUT_GUID;
use uefi::status::{Result, Status};
use uefi::text::TextOutputMode;
use super::display::{Display, ScaledDisplay, Output};
#[repr(C)]
#[allow(non_snake_case)]
pub struct TextDisplay<'a> {
pub Reset: extern "win64" fn(&mut TextDisplay, bool) -> Status,
pub OutputString: extern "win64" fn(&mut TextDisplay, *const u16) -> Status,
pub TestString: extern "win64" fn(&mut TextDisplay, *const u16) -> Status,
pub QueryMode: extern "win64" fn(&mut TextDisplay, usize, &mut usize, &mut usize) -> Status,
pub SetMode: extern "win64" fn(&mut TextDisplay, usize) -> Status,
pub SetAttribute: extern "win64" fn(&mut TextDisplay, usize) -> Status,
pub ClearScreen: extern "win64" fn(&mut TextDisplay) -> Status,
pub SetCursorPosition: extern "win64" fn(&mut TextDisplay, usize, usize) -> Status,
pub EnableCursor: extern "win64" fn(&mut TextDisplay, bool) -> Status,
pub Mode: &'static TextOutputMode,
pub mode: Box<TextOutputMode>,
pub off_x: i32,
pub off_y: i32,
pub cols: usize,
pub rows: usize,
pub display: ScaledDisplay<'a>,
}
extern "win64" fn reset(_output: &mut TextDisplay, _extra: bool) -> Status {
Status(0)
}
extern "win64" fn output_string(output: &mut TextDisplay, string: *const u16) -> Status {
output.write(string);
Status(0)
}
extern "win64" fn test_string(_output: &mut TextDisplay, _string: *const u16) -> Status {
Status(0)
}
extern "win64" fn query_mode(output: &mut TextDisplay, _mode: usize, columns: &mut usize, rows: &mut usize) -> Status {
*columns = output.cols;
*rows = output.rows;
Status(0)
}
extern "win64" fn set_mode(_output: &mut TextDisplay, _mode: usize) -> Status {
Status(0)
}
extern "win64" fn set_attribute(output: &mut TextDisplay, attribute: usize) -> Status {
output.mode.Attribute = attribute as i32;
Status(0)
}
extern "win64" fn clear_screen(output: &mut TextDisplay) -> Status {
output.clear();
Status(0)
}
extern "win64" fn set_cursor_position(output: &mut TextDisplay, column: usize, row: usize) -> Status {
output.set_cursor_pos(column as i32, row as i32);
Status(0)
}
extern "win64" fn enable_cursor(output: &mut TextDisplay, enable: bool) -> Status {
output.mode.CursorVisible = enable;
Status(0)
}
impl<'a> TextDisplay<'a> {
pub fn new(display: ScaledDisplay<'a>) -> TextDisplay<'a> {
let mode = Box::new(TextOutputMode {
MaxMode: 0,
Mode: 0,
Attribute: 0,
CursorColumn: 0,
CursorRow: 0,
CursorVisible: false,
});
let cols = display.width() as usize/8;
let rows = display.height() as usize/16;
TextDisplay {
Reset: reset,
OutputString: output_string,
TestString: test_string,
QueryMode: query_mode,
SetMode: set_mode,
SetAttribute: set_attribute,
ClearScreen: clear_screen,
SetCursorPosition: set_cursor_position,
EnableCursor: enable_cursor,
Mode: unsafe { mem::transmute(&*mode.deref()) },
mode,
off_x: 0,
off_y: 0,
cols,
rows,
display,
}
}
pub fn pos(&self) -> (i32, i32) {
(
self.mode.CursorColumn * 8 + self.off_x,
self.mode.CursorRow * 16 + self.off_y,
)
}
pub fn clear(&mut self) {
// Clears are ignored
//let bg = Color::rgb(0, 0, 0);
//self.display.rect(self.off_x, self.off_y, self.cols * 8, self.rows * 16, bg);
//self.display.blit(0, self.off_y, w, self.rows * 16);
self.display.sync();
}
pub fn scroll(&mut self, color: Color) {
if self.rows > 0 {
let w = self.display.width();
let dst = self.off_y * w as i32;
let src = (self.off_y + 16) * w as i32;
let len = (self.rows - 1) * 16 * w as usize;
unsafe {
let scale = self.display.scale() as isize;
let data_ptr = self.display.data_mut().as_mut_ptr() as *mut u32;
ptr::copy(
data_ptr.offset(src as isize * scale * scale),
data_ptr.offset(dst as isize * scale * scale),
len * (scale * scale) as usize);
}
self.display.rect(self.off_x, self.off_y + (self.rows as i32 - 1) * 16, self.cols as u32 * 8, 16, color);
}
}
pub fn set_cursor_pos(&mut self, column: i32, _row: i32) {
self.mode.CursorColumn = column;
}
pub fn write(&mut self, string: *const u16) {
let bg = Color::rgb(0, 0, 0);
let fg = Color::rgb(255, 255, 255);
let mut scrolled = false;
let mut changed = false;
let (_sx, sy) = self.pos();
let mut i = 0;
loop {
let w = unsafe { *string.offset(i) };
if w == 0 {
break;
}
let c = unsafe { char::from_u32_unchecked(w as u32) };
if self.mode.CursorColumn as usize >= self.cols {
self.mode.CursorColumn = 0;
self.mode.CursorRow += 1;
}
while self.mode.CursorRow as usize >= self.rows {
self.scroll(bg);
self.mode.CursorRow -= 1;
scrolled = true;
}
match c {
'\x08' => if self.mode.CursorColumn > 0 {
let (x, y) = self.pos();
self.display.rect(x, y, 8, 16, bg);
self.mode.CursorColumn -= 1;
changed = true;
},
'\r'=> {
self.mode.CursorColumn = 0;
},
'\n' => {
self.mode.CursorRow += 1;
},
_ => {
let (x, y) = self.pos();
self.display.rect(x, y, 8, 16, bg);
self.display.char(x, y, c, fg);
self.mode.CursorColumn += 1;
changed = true;
}
}
i += 1;
}
if scrolled {
let (cx, cw) = (0, self.display.width() as i32);
let (cy, ch) = (self.off_y, self.rows as u32 * 16);
self.display.blit(cx, cy, cw as u32, ch as u32);
} else if changed {
let (_x, y) = self.pos();
let (cx, cw) = (0, self.display.width() as i32);
let (cy, ch) = (sy, y + 16 - sy);
self.display.blit(cx, cy, cw as u32, ch as u32);
}
}
pub fn pipe<T, F: FnMut() -> Result<T>>(&mut self, mut f: F) -> Result<T> {
let uefi = unsafe { std::system_table_mut() };
let stdout = self as *mut _;
let mut stdout_handle = Handle(0);
(uefi.BootServices.InstallProtocolInterface)(&mut stdout_handle, &SIMPLE_TEXT_OUTPUT_GUID, InterfaceType::Native, stdout as usize)?;
let old_stdout_handle = uefi.ConsoleOutHandle;
let old_stdout = uefi.ConsoleOut as *mut _;
let old_stderr_handle = uefi.ConsoleErrorHandle;
let old_stderr = uefi.ConsoleError as *mut _;
uefi.ConsoleOutHandle = stdout_handle;
uefi.ConsoleOut = unsafe { mem::transmute(&mut *stdout) };
uefi.ConsoleErrorHandle = stdout_handle;
uefi.ConsoleError = unsafe { mem::transmute(&mut *stdout) };
let res = f();
uefi.ConsoleOutHandle = old_stdout_handle;
uefi.ConsoleOut = unsafe { mem::transmute(&mut *old_stdout) };
uefi.ConsoleErrorHandle = old_stderr_handle;
uefi.ConsoleError = unsafe { mem::transmute(&mut *old_stderr) };
let _ = (uefi.BootServices.UninstallProtocolInterface)(stdout_handle, &SIMPLE_TEXT_OUTPUT_GUID, stdout as usize);
res
}
}
pub fn pipe<T, F: FnMut() -> Result<T>>(f: F) -> Result<T> {
let mut output = Output::one()?;
let mut display = Display::new(&mut output);
TextDisplay::new(ScaledDisplay::new(&mut display)).pipe(f)
}