Skip to content

Commit 3f1af8c

Browse files
committed
cpio: mount a cpio miniroot as a ramdisk
Support mounting a cpio archive as a ramdisk, in lieu of UFS. The usual file commands work here, albeit without symlink support. Regardless, this is good enough to load a kernel and get it running, using the same commands and syntax as for UFS. Signed-off-by: Dan Cross <cross@oxidecomputer.com>
1 parent 30ef8e5 commit 3f1af8c

19 files changed

Lines changed: 512 additions & 200 deletions

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ Supported commands include:
118118
* `inflate <src addr>,<src len> [<dst addr>,<dst len>]`
119119
decompresses the a ZLIB compressed slice from the given
120120
source to the given destination.
121-
* `mount <addr,len>` to mount the UFS ramdisk.
121+
* `mount <addr,len>` to mount a UFS ramdisk or cpio miniroot.
122+
* `umount` to unmount the ramdisk.
122123
* `ls <file>` to list a file or directory on the ramdisk.
123124
* `cat <file>` to display the contents of a file.
124125
* `copy <file> <dst addr>,<dst len>` to copy the contents of a

src/bldb.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ use crate::mmu;
4747
use crate::ramdisk;
4848
use crate::result::Error;
4949
use crate::uart::{self, Uart};
50-
use crate::ufs;
5150
use alloc::boxed::Box;
5251
use core::fmt;
5352
use core::ops::Range;
@@ -68,13 +67,13 @@ pub(crate) struct Config {
6867
pub(crate) gpios: &'static mut gpio::Gpios,
6968
pub(crate) loader_region: Range<mem::V4KA>,
7069
pub(crate) page_table: mmu::LoaderPageTable,
71-
pub(crate) ramdisk: Option<ufs::FileSystem<'static>>,
70+
pub(crate) ramdisk: Option<Box<dyn ramdisk::FileSystem>>,
7271
pub(crate) prompt: cons::Prompt,
7372
}
7473

