Files
RedBear-OS/local/patches/base/P59-lived-disk-fallback.patch
T

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");