249 lines
9.5 KiB
Diff
249 lines
9.5 KiB
Diff
diff --git a/drivers/storage/lived/src/main.rs b/drivers/storage/lived/src/main.rs
|
|
index 2ca1ff27..8582e42a 100644
|
|
--- a/drivers/storage/lived/src/main.rs
|
|
+++ b/drivers/storage/lived/src/main.rs
|
|
@@ -1,0 +2,8 @@
|
|
+//!
|
|
+//! For live ISO boot: bootloader preloads the first N MiB of the filesystem into RAM.
|
|
+//! This daemon serves that preloaded region from RAM, and falls through to the physical
|
|
+//! disk (USB, AHCI, NVMe) for reads beyond the preload boundary.
|
|
+//!
|
|
+//! Boot order: lived(10) → drivers(40) → usbscsid(45) → rootfs(50)
|
|
+//! Since drivers load AFTER lived starts, the disk fallback is lazy: the physical disk
|
|
+//! handle is opened on the first out-of-range read, with retry backoff.
|
|
@@ -4,0 +13 @@
|
|
+use std::cell::RefCell;
|
|
@@ -7 +15,0 @@ use std::fs::File;
|
|
-
|
|
@@ -8,0 +17 @@ use std::os::fd::AsRawFd;
|
|
+use std::sync::atomic::{AtomicBool, Ordering};
|
|
@@ -19,0 +29,11 @@ use anyhow::{anyhow, Context};
|
|
+/// Block size must be 512 (redoxfs BLOCK_SIZE), not PAGE_SIZE.
|
|
+/// DiskWrapper::read rejects buffers not aligned to block_size, and redoxfs reads
|
|
+/// in 512-byte chunks.
|
|
+const BLOCK_SIZE: usize = 512;
|
|
+
|
|
+/// Maximum retries for opening the physical disk before giving up.
|
|
+/// Drivers (xhcid → usbscsid → /scheme/disk/) load between service 40-45,
|
|
+/// while lived starts at 10. Give plenty of retries.
|
|
+const DISK_OPEN_MAX_RETRIES: u32 = 60;
|
|
+const DISK_OPEN_RETRY_INTERVAL_MS: u64 = 500;
|
|
+
|
|
@@ -22 +41,0 @@ struct LiveDisk {
|
|
- //TODO: drop overlay blocks if they match the original
|
|
@@ -23,0 +43,4 @@ struct LiveDisk {
|
|
+ full_size: u64,
|
|
+ disk_phys_offset: u64,
|
|
+ disk_file: RefCell<Option<File>>,
|
|
+ logged_first_fallback: AtomicBool,
|
|
@@ -27 +50,6 @@ impl LiveDisk {
|
|
- fn new(phys: usize, size: usize) -> anyhow::Result<LiveDisk> {
|
|
+ fn new(
|
|
+ phys: usize,
|
|
+ preload_size: usize,
|
|
+ full_size: u64,
|
|
+ disk_phys_offset: u64,
|
|
+ ) -> anyhow::Result<LiveDisk> {
|
|
@@ -30,2 +58,2 @@ impl LiveDisk {
|
|
- .checked_add(size)
|
|
- .context("phys + size overflow")?
|
|
+ .checked_add(preload_size)
|
|
+ .context("phys + preload_size overflow")?
|
|
@@ -33 +61 @@ impl LiveDisk {
|
|
- let size = end - start;
|
|
+ let mapped_size = end - start;
|
|
@@ -41 +69 @@ impl LiveDisk {
|
|
- length: size,
|
|
+ length: mapped_size,
|
|
@@ -47 +75 @@ impl LiveDisk {
|
|
- std::slice::from_raw_parts_mut(base as *mut u8, size)
|
|
+ std::slice::from_raw_parts_mut(base as *mut u8, mapped_size)
|
|
@@ -49,0 +78,7 @@ impl LiveDisk {
|
|
+ eprintln!(
|
|
+ "lived: preload {} MiB, full filesystem {} MiB, disk_phys_offset {:#x}",
|
|
+ preload_size / (1024 * 1024),
|
|
+ full_size / (1024 * 1024),
|
|
+ disk_phys_offset,
|
|
+ );
|
|
+
|
|
@@ -52,0 +88,4 @@ impl LiveDisk {
|
|
+ full_size,
|
|
+ disk_phys_offset,
|
|
+ disk_file: RefCell::new(None),
|
|
+ logged_first_fallback: AtomicBool::new(false),
|
|
@@ -54,0 +94,69 @@ impl LiveDisk {
|
|
+
|
|
+ fn open_disk(&self) -> syscall::Result<()> {
|
|
+ if self.disk_file.borrow().is_some() {
|
|
+ return Ok(());
|
|
+ }
|
|
+ match self.try_open_disk() {
|
|
+ Ok(file) => {
|
|
+ *self.disk_file.borrow_mut() = Some(file);
|
|
+ Ok(())
|
|
+ }
|
|
+ Err(msg) => {
|
|
+ eprintln!("lived: disk fallback unavailable: {}", msg);
|
|
+ Err(syscall::Error::new(EIO))
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fn try_open_disk(&self) -> Result<File, String> {
|
|
+ // Try common disk scheme paths. USB boot appears as /scheme/disk/0 or /scheme/usbscsi/0.
|
|
+ // AHCI boot appears as /scheme/disk/0. NVMe appears as /scheme/disk/0.
|
|
+ let candidates = [
|
|
+ "/scheme/disk/0",
|
|
+ "/scheme/usbscsi/0",
|
|
+ "/scheme/disk/1",
|
|
+ "/scheme/usbscsi/1",
|
|
+ ];
|
|
+
|
|
+ for attempt in 0..DISK_OPEN_MAX_RETRIES {
|
|
+ for path in &candidates {
|
|
+ if let Ok(file) = File::open(path) {
|
|
+ eprintln!("lived: opened physical disk at {} (attempt {})", path, attempt + 1);
|
|
+ return Ok(file);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if attempt < DISK_OPEN_MAX_RETRIES - 1 {
|
|
+ std::thread::sleep(std::time::Duration::from_millis(DISK_OPEN_RETRY_INTERVAL_MS));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ Err(format!(
|
|
+ "no /scheme/disk/ found after {} retries",
|
|
+ DISK_OPEN_MAX_RETRIES
|
|
+ ))
|
|
+ }
|
|
+
|
|
+ fn read_from_disk(&self, offset: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
|
|
+ let disk_offset = self.disk_phys_offset + offset;
|
|
+
|
|
+ if !self.logged_first_fallback.swap(true, Ordering::Relaxed) {
|
|
+ eprintln!(
|
|
+ "lived: first disk fallback read at offset {} MiB (disk offset {:#x})",
|
|
+ offset / (1024 * 1024),
|
|
+ disk_offset,
|
|
+ );
|
|
+ }
|
|
+
|
|
+ self.open_disk()?;
|
|
+
|
|
+ use std::io::{Read, Seek, SeekFrom};
|
|
+ let mut disk = self.disk_file.borrow_mut();
|
|
+ let file = disk.as_mut().unwrap();
|
|
+ file.seek(SeekFrom::Start(disk_offset))
|
|
+ .map_err(|_| syscall::Error::new(EIO))?;
|
|
+ file.read_exact(buffer)
|
|
+ .map_err(|_| syscall::Error::new(EIO))?;
|
|
+
|
|
+ Ok(buffer.len())
|
|
+ }
|
|
@@ -59 +167 @@ impl Disk for LiveDisk {
|
|
- PAGE_SIZE as u32
|
|
+ BLOCK_SIZE as u32
|
|
@@ -63 +171 @@ impl Disk for LiveDisk {
|
|
- self.original.len() as u64
|
|
+ self.full_size
|
|
@@ -67,2 +175,4 @@ impl Disk for LiveDisk {
|
|
- let mut offset = (block as usize) * PAGE_SIZE;
|
|
- if offset + buffer.len() > self.original.len() {
|
|
+ let bs = self.block_size() as usize;
|
|
+ let mut offset = (block as usize) * bs;
|
|
+
|
|
+ if offset + buffer.len() > self.full_size as usize {
|
|
@@ -71 +181,28 @@ impl Disk for LiveDisk {
|
|
- for chunk in buffer.chunks_mut(PAGE_SIZE) {
|
|
+
|
|
+ let preload_len = self.original.len();
|
|
+
|
|
+ if offset + buffer.len() <= preload_len {
|
|
+ for chunk in buffer.chunks_mut(bs) {
|
|
+ match self.overlay.get(&block) {
|
|
+ Some(overlay) => {
|
|
+ chunk.copy_from_slice(&overlay[..chunk.len()]);
|
|
+ }
|
|
+ None => {
|
|
+ chunk.copy_from_slice(&self.original[offset..offset + chunk.len()]);
|
|
+ }
|
|
+ }
|
|
+ block += 1;
|
|
+ offset += bs;
|
|
+ }
|
|
+ return Ok(buffer.len());
|
|
+ }
|
|
+
|
|
+ if offset >= preload_len {
|
|
+ let fs_byte_offset = (block as u64) * bs as u64;
|
|
+ return self.read_from_disk(fs_byte_offset, buffer);
|
|
+ }
|
|
+
|
|
+ let preload_remaining = preload_len - offset;
|
|
+ let (ram_part, disk_part) = buffer.split_at_mut(preload_remaining);
|
|
+
|
|
+ for chunk in ram_part.chunks_mut(bs) {
|
|
@@ -81 +218 @@ impl Disk for LiveDisk {
|
|
- offset += PAGE_SIZE;
|
|
+ offset += bs;
|
|
@@ -82,0 +220,4 @@ impl Disk for LiveDisk {
|
|
+
|
|
+ let disk_fs_offset = (block as u64) * bs as u64;
|
|
+ self.read_from_disk(disk_fs_offset, disk_part)?;
|
|
+
|
|
@@ -87,2 +228,3 @@ impl Disk for LiveDisk {
|
|
- let mut offset = (block as usize) * PAGE_SIZE;
|
|
- if offset + buffer.len() > self.original.len() {
|
|
+ let bs = self.block_size() as usize;
|
|
+ let mut offset = (block as usize) * bs;
|
|
+ if offset + buffer.len() > self.full_size as usize {
|
|
@@ -91 +233 @@ impl Disk for LiveDisk {
|
|
- for chunk in buffer.chunks(PAGE_SIZE) {
|
|
+ for chunk in buffer.chunks(bs) {
|
|
@@ -93,2 +235,3 @@ impl Disk for LiveDisk {
|
|
- let offset = (block as usize) * PAGE_SIZE;
|
|
- self.original[offset..offset + PAGE_SIZE]
|
|
+ let off = (block as usize) * bs;
|
|
+ if off + bs <= self.original.len() {
|
|
+ self.original[off..off + bs]
|
|
@@ -96,0 +240,3 @@ impl Disk for LiveDisk {
|
|
+ } else {
|
|
+ vec![0u8; bs].into_boxed_slice()
|
|
+ }
|
|
@@ -100 +246 @@ impl Disk for LiveDisk {
|
|
- offset += PAGE_SIZE;
|
|
+ offset += bs;
|
|
@@ -112 +258,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
- let mut size = 0;
|
|
+ let mut preload_size = 0;
|
|
+ let mut full_size = 0u64;
|
|
+ let mut disk_phys_offset = 0u64;
|
|
@@ -129 +277 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
- size = usize::from_str_radix(value, 16).unwrap_or(0);
|
|
+ preload_size = usize::from_str_radix(value, 16).unwrap_or(0);
|
|
@@ -130,0 +279,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
+
|
|
+ if name == "REDOXFS_FULL_SIZE" {
|
|
+ full_size = u64::from_str_radix(value, 16).unwrap_or(0);
|
|
@@ -133,2 +284,6 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
- if phys == 0 || size == 0 {
|
|
- // No live disk data, no need to say anything or exit with
|
|
+ if name == "DISK_PHYS_BLOCK" {
|
|
+ disk_phys_offset = u64::from_str_radix(value, 16).unwrap_or(0) * BLOCK_SIZE as u64;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if phys == 0 || preload_size == 0 {
|
|
@@ -138,0 +294,4 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
+ if full_size == 0 {
|
|
+ full_size = preload_size as u64;
|
|
+ }
|
|
+
|
|
@@ -152 +311 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
- LiveDisk::new(phys, size).unwrap_or_else(|err| {
|
|
+ LiveDisk::new(phys, preload_size, full_size, disk_phys_offset).unwrap_or_else(|err| {
|
|
@@ -160 +319 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
- libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
|
|
+ libredox::call::setrens(0, 0).expect("lived: failed to enter null namespace");
|