From 4575b35479306e1942fb726454a8c892921548df Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Wed, 9 Mar 2022 20:47:42 +0900 Subject: [PATCH] rust: Initial Rust-based EFI FAT32 chainloader This code is gated behind the CHAINLOADING define. To build a release-style m1n1 with chainloading for use with the installer or kmutil, use: make CHAINLOADING=1 RELEASE=1 To tell m1n1 to chainload another binary, use this var payload: chainload=; e.g. chainload=a17b7e46-e950-bb4f-bc82-8ab1047a058e;m1n1/m1n1.bin Closes: #154 Co-authored-by: Finn Behrens Co-authored-by: Joey Gouly Signed-off-by: Hector Martin --- .clang-format | 2 + .github/workflows/build.yml | 38 +++++---- .github/workflows/main.yml | 10 ++- .gitmodules | 24 ++++++ Makefile | 39 +++++++-- README.md | 2 + m1n1-raw.ld | 2 + m1n1.ld | 2 + rust/Cargo.lock | 56 +++++++++++++ rust/Cargo.toml | 26 ++++++ rust/src/chainload.rs | 101 ++++++++++++++++++++++ rust/src/dlmalloc.rs | 57 +++++++++++++ rust/src/gpt.rs | 162 ++++++++++++++++++++++++++++++++++++ rust/src/lib.rs | 36 ++++++++ rust/src/nvme.rs | 93 +++++++++++++++++++++ rust/src/print.rs | 60 +++++++++++++ rust/vendor/bitflags | 1 + rust/vendor/cfg-if | 1 + rust/vendor/cstr_core | 1 + rust/vendor/cty | 1 + rust/vendor/log | 1 + rust/vendor/memchr | 1 + rust/vendor/rust-fatfs | 1 + rust/vendor/uuid | 1 + src/chainload.c | 31 +++++++ src/main.c | 5 +- src/payload.c | 9 ++ src/usb_dwc3.c | 4 +- 28 files changed, 742 insertions(+), 25 deletions(-) create mode 100644 rust/Cargo.lock create mode 100644 rust/Cargo.toml create mode 100644 rust/src/chainload.rs create mode 100644 rust/src/dlmalloc.rs create mode 100644 rust/src/gpt.rs create mode 100644 rust/src/lib.rs create mode 100644 rust/src/nvme.rs create mode 100644 rust/src/print.rs create mode 160000 rust/vendor/bitflags create mode 160000 rust/vendor/cfg-if create mode 160000 rust/vendor/cstr_core create mode 160000 rust/vendor/cty create mode 160000 rust/vendor/log create mode 160000 rust/vendor/memchr create mode 160000 rust/vendor/rust-fatfs create mode 160000 rust/vendor/uuid diff --git a/.clang-format b/.clang-format index 9ae138f4..131397a9 100644 --- a/.clang-format +++ b/.clang-format @@ -17,6 +17,8 @@ IncludeIsMainRegex: '(_.*)?$' # - 3rd party code headers # - build artifact headers (stuff outside of src/) IncludeCategories: + - Regex: '^"(\.\./)*build/build_.*\.h"$' + Priority: -3 - Regex: '^"(\.\./)*config\.h"$' Priority: -2 - Regex: '^<' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 85ed96a7..67ae0c65 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,18 +18,26 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - name: install aarch64-linux-gnu- toolchain - run: | - sudo apt-get update - sudo apt-get install --no-install-recommends -y device-tree-compiler gcc-aarch64-linux-gnu - - name: build - run: make -k -j2 ARCH=aarch64-linux-gnu- - - uses: actions/upload-artifact@v2 - with: - name: m1n1 - path: | - build/m1n1.macho - build/dtb/t8103-j274.dtb + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install aarch64-linux-gnu- toolchain + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends -y device-tree-compiler gcc-aarch64-linux-gnu + + - name: Install nightly rust + run: | + export RUSTUP_TOOLCHAIN=nightly + rustup target install aarch64-unknown-none-softfloat + + - name: Build + run: make -k -j2 ARCH=aarch64-linux-gnu- CHAINLOADING=1 + + - uses: actions/upload-artifact@v2 + with: + name: m1n1 + path: | + build/m1n1.macho + build/dtb/t8103-j274.dtb diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d3c13816..d6aac0c3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,5 +21,13 @@ jobs: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 + - name: install nightly rust + run: | + rm -f ~/.cargo/bin/rustfmt + rm -f ~/.cargo/bin/cargo-fmt + rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade + - name: Run format-check - run: make format-check + run: | + make format-check + make rustfmt-check diff --git a/.gitmodules b/.gitmodules index fbf3eff8..a0a12a8c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,27 @@ [submodule "artwork"] path = artwork url = https://github.com/AsahiLinux/artwork.git +[submodule "rust/vendor/rust-fatfs"] + path = rust/vendor/rust-fatfs + url = https://github.com/rafalh/rust-fatfs +[submodule "rust/vendor/bitflags"] + path = rust/vendor/bitflags + url = https://github.com/bitflags/bitflags +[submodule "rust/vendor/cfg-if"] + path = rust/vendor/cfg-if + url = https://github.com/alexcrichton/cfg-if +[submodule "rust/vendor/cstr_core"] + path = rust/vendor/cstr_core + url = https://github.com/Amanieu/cstr_core +[submodule "rust/vendor/cty"] + path = rust/vendor/cty + url = https://github.com/japaric/cty +[submodule "rust/vendor/uuid"] + path = rust/vendor/uuid + url = https://github.com/uuid-rs/uuid +[submodule "rust/vendor/log"] + path = rust/vendor/log + url = https://github.com/rust-lang/log +[submodule "rust/vendor/memchr"] + path = rust/vendor/memchr + url = https://github.com/BurntSushi/memchr diff --git a/Makefile b/Makefile index a2f9fbef..5da5c521 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ ARCH ?= aarch64-linux-gnu- +RUSTARCH ?= aarch64-unknown-none-softfloat ifeq ($(shell uname),Darwin) USE_CLANG ?= 1 @@ -35,8 +36,18 @@ CFLAGS := -O2 -Wall -g -Wundef -Werror=strict-prototypes -fno-common -fno-PIE \ -fno-stack-protector -mgeneral-regs-only -mstrict-align -march=armv8.2-a \ $(EXTRA_CFLAGS) +CFG := ifeq ($(RELEASE),1) -CFLAGS += -DRELEASE +CFG += \#define RELEASE\\n +endif + +# Required for no_std + alloc for now +export RUSTUP_TOOLCHAIN=nightly +RUST_LIB := librust.a +RUST_LIBS := +ifeq ($(CHAINLOADING),1) +CFG += \#define CHAINLOADING\\n +RUST_LIBS += $(RUST_LIB) endif LDFLAGS := -EL -maarch64elf --no-undefined -X -Bsymbolic \ @@ -102,7 +113,7 @@ OBJECTS := \ utils.o utils_asm.o \ vsprintf.o \ wdt.o \ - $(MINILZLIB_OBJECTS) $(TINF_OBJECTS) $(DLMALLOC_OBJECTS) $(LIBFDT_OBJECTS) + $(MINILZLIB_OBJECTS) $(TINF_OBJECTS) $(DLMALLOC_OBJECTS) $(LIBFDT_OBJECTS) $(RUST_LIBS) DTS := t8103-j274.dts @@ -115,14 +126,18 @@ TARGET_RAW := m1n1.bin DEPDIR := build/.deps -.PHONY: all clean format update_tag -all: build/$(TARGET) build/$(TARGET_RAW) $(DTBS) +.PHONY: all clean format update_tag update_cfg +all: update_tag update_cfg build/$(TARGET) build/$(TARGET_RAW) $(DTBS) clean: rm -rf build/* format: $(CLANG_FORMAT) -i src/*.c src/*.h sysinc/*.h format-check: $(CLANG_FORMAT) --dry-run --Werror src/*.c src/*.h sysinc/*.h +rustfmt: + cd rust && cargo fmt +rustfmt-check: + cd rust && cargo fmt --check build/dtb/%.dts: dts/%.dts @echo " DTCPP $@" @@ -134,6 +149,13 @@ build/dtb/%.dtb: build/dtb/%.dts @mkdir -p "$(dir $@)" @dtc -I dts -i dts $< -o $@ +build/$(RUST_LIB): rust/src/* rust/* + @echo " RS $@" + @mkdir -p $(DEPDIR) + @mkdir -p "$(dir $@)" + @cargo build --target $(RUSTARCH) --lib --release --manifest-path rust/Cargo.toml --target-dir build + @cp "build/$(RUSTARCH)/release/${RUST_LIB}" "$@" + build/%.o: src/%.S @echo " AS $@" @mkdir -p $(DEPDIR) @@ -167,7 +189,13 @@ update_tag: @cmp -s build/build_tag.h build/build_tag.tmp 2>/dev/null || \ ( mv -f build/build_tag.tmp build/build_tag.h && echo " TAG build/build_tag.h" ) +update_cfg: + @echo -ne "$(CFG)" > build/build_cfg.tmp + @cmp -s build/build_cfg.h build/build_cfg.tmp 2>/dev/null || \ + ( mv -f build/build_cfg.tmp build/build_cfg.h && echo " CFG build/build_cfg.h" ) + build/build_tag.h: update_tag +build/build_cfg.h: update_cfg build/%.bin: data/%.png @echo " IMG $@" @@ -181,7 +209,8 @@ build/%.bin: font/%.bin @echo " CP $@" @cp $< $@ -build/main.o: build/build_tag.h src/main.c +build/main.o: build/build_tag.h build/build_cfg.h src/main.c build/usb_dwc3.o: build/build_tag.h src/usb_dwc3.c +build/chainload.o: build/build_cfg.h src/usb_dwc3.c -include $(DEPDIR)/* diff --git a/README.md b/README.md index 9fc3ea53..ff44104e 100644 --- a/README.md +++ b/README.md @@ -132,3 +132,5 @@ licensed under the [OFL-1.1](3rdparty_licenses/LICENSE.OFL-1.1) license and copy m1n1 embeds portions of the [dwc3 usb linux driver](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/dwc3/core.h?id=7bc5a6ba369217e0137833f5955cf0b0f08b0712), which was [BSD-or-GPLv2 dual-licensed](3rdparty_licenses/LICENSE.BSD-3.dwc3) and copyright * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + +m1n1 embeds some rust crates. Licenses can be found in the vendor directory for every crate. diff --git a/m1n1-raw.ld b/m1n1-raw.ld index 61c51df3..56ebfe49 100644 --- a/m1n1-raw.ld +++ b/m1n1-raw.ld @@ -45,6 +45,8 @@ SECTIONS { *(.rela.text.*) *(.rela.data) *(.rela.data.*) + *(.rela.rodata) + *(.rela.rodata*) *(.rela.dyn) _rela_end = .; . = ALIGN(0x4000); diff --git a/m1n1.ld b/m1n1.ld index d02d6588..6ac86d7a 100644 --- a/m1n1.ld +++ b/m1n1.ld @@ -150,6 +150,8 @@ SECTIONS { *(.rela.text.*) *(.rela.data) *(.rela.data.*) + *(.rela.rodata) + *(.rela.rodata*) *(.rela.dyn) _rela_end = .; . = ALIGN(0x4000); diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 00000000..7376cd5c --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,56 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.3.2" + +[[package]] +name = "cfg-if" +version = "1.0.0" + +[[package]] +name = "cstr_core" +version = "0.2.5" +dependencies = [ + "cty", + "memchr", +] + +[[package]] +name = "cty" +version = "0.2.2" + +[[package]] +name = "fatfs" +version = "0.4.0" +dependencies = [ + "bitflags", + "log", +] + +[[package]] +name = "log" +version = "0.4.14" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.1" + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "cstr_core", + "cty", + "fatfs", + "uuid", +] + +[[package]] +name = "uuid" +version = "1.0.0-alpha.1" diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 00000000..0bef46c6 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" +repository = "https://github.com/AsahiLinux/m1n1" +license = "MIT" +publish = false + +[lib] +crate-type = [ "staticlib" ] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +fatfs = { path = "vendor/rust-fatfs", default-features = false, features = ["lfn", "alloc"] } +cstr_core = "0.2.5" +uuid = { version = "1.0.0-alpha.1", default-features = false } +cty = "0.2.2" + +[patch.crates-io] +uuid = { path = "vendor/uuid" } +cty = { path = "vendor/cty" } +cstr_core = { path = "vendor/cstr_core" } +memchr = { path = "vendor/memchr" } +log = { path = "vendor/log" } +bitflags = { path = "vendor/bitflags" } +cfg-if = { path = "vendor/cfg-if" } diff --git a/rust/src/chainload.rs b/rust/src/chainload.rs new file mode 100644 index 00000000..093158d9 --- /dev/null +++ b/rust/src/chainload.rs @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: MIT +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::gpt; +use crate::nvme; +use crate::println; +use alloc::vec::Vec; +use core::ffi::c_void; +use cstr_core::CStr; +use cty::*; +use fatfs::{FileSystem, FsOptions, Read, Seek, SeekFrom}; +use uuid::Uuid; + +#[derive(Debug)] +pub enum Error { + FATError(fatfs::Error), + GPTError(gpt::Error), + BadArgs, + PartitionNotFound, + Unknown, +} + +impl From> for Error { + fn from(err: fatfs::Error) -> Error { + Error::FATError(err) + } +} + +impl From> for Error { + fn from(err: gpt::Error) -> Error { + Error::GPTError(err) + } +} + +fn load_image(spec: &str) -> Result, Error> { + println!("Chainloading {}", spec); + + let mut args = spec.split(';'); + + let uuid = Uuid::parse_str(args.next().ok_or(Error::BadArgs)?).or(Err(Error::BadArgs))?; + let path = args.next().ok_or(Error::BadArgs)?; + + let part = { + let storage = nvme::NVMEStorage::new(1, 0); + let mut pt = gpt::GPT::new(storage)?; + + //println!("Partitions:"); + //pt.dump(); + + println!("Searching for partition UUID: {}", uuid); + pt.find_by_partuuid(uuid)?.ok_or(Error::PartitionNotFound)? + }; + + let offset = part.get_starting_lba(); + + println!("Partition offset: {}", offset); + + let storage = nvme::NVMEStorage::new(1, offset); + let opts = FsOptions::new().update_accessed_date(false); + + let fs = FileSystem::new(storage, opts)?; + let mut file = fs.root_dir().open_file(path)?; + + let size = file.seek(SeekFrom::End(0))? as usize; + file.seek(SeekFrom::Start(0))?; + + println!("File size: {}", size); + + let mut buf: Vec = vec![0; size]; + let mut slice = &mut buf[..]; + while !slice.is_empty() { + let read = file.read(slice)?; + slice = &mut slice[read..]; + } + println!("File read successfully"); + + Ok(buf) +} + +#[no_mangle] +pub unsafe extern "C" fn rust_load_image( + raw_spec: *const c_char, + image: *mut *mut c_void, + size: *mut size_t, +) -> c_int { + let spec = unsafe { CStr::from_ptr(raw_spec).to_str().unwrap() }; + + match load_image(spec) { + Ok(buf) => { + unsafe { + *size = buf.len(); + *image = buf.leak().as_mut_ptr() as *mut c_void; + } + 0 + } + Err(err) => { + println!("Chainload failed: {:?}", err); + -1 + } + } +} diff --git a/rust/src/dlmalloc.rs b/rust/src/dlmalloc.rs new file mode 100644 index 00000000..550b2747 --- /dev/null +++ b/rust/src/dlmalloc.rs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +use core::alloc::{GlobalAlloc, Layout}; +use core::ffi::c_void; +use core::ptr; +use cty::*; + +extern "C" { + pub fn malloc(size: size_t) -> *mut c_void; + pub fn realloc(p: *mut c_void, size: size_t) -> *mut c_void; + pub fn free(p: *mut c_void); + pub fn posix_memalign(p: *mut *mut c_void, alignment: size_t, size: size_t) -> c_int; +} + +pub struct DLMalloc; + +unsafe impl GlobalAlloc for DLMalloc { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let mut ptr = ptr::null_mut(); + let ret = unsafe { + posix_memalign( + &mut ptr, + layout.align().max(core::mem::size_of::()), + layout.size(), + ) + }; + if ret == 0 { + ptr as *mut u8 + } else { + ptr::null_mut() + } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // Unfortunately, calloc doesn't make any alignment guarantees, so the memory + // has to be manually zeroed-out. + let ptr = unsafe { self.alloc(layout) }; + if !ptr.is_null() { + unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; + } + ptr + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + unsafe { + free(ptr as *mut c_void); + } + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 { + unsafe { realloc(ptr as *mut c_void, new_size) as *mut u8 } + } +} diff --git a/rust/src/gpt.rs b/rust/src/gpt.rs new file mode 100644 index 00000000..0c65d0b7 --- /dev/null +++ b/rust/src/gpt.rs @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MIT + +use crate::println; +use core::convert::TryInto; +use core::result::Result; +use fatfs::{Read, Seek}; +use uuid::Uuid; + +const EFI_SIGNATURE: u64 = 0x5452415020494645; + +const SECTOR_SIZE: usize = 4096; + +#[derive(Debug)] +pub enum Error { + Io(T), + InvalidGPTHeader, +} + +impl From for Error { + fn from(err: T) -> Error { + Error::Io(err) + } +} + +struct TableHeader { + bytes: [u8; Self::SIZE], + my_lba: u64, +} + +impl TableHeader { + const SIZE: usize = 0x5C; + + fn read(rdr: &mut R, lba: u64) -> Result> { + let mut hdr = Self { + bytes: [0; Self::SIZE], + my_lba: lba, + }; + let off = SECTOR_SIZE * (lba as usize); + rdr.seek(fatfs::SeekFrom::Start(off as u64))?; + rdr.read_exact(&mut hdr.bytes)?; + match hdr.is_valid() { + true => Ok(hdr), + false => Err(Error::InvalidGPTHeader), + } + } + + fn get_signature(&self) -> u64 { + u64::from_le_bytes(self.bytes[0..8].try_into().unwrap()) + } + fn get_my_lba(&self) -> u64 { + u64::from_le_bytes(self.bytes[24..32].try_into().unwrap()) + } + fn get_partition_entry_lba(&self) -> u64 { + u64::from_le_bytes(self.bytes[72..80].try_into().unwrap()) + } + fn get_partition_entry_count(&self) -> usize { + u32::from_le_bytes(self.bytes[80..84].try_into().unwrap()) as usize + } + fn get_partition_entry_size(&self) -> usize { + u32::from_le_bytes(self.bytes[84..88].try_into().unwrap()) as usize + } + fn is_valid(&self) -> bool { + self.get_signature() == EFI_SIGNATURE && self.get_my_lba() == self.my_lba + } +} + +pub struct PartitionEntry { + bytes: [u8; Self::SIZE], +} + +impl PartitionEntry { + const SIZE: usize = 0x80; + + fn read(rdr: &mut R, off: usize) -> Result> { + let mut part = Self { + bytes: [0; Self::SIZE], + }; + rdr.seek(fatfs::SeekFrom::Start(off as u64))?; + rdr.read_exact(&mut part.bytes)?; + Ok(part) + } + + #[allow(dead_code)] + pub fn get_type_guid(&self) -> Uuid { + Uuid::from_bytes_le(self.bytes[0..16].try_into().unwrap()) + } + pub fn get_partition_guid(&self) -> Uuid { + Uuid::from_bytes_le(self.bytes[16..32].try_into().unwrap()) + } + pub fn get_starting_lba(&self) -> u64 { + u64::from_le_bytes(self.bytes[32..40].try_into().unwrap()) + } + pub fn get_ending_lba(&self) -> u64 { + u64::from_le_bytes(self.bytes[40..48].try_into().unwrap()) + } + pub fn get_attributes(&self) -> u64 { + u64::from_le_bytes(self.bytes[48..56].try_into().unwrap()) + } + pub fn get_name(&self) -> &[u8] { + &self.bytes[56..72] + } +} + +pub struct GPT { + disk: T, + hdr: TableHeader, +} + +impl GPT { + pub fn new>(storage: T) -> Result> { + let mut disk = storage.into_storage(); + + let hdr = TableHeader::read(&mut disk, 1)?; + + let gpt = Self { disk, hdr }; + Ok(gpt) + } + + pub fn count(&self) -> usize { + self.hdr.get_partition_entry_count() + } + + pub fn index(&mut self, index: usize) -> Result> { + let off = (self.hdr.get_partition_entry_lba() as usize * SECTOR_SIZE) + + index * self.hdr.get_partition_entry_size(); + PartitionEntry::read(&mut self.disk, off) + } + + pub fn find_by_partuuid( + &mut self, + uuid: Uuid, + ) -> Result, Error> { + for i in 0..self.count() { + let part = self.index(i)?; + if part.get_type_guid().is_nil() { + continue; + } + if part.get_partition_guid() == uuid { + return Ok(Some(part)); + } + } + Ok(None) + } + + pub fn dump(&mut self) { + for i in 0..self.count() { + let part = self.index(i).unwrap(); + let guid = part.get_type_guid(); + if guid.is_nil() { + continue; + } + println!( + "{}: {}..{} {:x} {:x}", + i, + part.get_starting_lba(), + part.get_ending_lba(), + guid, + part.get_partition_guid() + ); + } + } +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 00000000..a578b91a --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +#![no_std] +#![deny(unsafe_op_in_unsafe_fn)] +#![feature(alloc_error_handler)] +#![feature(mixed_integer_ops)] +#![feature(new_uninit)] + +#[macro_use] +extern crate alloc; + +pub mod chainload; +pub mod dlmalloc; +pub mod gpt; +pub mod nvme; +pub mod print; + +use crate::dlmalloc::DLMalloc; + +#[global_allocator] +static GLOBAL: DLMalloc = dlmalloc::DLMalloc; + +extern "C" { + fn flush_and_reboot(); +} + +#[panic_handler] +fn panic(info: &::core::panic::PanicInfo) -> ! { + println!("{}", info); + unsafe { flush_and_reboot() }; + loop {} +} + +#[alloc_error_handler] +fn alloc_error(layout: core::alloc::Layout) -> ! { + panic!("memory allocation of {} bytes failed", layout.size()) +} diff --git a/rust/src/nvme.rs b/rust/src/nvme.rs new file mode 100644 index 00000000..fdcec0e4 --- /dev/null +++ b/rust/src/nvme.rs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT +use crate::println; +use alloc::boxed::Box; +use core::cmp::min; +use core::ffi::c_void; +use fatfs::SeekFrom; + +extern "C" { + fn nvme_read(nsid: u32, lba: u64, buffer: *mut c_void) -> bool; +} + +const SECTOR_SIZE: usize = 4096; + +pub type Error = (); + +#[repr(C, align(4096))] +struct SectorBuffer([u8; SECTOR_SIZE]); + +fn alloc_sector_buf() -> Box { + let p: Box = unsafe { Box::new_zeroed().assume_init() }; + debug_assert_eq!(0, p.0.as_ptr().align_offset(4096)); + p +} + +pub struct NVMEStorage { + nsid: u32, + offset: u64, + lba: Option, + buf: Box, + pos: u64, +} + +impl NVMEStorage { + pub fn new(nsid: u32, offset: u64) -> NVMEStorage { + NVMEStorage { + nsid: nsid, + offset: offset, + lba: None, + buf: alloc_sector_buf(), + pos: 0, + } + } +} + +impl fatfs::IoBase for NVMEStorage { + type Error = Error; +} + +impl fatfs::Read for NVMEStorage { + fn read(&mut self, mut buf: &mut [u8]) -> Result { + let mut read = 0; + + while !buf.is_empty() { + let lba = self.pos / SECTOR_SIZE as u64; + let off = self.pos as usize % SECTOR_SIZE; + + if Some(lba) != self.lba { + self.lba = Some(lba); + let lba = lba + self.offset; + if !unsafe { nvme_read(self.nsid, lba, self.buf.0.as_mut_ptr() as *mut c_void) } { + println!("nvme_read({}, {}) failed", self.nsid, lba); + return Err(()); + } + } + let copy_len = min(SECTOR_SIZE - off, buf.len()); + buf[..copy_len].copy_from_slice(&self.buf.0[off..off + copy_len]); + buf = &mut buf[copy_len..]; + read += copy_len; + self.pos += copy_len as u64; + } + Ok(read) + } +} + +impl fatfs::Write for NVMEStorage { + fn write(&mut self, _buf: &[u8]) -> Result { + Err(()) + } + fn flush(&mut self) -> Result<(), Self::Error> { + Err(()) + } +} + +impl fatfs::Seek for NVMEStorage { + fn seek(&mut self, from: SeekFrom) -> Result { + self.pos = match from { + SeekFrom::Start(n) => n, + SeekFrom::End(_n) => panic!("SeekFrom::End not supported"), + SeekFrom::Current(n) => self.pos.checked_add_signed(n).ok_or(())?, + }; + Ok(self.pos) + } +} diff --git a/rust/src/print.rs b/rust/src/print.rs new file mode 100644 index 00000000..b9f0ead6 --- /dev/null +++ b/rust/src/print.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +use core::ffi::c_void; + +extern "C" { + fn iodev_console_write(buf: *const c_void, len: u64); +} + +pub struct IODevConsoleWriter; + +impl core::fmt::Write for IODevConsoleWriter { + #[inline] + fn write_str(&mut self, msg: &str) -> core::fmt::Result { + write(msg) + } +} + +impl IODevConsoleWriter { + #[inline] + pub fn write_fmt(args: core::fmt::Arguments) -> core::fmt::Result { + core::fmt::Write::write_fmt(&mut Self, args) + } + + #[inline] + pub fn write_str(msg: &str) -> core::fmt::Result { + write(msg) + } + + #[inline] + pub fn write_nl() -> core::fmt::Result { + write("\n") + } +} + +#[inline] +fn write(msg: &str) -> core::fmt::Result { + unsafe { iodev_console_write(msg.as_ptr() as _, msg.len() as u64) }; + Ok(()) +} + +#[macro_export] +macro_rules! println { + () => { $crate::println!("") }; + ($($arg:tt)*) => { + #[allow(unused_must_use)] + { + $crate::print::IODevConsoleWriter::write_fmt(format_args!($($arg)*)); + $crate::print::IODevConsoleWriter::write_nl(); + } + }; +} + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => { + #[allow(unused_must_use)] + { + $crate::print::IODevConsoleWriter::write_fmt(format_args!($($arg)*)); + } + }; +} diff --git a/rust/vendor/bitflags b/rust/vendor/bitflags new file mode 160000 index 00000000..ed185cfb --- /dev/null +++ b/rust/vendor/bitflags @@ -0,0 +1 @@ +Subproject commit ed185cfb1c447c1b4bd6ac021c9ec3bb02c9e2f2 diff --git a/rust/vendor/cfg-if b/rust/vendor/cfg-if new file mode 160000 index 00000000..e60fa1ef --- /dev/null +++ b/rust/vendor/cfg-if @@ -0,0 +1 @@ +Subproject commit e60fa1efeab0ec6e90c50d93ec526e1410459c23 diff --git a/rust/vendor/cstr_core b/rust/vendor/cstr_core new file mode 160000 index 00000000..35e44d2a --- /dev/null +++ b/rust/vendor/cstr_core @@ -0,0 +1 @@ +Subproject commit 35e44d2a128f29a52ac0a43baa2d9238bd7239dd diff --git a/rust/vendor/cty b/rust/vendor/cty new file mode 160000 index 00000000..dcc347dc --- /dev/null +++ b/rust/vendor/cty @@ -0,0 +1 @@ +Subproject commit dcc347dc8afb74906719e68e2f48e3ff38dcc76f diff --git a/rust/vendor/log b/rust/vendor/log new file mode 160000 index 00000000..9d420677 --- /dev/null +++ b/rust/vendor/log @@ -0,0 +1 @@ +Subproject commit 9d4206770dd93f07cb27c3e1f41dc21c45031302 diff --git a/rust/vendor/memchr b/rust/vendor/memchr new file mode 160000 index 00000000..8e1da98f --- /dev/null +++ b/rust/vendor/memchr @@ -0,0 +1 @@ +Subproject commit 8e1da98fee06d66c13e66c330e3a3dd6ccf0e3a0 diff --git a/rust/vendor/rust-fatfs b/rust/vendor/rust-fatfs new file mode 160000 index 00000000..87fc1ed5 --- /dev/null +++ b/rust/vendor/rust-fatfs @@ -0,0 +1 @@ +Subproject commit 87fc1ed5074a32b4e0344fcdde77359ef9e75432 diff --git a/rust/vendor/uuid b/rust/vendor/uuid new file mode 160000 index 00000000..b98c9586 --- /dev/null +++ b/rust/vendor/uuid @@ -0,0 +1 @@ +Subproject commit b98c9586c1359a5cc354defe20d8ea31e58f29f0 diff --git a/src/chainload.c b/src/chainload.c index 319b8877..87b41aea 100644 --- a/src/chainload.c +++ b/src/chainload.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: MIT */ +#include "../build/build_cfg.h" + #include "chainload.h" #include "adt.h" #include "malloc.h" @@ -10,6 +12,10 @@ #include "utils.h" #include "xnuboot.h" +#ifdef CHAINLOADING +int rust_load_image(const char *spec, void **image, size_t *size); +#endif + extern u8 _chainload_stub_start[]; extern u8 _chainload_stub_end[]; @@ -107,6 +113,29 @@ int chainload_image(void *image, size_t size, char **vars, size_t var_cnt) return 0; } +#ifdef CHAINLOADING + +int chainload_load(const char *spec, char **vars, size_t var_cnt) +{ + void *image; + size_t size; + int ret; + + if (!nvme_init()) { + printf("chainload: NVME init failed\n"); + return -1; + } + + ret = rust_load_image(spec, &image, &size); + nvme_shutdown(); + if (ret < 0) + return ret; + + return chainload_image(image, size, vars, var_cnt); +} + +#else + int chainload_load(const char *spec, char **vars, size_t var_cnt) { UNUSED(spec); @@ -116,3 +145,5 @@ int chainload_load(const char *spec, char **vars, size_t var_cnt) printf("Chainloading files not supported in this build!\n"); return -1; } + +#endif diff --git a/src/main.c b/src/main.c index ef4e4028..eaa57e4d 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: MIT */ +#include "../build/build_cfg.h" +#include "../build/build_tag.h" + #include "../config.h" #include "adt.h" @@ -26,8 +29,6 @@ #include "wdt.h" #include "xnuboot.h" -#include "../build/build_tag.h" - struct vector_args next_stage; const char version_tag[] = "##m1n1_ver##" BUILD_TAG; diff --git a/src/payload.c b/src/payload.c index 9ac44ef4..cc2fff42 100644 --- a/src/payload.c +++ b/src/payload.c @@ -3,6 +3,7 @@ #include "payload.h" #include "adt.h" #include "assert.h" +#include "chainload.h" #include "heapblock.h" #include "kboot.h" #include "smp.h" @@ -27,6 +28,7 @@ static const u8 empty[] = {0, 0, 0, 0}; static char expect_compatible[256]; static struct kernel_header *kernel = NULL; static void *fdt = NULL; +static char *chainload_spec = NULL; static void *load_one_payload(void *start, size_t size); @@ -172,6 +174,9 @@ static bool check_var(u8 **p) printf("Too many chosen vars, ignoring %s='%s'\n", *p, val); else chosen[chosen_cnt++] = (char *)*p; + } else if (IS_VAR("chainload=")) { + *end = 0; + chainload_spec = val; } else { printf("Unknown variable %s\n", *p); } @@ -242,6 +247,10 @@ int payload_run(void) while (p) p = load_one_payload(p, 0); + if (chainload_spec) { + return chainload_load(chainload_spec, chosen, chosen_cnt); + } + if (kernel && fdt) { smp_start_secondaries(); diff --git a/src/usb_dwc3.c b/src/usb_dwc3.c index 9338271a..0001fe57 100644 --- a/src/usb_dwc3.c +++ b/src/usb_dwc3.c @@ -7,6 +7,8 @@ * - https://www.beyondlogic.org/usbnutshell/usb1.shtml */ +#include "../build/build_tag.h" + #include "usb_dwc3.h" #include "dart.h" #include "malloc.h" @@ -18,8 +20,6 @@ #include "usb_types.h" #include "utils.h" -#include "../build/build_tag.h" - #define MAX_ENDPOINTS 16 #define CDC_BUFFER_SIZE SZ_1M