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=<ESP partition UUID>;<file path>

e.g.

chainload=a17b7e46-e950-bb4f-bc82-8ab1047a058e;m1n1/m1n1.bin

Closes: #154
Co-authored-by: Finn Behrens <me@kloenk.dev>
Co-authored-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
Hector Martin 2022-03-09 20:47:42 +09:00
parent e386e17550
commit 4575b35479
28 changed files with 742 additions and 25 deletions

View file

@ -17,6 +17,8 @@ IncludeIsMainRegex: '(_.*)?$'
# - 3rd party code headers # - 3rd party code headers
# - build artifact headers (stuff outside of src/) # - build artifact headers (stuff outside of src/)
IncludeCategories: IncludeCategories:
- Regex: '^"(\.\./)*build/build_.*\.h"$'
Priority: -3
- Regex: '^"(\.\./)*config\.h"$' - Regex: '^"(\.\./)*config\.h"$'
Priority: -2 Priority: -2
- Regex: '^<' - Regex: '^<'

View file

@ -21,12 +21,20 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
submodules: recursive submodules: recursive
- name: install aarch64-linux-gnu- toolchain
- name: Install aarch64-linux-gnu- toolchain
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install --no-install-recommends -y device-tree-compiler gcc-aarch64-linux-gnu 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- - 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 - uses: actions/upload-artifact@v2
with: with:
name: m1n1 name: m1n1

View file

@ -21,5 +21,13 @@ jobs:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2 - 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 - name: Run format-check
run: make format-check run: |
make format-check
make rustfmt-check

24
.gitmodules vendored
View file

@ -1,3 +1,27 @@
[submodule "artwork"] [submodule "artwork"]
path = artwork path = artwork
url = https://github.com/AsahiLinux/artwork.git 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

View file

