base: P25-P32 patch chain — fbcond VESA fallback, driver-manager initfs, init conditions, acpid graceful boot, xhcid interrupts

This commit is contained in:
2026-05-19 23:50:01 +03:00
parent 2a5eff93ec
commit a9051ebb91
8 changed files with 924 additions and 0 deletions
@@ -0,0 +1,519 @@
diff --git a/drivers/graphics/fbcond/src/display.rs b/drivers/graphics/fbcond/src/display.rs
index 9bc6000f..c315bbe9 100644
--- a/drivers/graphics/fbcond/src/display.rs
+++ b/drivers/graphics/fbcond/src/display.rs
@@ -5,9 +5,94 @@ use graphics_ipc::V2GraphicsHandle;
use inputd::ConsumerHandle;
use std::io;
+use common::{MemoryType, PhysBorrowed, Prot};
+
+pub struct DirectFbMap {
+ fb_mem: PhysBorrowed,
+ width: usize,
+ height: usize,
+ stride: usize,
+}
+
+impl DirectFbMap {
+ pub fn try_new() -> Option<Self> {
+ let addr = std::env::var("FRAMEBUFFER_ADDR").ok()?;
+ let width_str = std::env::var("FRAMEBUFFER_WIDTH").ok()?;
+ let height_str = std::env::var("FRAMEBUFFER_HEIGHT").ok()?;
+ let stride_str = std::env::var("FRAMEBUFFER_STRIDE").ok()?;
+
+ let phys = usize::from_str_radix(&addr, 16).ok()?;
+ let width = usize::from_str_radix(&width_str, 16).ok()?;
+ let height = usize::from_str_radix(&height_str, 16).ok()?;
+ let stride = usize::from_str_radix(&stride_str, 16).ok()?;
+
+ if phys == 0 || width == 0 || height == 0 || stride == 0 {
+ log::warn!("fbcond: FRAMEBUFFER_* env vars present but contain zero values");
+ return None;
+ }
+
+ let size = stride * height * 4;
+
+ let fb_mem = match PhysBorrowed::map(
+ phys,
+ size,
+ Prot {
+ read: true,
+ write: true,
+ },
+ MemoryType::WriteCombining,
+ ) {
+ Ok(m) => m,
+ Err(e) => {
+ log::warn!("fbcond: failed to map physical framebuffer at 0x{phys:X}: {e}");
+ return None;
+ }
+ };
+
+ log::info!(
+ "fbcond: Direct framebuffer mapped: {}x{} stride {} at 0x{phys:X}",
+ width,
+ height,
+ stride
+ );
+
+ Some(DirectFbMap {
+ fb_mem,
+ width,
+ height,
+ stride,
+ })
+ }
+
+ pub fn width(&self) -> usize {
+ self.width
+ }
+
+ pub fn height(&self) -> usize {
+ self.height
+ }
+
+ pub(crate) fn pixel_slice(&mut self) -> &mut [u32] {
+ unsafe {
+ let ptr = self.fb_mem.as_ptr() as *mut u32;
+ let len = self.stride * self.height;
+ std::slice::from_raw_parts_mut(ptr, len)
+ }
+ }
+
+ fn sync_rect(&mut self, _damage: Damage) {
+ // Direct framebuffer writes are immediately visible
+ }
+}
+
+pub enum DisplayMap {
+ Drm(V2DisplayMap),
+ Direct(DirectFbMap),
+}
+
pub struct Display {
pub input_handle: ConsumerHandle,
- pub map: Option<V2DisplayMap>,
+ pub map: Option<DisplayMap>,
}
impl Display {
@@ -22,19 +107,29 @@ impl Display {
Ok(display)
}
- /// Re-open the display after a handoff.
+ /// Re-open the display after a handoff. Tries DRM first, then falls back
+ /// to direct physical framebuffer via FRAMEBUFFER_* env vars.
pub fn reopen_for_handoff(&mut self) {
let display_file = match self.input_handle.open_display_v2() {
Ok(display_file) => display_file,
Err(err) => {
log::error!("fbcond: No display present yet: {err}");
+ if let Some(direct) = DirectFbMap::try_new() {
+ log::info!("fbcond: Falling back to direct framebuffer (no display handle)");
+ self.map = Some(DisplayMap::Direct(direct));
+ }
return;
}
};
+
let new_display_handle = match V2GraphicsHandle::from_file(display_file) {
Ok(handle) => handle,
Err(err) => {
- log::error!("fbcond: failed to create graphics handle (DRM ioctl unsupported): {err}");
+ log::warn!("fbcond: DRM ioctl unsupported, trying direct framebuffer: {err}");
+ if let Some(direct) = DirectFbMap::try_new() {
+ log::info!("fbcond: Using direct framebuffer fallback");
+ self.map = Some(DisplayMap::Direct(direct));
+ }
return;
}
};
@@ -48,43 +143,64 @@ impl Display {
map.buffer.buffer().size().0,
map.buffer.buffer().size().1,
);
- self.map = Some(map)
+ self.map = Some(DisplayMap::Drm(map))
}
Err(err) => {
- log::error!("fbcond: failed to map new display: {err}");
- return;
+ log::warn!("fbcond: failed to map DRM display, trying direct framebuffer: {err}");
+ if let Some(direct) = DirectFbMap::try_new() {
+ log::info!("fbcond: Using direct framebuffer fallback");
+ self.map = Some(DisplayMap::Direct(direct));
+ }
}
}
}
- pub fn handle_resize(map: &mut V2DisplayMap, text_screen: &mut TextScreen) {
- let mode = match map
- .display_handle
- .first_display()
- .and_then(|handle| Ok(map.display_handle.get_connector(handle, true)?.modes()[0]))
- {
- Ok(mode) => mode,
- Err(err) => {
- eprintln!("fbcond: failed to get display size: {}", err);
- return;
- }
- };
+ pub fn handle_resize(map: &mut DisplayMap, text_screen: &mut TextScreen) {
+ match map {
+ DisplayMap::Drm(drm_map) => {
+ let mode = match drm_map
+ .display_handle
+ .first_display()
+ .and_then(|handle| {
+ Ok(drm_map.display_handle.get_connector(handle, true)?.modes()[0])
+ }) {
+ Ok(mode) => mode,
+ Err(err) => {
+ eprintln!("fbcond: failed to get display size: {}", err);
+ return;
+ }
+ };
- if (u32::from(mode.size().0), u32::from(mode.size().1)) != map.buffer.buffer().size() {
- match text_screen.resize(map, mode) {
- Ok(()) => eprintln!("fbcond: mapped display"),
- Err(err) => {
- eprintln!("fbcond: failed to create or map framebuffer: {}", err);
- return;
+ if (u32::from(mode.size().0), u32::from(mode.size().1))
+ != drm_map.buffer.buffer().size()
+ {
+ match text_screen.resize(drm_map, mode) {
+ Ok(()) => eprintln!("fbcond: mapped display"),
+ Err(err) => {
+ eprintln!(
+ "fbcond: failed to create or map framebuffer: {}",
+ err
+ );
+ return;
+ }
+ }
}
}
+ DisplayMap::Direct(_) => {}
}
}
pub fn sync_rect(&mut self, damage: Damage) {
if let Some(map) = &mut self.map {
- if let Err(e) = map.dirty_fb(damage) {
- log::error!("fbcond: failed to sync framebuffer: {}", e);
+ match map {
+ DisplayMap::Drm(drm_map) => {
+ if let Err(e) = drm_map.dirty_fb(damage) {
+ log::error!("fbcond: failed to sync framebuffer: {}", e);
+ }
+ }
+ DisplayMap::Direct(direct_map) => {
+ direct_map.sync_rect(damage);
+ }
}
}
}
diff --git a/drivers/graphics/fbcond/src/text.rs b/drivers/graphics/fbcond/src/text.rs
index f9992d2b..4130b05f 100644
--- a/drivers/graphics/fbcond/src/text.rs
+++ b/drivers/graphics/fbcond/src/text.rs
@@ -3,9 +3,11 @@ use std::collections::VecDeque;
use orbclient::{Event, EventOption};
use syscall::error::*;
-use crate::display::Display;
+use crate::display::{DirectFbMap, Display, DisplayMap};
const SCROLLBACK_LINES: usize = 1000;
+const CHAR_WIDTH: usize = 8;
+const CHAR_HEIGHT: usize = 16;
pub struct TextScreen {
pub display: Display,
@@ -14,6 +16,8 @@ pub struct TextScreen {
input: VecDeque<u8>,
scrollback: VecDeque<Vec<u8>>,
scroll_pos: usize,
+ direct_console: ransid::Console,
+ direct_initialized: bool,
}
impl TextScreen {
@@ -22,9 +26,11 @@ impl TextScreen {
display,
inner: console_draw::TextScreen::new(),
ctrl: false,
- input: VecDeque::new(),
+ input: VecDeque::with_capacity(SCROLLBACK_LINES),
scrollback: VecDeque::with_capacity(SCROLLBACK_LINES),
scroll_pos: 0,
+ direct_console: ransid::Console::new(0, 0),
+ direct_initialized: false,
}
}
@@ -43,47 +49,36 @@ impl TextScreen {
} else if key_event.pressed {
match key_event.scancode {
0x0E => {
- // Backspace
buf.extend_from_slice(b"\x7F");
}
0x47 => {
- // Home
buf.extend_from_slice(b"\x1B[H");
}
0x48 => {
- // Up
buf.extend_from_slice(b"\x1B[A");
}
0x49 => {
- // Page up
buf.extend_from_slice(b"\x1B[5~");
}
0x4B => {
- // Left
buf.extend_from_slice(b"\x1B[D");
}
0x4D => {
- // Right
buf.extend_from_slice(b"\x1B[C");
}
0x4F => {
- // End
buf.extend_from_slice(b"\x1B[F");
}
0x50 => {
- // Down
buf.extend_from_slice(b"\x1B[B");
}
0x51 => {
- // Page down
buf.extend_from_slice(b"\x1B[6~");
}
0x52 => {
- // Insert
buf.extend_from_slice(b"\x1B[2~");
}
0x53 => {
- // Delete
buf.extend_from_slice(b"\x1B[3~");
}
_ => {
@@ -101,7 +96,7 @@ impl TextScreen {
}
}
}
- _ => (), //TODO: Mouse in terminal
+ _ => (),
}
for &b in buf.iter() {
@@ -130,21 +125,27 @@ impl TextScreen {
}
pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
- if let Some(map) = &mut self.display.map {
- Display::handle_resize(map, &mut self.inner);
+ match &mut self.display.map {
+ Some(DisplayMap::Drm(map)) => {
+ Display::handle_resize(map, &mut self.inner);
- let damage = self.inner.write(map, buf, &mut self.input);
+ let damage = self.inner.write(map, buf, &mut self.input);
- for line in buf.split(|&b| b == b'\n') {
- let mut v = line.to_vec();
- v.push(b'\n');
- self.scrollback.push_back(v);
+ for line in buf.split(|&b| b == b'\n') {
+ let mut v = line.to_vec();
+ v.push(b'\n');
+ self.scrollback.push_back(v);
+ }
+ while self.scrollback.len() > SCROLLBACK_LINES {
+ self.scrollback.pop_front();
+ }
+
+ self.display.sync_rect(damage);
}
- while self.scrollback.len() > SCROLLBACK_LINES {
- self.scrollback.pop_front();
+ Some(DisplayMap::Direct(direct_map)) => {
+ self.write_direct(direct_map, buf);
}
-
- self.display.sync_rect(damage);
+ None => {}
}
Ok(buf.len())
@@ -158,3 +159,163 @@ impl TextScreen {
result
}
}
+
+impl TextScreen {
+ fn write_direct(&mut self, direct_map: &mut DirectFbMap, buf: &[u8]) {
+ let width = direct_map.width();
+ let height = direct_map.height();
+
+ if width < CHAR_WIDTH || height < CHAR_HEIGHT {
+ return;
+ }
+
+ if !self.direct_initialized {
+ self.direct_console.resize(width / CHAR_WIDTH, height / CHAR_HEIGHT);
+ self.direct_initialized = true;
+ }
+
+ let console = &mut self.direct_console;
+
+ if console.state.cursor
+ && console.state.x < console.state.w
+ && console.state.y < console.state.h
+ {
+ let x = console.state.x;
+ let y = console.state.y;
+ Self::invert_direct(direct_map, x * CHAR_WIDTH, y * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT);
+ }
+
+ let pixels = direct_map.pixel_slice();
+ let stride = direct_map.width();
+
+ console.write(buf, |event| match event {
+ ransid::Event::Char {
+ x,
+ y,
+ c,
+ color,
+ bold,
+ ..
+ } => {
+ Self::char_direct(direct_map, x * CHAR_WIDTH, y * CHAR_HEIGHT, c, color.as_rgb(), bold);
+ }
+ ransid::Event::Input { data } => self.input.extend(data),
+ ransid::Event::Rect { x, y, w, h, color } => {
+ Self::rect_direct(direct_map, x * CHAR_WIDTH, y * CHAR_HEIGHT, w * CHAR_WIDTH, h * CHAR_HEIGHT, color.as_rgb());
+ }
+ ransid::Event::ScreenBuffer { .. } => (),
+ ransid::Event::Move {
+ from_x,
+ from_y,
+ to_x,
+ to_y,
+ w,
+ h,
+ } => {
+ for row in 0..h {
+ let src_y = if from_y > to_y { row } else { h - row - 1 };
+ for pixel_y in 0..CHAR_HEIGHT {
+ let off_from = ((from_y + src_y) * CHAR_HEIGHT + pixel_y) * stride + from_x * CHAR_WIDTH;
+ let off_to = ((to_y + src_y) * CHAR_HEIGHT + pixel_y) * stride + to_x * CHAR_WIDTH;
+ let len = w * CHAR_WIDTH;
+
+ if off_from + len <= pixels.len() && off_to + len <= pixels.len() {
+ unsafe {
+ let data_ptr = pixels.as_mut_ptr();
+ std::ptr::copy(
+ data_ptr.add(off_from),
+ data_ptr.add(off_to),
+ len,
+ );
+ }
+ }
+ }
+ }
+ }
+ ransid::Event::Resize { .. } => (),
+ ransid::Event::Title { .. } => (),
+ });
+
+ if console.state.cursor
+ && console.state.x < console.state.w
+ && console.state.y < console.state.h
+ {
+ let x = console.state.x;
+ let y = console.state.y;
+ Self::invert_direct(direct_map, x * CHAR_WIDTH, y * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT);
+ }
+
+ for line in buf.split(|&b| b == b'\n') {
+ let mut v = line.to_vec();
+ v.push(b'\n');
+ self.scrollback.push_back(v);
+ }
+ while self.scrollback.len() > SCROLLBACK_LINES {
+ self.scrollback.pop_front();
+ }
+ }
+
+ fn rect_direct(map: &mut DirectFbMap, x: usize, y: usize, w: usize, h: usize, color: u32) {
+ let width = map.width();
+ let height = map.height();
+ let pixels = map.pixel_slice();
+ let stride = width;
+
+ let start_y = y.min(height);
+ let end_y = (y + h).min(height);
+ let start_x = x.min(width);
+ let len = (x + w).min(width) - start_x;
+
+ for row in start_y..end_y {
+ let offset = row * stride + start_x;
+ for i in 0..len {
+ pixels[offset + i] = color;
+ }
+ }
+ }
+
+ fn char_direct(map: &mut DirectFbMap, x: usize, y: usize, character: char, color: u32, _bold: bool) {
+ let width = map.width();
+ let height = map.height();
+
+ if x + CHAR_WIDTH > width || y + CHAR_HEIGHT > height {
+ return;
+ }
+
+ let pixels = map.pixel_slice();
+ let stride = width;
+ let font_i = CHAR_HEIGHT * (character as usize);
+ if font_i + CHAR_HEIGHT > orbclient::FONT.len() {
+ return;
+ }
+
+ for row in 0..CHAR_HEIGHT {
+ let row_data = orbclient::FONT[font_i + row];
+ let offset = (y + row) * stride + x;
+ for col in 0..CHAR_WIDTH {
+ if (row_data >> (7 - col)) & 1 == 1 {
+ pixels[offset + col] = color;
+ }
+ }
+ }
+ }
+
+ fn invert_direct(map: &mut DirectFbMap, x: usize, y: usize, w: usize, h: usize) {
+ let width = map.width();
+ let height = map.height();
+ let pixels = map.pixel_slice();
+ let stride = width;
+
+ let start_y = y.min(height);
+ let end_y = (y + h).min(height);
+ let start_x = x.min(width);
+ let len = (x + w).min(width) - start_x;
+
+ for row in start_y..end_y {
+ let offset = row * stride + start_x;
+ for i in 0..len {
+ pixels[offset + i] = !pixels[offset + i];
+ }
+ }
+ }
+}
@@ -0,0 +1,147 @@
diff --git a/drivers/initfs-storage.toml b/drivers/initfs-storage.toml
new file mode 100644
index 00000000..4a9a603a
--- /dev/null
+++ b/drivers/initfs-storage.toml
@@ -0,0 +1,51 @@
+## Initfs driver configs for driver-manager
+## Read by driver-manager --initfs from /scheme/initfs/lib/drivers.d/
+##
+## Storage drivers essential for early boot (mounting rootfs).
+## GPU/display drivers are NOT probed in initfs.
+
+[[driver]]
+name = "ahcid"
+description = "AHCI SATA storage driver"
+priority = 100
+command = ["/scheme/initfs/lib/drivers/ahcid"]
+
+[[driver.match]]
+bus = "pci"
+class = 1
+subclass = 6
+
+[[driver]]
+name = "ided"
+description = "PATA IDE storage driver"
+priority = 100
+command = ["/scheme/initfs/lib/drivers/ided"]
+
+[[driver.match]]
+bus = "pci"
+class = 1
+subclass = 1
+
+[[driver]]
+name = "nvmed"
+description = "NVMe storage driver"
+priority = 100
+command = ["/scheme/initfs/lib/drivers/nvmed"]
+
+[[driver.match]]
+bus = "pci"
+class = 1
+subclass = 8
+
+[[driver]]
+name = "virtio-blkd"
+description = "VirtIO block device driver"
+priority = 100
+command = ["/scheme/initfs/lib/drivers/virtio-blkd"]
+
+[[driver.match]]
+bus = "pci"
+vendor = 0x1AF4
+device = 0x1001
+class = 1
+subclass = 0
diff --git a/init.d/00_base.target b/init.d/00_base.target
index 03c25798..9411cc88 100644
--- a/init.d/00_base.target
+++ b/init.d/00_base.target
@@ -7 +7 @@ requires_weak = [
- "00_pcid-spawner.service",
+ "00_driver-manager.service",
diff --git a/init.d/00_pcid-spawner.service b/init.d/00_pcid-spawner.service
deleted file mode 100644
index 8ba6fc6c..00000000
--- a/init.d/00_pcid-spawner.service
+++ /dev/null
@@ -1,6 +0,0 @@
-[unit]
-description = "PCI driver spawner"
-
-[service]
-cmd = "pcid-spawner"
-type = "oneshot"
diff --git a/init.d/10_smolnetd.service b/init.d/10_smolnetd.service
index 1ee54ad0..29563e65 100644
--- a/init.d/10_smolnetd.service
+++ b/init.d/10_smolnetd.service
@@ -6 +6 @@ requires_weak = [
- "00_pcid-spawner.service",
+ "00_driver-manager.service",
diff --git a/init.initfs.d/00_driver-manager-initfs.service b/init.initfs.d/00_driver-manager-initfs.service
new file mode 100644
index 00000000..39613261
--- /dev/null
+++ b/init.initfs.d/00_driver-manager-initfs.service
@@ -0,0 +1,8 @@
+[unit]
+description = "Red Bear driver manager (initfs)"
+requires_weak = ["10_inputd.service", "10_lived.service", "20_graphics.target", "40_hwd.service"]
+
+[service]
+cmd = "driver-manager"
+args = ["--initfs"]
+type = "oneshot_async"
diff --git a/init.initfs.d/30_redox-drm.service b/init.initfs.d/30_redox-drm.service
index ba380bf2..3e051003 100644
--- a/init.initfs.d/30_redox-drm.service
+++ b/init.initfs.d/30_redox-drm.service
@@ -3 +3 @@ description = "DRM/KMS Display Driver"
-requires_weak = ["20_graphics.target", "40_hwd.service", "40_pcid-spawner-initfs.service"]
+requires_weak = ["20_graphics.target", "40_hwd.service", "00_driver-manager-initfs.service"]
diff --git a/init.initfs.d/40_drivers.target b/init.initfs.d/40_drivers.target
index 061c2bed..2a91d2d4 100644
--- a/init.initfs.d/40_drivers.target
+++ b/init.initfs.d/40_drivers.target
@@ -6 +5,0 @@ requires_weak = [
- "40_pcid.service",
@@ -10 +9 @@ requires_weak = [
- "40_pcid-spawner-initfs.service",
+ "00_driver-manager-initfs.service",
diff --git a/init.initfs.d/40_hwd.service b/init.initfs.d/40_hwd.service
index ff0e76dc..d4625542 100644
--- a/init.initfs.d/40_hwd.service
+++ b/init.initfs.d/40_hwd.service
@@ -3 +3 @@ description = "Hardware manager"
-requires_weak = ["10_inputd.service", "10_lived.service", "20_graphics.target", "40_pcid.service", "41_acpid.service"]
+requires_weak = ["10_inputd.service", "10_lived.service", "20_graphics.target", "00_driver-manager-initfs.service", "41_acpid.service"]
diff --git a/init.initfs.d/40_pcid-spawner-initfs.service b/init.initfs.d/40_pcid-spawner-initfs.service
deleted file mode 100644
index 2d36c9d5..00000000
--- a/init.initfs.d/40_pcid-spawner-initfs.service
+++ /dev/null
@@ -1,8 +0,0 @@
-[unit]
-description = "PCI driver spawner"
-requires_weak = ["10_inputd.service", "20_graphics.target", "40_pcid.service"]
-
-[service]
-cmd = "pcid-spawner"
-args = ["--initfs"]
-type = "oneshot"
diff --git a/init.initfs.d/40_pcid.service b/init.initfs.d/40_pcid.service
deleted file mode 100644
index 6c3a83d8..00000000
--- a/init.initfs.d/40_pcid.service
+++ /dev/null
@@ -1,7 +0,0 @@
-[unit]
-description = "PCI daemon"
-requires_weak = ["41_acpid.service"]
-
-[service]
-cmd = "pcid"
-type = "notify"
@@ -0,0 +1,127 @@
diff --git a/drivers/graphics/fbcond/src/text.rs b/drivers/graphics/fbcond/src/text.rs
index 4130b05f..a71a9f51 100644
--- a/drivers/graphics/fbcond/src/text.rs
+++ b/drivers/graphics/fbcond/src/text.rs
@@ -128,2 +128 @@ impl TextScreen {
- match &mut self.display.map {
- Some(DisplayMap::Drm(map)) => {
+ if let Some(map) = &mut self.display.map {
@@ -130,0 +130 @@ impl TextScreen {
+ }
@@ -131,0 +132,2 @@ impl TextScreen {
+ match &mut self.display.map {
+ Some(DisplayMap::Drm(map)) => {
@@ -146 +148,7 @@ impl TextScreen {
- self.write_direct(direct_map, buf);
+ Self::write_direct(
+ direct_map,
+ buf,
+ &mut self.direct_console,
+ &mut self.direct_initialized,
+ &mut self.scrollback,
+ );
@@ -164 +172,7 @@ impl TextScreen {
- fn write_direct(&mut self, direct_map: &mut DirectFbMap, buf: &[u8]) {
+ fn write_direct(
+ direct_map: &mut DirectFbMap,
+ buf: &[u8],
+ direct_console: &mut ransid::Console,
+ direct_initialized: &mut bool,
+ scrollback: &mut VecDeque<Vec<u8>>,
+ ) {
@@ -172,3 +186,3 @@ impl TextScreen {
- if !self.direct_initialized {
- self.direct_console.resize(width / CHAR_WIDTH, height / CHAR_HEIGHT);
- self.direct_initialized = true;
+ if !*direct_initialized {
+ direct_console.resize(width / CHAR_WIDTH, height / CHAR_HEIGHT);
+ *direct_initialized = true;
@@ -177,5 +191,3 @@ impl TextScreen {
- let console = &mut self.direct_console;
-
- if console.state.cursor
- && console.state.x < console.state.w
- && console.state.y < console.state.h
+ if direct_console.state.cursor
+ && direct_console.state.x < direct_console.state.w
+ && direct_console.state.y < direct_console.state.h
@@ -183,2 +195,2 @@ impl TextScreen {
- let x = console.state.x;
- let y = console.state.y;
+ let x = direct_console.state.x;
+ let y = direct_console.state.y;
@@ -188 +200,2 @@ impl TextScreen {
- let pixels = direct_map.pixel_slice();
+ // Extract stride before pixel_slice: borrow sequence must be
+ // width/height → stride → pixel_slice to avoid E0502/E0500.
@@ -189,0 +203,3 @@ impl TextScreen {
+ let pixels = direct_map.pixel_slice();
+ let fb_width = width;
+ let fb_height = height;
@@ -191 +207,4 @@ impl TextScreen {
- console.write(buf, |event| match event {
+ // Use pixels/stride inside the closure — direct_map is already mutably
+ // borrowed via pixel_slice, so we cannot call Self::char_direct etc.
+ // which would re-borrow direct_map.
+ direct_console.write(buf, |event| match event {
@@ -197 +216 @@ impl TextScreen {
- bold,
+ bold: _,
@@ -200 +219,13 @@ impl TextScreen {
- Self::char_direct(direct_map, x * CHAR_WIDTH, y * CHAR_HEIGHT, c, color.as_rgb(), bold);
+ let cx = x * CHAR_WIDTH;
+ let cy = y * CHAR_HEIGHT;
+ if cx + CHAR_WIDTH <= fb_width && cy + CHAR_HEIGHT <= fb_height {
+ let font_i = CHAR_HEIGHT * (c as usize);
+ if font_i + CHAR_HEIGHT <= orbclient::FONT.len() {
+ for row in 0..CHAR_HEIGHT {
+ let row_data = orbclient::FONT[font_i + row];
+ let offset = (cy + row) * stride + cx;
+ for col in 0..CHAR_WIDTH {
+ if (row_data >> (7 - col)) & 1 == 1 {
+ pixels[offset + col] = color.as_rgb();
+ }
+ }
@@ -202 +233,4 @@ impl TextScreen {
- ransid::Event::Input { data } => self.input.extend(data),
+ }
+ }
+ }
+ ransid::Event::Input { .. } => (),
@@ -204 +238,15 @@ impl TextScreen {
- Self::rect_direct(direct_map, x * CHAR_WIDTH, y * CHAR_HEIGHT, w * CHAR_WIDTH, h * CHAR_HEIGHT, color.as_rgb());
+ let rx = x * CHAR_WIDTH;
+ let ry = y * CHAR_HEIGHT;
+ let rw = w * CHAR_WIDTH;
+ let rh = h * CHAR_HEIGHT;
+ let start_y = ry.min(fb_height);
+ let end_y = (ry + rh).min(fb_height);
+ let start_x = rx.min(fb_width);
+ let len = (rx + rw).min(fb_width) - start_x;
+ let rgb = color.as_rgb();
+ for row in start_y..end_y {
+ let offset = row * stride + start_x;
+ for i in 0..len {
+ pixels[offset + i] = rgb;
+ }
+ }
@@ -239,3 +287,3 @@ impl TextScreen {
- if console.state.cursor
- && console.state.x < console.state.w
- && console.state.y < console.state.h
+ if direct_console.state.cursor
+ && direct_console.state.x < direct_console.state.w
+ && direct_console.state.y < direct_console.state.h
@@ -243,2 +291,2 @@ impl TextScreen {
- let x = console.state.x;
- let y = console.state.y;
+ let x = direct_console.state.x;
+ let y = direct_console.state.y;
@@ -251 +299 @@ impl TextScreen {
- self.scrollback.push_back(v);
+ scrollback.push_back(v);
@@ -253,2 +301,2 @@ impl TextScreen {
- while self.scrollback.len() > SCROLLBACK_LINES {
- self.scrollback.pop_front();
+ while scrollback.len() > SCROLLBACK_LINES {
+ scrollback.pop_front();
@@ -0,0 +1,9 @@
diff --git a/init/src/unit.rs b/init/src/unit.rs
index 98053cb2..eabb031b 100644
--- a/init/src/unit.rs
+++ b/init/src/unit.rs
@@ -60,0 +61,4 @@ impl UnitStore {
+ if !unit.conditions_met() {
+ return None;
+ }
+
@@ -0,0 +1,15 @@
diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs
index 059254b3..a43bab83 100644
--- a/drivers/acpid/src/main.rs
+++ b/drivers/acpid/src/main.rs
@@ -96,2 +96,8 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- register_sync_scheme(&socket, "acpi", &mut scheme)
- .expect("acpid: failed to register acpi scheme to namespace");
+ if let Err(err) = register_sync_scheme(&socket, "acpi", &mut scheme) {
+ log::warn!(
+ "acpid: failed to register acpi scheme (error: {}). Another acpid instance may already own it.",
+ err
+ );
+ daemon.ready();
+ std::process::exit(0);
+ }
@@ -0,0 +1,30 @@
diff --git a/drivers/usb/xhcid/src/main.rs b/drivers/usb/xhcid/src/main.rs
index da9cabe1..042173d6 100644
--- a/drivers/usb/xhcid/src/main.rs
+++ b/drivers/usb/xhcid/src/main.rs
@@ -65 +64,0 @@ fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option<File>, Interru
- PciFeatureInfo::Msi(_) => panic!(),
@@ -66,0 +66,4 @@ fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option<File>, Interru
+ other => {
+ log::error!("xhcid: unexpected MSI-X feature info response: {:?}", other);
+ return (None, InterruptMethod::Polling);
+ }
@@ -78 +81,7 @@ fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option<File>, Interru
- let destination_id = read_bsp_apic_id().expect("xhcid: failed to read BSP apic id");
+ let destination_id = match read_bsp_apic_id() {
+ Ok(id) => id,
+ Err(err) => {
+ log::error!("xhcid: failed to read BSP APIC ID: {}", err);
+ return (None, InterruptMethod::Polling);
+ }
+ };
@@ -150,2 +159,7 @@ fn daemon_with_context_size<const N: usize>(
- let (irq_file, interrupt_method) = (None, InterruptMethod::Polling); //get_int_method(&mut pcid_handle);
- //TODO: Fix interrupts.
+ let (irq_file, interrupt_method) = get_int_method(&mut pcid_handle);
+
+ match interrupt_method {
+ InterruptMethod::Msi => log::info!("xhcid: using MSI/MSI-X interrupt delivery"),
+ InterruptMethod::Intx => log::info!("xhcid: using legacy INTx interrupt delivery"),
+ InterruptMethod::Polling => log::warn!("xhcid: falling back to polling mode"),
+ }
@@ -0,0 +1,70 @@
diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs
index 5528ad0a..b05102f6 100644
--- a/drivers/acpid/src/main.rs
+++ b/drivers/acpid/src/main.rs
@@ -50,2 +50,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- log::error!("acpid: failed to parse [RX]SDT: {}", e);
- std::process::exit(1);
+ log::warn!("acpid: failed to parse [RX]SDT: {} — booting without ACPI", e);
+ daemon.ready();
+ std::process::exit(0);
@@ -80,2 +81,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- log::error!("acpid: expected [RX]SDT from kernel to be RSDT or XSDT, got {:?}", String::from_utf8_lossy(&sdt.signature));
- std::process::exit(1);
+ log::warn!("acpid: expected [RX]SDT from kernel to be RSDT or XSDT, got {:?} — booting without ACPI", String::from_utf8_lossy(&sdt.signature));
+ daemon.ready();
+ std::process::exit(0);
@@ -102,2 +104 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- log::error!("acpid: failed to set I/O privilege level to Ring 3: {}", e);
- std::process::exit(1);
+ log::warn!("acpid: failed to set I/O privilege level to Ring 3: {} — continuing without port I/O", e);
@@ -106,2 +107,17 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- let shutdown_pipe = File::open("/scheme/kernel.acpi/kstop")
- .expect("acpid: failed to open `/scheme/kernel.acpi/kstop`");
+ let shutdown_pipe = match File::open("/scheme/kernel.acpi/kstop") {
+ Ok(f) => f,
+ Err(e) => {
+ log::warn!("acpid: failed to open `/scheme/kernel.acpi/kstop`: {} — booting without ACPI shutdown", e);
+ daemon.ready();
+ std::process::exit(0);
+ }
+ };
+
+ let mut event_queue = match RawEventQueue::new() {
+ Ok(q) => q,
+ Err(e) => {
+ log::warn!("acpid: failed to create event queue: {} — booting without ACPI", e);
+ daemon.ready();
+ std::process::exit(0);
+ }
+ };
@@ -109,2 +125,8 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- let mut event_queue = RawEventQueue::new().expect("acpid: failed to create event queue");
- let socket = Socket::nonblock().expect("acpid: failed to create disk scheme");
+ let socket = match Socket::nonblock() {
+ Ok(s) => s,
+ Err(e) => {
+ log::warn!("acpid: failed to create scheme socket: {} — booting without ACPI", e);
+ daemon.ready();
+ std::process::exit(0);
+ }
+ };
@@ -115,6 +137,12 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- event_queue
- .subscribe(shutdown_pipe.as_raw_fd() as usize, 0, EventFlags::READ)
- .expect("acpid: failed to register shutdown pipe for event queue");
- event_queue
- .subscribe(socket.inner().raw(), 1, EventFlags::READ)
- .expect("acpid: failed to register scheme socket for event queue");
+ if let Err(e) = event_queue
+ .subscribe(shutdown_pipe.as_raw_fd() as usize, 0, EventFlags::READ) {
+ log::warn!("acpid: failed to register shutdown pipe for event queue: {} — booting without ACPI", e);
+ daemon.ready();
+ std::process::exit(0);
+ }
+ if let Err(e) = event_queue
+ .subscribe(socket.inner().raw(), 1, EventFlags::READ) {
+ log::warn!("acpid: failed to register scheme socket for event queue: {} — booting without ACPI", e);
+ daemon.ready();
+ std::process::exit(0);
+ }
+7
View File
@@ -70,6 +70,13 @@ patches = [
"P21-boot-daemon-graceful-panic.patch",
"P23-rootfs-hard-dep-on-drivers.patch",
"P24-acpi-s5-derivation-shutdown-semantics.patch",
"P25-fbcond-vesa-fallback.patch",
"P26-driver-manager-initfs-conversion.patch",
"P27-fbcond-borrow-fix.patch",
"P28-init-skip-unmet-conditions.patch",
"P30-acpid-graceful-scheme-exists.patch",
"P31-xhcid-restore-interrupts.patch",
"P32-acpid-graceful-boot.patch",
]
[package]