diff --git a/local/patches/base/P21-boot-daemon-graceful-panic.patch b/local/patches/base/P21-boot-daemon-graceful-panic.patch new file mode 100644 index 0000000000..ae4da4603d --- /dev/null +++ b/local/patches/base/P21-boot-daemon-graceful-panic.patch @@ -0,0 +1,471 @@ +diff --git a/drivers/graphics/fbbootlogd/src/main.rs b/drivers/graphics/fbbootlogd/src/main.rs +index 3e42d590..1f49a227 100644 +--- a/drivers/graphics/fbbootlogd/src/main.rs ++++ b/drivers/graphics/fbbootlogd/src/main.rs +@@ -27 +27,4 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- let event_queue = EventQueue::new().expect("fbbootlogd: failed to create event queue"); ++ let event_queue = EventQueue::new().unwrap_or_else(|e| { ++ eprintln!("fbbootlogd: failed to create event queue: {}", e); ++ std::process::exit(1); ++ }); +@@ -36 +39,4 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- let socket = Socket::nonblock().expect("fbbootlogd: failed to create fbbootlog scheme"); ++ let socket = Socket::nonblock().unwrap_or_else(|e| { ++ eprintln!("fbbootlogd: failed to create fbbootlog scheme: {}", e); ++ std::process::exit(1); ++ }); +@@ -47 +53,4 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- .expect("fbbootlogd: failed to subscribe to scheme events"); ++ .unwrap_or_else(|e| { ++ eprintln!("fbbootlogd: failed to subscribe to scheme events: {}", e); ++ std::process::exit(1); ++ }); +@@ -55 +64,4 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- .expect("fbbootlogd: failed to subscribe to scheme events"); ++ .unwrap_or_else(|e| { ++ eprintln!("fbbootlogd: failed to subscribe to scheme events: {}", e); ++ std::process::exit(1); ++ }); +@@ -60 +72,4 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- .expect("fbbootlogd: failed to create log fd"); ++ .unwrap_or_else(|e| { ++ eprintln!("fbbootlogd: failed to create log fd: {}", e); ++ std::process::exit(1); ++ }); +@@ -67 +82,4 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- .expect("fbbootlogd: failed to open log/add_sink"); ++ .unwrap_or_else(|e| { ++ eprintln!("fbbootlogd: failed to open log/add_sink: {}", e); ++ std::process::exit(1); ++ }); +@@ -70 +88,4 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- .expect("fbbootlogd: failed to send log fd to log scheme."); ++ .unwrap_or_else(|e| { ++ eprintln!("fbbootlogd: failed to send log fd to log scheme: {}", e); ++ 0 ++ }); +@@ -77,2 +97,0 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- //libredox::call::setrens(0, 0).expect("fbbootlogd: failed to enter null namespace"); +- +@@ -80 +99,2 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- match event.expect("fbbootlogd: failed to get event").user_data { ++ match event { ++ Ok(event) => match event.user_data { +@@ -82,6 +102,7 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- match handler +- .process_requests_nonblocking(&mut scheme) +- .expect("fbbootlogd: failed to process requests") +- { +- ControlFlow::Continue(()) => {} +- ControlFlow::Break(()) => break, ++ match handler.process_requests_nonblocking(&mut scheme) { ++ Ok(ControlFlow::Continue(())) => {} ++ Ok(ControlFlow::Break(())) => break, ++ Err(e) => { ++ eprintln!("fbbootlogd: failed to process requests: {}", e); ++ break; ++ } +@@ -93,7 +114,3 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- match scheme +- .input_handle +- .read_events(&mut events) +- .expect("fbbootlogd: error while reading events") +- { +- ConsumerHandleEvent::Events(&[]) => break, +- ConsumerHandleEvent::Events(events) => { ++ match scheme.input_handle.read_events(&mut events) { ++ Ok(ConsumerHandleEvent::Events(&[]) ) => break, ++ Ok(ConsumerHandleEvent::Events(events)) => { +@@ -104 +121 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- ConsumerHandleEvent::Handoff => { ++ Ok(ConsumerHandleEvent::Handoff) => { +@@ -107,0 +125,3 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { ++ Err(e) => { ++ eprintln!("fbbootlogd: error while reading events: {}", e); ++ break; +@@ -111,0 +132,5 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { ++ }, ++ Err(e) => { ++ eprintln!("fbbootlogd: failed to get event: {}", e); ++ } ++ } +diff --git a/drivers/graphics/fbbootlogd/src/scheme.rs b/drivers/graphics/fbbootlogd/src/scheme.rs +index 08bd1805..67db5c54 100644 +--- a/drivers/graphics/fbbootlogd/src/scheme.rs ++++ b/drivers/graphics/fbbootlogd/src/scheme.rs +@@ -29 +29,4 @@ impl FbbootlogScheme { +- input_handle: ConsumerHandle::bootlog_vt().expect("fbbootlogd: Failed to open vt"), ++ input_handle: ConsumerHandle::bootlog_vt().unwrap_or_else(|e| { ++ eprintln!("fbbootlogd: failed to open vt: {}", e); ++ std::process::exit(1); ++ }), +@@ -151 +154,3 @@ impl FbbootlogScheme { +- map.dirty_fb(total_damage).unwrap(); ++ if let Err(e) = map.dirty_fb(total_damage) { ++ eprintln!("fbbootlogd: failed to sync framebuffer: {}", e); ++ } +@@ -245 +250,3 @@ impl SchemeSync for FbbootlogScheme { +- map.dirty_fb(damage).unwrap(); ++ if let Err(e) = map.dirty_fb(damage) { ++ eprintln!("fbbootlogd: failed to sync framebuffer: {}", e); ++ } +diff --git a/drivers/graphics/fbcond/src/display.rs b/drivers/graphics/fbcond/src/display.rs +index e8543583..9bc6000f 100644 +--- a/drivers/graphics/fbcond/src/display.rs ++++ b/drivers/graphics/fbcond/src/display.rs +@@ -86 +86,3 @@ impl Display { +- map.dirty_fb(damage).unwrap(); ++ if let Err(e) = map.dirty_fb(damage) { ++ log::error!("fbcond: failed to sync framebuffer: {}", e); ++ } +diff --git a/drivers/graphics/fbcond/src/main.rs b/drivers/graphics/fbcond/src/main.rs +index d7d4abb8..45c04449 100644 +--- a/drivers/graphics/fbcond/src/main.rs ++++ b/drivers/graphics/fbcond/src/main.rs +@@ -24 +24,6 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- .map(|arg| arg.parse().expect("invalid vt number")) ++ .map(|arg| { ++ arg.parse().unwrap_or_else(|e| { ++ eprintln!("fbcond: invalid vt number: {}", e); ++ std::process::exit(1); ++ }) ++ }) +@@ -34 +39,4 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- let mut event_queue = EventQueue::new().expect("fbcond: failed to create event queue"); ++ let mut event_queue = EventQueue::new().unwrap_or_else(|e| { ++ eprintln!("fbcond: failed to create event queue: {}", e); ++ std::process::exit(1); ++ }); +@@ -38 +46,4 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- let mut socket = Socket::nonblock().expect("fbcond: failed to create fbcon scheme"); ++ let mut socket = Socket::nonblock().unwrap_or_else(|e| { ++ eprintln!("fbcond: failed to create fbcon scheme: {}", e); ++ std::process::exit(1); ++ }); +@@ -45 +56,4 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- .expect("fbcond: failed to subscribe to scheme events"); ++ .unwrap_or_else(|e| { ++ eprintln!("fbcond: failed to subscribe to scheme events: {}", e); ++ std::process::exit(1); ++ }); +@@ -54,2 +67,0 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- // libredox::call::setrens(0, 0).expect("fbcond: failed to enter null namespace"); +- +@@ -71 +83,7 @@ fn daemon(daemon: daemon::SchemeDaemon) -> ! { +- let event = event.expect("fbcond: failed to read event from event queue"); ++ let event = match event { ++ Ok(event) => event, ++ Err(e) => { ++ log::error!("fbcond: failed to read event from event queue: {}", e); ++ continue; ++ } ++ }; +@@ -102 +120,4 @@ fn handle_event( +- Err(err) => panic!("fbcond: failed to read display scheme: {err}"), ++ Err(err) => { ++ log::error!("fbcond: failed to read display scheme: {err}"); ++ break; ++ } +@@ -116 +137,4 @@ fn handle_event( +- .expect("fbcond: failed to write responses to fbcon scheme"); ++ .unwrap_or_else(|e| { ++ log::error!("fbcond: failed to write responses to fbcon scheme: {}", e); ++ false ++ }); +@@ -130 +154,4 @@ fn handle_event( +- .expect("fbcond: failed to write responses to fbcon scheme"); ++ .unwrap_or_else(|e| { ++ log::error!("fbcond: failed to write responses to fbcon scheme: {}", e); ++ false ++ }); +@@ -138 +165,4 @@ fn handle_event( +- .expect("fbcond: failed to write responses to fbcon scheme"); ++ .unwrap_or_else(|e| { ++ log::error!("fbcond: failed to write responses to fbcon scheme: {}", e); ++ false ++ }); +@@ -146 +176,4 @@ fn handle_event( +- .expect("fbcond: failed to write scheme"); ++ .unwrap_or_else(|e| { ++ log::error!("fbcond: failed to write scheme: {}", e); ++ false ++ }); +@@ -162 +195,4 @@ fn handle_event( +- .expect("vesad: failed to write display scheme"); ++ .unwrap_or_else(|e| { ++ log::error!("vesad: failed to write display scheme: {}", e); ++ false ++ }); +@@ -169 +205,4 @@ fn handle_event( +- let vt = scheme.vts.get_mut(&vt_i).unwrap(); ++ let Some(vt) = scheme.vts.get_mut(&vt_i) else { ++ log::error!("fbcond: missing vt {:?}", vt_i); ++ return; ++ }; +@@ -173,6 +212,9 @@ fn handle_event( +- match vt +- .display +- .input_handle +- .read_events(&mut events) +- .expect("fbcond: Error while reading events") +- { ++ let read_events = match vt.display.input_handle.read_events(&mut events) { ++ Ok(events) => events, ++ Err(e) => { ++ log::error!("fbcond: Error while reading events: {}", e); ++ break; ++ } ++ }; ++ ++ match read_events { +@@ -196,3 +238,4 @@ fn handle_event( +- let (op, caller) = blocked +- .get_mut(i) +- .expect("vesad: Failed to get blocked request"); ++ let Some((op, caller)) = blocked.get_mut(i) else { ++ log::error!("vesad: Failed to get blocked request"); ++ return; ++ }; +@@ -222 +265,4 @@ fn handle_event( +- .expect("vesad: failed to write display scheme"); ++ .unwrap_or_else(|e| { ++ log::error!("vesad: failed to write display scheme: {}", e); ++ false ++ }); +@@ -247 +293,4 @@ fn handle_event( +- .expect("fbcond: failed to write display event"); ++ .unwrap_or_else(|e| { ++ log::error!("fbcond: failed to write display event: {}", e); ++ false ++ }); +diff --git a/drivers/graphics/fbcond/src/scheme.rs b/drivers/graphics/fbcond/src/scheme.rs +index b31ee2e3..2cd98086 100644 +--- a/drivers/graphics/fbcond/src/scheme.rs ++++ b/drivers/graphics/fbcond/src/scheme.rs +@@ -54 +54,4 @@ impl FbconScheme { +- let display = Display::open_new_vt().expect("Failed to open display for vt"); ++ let display = Display::open_new_vt().unwrap_or_else(|e| { ++ log::error!("fbcond: failed to open display for vt: {}", e); ++ std::process::exit(1); ++ }); +@@ -61 +64,4 @@ impl FbconScheme { +- .expect("Failed to subscribe to input events for vt"); ++ .unwrap_or_else(|e| { ++ log::error!("fbcond: failed to subscribe to input events for vt: {}", e); ++ std::process::exit(1); ++ }); +@@ -140 +146 @@ impl SchemeSync for FbconScheme { +- write!(w, "{}", handle.vt_i.0).unwrap(); ++ write!(w, "{}", handle.vt_i.0).map_err(|_| Error::new(ENOENT))?; +diff --git a/drivers/graphics/fbcond/src/text.rs b/drivers/graphics/fbcond/src/text.rs +index 8c85bf77..f9992d2b 100644 +--- a/drivers/graphics/fbcond/src/text.rs ++++ b/drivers/graphics/fbcond/src/text.rs +@@ -122 +122,4 @@ impl TextScreen { +- buf[i] = self.input.pop_front().unwrap(); ++ buf[i] = match self.input.pop_front() { ++ Some(v) => v, ++ None => break, ++ }; +diff --git a/drivers/input/ps2d/src/main.rs b/drivers/input/ps2d/src/main.rs +index 86f903bf..569c73b9 100644 +--- a/drivers/input/ps2d/src/main.rs ++++ b/drivers/input/ps2d/src/main.rs +@@ -32 +32,4 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- acquire_port_io_rights().expect("ps2d: failed to get I/O permission"); ++ if let Err(e) = acquire_port_io_rights() { ++ eprintln!("ps2d: failed to get I/O permission: {}", e); ++ process::exit(1); ++ } +@@ -34,2 +37,8 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- let keyboard_input = InputProducer::new_named_or_fallback("ps2-keyboard").expect("ps2d: failed to open keyboard input"); +- let mouse_input = InputProducer::new_named_or_fallback("ps2-mouse").expect("ps2d: failed to open mouse input"); ++ let keyboard_input = InputProducer::new_named_or_fallback("ps2-keyboard").unwrap_or_else(|e| { ++ eprintln!("ps2d: failed to open keyboard input: {}", e); ++ process::exit(1); ++ }); ++ let mouse_input = InputProducer::new_named_or_fallback("ps2-mouse").unwrap_or_else(|e| { ++ eprintln!("ps2d: failed to open mouse input: {}", e); ++ process::exit(1); ++ }); +@@ -46 +55,4 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- EventQueue::new().expect("ps2d: failed to create event queue"); ++ EventQueue::new().unwrap_or_else(|e| { ++ eprintln!("ps2d: failed to create event queue: {}", e); ++ process::exit(1); ++ }); +@@ -53 +65,4 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- .expect("ps2d: failed to open /scheme/serio/0"); ++ .unwrap_or_else(|e| { ++ eprintln!("ps2d: failed to open /scheme/serio/0: {}", e); ++ process::exit(1); ++ }); +@@ -61 +76,4 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- .unwrap(); ++ .unwrap_or_else(|e| { ++ eprintln!("ps2d: failed to subscribe to keyboard events: {}", e); ++ process::exit(1); ++ }); +@@ -68 +86,4 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- .expect("ps2d: failed to open /scheme/serio/1"); ++ .unwrap_or_else(|e| { ++ eprintln!("ps2d: failed to open /scheme/serio/1: {}", e); ++ process::exit(1); ++ }); +@@ -76 +97,4 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- .unwrap(); ++ .unwrap_or_else(|e| { ++ eprintln!("ps2d: failed to subscribe to mouse events: {}", e); ++ process::exit(1); ++ }); +@@ -83 +107,4 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- .expect("ps2d: failed to open /scheme/time"); ++ .unwrap_or_else(|e| { ++ eprintln!("ps2d: failed to open /scheme/time: {}", e); ++ process::exit(1); ++ }); +@@ -91 +118,4 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- .unwrap(); ++ .unwrap_or_else(|e| { ++ eprintln!("ps2d: failed to subscribe to time events: {}", e); ++ process::exit(1); ++ }); +@@ -93 +123,3 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- libredox::call::setrens(0, 0).expect("ps2d: failed to enter null namespace"); ++ if let Err(e) = libredox::call::setrens(0, 0) { ++ eprintln!("ps2d: failed to enter null namespace: {}", e); ++ } +@@ -100 +132,7 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- for event in event_queue.map(|e| e.expect("ps2d: failed to get next event").user_data) { ++ for event in event_queue.map(|e| { ++ e.unwrap_or_else(|e2| { ++ eprintln!("ps2d: event read error: {}", e2); ++ process::exit(1); ++ }) ++ .user_data ++ }) { +diff --git a/drivers/input/ps2d/src/state.rs b/drivers/input/ps2d/src/state.rs +index 8f5832f6..3e37f344 100644 +--- a/drivers/input/ps2d/src/state.rs ++++ b/drivers/input/ps2d/src/state.rs +@@ -31,2 +31,2 @@ fn timespec_from_duration(duration: Duration) -> TimeSpec { +- tv_sec: duration.as_secs().try_into().unwrap(), +- tv_nsec: duration.subsec_nanos().try_into().unwrap(), ++ tv_sec: duration.as_secs().try_into().unwrap_or(i64::MAX), ++ tv_nsec: duration.subsec_nanos().try_into().unwrap_or(i32::MAX), +@@ -38,2 +38,2 @@ fn duration_from_timespec(timespec: TimeSpec) -> Duration { +- timespec.tv_sec.try_into().unwrap(), +- timespec.tv_nsec.try_into().unwrap(), ++ timespec.tv_sec.try_into().unwrap_or(u64::MAX), ++ timespec.tv_nsec.try_into().unwrap_or(u32::MAX), +@@ -320 +320,3 @@ impl Ps2d { +- .expect("failed to write key event"); ++ .unwrap_or_else(|e| { ++ log::error!("ps2d: failed to write key event: {}", e); ++ }); +@@ -350 +352,3 @@ impl Ps2d { +- .expect("ps2d: failed to write mouse event"); ++ .unwrap_or_else(|e| { ++ log::error!("ps2d: failed to write mouse event: {}", e); ++ }); +@@ -360 +364,3 @@ impl Ps2d { +- .expect("ps2d: failed to write mouse event"); ++ .unwrap_or_else(|e| { ++ log::error!("ps2d: failed to write mouse event: {}", e); ++ }); +@@ -373 +379,3 @@ impl Ps2d { +- .expect("ps2d: failed to write scroll event"); ++ .unwrap_or_else(|e| { ++ log::error!("ps2d: failed to write scroll event: {}", e); ++ }); +@@ -395 +403,3 @@ impl Ps2d { +- .expect("ps2d: failed to write button event"); ++ .unwrap_or_else(|e| { ++ log::error!("ps2d: failed to write button event: {}", e); ++ }); +@@ -494 +504,3 @@ impl Ps2d { +- .expect("ps2d: failed to write mouse event"); ++ .unwrap_or_else(|e| { ++ log::error!("ps2d: failed to write mouse event: {}", e); ++ }); +@@ -500 +512,3 @@ impl Ps2d { +- .expect("ps2d: failed to write scroll event"); ++ .unwrap_or_else(|e| { ++ log::error!("ps2d: failed to write scroll event: {}", e); ++ }); +@@ -526 +540,3 @@ impl Ps2d { +- .expect("ps2d: failed to write button event"); ++ .unwrap_or_else(|e| { ++ log::error!("ps2d: failed to write button event: {}", e); ++ }); +diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs +index 07aa943e..5b888a7c 100644 +--- a/drivers/inputd/src/main.rs ++++ b/drivers/inputd/src/main.rs +@@ -277 +277 @@ impl SchemeSync for InputScheme { +- write!(w, "{vt}").unwrap(); ++ write!(w, "{vt}").map_err(|_| SysError::new(EINVAL))?; +@@ -441 +441,4 @@ impl SchemeSync for InputScheme { +- assert!(matches!(handle, Handle::Producer)); ++ if !matches!(handle, Handle::Producer) { ++ log::error!("inputd: unexpected non-producer handle in write"); ++ return Err(SysError::new(EINVAL)); ++ } +@@ -508,2 +511,2 @@ impl SchemeSync for InputScheme { +- match self.handles.remove(id).unwrap() { +- Handle::Consumer { vt, .. } => { ++ match self.handles.remove(id) { ++ Some(Handle::Consumer { vt, .. }) => { +@@ -519 +522,4 @@ impl SchemeSync for InputScheme { +- _ => {} ++ Some(_) => {} ++ None => { ++ log::error!("inputd: missing handle on close"); ++ } +@@ -592 +598,4 @@ fn daemon_runner(daemon: daemon::SchemeDaemon) -> ! { +- deamon(daemon).unwrap(); ++ if let Err(e) = deamon(daemon) { ++ log::error!("inputd: daemon failed: {:?}", e); ++ std::process::exit(1); ++ } +@@ -611 +620,7 @@ fn main() { +- let vt = args.next().unwrap().parse::().unwrap(); ++ let vt = args ++ .next() ++ .and_then(|a| a.parse::().ok()) ++ .unwrap_or_else(|| { ++ eprintln!("inputd: -A requires a VT number"); ++ std::process::exit(1); ++ }); +@@ -614,4 +629,8 @@ fn main() { +- inputd::ControlHandle::new().expect("inputd: failed to open control handle"); +- handle +- .activate_vt(vt) +- .expect("inputd: failed to activate VT"); ++ inputd::ControlHandle::new().unwrap_or_else(|e| { ++ eprintln!("inputd: failed to open control handle: {}", e); ++ std::process::exit(1); ++ }); ++ handle.activate_vt(vt).unwrap_or_else(|e| { ++ eprintln!("inputd: failed to activate VT: {}", e); ++ std::process::exit(1); ++ }); +@@ -634,4 +653,8 @@ fn main() { +- inputd::ControlHandle::new().expect("inputd: failed to open control handle"); +- handle +- .activate_keymap(vt as usize) +- .expect("inputd: failed to activate keymap"); ++ inputd::ControlHandle::new().unwrap_or_else(|e| { ++ eprintln!("inputd: failed to open control handle: {}", e); ++ std::process::exit(1); ++ }); ++ handle.activate_keymap(vt as usize).unwrap_or_else(|e| { ++ eprintln!("inputd: failed to activate keymap: {}", e); ++ std::process::exit(1); ++ }); +@@ -650 +673,4 @@ fn main() { +- _ => panic!("inputd: invalid argument: {}", val), ++ _ => { ++ eprintln!("inputd: invalid argument: {}", val); ++ std::process::exit(1); ++ } diff --git a/local/patches/base/P23-rootfs-hard-dep-on-drivers.patch b/local/patches/base/P23-rootfs-hard-dep-on-drivers.patch new file mode 100644 index 0000000000..01b3353e5d --- /dev/null +++ b/local/patches/base/P23-rootfs-hard-dep-on-drivers.patch @@ -0,0 +1,7 @@ +diff --git a/init.initfs.d/50_rootfs.service b/init.initfs.d/50_rootfs.service +index c2d8e477..db7ba429 100644 +--- a/init.initfs.d/50_rootfs.service ++++ b/init.initfs.d/50_rootfs.service +@@ -3 +3 @@ description = "Rootfs" +-requires_weak = ["40_drivers.target"] ++requires = ["40_drivers.target"] diff --git a/local/patches/kernel/P22-x2apic-madt-fallback.patch b/local/patches/kernel/P22-x2apic-madt-fallback.patch new file mode 100644 index 0000000000..116c609a05 --- /dev/null +++ b/local/patches/kernel/P22-x2apic-madt-fallback.patch @@ -0,0 +1,165 @@ +diff --git a/src/acpi/madt/arch/x86.rs b/src/acpi/madt/arch/x86.rs +index 306ec154..983fc409 100644 +--- a/src/acpi/madt/arch/x86.rs ++++ b/src/acpi/madt/arch/x86.rs +@@ -187,0 +188,10 @@ pub(super) fn init(madt: Madt) { ++ // Detect whether MADT contains any LocalX2Apic entries. ++ // Some firmware (notably QEMU and some older BIOS) provides only 8-bit ++ // LocalApic entries even when the CPU supports x2APIC. In that case we must ++ // fall back to processing LocalApic entries with zero-extended IDs. ++ let has_x2apic_entries = madt.iter().any(|e| matches!(e, MadtEntry::LocalX2Apic(_))); ++ let x2apic_fallback = local_apic.x2 && !has_x2apic_entries; ++ if x2apic_fallback { ++ warn!("MADT: x2APIC mode active but no LocalX2Apic entries found; falling back to LocalApic entries with zero-extended IDs"); ++ } ++ +@@ -196,0 +207,3 @@ pub(super) fn init(madt: Madt) { ++ MadtEntry::LocalApic(local) if local_apic.x2 && x2apic_fallback => { ++ u32::from(local.id) == me.get() || local.flags & 1 == 1 ++ } +@@ -225,2 +238,6 @@ pub(super) fn init(madt: Madt) { +- // x2APIC mode: skip 8-bit LocalApic IDs; they conflict with +- // 32-bit x2APIC IDs. Dedup only among LocalX2Apic entries. ++ if x2apic_fallback { ++ let id = u32::from(local.id); ++ if !seen_apic_ids.insert(id) { ++ warn!("MADT: duplicate APIC ID {} in LocalApic entry (x2APIC fallback), firmware bug", id); ++ } ++ } else { +@@ -228,0 +246 @@ pub(super) fn init(madt: Madt) { ++ } +@@ -252 +270 @@ pub(super) fn init(madt: Madt) { +- if local_apic.x2 { ++ if local_apic.x2 && !x2apic_fallback { +@@ -256,0 +275,131 @@ pub(super) fn init(madt: Madt) { ++ } else if local_apic.x2 && x2apic_fallback { ++ let apic_id = u32::from(ap_local_apic.id); ++ if apic_id == me.get() { ++ debug!(" This is my local APIC (x2APIC fallback, id={})", apic_id); ++ } else if ap_local_apic.flags & 1 == 1 { ++ let alloc = match allocate_p2frame(4) { ++ Some(frame) => frame, ++ None => { ++ println!("KERNEL AP: CPU {} no memory for stack, skipping", apic_id); ++ continue; ++ } ++ }; ++ let stack_start = RmmA::phys_to_virt(alloc.base()).data(); ++ let stack_end = stack_start + (PAGE_SIZE << 4); ++ ++ let cpu_id = LogicalCpuId::new(crate::CPU_COUNT.fetch_add(1, Ordering::SeqCst)); ++ if cpu_id.get() >= crate::cpu_set::MAX_CPU_COUNT { ++ println!( ++ "KERNEL AP: CPU {} exceeds logical CPU limit, skipping", ++ apic_id ++ ); ++ continue; ++ } ++ ++ let pcr_ptr = crate::arch::gdt::allocate_and_init_pcr(cpu_id, stack_end); ++ let idt_ptr = crate::arch::idt::allocate_and_init_idt(cpu_id); ++ ++ let args = KernelArgsAp { ++ stack_end: stack_end as *mut u8, ++ cpu_id, ++ pcr_ptr, ++ idt_ptr, ++ }; ++ ++ let ap_ready = (TRAMPOLINE + 8) as *mut u64; ++ let ap_args_ptr = unsafe { ap_ready.add(1) }; ++ let ap_page_table = unsafe { ap_ready.add(2) }; ++ let ap_code = unsafe { ap_ready.add(3) }; ++ ++ unsafe { ++ ap_ready.write(0); ++ ap_args_ptr.write(&args as *const _ as u64); ++ ap_page_table.write(page_table_physaddr as u64); ++ #[expect(clippy::fn_to_numeric_cast)] ++ ap_code.write(kstart_ap as u64); ++ ++ core::sync::atomic::fence(Ordering::SeqCst); ++ }; ++ AP_READY.store(false, Ordering::SeqCst); ++ ++ // Clear APIC Error Status Register before starting AP. ++ unsafe { local_apic.esr(); } ++ ++ // Send INIT IPI (Assert) — x2APIC uses 64-bit ICR format. ++ { ++ let mut icr = 0x4500u64; ++ icr |= u64::from(apic_id) << 32; ++ local_apic.set_icr(icr); ++ } ++ ++ // Intel SDM Vol 3A §8.4.4: wait 10ms after INIT deassert ++ early_udelay(10_000); ++ ++ // Send START IPI #1 ++ { ++ let ap_segment = (TRAMPOLINE >> 12) & 0xFF; ++ let mut icr = 0x0600 | ap_segment as u64; ++ icr |= u64::from(apic_id) << 32; ++ local_apic.set_icr(icr); ++ } ++ ++ early_udelay(200); ++ ++ // Send START IPI #2 (recommended for compatibility) ++ { ++ let ap_segment = (TRAMPOLINE >> 12) & 0xFF; ++ let mut icr = 0x0600 | ap_segment as u64; ++ icr |= u64::from(apic_id) << 32; ++ local_apic.set_icr(icr); ++ } ++ ++ early_udelay(200); ++ ++ // Check ESR for delivery errors after SIPI sequence. ++ let esr_val = unsafe { local_apic.esr() }; ++ if esr_val != 0 { ++ println!( ++ "KERNEL AP: CPU {} SIPI delivery error (ESR={:#x}), continuing", ++ apic_id, esr_val ++ ); ++ } ++ ++ let mut trampoline_ready = false; ++ for _ in 0..AP_SPIN_LIMIT { ++ if unsafe { (*ap_ready.cast::()).load(Ordering::SeqCst) } != 0 { ++ trampoline_ready = true; ++ break; ++ } ++ hint::spin_loop(); ++ } ++ if !trampoline_ready { ++ println!("KERNEL AP: CPU {} trampoline timeout, skipping", apic_id); ++ continue; ++ } ++ ++ let mut kernel_ready = false; ++ for _ in 0..AP_SPIN_LIMIT { ++ if AP_READY.load(Ordering::SeqCst) { ++ kernel_ready = true; ++ break; ++ } ++ hint::spin_loop(); ++ } ++ if !kernel_ready { ++ println!("KERNEL AP: CPU {} AP_READY timeout, skipping", apic_id); ++ continue; ++ } ++ ++ // Record APIC→CPU mapping for NUMA topology. ++ unsafe { ++ record_apic_mapping(apic_id, cpu_id); ++ } ++ // Set NUMA node from SRAT data. ++ if let Some(percpu) = crate::percpu::get_for_cpu(cpu_id) { ++ if let Some(node) = crate::acpi::srat::numa_node_for_apic(apic_id) { ++ percpu.numa_node.set(node); ++ } ++ } ++ ++ RmmA::invalidate_all(); ++ } diff --git a/recipes/core/base/P23-rootfs-hard-dep-on-drivers.patch b/recipes/core/base/P23-rootfs-hard-dep-on-drivers.patch new file mode 120000 index 0000000000..77c0595029 --- /dev/null +++ b/recipes/core/base/P23-rootfs-hard-dep-on-drivers.patch @@ -0,0 +1 @@ +../../../local/patches/base/P23-rootfs-hard-dep-on-drivers.patch \ No newline at end of file diff --git a/recipes/core/base/recipe.toml b/recipes/core/base/recipe.toml index accd6a0813..2f2c4c2237 100644 --- a/recipes/core/base/recipe.toml +++ b/recipes/core/base/recipe.toml @@ -67,6 +67,8 @@ patches = [ "P19-init-startup-hardening.patch", "P19-acpid-startup-hardening.patch", "P20-ramfs-requires-randd.patch", + "P21-boot-daemon-graceful-panic.patch", + "P23-rootfs-hard-dep-on-drivers.patch", ] [package] @@ -244,4 +246,4 @@ done mkdir -pv "${COOKBOOK_STAGE}/usr/lib/init.d" cp -v "${COOKBOOK_SOURCE}/init.d"/* "${COOKBOOK_STAGE}/usr/lib/init.d/" -""" \ No newline at end of file +""" diff --git a/recipes/core/kernel/recipe.toml b/recipes/core/kernel/recipe.toml index 3768f3e736..a5a1d976e5 100644 --- a/recipes/core/kernel/recipe.toml +++ b/recipes/core/kernel/recipe.toml @@ -39,6 +39,10 @@ patches = [ # P21: x2APIC SMP bring-up fix — skip 8-bit LocalApic entries when x2APIC # is active (BSP ID mismatch causes all APs to be skipped on bare metal Intel) "../../../local/patches/kernel/P21-x2apic-smp-fix.patch", + # P22: x2APIC MADT fallback — when x2APIC is active but MADT has no + # LocalX2Apic entries (QEMU, some BIOS), fall back to processing LocalApic + # entries with zero-extended IDs using x2APIC 64-bit ICR format + "../../../local/patches/kernel/P22-x2apic-madt-fallback.patch", ] [build]