diff --git a/local/patches/base/P25-fbcond-vesa-fallback.patch b/local/patches/base/P25-fbcond-vesa-fallback.patch new file mode 100644 index 0000000000..805be29cf3 --- /dev/null +++ b/local/patches/base/P25-fbcond-vesa-fallback.patch @@ -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 { ++ 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, ++ pub map: Option, + } + + 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, + scrollback: VecDeque>, + 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 { +- 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]; ++ } ++ } ++ } ++} diff --git a/local/patches/base/P26-driver-manager-initfs-conversion.patch b/local/patches/base/P26-driver-manager-initfs-conversion.patch new file mode 100644 index 0000000000..d2b17f795b --- /dev/null +++ b/local/patches/base/P26-driver-manager-initfs-conversion.patch @@ -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" diff --git a/local/patches/base/P27-fbcond-borrow-fix.patch b/local/patches/base/P27-fbcond-borrow-fix.patch new file mode 100644 index 0000000000..fac6a20e36 --- /dev/null +++ b/local/patches/base/P27-fbcond-borrow-fix.patch @@ -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>, ++ ) { +@@ -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(); diff --git a/local/patches/base/P28-init-skip-unmet-conditions.patch b/local/patches/base/P28-init-skip-unmet-conditions.patch new file mode 100644 index 0000000000..9a33de47c7 --- /dev/null +++ b/local/patches/base/P28-init-skip-unmet-conditions.patch @@ -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; ++ } ++ diff --git a/local/patches/base/P30-acpid-graceful-scheme-exists.patch b/local/patches/base/P30-acpid-graceful-scheme-exists.patch new file mode 100644 index 0000000000..9ad58c09dd --- /dev/null +++ b/local/patches/base/P30-acpid-graceful-scheme-exists.patch @@ -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); ++ } diff --git a/local/patches/base/P31-xhcid-restore-interrupts.patch b/local/patches/base/P31-xhcid-restore-interrupts.patch new file mode 100644 index 0000000000..00d3782b8d --- /dev/null +++ b/local/patches/base/P31-xhcid-restore-interrupts.patch @@ -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, Interru +- PciFeatureInfo::Msi(_) => panic!(), +@@ -66,0 +66,4 @@ fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option, 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, 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( +- 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"), ++ } diff --git a/local/patches/base/P32-acpid-graceful-boot.patch b/local/patches/base/P32-acpid-graceful-boot.patch new file mode 100644 index 0000000000..641971fdd4 --- /dev/null +++ b/local/patches/base/P32-acpid-graceful-boot.patch @@ -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); ++ } diff --git a/recipes/core/base/recipe.toml b/recipes/core/base/recipe.toml index 23c7bb2d7e..fb7fd26a80 100644 --- a/recipes/core/base/recipe.toml +++ b/recipes/core/base/recipe.toml @@ -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]