7574
impl Config {
7675
pub fn mount(&mut self, ramdisk: &'static [u8]) -> Result<(), Error> {
77-
self.ramdisk = ramdisk::mount(ramdisk)?;
76+
self.ramdisk = Some(ramdisk::mount(ramdisk)?);
7877
Ok(())
7978
}
8079
}
@@ -92,7 +91,7 @@ impl fmt::Debug for Config {
9291
writeln!(
9392
f,
9493
" ramdisk: {:?}",
95-
self.ramdisk.as_ref().map(|_| "mounted")
94+
self.ramdisk.as_ref().map(|fs| fs.as_str())
9695
)?;
9796
writeln!(f, " prompt: {:?}", self.prompt)?;
9897
write!(f, "}}")

src/cpio.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
//! cpio miniroot support.
6+
7+
use crate::io;
8+
use crate::ramdisk;
9+
use crate::result::{Error, Result};
10+
use crate::{print, println};
11+
use alloc::boxed::Box;
12+
use core::slice;
13+
14+
pub(crate) struct FileSystem {
15+
sd: io::Sd,
16+
}
17+
18+
impl FileSystem {
19+
pub(crate) fn try_new(bs: &[u8]) -> Result<FileSystem> {
20+
if bs.starts_with(b"070707") {
21+
let sd = io::Sd::from_slice(bs);
22+
Ok(FileSystem { sd })
23+
} else {
24+
Err(Error::FsInvMagic)
25+
}
26+
}
27+
}
28+
29+
pub(crate) struct File {
30+
data: io::Sd,
31+
}
32+
33+
impl File {
34+
fn as_slice(&self) -> &[u8] {
35+
let ptr = self.data.as_ptr();
36+
let len = self.data.len();
37+
unsafe { slice::from_raw_parts(ptr, len) }
38+
}
39+
}
40+
41+
impl ramdisk::File for File {
42+
fn file_type(&self) -> ramdisk::FileType {
43+
ramdisk::FileType::Regular
44+
}
45+
}
46+
47+
impl io::Read for File {
48+
fn read(&self, offset: u64, dst: &mut [u8]) -> Result<usize> {
49+
let s = self.as_slice();
50+
s.read(offset, dst)
51+
}
52+
53+
fn size(&self) -> usize {
54+
self.data.len()
55+
}
56+
}
57+
58+
impl ramdisk::FileSystem for FileSystem {
59+
fn open(&self, path: &str) -> Result<Box<dyn ramdisk::File>> {
60+
let ptr: *const u8 = self.sd.as_ptr();
61+
let len = self.sd.len();
62+
let cpio = unsafe { core::slice::from_raw_parts(ptr, len) };
63+
let key = path.strip_prefix("/").unwrap_or(path);
64+
for file in cpio_reader::iter_files(cpio) {
65+
if file.name() == key {
66+
let data = io::Sd::from_slice(file.file());
67+
return Ok(Box::new(File { data }));
68+
}
69+
}
70+
Err(Error::FsNoFile)
71+
}
72+
73+
fn list(&self, path: &str) -> Result<()> {
74+
let ptr: *const u8 = self.sd.as_ptr();
75+
let len = self.sd.len();
76+
let cpio = unsafe { core::slice::from_raw_parts(ptr, len) };
77+
let key = path.strip_prefix('/').unwrap_or(path);
78+
for file in cpio_reader::iter_files(cpio) {
79+
if file.name() == key {
80+
lsfile(path, &file);
81+
return Ok(());
82+
}
83+
}
84+
let mut found = false;
85+
for file in cpio_reader::iter_files(cpio) {
86+
if file.name().starts_with(key) {
87+
lsfile(file.name(), &file);
88+
found = true;
89+
}
90+
}
91+
if found { Ok(()) } else { Err(Error::FsNoFile) }
92+
}
93+
94+
fn as_str(&self) -> &str {
95+
"cpio"
96+
}
97+
98+
fn with_addr(&self, addr: usize) -> *const u8 {
99+
self.sd.as_ptr().with_addr(addr)
100+
}
101+
}
102+
103+
fn lsfile(path: &str, file: &cpio_reader::Entry) {
104+
print!("#{ino:<4} ", ino = file.ino());
105+
print_mode(file.mode());
106+
println!(
107+
" {nlink:<2} {uid:<3} {gid:<3} {size:>8} {path}",
108+
nlink = file.nlink(),
109+
uid = file.uid(),
110+
gid = file.gid(),
111+
size = file.file().len(),
112+
);
113+
}
114+
115+
fn first_char(mode: cpio_reader::Mode) -> char {
116+
use cpio_reader::Mode;
117+
match mode {
118+
_ if mode.contains(Mode::DIRECTORY) => 'd',
119+
_ if mode.contains(Mode::CHARACTER_SPECIAL_DEVICE) => 'c',
120+
_ if mode.contains(Mode::BLOCK_SPECIAL_DEVICE) => 'b',
121+
_ if mode.contains(Mode::SYMBOLIK_LINK) => 'l',
122+
_ if mode.contains(Mode::SOCKET) => 's',
123+
_ if mode.contains(Mode::NAMED_PIPE_FIFO) => 'f',
124+
_ => '-',
125+
}
126+
}
127+
128+
fn print_mode(mode: cpio_reader::Mode) {
129+
use cpio_reader::Mode;
130+
print!("{}", first_char(mode));
131+
let alt = |bit, t, f| {
132+
if mode.contains(bit) { t } else { f }
133+
};
134+
// For some reason, the cpio reader library appears to have
135+
// the meaning of these bits mirrored with respect to the owner
136+
// bits.
137+
print!("{}", alt(Mode::WORLD_READABLE, 'r', '-'));
138+
print!("{}", alt(Mode::WORLD_WRITABLE, 'w', '-'));
139+
if !mode.contains(Mode::SUID) {
140+
print!("{}", alt(Mode::WORLD_EXECUTABLE, 'x', '-'));
141+
} else {
142+
print!("{}", alt(Mode::WORLD_EXECUTABLE, 's', 'S'));
143+
}
144+
145+
print!("{}", alt(Mode::GROUP_READABLE, 'r', '-'));
146+
print!("{}", alt(Mode::GROUP_WRITABLE, 'w', '-'));
147+
if !mode.contains(Mode::SGID) {
148+
print!("{}", alt(Mode::GROUP_EXECUTABLE, 'x', '-'));
149+
} else {
150+
print!("{}", alt(Mode::GROUP_EXECUTABLE, 's', 'S'));
151+
}
152+
153+
print!("{}", alt(Mode::USER_READABLE, 'r', '-'));
154+
print!("{}", alt(Mode::USER_WRITABLE, 'w', '-'));
155+
if !mode.contains(Mode::STICKY) {
156+
print!("{}", alt(Mode::USER_EXECUTABLE, 'x', '-'));
157+
} else {
158+
print!("{}", alt(Mode::USER_EXECUTABLE, 't', 'T'));
159+
}
160+
}

src/io.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
use crate::result::Result;
6+
7+
#[derive(Debug)]
8+
pub(crate) struct Sd {
9+
pub(crate) ptr: *const u8,
10+
pub(crate) len: usize,
11+
}
12+
13+
impl Sd {
14+
pub(crate) fn new(ptr: *const u8, len: usize) -> Sd {
15+
Sd { ptr, len }
16+
}
17+
18+
pub(crate) fn from_slice(bs: &[u8]) -> Sd {
19+
Sd::new(bs.as_ptr(), bs.len())
20+
}
21+
22+
pub(crate) fn as_ptr(&self) -> *const u8 {
23+
self.ptr
24+
}
25+
26+
pub(crate) fn len(&self) -> usize {
27+
self.len
28+
}
29+
30+
pub(crate) fn subset(&self, offset: usize, len: usize) -> Sd {
31+
assert!(offset + len <= self.len);
32+
let ptr = self.ptr.wrapping_add(offset);
33+
Sd { ptr, len }
34+
}
35+
}
36+
37+
pub(crate) trait Read {
38+
fn read(&self, off: u64, dst: &mut [u8]) -> Result<usize>;
39+
fn size(&self) -> usize;
40+
}
41+
42+
impl Read for &[u8] {
43+
fn read(&self, off: u64, dst: &mut [u8]) -> Result<usize> {
44+
let off = off as usize;
45+
if off > self.len() {
46+
return Ok(0);
47+
}
48+
let bytes = &self[off..];
49+
let len = usize::min(bytes.len(), dst.len());
50+
if len > 0 {
51+
dst[..len].copy_from_slice(&bytes[..len]);
52+
}
53+
Ok(len)
54+
}
55+
56+
fn size(&self) -> usize {
57+
self.len()
58+
}
59+
}

src/loader.rs

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
88
extern crate alloc;
99

10+
use crate::io::Read;
1011
use crate::mem;
1112
use crate::mmu::LoaderPageTable;
1213
use crate::println;
14+
use crate::ramdisk::File;
1315
use crate::result::{Error, Result};
14-
use crate::ufs;
1516
use alloc::vec::Vec;
1617
use goblin::container::{Container, Ctx, Endian};
1718
use goblin::elf::ProgramHeader;
@@ -20,37 +21,12 @@ use goblin::elf::{self, Elf};
2021

2122
const PAGE_SIZE: usize = 4096;
2223

23-
pub(crate) trait Read {
24-
fn read(&self, off: u64, dst: &mut [u8]) -> Result<usize>;
25-
}
26-
27-
impl Read for ufs::Inode<'_> {
28-
fn read(&self, off: u64, dst: &mut [u8]) -> Result<usize> {
29-
self.read(off, dst).map_err(|_| Error::FsRead)
30-
}
31-
}
32-
33-
impl Read for &[u8] {
34-
fn read(&self, off: u64, dst: &mut [u8]) -> Result<usize> {
35-
let off = off as usize;
36-
if off > self.len() {
37-
return Err(Error::ElfTruncatedObj);
38-
}
39-
let bytes = &self[off..];
40-
let len = usize::min(bytes.len(), dst.len());
41-
if len > 0 {
42-
dst[..len].copy_from_slice(&bytes[..len]);
43-
}
44-
Ok(0)
45-
}
46-
}
47-
4824
/// Loads an executable image contained in the given file
4925
/// creating virtual mappings as required. Returns the image's
5026
/// ELF entry point on success.
5127
pub(crate) fn load(
5228
page_table: &mut LoaderPageTable,
53-
file: &ufs::Inode<'_>,
29+
file: &dyn File,
5430
) -> Result<u64> {
5531
let mut buf = [0u8; PAGE_SIZE];
5632
file.read(0, &mut buf).map_err(|_| Error::FsRead)?;
@@ -88,7 +64,7 @@ pub(crate) fn load_bytes(
8864
Ok(elf.entry)
8965
}
9066

91-
pub(crate) fn elfinfo<T: Read>(file: &T) -> Result<()> {
67+
pub(crate) fn elfinfo(file: &dyn File) -> Result<()> {
9268
use goblin::elf;
9369

9470
let mut buf = [0u8; PAGE_SIZE];
@@ -193,7 +169,7 @@ fn parse_program_headers(
193169

194170
/// Loads the given ELF segment, creating virtual mappings for
195171
/// it as required.
196-
fn load_segment<T: Read>(
172+
fn load_segment<T: Read + ?Sized>(
197173
page_table: &mut LoaderPageTable,
198174
segment: &ProgramHeader,
199175
file: &T,
@@ -226,8 +202,8 @@ fn load_segment<T: Read>(
226202
};
227203
let filesz = segment.p_filesz as usize;
228204
let len = usize::min(filesz, dst.len());
229-
if len > 0 {
230-
file.read(segment.p_offset, &mut dst[..len])?;
205+
if len > 0 && file.read(segment.p_offset, &mut dst[..len])? != len {
206+
return Err(Error::ElfTruncatedObj);
231207
}
232208
}
233209
let attrs = mem::Attrs::new_kernel(

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ mod allocator;
1818
mod bldb;
1919
mod clock;
2020
mod cons;
21+
mod cpio;
2122
mod cpuid;
2223
mod gpio;
2324
mod idt;
25+
mod io;
2426
mod iomux;
2527
mod loader;
2628
mod mem;

0 commit comments

Comments
 (0)