@ -1,4 +1,5 @@
ARCH ?= aarch64-linux-gnu- ARCH ?= aarch64-linux-gnu-
RUSTARCH ?= aarch64-unknown-none-softfloat
ifeq ($(shell uname),Darwin) ifeq ($(shell uname),Darwin)
USE_CLANG ?= 1 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 \ -fno-stack-protector -mgeneral-regs-only -mstrict-align -march=armv8.2-a \
$(EXTRA_CFLAGS) $(EXTRA_CFLAGS)
CFG :=
ifeq ($(RELEASE),1) 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 endif
LDFLAGS := -EL -maarch64elf --no-undefined -X -Bsymbolic \ LDFLAGS := -EL -maarch64elf --no-undefined -X -Bsymbolic \
@ -102,7 +113,7 @@ OBJECTS := \
utils.o utils_asm.o \ utils.o utils_asm.o \
vsprintf.o \ vsprintf.o \
wdt.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 DTS := t8103-j274.dts
@ -115,14 +126,18 @@ TARGET_RAW := m1n1.bin
DEPDIR := build/.deps DEPDIR := build/.deps
.PHONY: all clean format update_tag .PHONY: all clean format update_tag update_cfg
all: build/$(TARGET) build/$(TARGET_RAW) $(DTBS) all: update_tag update_cfg build/$(TARGET) build/$(TARGET_RAW) $(DTBS)
clean: clean:
rm -rf build/* rm -rf build/*
format: format:
$(CLANG_FORMAT) -i src/*.c src/*.h sysinc/*.h $(CLANG_FORMAT) -i src/*.c src/*.h sysinc/*.h
format-check: format-check:
$(CLANG_FORMAT) --dry-run --Werror src/*.c src/*.h sysinc/*.h $(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 build/dtb/%.dts: dts/%.dts
@echo " DTCPP $@" @echo " DTCPP $@"
@ -134,6 +149,13 @@ build/dtb/%.dtb: build/dtb/%.dts
@mkdir -p "$(dir $@)" @mkdir -p "$(dir $@)"
@dtc -I dts -i dts $< -o $@ @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 build/%.o: src/%.S
@echo " AS $@" @echo " AS $@"
@mkdir -p $(DEPDIR) @mkdir -p $(DEPDIR)
@ -167,7 +189,13 @@ update_tag:
@cmp -s build/build_tag.h build/build_tag.tmp 2>/dev/null || \ @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" ) ( 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_tag.h: update_tag
build/build_cfg.h: update_cfg
build/%.bin: data/%.png build/%.bin: data/%.png
@echo " IMG $@" @echo " IMG $@"
@ -181,7 +209,8 @@ build/%.bin: font/%.bin
@echo " CP $@" @echo " CP $@"
@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/usb_dwc3.o: build/build_tag.h src/usb_dwc3.c
build/chainload.o: build/build_cfg.h src/usb_dwc3.c
-include $(DEPDIR)/* -include $(DEPDIR)/*

View file

@ -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 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 * 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.

View file

@ -45,6 +45,8 @@ SECTIONS {
*(.rela.text.*) *(.rela.text.*)
*(.rela.data) *(.rela.data)
*(.rela.data.*) *(.rela.data.*)
*(.rela.rodata)
*(.rela.rodata*)
*(.rela.dyn) *(.rela.dyn)
_rela_end = .; _rela_end = .;
. = ALIGN(0x4000); . = ALIGN(0x4000);

View file

@ -150,6 +150,8 @@ SECTIONS {
*(.rela.text.*) *(.rela.text.*)
*(.rela.data) *(.rela.data)
*(.rela.data.*) *(.rela.data.*)
*(.rela.rodata)
*(.rela.rodata*)
*(.rela.dyn) *(.rela.dyn)
_rela_end = .; _rela_end = .;
. = ALIGN(0x4000); . = ALIGN(0x4000);

56
rust/Cargo.lock generated Normal file
View file

@ -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"

26
rust/Cargo.toml Normal file
View file

@ -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" }

101
rust/src/chainload.rs Normal file
View file

@ -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<nvme::Error>),
GPTError(gpt::Error<nvme::Error>),
BadArgs,
PartitionNotFound,
Unknown,
}
impl From<fatfs::Error<nvme::Error>> for Error {
fn from(err: fatfs::Error<nvme::Error>) -> Error {
Error::FATError(err)
}
}
impl From<gpt::Error<nvme::Error>> for Error {
fn from(err: gpt::Error<nvme::Error>) -> Error {
Error::GPTError(err)
}
}
fn load_image(spec: &str) -> Result<Vec<u8>, 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<u8> = 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
}
}
}

57
rust/src/dlmalloc.rs Normal file
View file

@ -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::<usize>()),
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 }
}
}

162
rust/src/gpt.rs Normal file
View file

@ -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<T> {
Io(T),
InvalidGPTHeader,
}
impl<T> From<T> for Error<T> {
fn from(err: T) -> Error<T> {
Error::Io(err)
}
}
struct TableHeader {
bytes: [u8; Self::SIZE],
my_lba: u64,
}
impl TableHeader {
const SIZE: usize = 0x5C;
fn read<R: Read + Seek>(rdr: &mut R, lba: u64) -> Result<Self, Error<R::Error>> {
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<R: Read + Seek>(rdr: &mut R, off: usize) -> Result<Self, Error<R::Error>> {
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<T: fatfs::ReadWriteSeek> {
disk: T,
hdr: TableHeader,
}
impl<IO: fatfs::ReadWriteSeek> GPT<IO> {
pub fn new<T: fatfs::IntoStorage<IO>>(storage: T) -> Result<Self, Error<IO::Error>> {
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<PartitionEntry, Error<IO::Error>> {
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<Option<PartitionEntry>, Error<IO::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()
);
}
}
}

36
rust/src/lib.rs Normal file
View file

@ -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())
}

93
rust/src/nvme.rs Normal file
View file

@ -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<SectorBuffer> {
let p: Box<SectorBuffer> = 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<u64>,
buf: Box<SectorBuffer>,
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<usize, Self::Error> {
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<usize, Self::Error> {
Err(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
Err(())
}
}
impl fatfs::Seek for NVMEStorage {
fn seek(&mut self, from: SeekFrom) -> Result<u64, Self::Error> {
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)
}
}

60
rust/src/print.rs Normal file
View file

@ -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)*));
}
};
}

1
rust/vendor/bitflags vendored Submodule

@ -0,0 +1 @@
Subproject commit ed185cfb1c447c1b4bd6ac021c9ec3bb02c9e2f2

1
rust/vendor/cfg-if vendored Submodule

@ -0,0 +1 @@
Subproject commit e60fa1efeab0ec6e90c50d93ec526e1410459c23

1
rust/vendor/cstr_core vendored Submodule

@ -0,0 +1 @@
Subproject commit 35e44d2a128f29a52ac0a43baa2d9238bd7239dd

1
rust/vendor/cty vendored Submodule

@ -0,0 +1 @@
Subproject commit dcc347dc8afb74906719e68e2f48e3ff38dcc76f

1
rust/vendor/log vendored Submodule

@ -0,0 +1 @@
Subproject commit 9d4206770dd93f07cb27c3e1f41dc21c45031302

1
rust/vendor/memchr vendored Submodule

@ -0,0 +1 @@
Subproject commit 8e1da98fee06d66c13e66c330e3a3dd6ccf0e3a0

1
rust/vendor/rust-fatfs vendored Submodule

@ -0,0 +1 @@
Subproject commit 87fc1ed5074a32b4e0344fcdde77359ef9e75432

1
rust/vendor/uuid vendored Submodule

@ -0,0 +1 @@
Subproject commit b98c9586c1359a5cc354defe20d8ea31e58f29f0

View file

@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
#include "../build/build_cfg.h"
#include "chainload.h" #include "chainload.h"
#include "adt.h" #include "adt.h"
#include "malloc.h" #include "malloc.h"
@ -10,6 +12,10 @@
#include "utils.h" #include "utils.h"
#include "xnuboot.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_start[];
extern u8 _chainload_stub_end[]; 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; 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) int chainload_load(const char *spec, char **vars, size_t var_cnt)
{ {
UNUSED(spec); 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"); printf("Chainloading files not supported in this build!\n");
return -1; return -1;
} }
#endif

View file

@ -1,5 +1,8 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
#include "../build/build_cfg.h"
#include "../build/build_tag.h"
#include "../config.h" #include "../config.h"
#include "adt.h" #include "adt.h"
@ -26,8 +29,6 @@
#include "wdt.h" #include "wdt.h"
#include "xnuboot.h" #include "xnuboot.h"
#include "../build/build_tag.h"
struct vector_args next_stage; struct vector_args next_stage;
const char version_tag[] = "##m1n1_ver##" BUILD_TAG; const char version_tag[] = "##m1n1_ver##" BUILD_TAG;

View file

@ -3,6 +3,7 @@
#include "payload.h" #include "payload.h"
#include "adt.h" #include "adt.h"
#include "assert.h" #include "assert.h"
#include "chainload.h"
#include "heapblock.h" #include "heapblock.h"
#include "kboot.h" #include "kboot.h"
#include "smp.h" #include "smp.h"
@ -27,6 +28,7 @@ static const u8 empty[] = {0, 0, 0, 0};
static char expect_compatible[256]; static char expect_compatible[256];
static struct kernel_header *kernel = NULL; static struct kernel_header *kernel = NULL;
static void *fdt = NULL; static void *fdt = NULL;
static char *chainload_spec = NULL;
static void *load_one_payload(void *start, size_t size); 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); printf("Too many chosen vars, ignoring %s='%s'\n", *p, val);
else else
chosen[chosen_cnt++] = (char *)*p; chosen[chosen_cnt++] = (char *)*p;
} else if (IS_VAR("chainload=")) {
*end = 0;
chainload_spec = val;
} else { } else {
printf("Unknown variable %s\n", *p); printf("Unknown variable %s\n", *p);
} }
@ -242,6 +247,10 @@ int payload_run(void)
while (p) while (p)
p = load_one_payload(p, 0); p = load_one_payload(p, 0);
if (chainload_spec) {
return chainload_load(chainload_spec, chosen, chosen_cnt);
}
if (kernel && fdt) { if (kernel && fdt) {
smp_start_secondaries(); smp_start_secondaries();

View file

@ -7,6 +7,8 @@
* - https://www.beyondlogic.org/usbnutshell/usb1.shtml * - https://www.beyondlogic.org/usbnutshell/usb1.shtml
*/ */
#include "../build/build_tag.h"
#include "usb_dwc3.h" #include "usb_dwc3.h"
#include "dart.h" #include "dart.h"
#include "malloc.h" #include "malloc.h"
@ -18,8 +20,6 @@
#include "usb_types.h" #include "usb_types.h"
#include "utils.h" #include "utils.h"
#include "../build/build_tag.h"
#define MAX_ENDPOINTS 16 #define MAX_ENDPOINTS 16
#define CDC_BUFFER_SIZE SZ_1M #define CDC_BUFFER_SIZE SZ_1M