Add basic kernel loading

This commit is contained in:
Mark
2020-09-17 12:50:44 +03:00
parent 9e5d4444c5
commit acd890341f
13 changed files with 531 additions and 27 deletions
+1
View File
@@ -1 +1,2 @@
/target
image
Generated
+5
View File
@@ -1,5 +1,9 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "char16-literal"
version = "0.1.0"
[[package]]
name = "core-rt"
version = "0.1.0"
@@ -12,6 +16,7 @@ version = "0.1.0"
name = "yboot2"
version = "0.1.0"
dependencies = [
"char16-literal",
"core-rt",
"efi",
]
+3 -3
View File
@@ -7,6 +7,6 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
#spin = { version = "0.5.2" }
efi = { path = "crates/efi" }
core-rt = { path = "crates/core-rt" }
efi = { path = "crates/efi" }
char16-literal = { path = "crates/char16-literal" }
core-rt = { path = "crates/core-rt" }
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "char16-literal"
version = "0.1.0"
authors = ["Mark <alnyan@airmail.cc>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[lib]
proc-macro = true
+64
View File
@@ -0,0 +1,64 @@
use proc_macro::{
TokenStream,
TokenTree,
Literal,
Group,
Delimiter,
Punct,
Spacing
};
fn convert(input: &[u8]) -> Vec<u16> {
// TODO: support escaping
let mut iter = input.iter();
let first = *iter.next().unwrap();
let ch = if first == b'r' {
*iter.next().unwrap()
} else {
first
};
if ch != b'"' {
panic!("Expected a string literal");
}
// TODO: check for non-ascii?
let mut vec = Vec::<u16>::new();
for b in iter {
if *b == b'"' {
break;
}
vec.push(*b as u16);
}
vec.push(0);
vec
}
#[proc_macro]
pub fn cstr16(input: TokenStream) -> TokenStream {
use std::iter::FromIterator;
let s = input.into_iter().next().unwrap();
match s {
TokenTree::Literal(data) => {
let text = data.to_string();
let conv = convert(text.as_bytes());
TokenStream::from_iter(vec![
TokenTree::from(Punct::new('&', Spacing::Joint)),
TokenTree::Group(Group::new(
Delimiter::Bracket,
TokenStream::from_iter(
conv.iter().map(|word| {
vec![
TokenTree::from(Literal::u16_suffixed(*word)),
TokenTree::from(Punct::new(',', Spacing::Joint)),
]
}).flatten()
)
))
])
},
other => panic!("Unexpected {:?}", other)
}
}
+4 -1
View File
@@ -1,4 +1,7 @@
#![no_std]
#![feature(core_intrinsics)]
use core::intrinsics::wrapping_sub;
#[no_mangle]
unsafe extern "C" fn memcpy(dst: *mut u8, src: *const u8, cnt: usize) -> *mut u8 {
@@ -22,7 +25,7 @@ unsafe extern "C" fn memset(dst: *mut u8, val: i32, cnt: usize) -> *mut u8 {
unsafe extern "C" fn memcmp(a: *const u8, b: *const u8, cnt: usize) -> i32 {
let mut i = 0isize;
while i < cnt as isize {
let diff = *(a.offset(i)) - *(b.offset(i));
let diff = wrapping_sub(*(a.offset(i)), *(b.offset(i)));
if diff != 0u8 {
return diff as i32;
}
+35
View File
@@ -1,4 +1,5 @@
use core::ffi::c_void;
use core::fmt;
pub type Handle = *mut c_void;
pub type Event = *mut c_void;
@@ -84,3 +85,37 @@ pub struct Guid {
pub data3: u16,
pub data4: [u8; 8]
}
#[repr(transparent)]
pub struct CStr16 {
data: [u16]
}
impl CStr16 {
pub fn from_literal(data: &'static [u16]) -> &'static CStr16 {
return unsafe {&*(data as *const _ as *const _)};
}
pub fn as_ptr(&self) -> *const u16 {
return &self.data[0]
}
}
impl fmt::Debug for CStr16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"")?;
<Self as fmt::Display>::fmt(self, f)?;
write!(f, "\"")
}
}
impl fmt::Display for CStr16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for word in self.data.iter() {
if *word != 0 {
write!(f, "{}", *word as u8 as char)?;
}
}
Ok(())
}
}
+19
View File
@@ -15,6 +15,9 @@ impl MemoryDescriptor {
pub fn end(&self) -> usize {
return self.physical_start + (self.number_of_pages as usize) * 0x1000;
}
pub fn is_usable_now(&self) -> bool {
return self._type == 7;
}
}
pub struct MemoryMapIterator<'a> {
@@ -68,4 +71,20 @@ impl<'a> MemoryMap<'a> {
count: self.size / self.descriptor_size
});
}
pub fn is_usable_now(&self, page: usize) -> bool {
for item in self.iter().unwrap() {
if item.is_usable_now() {
continue;
}
let aligned_begin = (item.begin() + 0xFFF) & !0xFFF;
let aligned_end = item.end() & !0xFFF;
if page >= aligned_begin && page < aligned_end {
return false;
}
}
return true;
}
}
+68 -15
View File
@@ -1,35 +1,88 @@
use crate::{Protocol, Guid};
use crate::{Protocol, Guid, CStr16, Status};
use core::ptr::null_mut;
use core::ffi::c_void;
// Use altlib's io module
#[cfg(feature = "with-altlib")]
use altlib::io;
pub const OPEN_MODE_READ: u64 = 1;
#[repr(C)]
pub struct FileProtocol {
revision: u64,
open: *mut c_void,
close: unsafe fn (*mut FileProtocol) -> u64,
delete: *mut c_void,
read: *mut c_void,
write: *mut c_void,
// ...
revision: u64,
open: unsafe fn (*const FileProtocol,
*mut *mut FileProtocol,
*const u16,
u64, u64) -> u64,
close: unsafe fn (*mut FileProtocol) -> u64,
delete: *mut c_void,
read: unsafe fn (*mut FileProtocol, *mut usize, *mut c_void) -> u64,
write: *mut c_void,
get_position: *mut c_void,
set_position: unsafe fn (*mut FileProtocol, u64) -> u64,
}
pub struct File<'a> {
inner: &'a mut FileProtocol
pub struct File {
inner: *mut FileProtocol
}
impl<'a> From<*mut FileProtocol> for File<'a> {
fn from(proto: *mut FileProtocol) -> File<'a> {
impl From<*mut FileProtocol> for File {
fn from(proto: *mut FileProtocol) -> File {
File {
inner: unsafe { &mut *proto }
inner: proto
}
}
}
impl<'a> Drop for File<'a> {
impl Drop for File {
fn drop(&mut self) {
unsafe {
(self.inner.close)(
((*self.inner).close)(
self.inner
);
}
self.inner = null_mut();
}
}
impl File {
pub fn open(&self, name: &CStr16, mode: u64, attr: u64) -> Result<File, Status> {
let mut ptr: *mut FileProtocol = null_mut();
match Status::from_num(unsafe {
((*self.inner).open)(
self.inner,
&mut ptr,
name.as_ptr(),
mode,
attr
)
}) {
Status::Success => Ok(File::from(ptr)),
err => Err(err)
}
}
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, Status> {
let mut len = buf.len();
match Status::from_num(unsafe {
((*self.inner).read)(
self.inner,
&mut len,
buf as *mut _ as *mut _
)
}) {
Status::Success => Ok(len),
err => Err(err)
}
}
pub fn seek(&mut self, pos: u64) -> Result<(), Status> {
Status::from_num(unsafe {
((*self.inner).set_position)(
self.inner,
pos
)
}).to_result()
}
}
+9 -2
View File
@@ -1,17 +1,24 @@
#!/bin/sh
BIOS=/usr/share/edk2-ovmf/OVMF_CODE.fd
IMAGE=target/x86_64-unknown-uefi/debug/image.fat32
IMAGE=image/image.fat32
TARGET=x86_64-unknown-uefi
CONFIG=debug
set -e
mkdir -p image
cargo build -Z build-std=core
dd if=/dev/zero of=${IMAGE} bs=1M count=64
mkfs.vfat -F32 ${IMAGE}
mcopy -i ${IMAGE} target/x86_64-unknown-uefi/debug/yboot2.efi ::app.efi
mcopy -i ${IMAGE} target/${TARGET}/${CONFIG}/yboot2.efi ::app.efi
mcopy -i ${IMAGE} image/config.txt ::config.txt
mcopy -i ${IMAGE} image/kernel.elf ::kernel.elf
qemu-system-x86_64 \
-s \
-serial stdio \
-m 256 \
-drive format=raw,file=$BIOS,readonly=on,if=pflash \
+198
View File
@@ -0,0 +1,198 @@
use efi::{File, Status, CStr16, MemoryMap};
use core::mem::{MaybeUninit, size_of};
use crate::proto::LoadProtocol;
type Off = u64;
type Addr = u64;
type Half = u16;
type Word = u32;
type XWord = u64;
type SHalf = i16;
type SWord = i32;
type SXWord = i64;
const PT_LOAD: Word = 1;
const SHT_PROGBITS: Word = 1;
const SHF_WRITE: XWord = 1 << 0;
const SHF_ALLOC: XWord = 1 << 1;
#[repr(C)]
struct Ehdr {
ident: [u8; 16],
_type: Half,
machine: Half,
version: Word,
entry: Addr,
phoff: Off,
shoff: Off,
flags: Word,
ehsize: Half,
phentsize: Half,
phnum: Half,
shentsize: Half,
shnum: Half,
shstrndx: Half
}
#[repr(C)]
struct Shdr {
name: Word,
_type: Word,
flags: XWord,
addr: Addr,
offset: Off,
size: XWord,
link: Word,
info: Word,
addralign: XWord,
entsize: XWord
}
#[repr(C)]
struct Phdr {
_type: Word,
offset: Off,
vaddr: Addr,
paddr: Addr,
filesz: Word,
memsz: Word,
flags: Word,
align: Word
}
pub struct Object {
file: File,
ehdr: Ehdr,
}
unsafe fn any_as_u8_slice<T: Sized>(p: &mut T) -> &mut [u8] {
::core::slice::from_raw_parts_mut(
(p as *mut T) as *mut u8,
::core::mem::size_of::<T>(),
)
}
impl Object {
// Reason: EFI autism
pub fn open(root: &mut File, path: &CStr16) -> Result<Object, Status> {
let mut obj = Object {
file: root.open(path, efi::proto::fp::OPEN_MODE_READ, 0)?,
ehdr: unsafe { MaybeUninit::uninit().assume_init() },
};
// Load header
obj.file.seek(0)?;
obj.file.read(unsafe {any_as_u8_slice(&mut obj.ehdr)})?;
// Validate that this is ELF
if &obj.ehdr.ident[0 .. 4] != [0x7F, b'E', b'L', b'F'] {
return Err(Status::InvalidParameter);
}
// Validate that bitness matches requested
if obj.ehdr.ident[4] != 2 {
return Err(Status::InvalidParameter);
}
Ok(obj)
}
fn read_phdr(&mut self, phdr: &mut Phdr, index: usize) -> Result<(), Status> {
let off = self.ehdr.phoff + self.ehdr.phentsize as u64 * index as u64;
self.file.seek(off)?;
if self.file.read(unsafe { any_as_u8_slice(phdr) })? != size_of::<Phdr>() {
Err(Status::Err)
} else {
Ok(())
}
}
fn read_shdr(&mut self, shdr: &mut Shdr, index: usize) -> Result<(), Status> {
let off = self.ehdr.shoff + self.ehdr.shentsize as u64 * index as u64;
self.file.seek(off)?;
if self.file.read(unsafe { any_as_u8_slice(shdr) })? != size_of::<Shdr>() {
Err(Status::Err)
} else {
Ok(())
}
}
// Called after load()
pub fn locate_protocol_data<T: LoadProtocol>(&mut self)
-> Result<&'static mut T, Status>
{
let mut shdr = unsafe { MaybeUninit::<Shdr>::uninit().assume_init() };
for i in 0 .. self.ehdr.shnum {
self.read_shdr(&mut shdr, i as usize)?;
if shdr._type == SHT_PROGBITS &&
(shdr.flags & (SHF_ALLOC | SHF_WRITE)) == SHF_ALLOC | SHF_WRITE {
if shdr.size as usize >= size_of::<T>() {
// Make a physical address
let ptr = shdr.addr - 0xFFFFFF0000000000;
let magic: &[u8] = unsafe { core::slice::from_raw_parts(ptr as *const _, 8)};
if magic == T::KERNEL_MAGIC {
return Ok(unsafe { &mut *(ptr as *mut _) })
}
}
}
}
Err(Status::InvalidParameter)
}
pub fn load(&mut self, mmap: &MemoryMap) -> Result<usize, Status> {
extern {
fn memset(block: *mut u8, value: i32, count: usize) -> *mut u8;
}
let mut phdr = unsafe { MaybeUninit::<Phdr>::uninit().assume_init() };
// 1. Check that all pages in load segments are usable
for i in 0 .. self.ehdr.phnum {
self.read_phdr(&mut phdr, i as usize)?;
if phdr._type == PT_LOAD {
let start = phdr.paddr & !0xFFF;
let end = (phdr.paddr + phdr.memsz as u64 + 0xFFF) & !0xFFF;
for addr in (start .. end).step_by(0x1000) {
if !mmap.is_usable_now(addr as usize) {
return Err(Status::InvalidParameter);
}
}
}
}
// 2. Load segments
for i in 0 .. self.ehdr.phnum {
self.read_phdr(&mut phdr, i as usize)?;
if phdr._type == PT_LOAD {
// Load what's provided in ELF
if phdr.filesz > 0 {
let mut data = unsafe {core::slice::from_raw_parts_mut(
phdr.paddr as *mut u8,
phdr.filesz as usize,
)};
self.file.seek(phdr.offset)?;
self.file.read(&mut data)?;
}
// Zero the rest
if phdr.memsz > phdr.filesz {
unsafe {
memset((phdr.paddr as usize + phdr.filesz as usize) as *mut u8,
0,
(phdr.memsz - phdr.filesz) as usize);
}
}
}
}
Ok(self.ehdr.entry as usize - 0xFFFFFF0000000000)
}
}
+38 -6
View File
@@ -1,33 +1,65 @@
#![feature(asm)]
#![feature(asm,const_fn,rustc_private,compiler_builtins)]
#![no_main]
#![no_std]
extern crate efi;
extern crate core_rt;
#[macro_use]
extern crate char16_literal;
pub (crate) use char16_literal::cstr16;
use efi::{
CStr16,
Status,
ConfigurationTableEntry,
ImageHandle,
SystemTable,
system_table,
image_handle
image_handle,
};
#[macro_use]
mod println;
mod proto;
mod elf;
fn main() -> efi::Result<()> {
let mut desc_array = [0u8; 4096];
let mut mmap = efi::MemoryMap::new(&mut desc_array);
let bs = &system_table().boot_services;
let rs = &system_table().runtime_services;
bs.get_memory_map(&mut mmap)?;
let path = image_handle().get_boot_path()?;
let root = path.open_partition()?;
let rsdp = system_table()
.config_iter()
.find(|x| matches!(x, ConfigurationTableEntry::Acpi10Table(_)))
.map(|x| match x {
ConfigurationTableEntry::Acpi10Table(ptr) => ptr,
_ => panic!()
})
.unwrap();
Ok(())
let mut root = image_handle().get_boot_path()?.open_partition()?;
let mut obj = elf::Object::open(&mut root, CStr16::from_literal(cstr16!(r"\kernel.elf")))?;
let entry = obj.load(&mmap)?;
let data = obj.locate_protocol_data::<proto::V1>()?;
// TODO: load initrd
// Get the new memory map and terminate boot services
bs.get_memory_map(&mut mmap)?;
bs.exit_boot_services(mmap.key)?;
use proto::LoadProtocol;
data.set_mmap(&mmap);
data.set_initrd(0, 0);
data.set_acpi_rsdp(rsdp as usize);
data.set_loader_magic();
unsafe {
let entry_fn: unsafe fn () -> ! = core::mem::transmute(entry);
entry_fn();
}
}
#[no_mangle]
+75
View File
@@ -0,0 +1,75 @@
use core::convert::TryInto;
use efi::MemoryMap;
const CMDLINE_SIZE: usize = 256;
pub trait LoadProtocol: Sized {
const KERNEL_MAGIC: [u8; 8];
fn set_loader_magic(&mut self);
fn set_mmap(&mut self, map: &MemoryMap);
fn set_initrd(&mut self, base: usize, size: usize);
fn set_acpi_rsdp(&mut self, rsdp: usize);
}
#[repr(C)]
pub struct Header {
kernel_magic: [u8; 8],
loader_magic: [u8; 8]
}
#[repr(C)]
pub struct V1 {
hdr: Header,
memory_map_data: u64,
memory_map_size: u32,
memory_map_entsize: u32,
video_width: u32,
video_height: u32,
video_format: u32,
_pad0: u32,
video_framebuffer: u64,
video_pitch: u64,
elf_symtab_hdr: u64,
elf_symtab_data: u64,
elf_strtab_hdr: u64,
elf_strtab_data: u64,
initrd_base: u64,
initrd_size: u64,
rsdp: u64,
cmdline: [u8; CMDLINE_SIZE]
}
impl LoadProtocol for V1 {
const KERNEL_MAGIC: [u8; 8] = [
0x07, 0xB0, 0x07, 0xB0, 0xA9, 0x97, 0xA1, 0x00
];
fn set_loader_magic(&mut self) {
const LOADER_MAGIC: [u8; 8] = [
0x1A, 0x79, 0x9A, 0x0B, 0x70, 0x0B, 0x70, 0x00
];
self.hdr.loader_magic = LOADER_MAGIC;
}
fn set_mmap(&mut self, map: &MemoryMap) {
self.memory_map_data = (map.storage_ref.as_ptr() as usize).try_into().unwrap();
self.memory_map_size = map.size.try_into().unwrap();
self.memory_map_entsize = map.descriptor_size.try_into().unwrap();
}
fn set_initrd(&mut self, base: usize, size: usize) {
self.initrd_base = base.try_into().unwrap();
self.initrd_size = size.try_into().unwrap();
}
fn set_acpi_rsdp(&mut self, rsdp: usize) {
self.rsdp = rsdp.try_into().unwrap();
}
}