Skip to content

Commit 7b37c85

Browse files
committed
Hack up an initrd
1 parent 7f5782a commit 7b37c85

File tree

4 files changed

+328
-59
lines changed

4 files changed

+328
-59
lines changed

Kernel/Modules/vfs/fs_initrd.rs

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
//! Read-only initial RAM Disk
2+
//!
3+
//! A rather crude format, and very hacky code to run it
4+
use core::convert::TryInto;
5+
use kernel::lib::mem::Box;
6+
use kernel::metadevs::storage::{IoError,VolumeHandle};
7+
use core::mem::size_of;
8+
9+
const BLOCK_SIZE: usize = 0x1000;
10+
mod repr {
11+
pub const MAGIC_NUMBER: u32 = 0x0;
12+
13+
#[repr(C)]
14+
pub struct Header {
15+
pub magic: u32,
16+
pub total_length: u32,
17+
pub node_count: u32,
18+
pub root_length: u32,
19+
}
20+
#[repr(C)]
21+
pub struct Inode {
22+
pub length: u32,
23+
pub ofs: u32,
24+
pub ty: u8,
25+
_reserved: [u8; 3]
26+
// TODO: Anything else?
27+
}
28+
29+
pub const NODE_TY_REGULAR: u8 = 0;
30+
pub const NODE_TY_DIRECTORY: u8 = 1;
31+
//pub const NODE_TY_SYMLINK: u8 = 2;
32+
33+
#[repr(C)]
34+
pub struct DirEntry {
35+
pub node: u32,
36+
pub filename: [u8; 64-4],
37+
}
38+
impl DirEntry {
39+
pub fn name(&self) -> &super::ByteStr {
40+
let l = self.filename.iter().position(|v| *v == 0).unwrap_or(self.filename.len());
41+
super::ByteStr::new(&self.filename[..l])
42+
}
43+
}
44+
}
45+
46+
/// The underlying "storage device" for an initrd
47+
///
48+
/// Acts like a normal device, except that if you read from block -1 it returns a magic number
49+
/// and the slice (pointer and len) to the raw data.
50+
pub struct InitrdVol {
51+
handle: ::kernel::memory::virt::AllocHandle,
52+
ofs: u32,
53+
len: u32,
54+
}
55+
impl InitrdVol {
56+
/// UNSAFE: Takes raw physical memory locations, and thus can read from any memory
57+
pub unsafe fn new(base: u64, length: usize) -> Result<Self,()> {
58+
let handle = match ::kernel::memory::virt::map_hw_ro(
59+
base, (length + ::kernel::PAGE_SIZE-1) / ::kernel::PAGE_SIZE,
60+
"initrd"
61+
)
62+
{
63+
Ok(v) => v,
64+
Err(_e) => return Err(()),
65+
};
66+
let ofs = (base % (::kernel::PAGE_SIZE as u64)) as usize;
67+
68+
Ok(Self { handle, ofs: ofs as u32, len: length as u32 })
69+
}
70+
}
71+
impl ::kernel::metadevs::storage::PhysicalVolume for InitrdVol {
72+
fn name(&self) -> &str {
73+
"initrd"
74+
}
75+
76+
fn blocksize(&self) -> usize {
77+
BLOCK_SIZE
78+
}
79+
80+
fn capacity(&self) -> Option<u64> {
81+
Some(self.len as u64 / BLOCK_SIZE as u64)
82+
}
83+
84+
fn read<'a>(&'a self, _: u8, blockidx: u64, count: usize, dst: &'a mut [u8]) -> kernel::metadevs::storage::AsyncIoResult<'a, usize> {
85+
// Handle the magic protocol
86+
if blockidx == !0 && count == 1 {
87+
dst[ 0..][..8].copy_from_slice(&(repr::MAGIC_NUMBER as u64).to_le_bytes() );
88+
dst[ 8..][..8].copy_from_slice(&(self.handle.as_slice::<u8>(self.ofs as usize, 0).as_ptr() as usize as u64).to_le_bytes());
89+
dst[16..][..8].copy_from_slice(&(self.len as u64).to_le_bytes());
90+
return Box::pin(async move { Ok(1) });
91+
}
92+
if blockidx as usize > (self.len as usize) / BLOCK_SIZE {
93+
return Box::pin(async move { Err(IoError::BadBlock) });
94+
}
95+
let count = count.min( self.len as usize / BLOCK_SIZE - blockidx as usize );
96+
assert!(dst.len() >= count * BLOCK_SIZE);
97+
let ofs = self.ofs as usize + blockidx as usize * BLOCK_SIZE;
98+
let src = self.handle.as_slice(ofs, dst.len());
99+
dst.copy_from_slice(src);
100+
101+
Box::pin(async move { Ok(count) })
102+
}
103+
104+
fn write<'a>(&'a self, _: u8, _: u64, _: usize, _: &'a [u8]) -> kernel::metadevs::storage::AsyncIoResult<'a, usize> {
105+
Box::pin(async move { Err(IoError::ReadOnly) })
106+
}
107+
108+
fn wipe<'a>(&'a self, _: u64, _: usize) -> kernel::metadevs::storage::AsyncIoResult<'a,()> {
109+
Box::pin(async move { Err(IoError::ReadOnly) })
110+
}
111+
}
112+
113+
pub struct InitrdDriver;
114+
impl super::mount::Driver for InitrdDriver {
115+
fn detect(&self, vol: &VolumeHandle) -> crate::Result<usize> {
116+
let mut tmp = [0; 4096];
117+
::kernel::futures::block_on(vol.read_blocks(!0, &mut tmp))?;
118+
if tmp[..4] == repr::MAGIC_NUMBER.to_le_bytes() {
119+
Ok(3)
120+
}
121+
else {
122+
Ok(0)
123+
}
124+
}
125+
126+
fn mount(&self, vol: VolumeHandle, _self_handle: crate::mount::SelfHandle) -> crate::Result<Box<dyn crate::mount::Filesystem>> {
127+
let mut tmp = [0; 4096];
128+
::kernel::futures::block_on(vol.read_blocks(!0, &mut tmp))?;
129+
if tmp[..4] != repr::MAGIC_NUMBER.to_le_bytes() {
130+
return Err(crate::Error::InconsistentFilesystem)
131+
}
132+
let ptr = u64::from_le_bytes(tmp[8..][..8].try_into().unwrap());
133+
let len = u64::from_le_bytes(tmp[16..][..8].try_into().unwrap());
134+
// SAFE: Since the magic passed, assume that the encoded slice is also valid
135+
let data = unsafe { ::core::slice::from_raw_parts(ptr as usize as *const u8, len as usize) };
136+
if data.as_ptr() as usize % ::core::mem::align_of::<repr::Header>() != 0 {
137+
return Err(crate::Error::InconsistentFilesystem)
138+
}
139+
if data.len() < size_of::<repr::Header>() {
140+
return Err(crate::Error::InconsistentFilesystem)
141+
}
142+
143+
let instance = Inner {
144+
_vol_handle: vol,
145+
//self_handle,
146+
data
147+
};
148+
if data.len() < size_of::<repr::Header>() + instance.header().node_count as usize * size_of::<repr::Inode>() {
149+
return Err(crate::Error::InconsistentFilesystem)
150+
}
151+
152+
// SAFE: The ArefInner is going right in a box, and won't move until the box is dropped
153+
Ok(Box::new(InitrdInstance(unsafe { ::kernel::lib::mem::aref::ArefInner::new(instance) })))
154+
}
155+
}
156+
struct Inner {
157+
_vol_handle: VolumeHandle,
158+
//self_handle: super::mount::SelfHandle,
159+
data: &'static [u8],
160+
}
161+
impl Inner {
162+
fn header(&self) -> &repr::Header {
163+
// SAFE: Data alignment cheked by the original mount, and is valid for this type
164+
unsafe { &*(self.data.as_ptr() as *const repr::Header) }
165+
}
166+
fn inodes(&self) -> &[repr::Inode] {
167+
unsafe {
168+
::core::slice::from_raw_parts(
169+
self.data.as_ptr().offset(size_of::<repr::Header>() as isize) as *const repr::Inode,
170+
self.header().node_count as usize
171+
)
172+
}
173+
}
174+
fn get_data(&self, ofs: u32, len: u32) -> crate::Result<&[u8]> {
175+
if ofs as usize > self.data.len() {
176+
return Err(crate::Error::InconsistentFilesystem);
177+
}
178+
if ofs as usize + len as usize > self.data.len() {
179+
return Err(crate::Error::InconsistentFilesystem);
180+
}
181+
Ok(&self.data[ofs as usize..][..len as usize])
182+
}
183+
}
184+
struct InitrdInstance(::kernel::lib::mem::aref::ArefInner<Inner>);
185+
impl crate::mount::Filesystem for InitrdInstance {
186+
fn root_inode(&self) -> crate::node::InodeId {
187+
0
188+
}
189+
190+
fn get_node_by_inode(&self, i: crate::node::InodeId) -> Option<crate::node::Node> {
191+
use ::core::convert::TryFrom;
192+
let inodes = self.0.inodes();
193+
let inode = inodes.get(usize::try_from(i).ok()?)?;
194+
Some(match inode.ty {
195+
repr::NODE_TY_REGULAR => crate::node::Node::File(Box::new(NodeFile {
196+
parent: self.0.borrow(),
197+
node_id: i as u32,
198+
ofs: inode.ofs,
199+
size: inode.length,
200+
})),
201+
repr::NODE_TY_DIRECTORY => crate::node::Node::Dir(Box::new(NodeDir {
202+
parent: self.0.borrow(),
203+
node_id: i as u32,
204+
ofs: inode.ofs,
205+
size: inode.length,
206+
})),
207+
_ => return None,
208+
})
209+
}
210+
}
211+
212+
struct NodeFile {
213+
parent: ::kernel::lib::mem::aref::ArefBorrow<Inner>,
214+
node_id: u32,
215+
ofs: u32,
216+
size: u32,
217+
}
218+
impl crate::node::NodeBase for NodeFile {
219+
fn get_id(&self) -> crate::node::InodeId {
220+
self.node_id as _
221+
}
222+
223+
fn get_any(&self) -> &dyn core::any::Any {
224+
self
225+
}
226+
}
227+
impl crate::node::File for NodeFile {
228+
fn size(&self) -> u64 {
229+
self.size as u64
230+
}
231+
232+
fn truncate(&self, _newsize: u64) -> crate::node::Result<u64> {
233+
Err(crate::Error::ReadOnlyFilesystem)
234+
}
235+
236+
fn clear(&self, _ofs: u64, _size: u64) -> crate::node::Result<()> {
237+
Err(crate::Error::ReadOnlyFilesystem)
238+
}
239+
240+
fn read(&self, ofs: u64, buf: &mut [u8]) -> crate::node::Result<usize> {
241+
let src = self.parent.get_data(self.ofs, self.size)?;
242+
if ofs > src.len() as u64 {
243+
return Err(crate::Error::InvalidParameter);
244+
}
245+
let src = &src[ofs as usize..];
246+
let rv = buf.len().min( src.len() );
247+
buf[..rv].copy_from_slice(&src[..rv]);
248+
Ok(rv)
249+
}
250+
251+
fn write(&self, _ofs: u64, _buf: &[u8]) -> crate::node::Result<usize> {
252+
Err(crate::Error::ReadOnlyFilesystem)
253+
}
254+
}
255+
256+
struct NodeDir {
257+
parent: ::kernel::lib::mem::aref::ArefBorrow<Inner>,
258+
node_id: u32,
259+
ofs: u32,
260+
size: u32,
261+
}
262+
impl NodeDir {
263+
fn entries(&self) -> Result<&[repr::DirEntry],super::Error> {
264+
let d = self.parent.get_data(self.ofs, self.size)?;
265+
if d.as_ptr() as usize % align_of::<repr::DirEntry>() != 0 {
266+
return Err(crate::Error::InconsistentFilesystem);
267+
}
268+
if d.len() as usize % size_of::<repr::DirEntry>() != 0 {
269+
return Err(crate::Error::InconsistentFilesystem);
270+
}
271+
// SAFE: Alignment and size checked above, data is functionally POD
272+
Ok(unsafe { ::core::slice::from_raw_parts(d.as_ptr() as *const _, d.len() / size_of::<repr::DirEntry>()) })
273+
}
274+
}
275+
impl crate::node::NodeBase for NodeDir {
276+
fn get_id(&self) -> crate::node::InodeId {
277+
self.node_id as _
278+
}
279+
280+
fn get_any(&self) -> &dyn core::any::Any {
281+
self
282+
}
283+
}
284+
use kernel::lib::byte_str::ByteStr;
285+
impl crate::node::Dir for NodeDir {
286+
fn lookup(&self, name: &ByteStr) -> crate::node::Result<crate::node::InodeId> {
287+
for e in self.entries()? {
288+
if e.name() == name {
289+
return Ok(e.node as _);
290+
}
291+
}
292+
Err(super::Error::NotFound)
293+
}
294+
295+
fn read(&self, start_ofs: usize, callback: &mut crate::node::ReadDirCallback) -> crate::node::Result<usize> {
296+
let ents = self.entries()?;
297+
if start_ofs >= ents.len() {
298+
return Ok(ents.len());
299+
}
300+
let ents_to_visit = ents.get(start_ofs..).unwrap_or(&[]);
301+
for (i,e) in ents_to_visit.iter().enumerate() {
302+
if callback(e.node as u64, &mut e.name().as_bytes().iter().copied()) == false {
303+
return Ok(start_ofs + i + 1);
304+
}
305+
}
306+
Ok(start_ofs + ents_to_visit.len())
307+
}
308+
309+
fn create(&self, _name: &ByteStr, _nodetype: crate::node::NodeType) -> crate::node::Result<crate::node::InodeId> {
310+
Err(super::Error::ReadOnlyFilesystem)
311+
}
312+
313+
fn link(&self, _name: &ByteStr, _inode: &dyn crate::node::NodeBase) -> crate::node::Result<()> {
314+
Err(super::Error::ReadOnlyFilesystem)
315+
}
316+
317+
fn unlink(&self, _name: &ByteStr) -> crate::node::Result<()> {
318+
Err(super::Error::ReadOnlyFilesystem)
319+
}
320+
}

Kernel/Modules/vfs/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,12 @@ pub mod node;
7979
pub mod node_cache;
8080
pub mod mount;
8181
pub mod handle;
82+
mod fs_initrd;
8283
mod path;
8384
mod ramfs;
8485

86+
pub use self::fs_initrd::InitrdVol;
87+
8588
fn init()
8689
{
8790
// 1. Initialise global structures

Kernel/Modules/vfs/node.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ pub trait Dir: NodeBase {
4848
/// Acquire a node given the name
4949
fn lookup(&self, name: &ByteStr) -> Result<InodeId>;
5050

51-
/// Read Entry
51+
/// Read a directory entry, and pass it to the provided callback.
52+
/// - If the callback returns `false`, the routine should end and return `Ok` with the `start_ofs` for the next call
53+
/// - If an offset is passed that is at or past the end of the directory, return immediately with the end of directory value
5254
///
5355
/// Returns:
5456
/// - Ok(Next Offset)

0 commit comments

Comments
 (0)