Integrate EFI bootloader
This commit is contained in:
Generated
+60
@@ -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
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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,4 +1,3 @@
|
||||
pub use self::paging::paging_create;
|
||||
|
||||
mod paging;
|
||||
mod panic;
|
||||
|
||||
-13
@@ -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
@@ -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;
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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") _
|
||||
);
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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::*;
|
||||
@@ -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
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user