Verified Commit 5baae1cc authored by Jeremy Soller's avatar Jeremy Soller
Browse files

Cleanup to use most x86_64 UEFI code for all archs

parent f12a4ff1
......@@ -56,7 +56,6 @@ $(BUILD)/firmware.rom:
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 \
......
......@@ -47,7 +47,6 @@ $(BUILD)/harddrive.bin: $(BUILD)/bootloader.bin $(BUILD)/filesystem.bin
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 \
......
......@@ -56,7 +56,6 @@ $(BUILD)/firmware.rom:
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 \
......
......@@ -59,7 +59,6 @@ impl<'a> Write for SliceWriter<'a> {
}
#[allow(dead_code)]
#[derive(Debug)]
#[repr(packed)]
pub struct KernelArgs {
kernel_base: u64,
......@@ -81,25 +80,11 @@ pub struct KernelArgs {
acpi_rsdps_size: u64,
}
fn main<
fn select_mode<
D: Disk,
M: Iterator<Item=OsMemoryEntry>,
V: Iterator<Item=OsVideoMode>
>(os: &mut dyn Os<D, M, V>) -> (usize, KernelArgs) {
println!("Redox OS Bootloader {} on {}", env!("CARGO_PKG_VERSION"), os.name());
let mut fs = os.filesystem();
print!("RedoxFS ");
for i in 0..fs.header.1.uuid.len() {
if i == 4 || i == 6 || i == 8 || i == 10 {
print!("-");
}
print!("{:>02x}", fs.header.1.uuid[i]);
}
println!(": {} MiB", fs.header.1.size / MIBI as u64);
>(os: &mut dyn Os<D, M, V>) -> Option<OsVideoMode> {
let mut modes = Vec::new();
for mode in os.video_modes() {
let mut aspect_w = mode.width;
......@@ -117,6 +102,10 @@ fn main<
));
}
if modes.is_empty() {
return None;
}
// Sort modes by pixel area, reversed
modes.sort_by(|a, b| (b.0.width * b.0.height).cmp(&(a.0.width * a.0.height)));
......@@ -227,6 +216,30 @@ fn main<
os.set_text_highlight(false);
println!();
mode_opt
}
fn main<
D: Disk,
M: Iterator<Item=OsMemoryEntry>,
V: Iterator<Item=OsVideoMode>
>(os: &mut dyn Os<D, M, V>) -> (usize, KernelArgs) {
println!("Redox OS Bootloader {} on {}", env!("CARGO_PKG_VERSION"), os.name());
let mut fs = os.filesystem();
print!("RedoxFS ");
for i in 0..fs.header.1.uuid.len() {
if i == 4 || i == 6 || i == 8 || i == 10 {
print!("-");
}
print!("{:>02x}", fs.header.1.uuid[i]);
}
println!(": {} MiB", fs.header.1.size / MIBI as u64);
let mode_opt = select_mode(os);
let stack_size = 128 * KIBI;
let stack_base = os.alloc_zeroed_page_aligned(stack_size);
if stack_base.is_null() {
......
use alloc::{
string::String,
};
use core::{mem, ptr, slice};
use std::fs::find;
use std::proto::Protocol;
use core::{mem, ptr};
use uefi::guid::Guid;
use uefi::memory::MemoryType;
use uefi::status::{Error, Result};
use crate::{
KernelArgs,
logger::LOGGER,
};
use super::super::{
disk::DiskEfi,
display::{Output},
OsEfi,
};
use self::memory_map::memory_map;
......@@ -19,11 +17,7 @@ use self::paging::paging;
mod memory_map;
mod paging;
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 PHYS_OFFSET: u64 = 0xFFFF800000000000;
static mut DTB_PHYSICAL: u64 = 0;
......@@ -32,22 +26,6 @@ pub extern "C" fn __chkstk() {
//TODO
}
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();
......@@ -55,13 +33,6 @@ unsafe fn exit_boot_services(key: usize) {
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);
}
static DTB_GUID: Guid = Guid(0xb1b621d5, 0xf19c, 0x41a5, [0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0]);
fn find_dtb() -> Result<()> {
......@@ -79,143 +50,42 @@ fn find_dtb() -> Result<()> {
Err(Error::NotFound)
}
fn redoxfs() -> Result<redoxfs::FileSystem<DiskEfi>> {
for (i, block_io) in DiskEfi::all().into_iter().enumerate() {
if !block_io.0.Media.LogicalPartition {
continue;
}
match redoxfs::FileSystem::open(block_io, Some(0)) {
Ok(ok) => return Ok(ok),
Err(err) => {
log::error!("Failed to open RedoxFS on block I/O {}: {}", i, err);
}
}
}
panic!("Failed to find RedoxFS");
unsafe extern "C" fn kernel_entry(
page_phys: usize,
stack: u64,
func: u64,
args: *const KernelArgs,
) -> ! {
// Read memory map and exit boot services
let key = memory_map();
exit_boot_services(key);
// Enable paging
asm!("msr daifset, #2");
paging();
// Call kernel entry
let entry_fn: extern "C" fn(dtb: u64) -> ! = mem::transmute(func);
entry_fn(DTB_PHYSICAL);
}
const MB: usize = 1024 * 1024;
pub fn main() -> Result<()> {
LOGGER.init();
fn inner() -> Result<()> {
find_dtb()?;
//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 = if let Ok((_i, mut kernel_file)) = find("\\redox_bootloader\\kernel") {
let info = kernel_file.info()?;
let len = info.FileSize;
let kernel = unsafe {
let ptr = allocate_zero_pages((len as usize + page_size - 1) / page_size)?;
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 = kernel_file.read(&mut chunk)?;
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);
kernel
} else {
let mut fs = redoxfs()?;
let mut os = OsEfi {
st: std::system_table(),
};
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
};
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();
}
let (page_phys, args) = crate::main(&mut os);
unsafe {
enter();
kernel_entry(
page_phys,
args.stack_base + args.stack_size + PHYS_OFFSET,
ptr::read((args.kernel_base + 0x18) as *const u64),
&args,
);
}
}
pub fn main() -> Result<()> {
inner()?;
Ok(())
}
use core::{mem, ptr};
use log::error;
use uefi::memory::{MemoryDescriptor, MemoryType};
use crate::os::{OsMemoryEntry, OsMemoryKind};
pub struct MemoryMapIter {
map: [u8; 4096],
map_size: usize,
descriptor_size: usize,
i: usize,
}
impl MemoryMapIter {
pub fn new() -> Self {
let uefi = std::system_table();
let mut map: [u8; 4096] = [0; 4096];
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
);
Self {
map,
map_size,
descriptor_size,
i: 0,
}
}
}
impl Iterator for MemoryMapIter {
type Item=OsMemoryEntry;
fn next(&mut self) -> Option<Self::Item> {
if
self.descriptor_size >= mem::size_of::<MemoryDescriptor>() &&
self.i < self.map_size/self.descriptor_size
{
let descriptor_ptr = unsafe { self.map.as_ptr().add(self.i * self.descriptor_size) };
self.i += 1;
let descriptor = unsafe { ptr::read(descriptor_ptr as *const MemoryDescriptor) };
let descriptor_type: MemoryType = unsafe { mem::transmute(descriptor.Type) };
Some(OsMemoryEntry {
base: descriptor.PhysicalStart.0,
//TODO: do not hard code page size
size: descriptor.NumberOfPages * 4096,
kind: match descriptor_type {
MemoryType::EfiBootServicesCode |
MemoryType::EfiBootServicesData |
MemoryType::EfiConventionalMemory => {
OsMemoryKind::Free
},
MemoryType::EfiLoaderCode |
MemoryType::EfiLoaderData |
MemoryType::EfiACPIReclaimMemory => {
OsMemoryKind::Reclaim
},
_ => {
OsMemoryKind::Reserved
}
}
})
} else {
error!("Unknown memory descriptor size: {}", self.descriptor_size);
None
}
}
}
static MM_BASE: u64 = 0x500;
static MM_SIZE: u64 = 0x4B00;
......
use core::{mem, ops::{ControlFlow, Try}, ptr, slice};
use std::proto::Protocol;
use std::vec::Vec;
use uefi::status::{Result, Status};
use uefi::guid::GuidKind;
use uefi::memory::MemoryType;
use uefi::system::SystemTable;
use uefi::text::TextInputKey;
use core::{
mem,
ptr
};
use std::{
vec::Vec,
};
use uefi::{
guid::GuidKind,
status::Result,
};
use crate::{
KernelArgs,
Os,
OsKey,
OsVideoMode,
logger::LOGGER,
};
use super::super::{
disk::DiskEfi,
display::{EdidActive, Output},
OsEfi,
};
use self::memory_map::{MemoryMapIter, memory_map};
use self::memory_map::memory_map;
use self::paging::paging_enter;
use self::video_mode::VideoModeIter;
mod memory_map;
mod paging;
mod video_mode;
static PHYS_OFFSET: u64 = 0xFFFF800000000000;
......@@ -119,173 +116,6 @@ fn find_acpi_table_pointers() {
}
}
pub struct OsEfi {
st: &'static SystemTable,
}
fn status_to_result(status: Status) -> Result<usize> {
match status.branch() {
ControlFlow::Continue(ok) => Ok(ok),
ControlFlow::Break(err) => Err(err),
}
}
impl Os<
DiskEfi,
MemoryMapIter,
VideoModeIter
> for OsEfi {
fn name(&self) -> &str {
"x86_64/UEFI"
}
fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8 {
assert!(size != 0);
let page_size = self.page_size();
let pages = (size + page_size - 1) / page_size;
let ptr = {
let mut ptr = 0;
status_to_result(
(self.st.BootServices.AllocatePages)(
0, // AllocateAnyPages
MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list
pages,
&mut ptr
)
).unwrap();
ptr as *mut u8
};
assert!(!ptr.is_null());
unsafe { ptr::write_bytes(ptr, 0, pages * page_size) };
ptr
}
fn page_size(&self) -> usize {
4096
}
fn filesystem(&self) -> redoxfs::FileSystem<DiskEfi> {
for (i, block_io) in DiskEfi::all().into_iter().enumerate() {
if !block_io.0.Media.LogicalPartition {
continue;
}
match redoxfs::FileSystem::open(block_io, Some(0)) {
Ok(ok) => return ok,
Err(err) => match err.errno {
// Ignore header not found error
syscall::ENOENT => (),
// Print any other errors
_ => log::error!("Failed to open RedoxFS on block I/O {}: {}", i, err),
}
}
}
panic!("Failed to find RedoxFS");
}
fn memory(&self) -> MemoryMapIter {
MemoryMapIter::new()
}
fn video_modes(&self) -> VideoModeIter {
VideoModeIter::new()
}
fn set_video_mode(&self, mode: &mut OsVideoMode) {
let output = Output::one().unwrap();
status_to_result(
(output.0.SetMode)(output.0, mode.id)
).unwrap();
// Update frame buffer base
mode.base = output.0.Mode.FrameBufferBase as u64;
}
fn best_resolution(&self) -> Option<(u32, u32)> {
//TODO: get this per output
match EdidActive::one() {
Ok(efi_edid) => {
let edid = unsafe {
slice::from_raw_parts(efi_edid.0.Edid, efi_edid.0.SizeOfEdid as usize)
};
Some((
(edid[0x38] as u32) | (((edid[0x3A] as u32) & 0xF0) << 4),
(edid[0x3B] as u32) | (((edid[0x3D] as u32) & 0xF0) << 4),
))
},
Err(err) => {
log::warn!("Failed to get EFI EDID: {:?}", err);
// Fallback to the current output resolution
match Output::one() {
Ok(output) => {
Some((
output.0.Mode.Info.HorizontalResolution,
output.0.Mode.Info.VerticalResolution,
))
},
Err(err) => {
log::error!("Failed to get output: {:?}", err);
None
}
}
}
}
}
fn get_key(&self) -> OsKey {
//TODO: do not unwrap
let mut index = 0;
status_to_result(
(self.st.BootServices.WaitForEvent)(1, &self.st.ConsoleIn.WaitForKey, &mut index)
).unwrap();
let mut key = TextInputKey {
ScanCode: 0,
UnicodeChar: 0
};