Fix IOAPIC/HPET/NMI, PS/2 driver, and remove duplicate VT service entries
- IOAPIC: enable full IOAPIC initialization on AMD/Intel bare metal, dual GSI 0/2 timer mapping for platform compatibility, NMI handler uses raw COM1 PIO writes to avoid mutex deadlock - HPET: counter validation, graceful fallback to PIT when HPET missing - PS/2: fix 0xFE RESEND handling in all MouseState variants, add controller flush/self-test retry/aux port test from Linux 7.0 - ACPI: defer AML evaluation to avoid blocking initfs driver spawn - VT chain: remove duplicate rootfs service files (inputd, vesad, fbcond, getty) that were already handled by initfs phase 1 and the legacy 30_console script from minimal.toml - QEMU verified: boots to login prompt, 20 rootfs units (was 26), single login prompt (was double), only 1 expected error (wifictl)
This commit is contained in:
@@ -5,8 +5,9 @@
|
||||
#
|
||||
# Target contract:
|
||||
# - keep a text-login live/recovery surface only
|
||||
# - use boot framebuffer for VT text consoles via vesad + fbcond
|
||||
# - avoid the shared firmware/input/device-service stack from redbear-minimal
|
||||
# - ship no graphics packages/services and no linux-firmware payload
|
||||
# - ship no linux-firmware payload
|
||||
|
||||
include = ["minimal.toml", "redbear-legacy-base.toml", "redbear-netctl.toml"]
|
||||
|
||||
@@ -34,66 +35,13 @@ redbear-info = {}
|
||||
# Keep package builder utility in live environment.
|
||||
cub = {}
|
||||
|
||||
# VT/getty/login chain: initfs starts inputd + vesad + fbcond in phase 1,
|
||||
# then minimal.toml legacy 30_console runs inputd -A 2 + getty 2 + getty debug.
|
||||
|
||||
[[files]]
|
||||
path = "/etc/netctl/active"
|
||||
data = "wired-dhcp\n"
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/30_console"
|
||||
data = """
|
||||
requires_weak 10_net
|
||||
inputd -A 2
|
||||
nowait getty 2
|
||||
nowait getty /scheme/debug/no-preserve -J
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/29_activate_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Activate console VT"
|
||||
requires_weak = [
|
||||
"10_net.target",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "inputd"
|
||||
args = ["-A", "2"]
|
||||
type = "oneshot"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/30_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Console terminals"
|
||||
requires_weak = [
|
||||
"29_activate_console.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "getty"
|
||||
args = ["2"]
|
||||
type = "oneshot_async"
|
||||
respawn = true
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/31_debug_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Debug console"
|
||||
requires_weak = [
|
||||
"10_net.target",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "getty"
|
||||
args = ["/scheme/debug/no-preserve", "-J"]
|
||||
type = "oneshot_async"
|
||||
respawn = true
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/10_smolnetd.service"
|
||||
data = """
|
||||
@@ -147,22 +95,6 @@ cmd = "audiod"
|
||||
type = "oneshot_async"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/01_debug_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Debug serial login"
|
||||
requires_weak = [
|
||||
"00_ptyd.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "getty"
|
||||
args = ["/scheme/debug/no-preserve", "-J"]
|
||||
type = "oneshot_async"
|
||||
respawn = true
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/02_serial_probe.service"
|
||||
data = """
|
||||
@@ -283,10 +215,5 @@ data = """
|
||||
[[files]]
|
||||
path = "/etc/pcid.d/00_text_mode_gpu_mask.toml"
|
||||
data = """
|
||||
# redbear-live-mini: force text-only mode by consuming all display-class PCI devices
|
||||
# with a no-op command, before any graphics-capable driver rules are evaluated.
|
||||
[[drivers]]
|
||||
name = "Text-only live-mini display mask"
|
||||
class = 0x03
|
||||
command = ["/bin/true"]
|
||||
# redbear-live-mini: no display driver matched; class 0x03 devices are skipped
|
||||
"""
|
||||
|
||||
@@ -48,59 +48,7 @@ redbear-info = {}
|
||||
path = "/etc/netctl/active"
|
||||
data = "wired-dhcp\n"
|
||||
|
||||
# minimal.toml: "inputd -A 2", "nowait getty 2", "nowait getty /scheme/debug/no-preserve -J"
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/30_console"
|
||||
data = """
|
||||
requires_weak 10_net
|
||||
inputd -A 2
|
||||
nowait getty 2
|
||||
nowait getty /scheme/debug/no-preserve -J
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/29_activate_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Activate console VT"
|
||||
requires_weak = [
|
||||
"10_net.target",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "inputd"
|
||||
args = ["-A", "2"]
|
||||
type = "oneshot"
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/30_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Console terminals"
|
||||
requires_weak = [
|
||||
"29_activate_console.service",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "getty"
|
||||
args = ["2"]
|
||||
type = "oneshot_async"
|
||||
respawn = true
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/31_debug_console.service"
|
||||
data = """
|
||||
[unit]
|
||||
description = "Debug console"
|
||||
requires_weak = [
|
||||
"10_net.target",
|
||||
]
|
||||
|
||||
[service]
|
||||
cmd = "getty"
|
||||
args = ["/scheme/debug/no-preserve", "-J"]
|
||||
type = "oneshot_async"
|
||||
respawn = true
|
||||
"""
|
||||
# VT/getty/login chain is handled by the combination of:
|
||||
# 1. initfs (phase 1): inputd daemon, vesad, fbcond — register input/display/fbcon schemes
|
||||
# 2. minimal.toml legacy 30_console: inputd -A 2 + nowait getty 2 + nowait getty /scheme/debug
|
||||
# No additional rootfs service files needed — initfs + legacy script covers the full chain.
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
Defer AML initialization until PCI registration completes.
|
||||
|
||||
When acpid starts before pcid has registered the PCI fd, AML
|
||||
initialization fails with a misleading ERROR-level message. This is
|
||||
expected on every boot because the service ordering requires acpid to
|
||||
start before pcid-spawner. The AML interpreter initializes successfully
|
||||
after pcid registers via /scheme/acpi/register_pci.
|
||||
|
||||
Changes:
|
||||
- aml_context_mut(): log at DEBUG instead of ERROR when PCI fd is None
|
||||
(expected pre-registration state, not a fault)
|
||||
- Fadt::init(): skip \\_S5 evaluation when PCI is not yet registered,
|
||||
since refresh_s5_values() is retried in register_pci_fd() after PCI
|
||||
registration completes
|
||||
|
||||
diff -urN a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs
|
||||
--- a/drivers/acpid/src/acpi.rs
|
||||
+++ b/drivers/acpid/src/acpi.rs
|
||||
@@ -896,7 +896,11 @@
|
||||
match self.init(pci_fd) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
- log::error!("failed to initialize AML context: {}", err);
|
||||
+ if pci_fd.is_none() {
|
||||
+ log::debug!("AML init deferred until PCI registration: {}", err);
|
||||
+ } else {
|
||||
+ log::error!("failed to initialize AML context: {}", err);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2004,8 +2008,12 @@
|
||||
|
||||
context.tables.push(dsdt_sdt);
|
||||
|
||||
- if let Err(error) = context.refresh_s5_values() {
|
||||
- log::warn!("Failed to evaluate \\_S5 during FADT init: {error}");
|
||||
+ if context.pci_ready() {
|
||||
+ if let Err(error) = context.refresh_s5_values() {
|
||||
+ log::warn!("Failed to evaluate \\_S5 during FADT init: {error}");
|
||||
+ }
|
||||
+ } else {
|
||||
+ log::debug!("Deferring \\_S5 evaluation until PCI registration");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
diff --git a/drivers/common/src/logger.rs b/drivers/common/src/logger.rs
|
||||
index 82ec2bd0..a531edd9 100644
|
||||
--- a/drivers/common/src/logger.rs
|
||||
+++ b/drivers/common/src/logger.rs
|
||||
@@ -44,6 +44,7 @@ pub fn setup_logging(
|
||||
Ok(b) => {
|
||||
logger = logger.with_output(b.with_filter(file_level).flush_on_newline(true).build())
|
||||
}
|
||||
+ Err(error) if error.raw_os_error() == Some(19) => {}
|
||||
Err(error) => eprintln!("Failed to create {logfile_base}.log: {}", error),
|
||||
}
|
||||
|
||||
@@ -61,6 +62,7 @@ pub fn setup_logging(
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
+ Err(error) if error.raw_os_error() == Some(19) => {}
|
||||
Err(error) => eprintln!("Failed to create {logfile_base}.ansi.log: {}", error),
|
||||
}
|
||||
|
||||
diff --git a/drivers/pcid-spawner/src/main.rs b/drivers/pcid-spawner/src/main.rs
|
||||
index a968f4d4..a39b2af8 100644
|
||||
--- a/drivers/pcid-spawner/src/main.rs
|
||||
+++ b/drivers/pcid-spawner/src/main.rs
|
||||
@@ -1,11 +1,40 @@
|
||||
+use std::env;
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
+use std::thread;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
||||
use pcid_interface::config::Config;
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
|
||||
+fn strict_usb_boot() -> bool {
|
||||
+ matches!(
|
||||
+ env::var("REDBEAR_STRICT_USB_BOOT")
|
||||
+ .ok()
|
||||
+ .as_deref()
|
||||
+ .map(str::to_ascii_lowercase)
|
||||
+ .as_deref(),
|
||||
+ Some("1" | "true" | "yes" | "on")
|
||||
+ )
|
||||
+}
|
||||
+
|
||||
+fn should_detach_in_initfs(initfs: bool, class: u8, subclass: u8, strict_usb_boot: bool) -> bool {
|
||||
+ if !initfs {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if class == 0x01 {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if strict_usb_boot && class == 0x0C && subclass == 0x03 {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ true
|
||||
+}
|
||||
+
|
||||
fn main() -> Result<()> {
|
||||
let mut args = pico_args::Arguments::from_env();
|
||||
let initfs = args.contains("--initfs");
|
||||
@@ -30,6 +59,12 @@ fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
let config: Config = toml::from_str(&config_data)?;
|
||||
+ let strict_usb_boot = strict_usb_boot();
|
||||
+
|
||||
+ log::info!(
|
||||
+ "pcid-spawner: starting (initfs={}, strict_usb_boot={})",
|
||||
+ initfs, strict_usb_boot
|
||||
+ );
|
||||
|
||||
for entry in fs::read_dir("/scheme/pci")? {
|
||||
let entry = entry.context("failed to get entry")?;
|
||||
@@ -55,10 +90,11 @@ fn main() -> Result<()> {
|
||||
};
|
||||
|
||||
let full_device_id = handle.config().func.full_device_id;
|
||||
+ let device_addr = handle.config().func.addr;
|
||||
|
||||
log::debug!(
|
||||
"pcid-spawner enumerated: PCI {} {}",
|
||||
- handle.config().func.addr,
|
||||
+ device_addr,
|
||||
full_device_id.display()
|
||||
);
|
||||
|
||||
@@ -67,7 +103,7 @@ fn main() -> Result<()> {
|
||||
.iter()
|
||||
.find(|driver| driver.match_function(&full_device_id))
|
||||
else {
|
||||
- log::debug!("no driver for {}, continuing", handle.config().func.addr);
|
||||
+ log::debug!("no driver for {}, continuing", device_addr);
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -85,16 +121,93 @@ fn main() -> Result<()> {
|
||||
let mut command = Command::new(program);
|
||||
command.args(args);
|
||||
|
||||
- log::info!("pcid-spawner: spawn {:?}", command);
|
||||
-
|
||||
- handle.enable_device();
|
||||
+ log::info!(
|
||||
+ "pcid-spawner: matched {} to driver {:?}",
|
||||
+ device_addr,
|
||||
+ driver.command
|
||||
+ );
|
||||
+ log::info!("pcid-spawner: enabling {} before spawn", device_addr);
|
||||
+
|
||||
+ if let Err(err) = handle.try_enable_device() {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: failed to enable {} before spawn: {}",
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
let channel_fd = handle.into_inner_fd();
|
||||
command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string());
|
||||
|
||||
+ log::info!("pcid-spawner: spawn {:?}", command);
|
||||
#[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
- daemon::Daemon::spawn(command);
|
||||
- syscall::close(channel_fd as usize).unwrap();
|
||||
+ if should_detach_in_initfs(
|
||||
+ initfs,
|
||||
+ full_device_id.class,
|
||||
+ full_device_id.subclass,
|
||||
+ strict_usb_boot,
|
||||
+ ) {
|
||||
+ log::warn!(
|
||||
+ "pcid-spawner: detached initfs spawn for {} to avoid blocking early boot",
|
||||
+ device_addr
|
||||
+ );
|
||||
+
|
||||
+ let device_addr = device_addr.to_string();
|
||||
+ thread::spawn(move || {
|
||||
+ #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
+ if let Err(err) = daemon::Daemon::spawn(command) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: spawn/readiness failed for {}: {}",
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: {} remains enabled without a confirmed ready driver",
|
||||
+ device_addr
|
||||
+ );
|
||||
+ }
|
||||
+ if let Err(err) = syscall::close(channel_fd as usize) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: failed to close channel fd {} for {}: {}",
|
||||
+ channel_fd,
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ }
|
||||
+ });
|
||||
+ } else {
|
||||
+ log::info!(
|
||||
+ "pcid-spawner: blocking on storage driver spawn for {} (class={:#04x})",
|
||||
+ device_addr,
|
||||
+ full_device_id.class
|
||||
+ );
|
||||
+ #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
+ if let Err(err) = daemon::Daemon::spawn(command) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: spawn/readiness failed for {}: {}",
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: {} remains enabled without a confirmed ready driver",
|
||||
+ device_addr
|
||||
+ );
|
||||
+ } else {
|
||||
+ log::info!(
|
||||
+ "pcid-spawner: storage driver ready for {}",
|
||||
+ device_addr
|
||||
+ );
|
||||
+ }
|
||||
+ if let Err(err) = syscall::close(channel_fd as usize) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: failed to close channel fd {} for {}: {}",
|
||||
+ channel_fd,
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
Ok(())
|
||||
diff --git a/init/src/main.rs b/init/src/main.rs
|
||||
index 5682cf44..ed436619 100644
|
||||
--- a/init/src/main.rs
|
||||
+++ b/init/src/main.rs
|
||||
@@ -117,6 +117,8 @@ fn main() {
|
||||
let mut unit_store = UnitStore::new();
|
||||
let mut scheduler = Scheduler::new();
|
||||
|
||||
+ eprintln!("init: phase 1 — initfs boot");
|
||||
+
|
||||
switch_root(
|
||||
&mut unit_store,
|
||||
&mut init_config,
|
||||
@@ -125,6 +127,7 @@ fn main() {
|
||||
);
|
||||
|
||||
// Start logd first such that we can pass /scheme/log as stdio to all other services
|
||||
+ eprintln!("init: starting logd");
|
||||
scheduler
|
||||
.schedule_start_and_report_errors(&mut unit_store, UnitId("00_logd.service".to_owned()));
|
||||
scheduler.step(&mut unit_store, &mut init_config);
|
||||
@@ -132,14 +135,18 @@ fn main() {
|
||||
eprintln!("init: failed to switch stdio to '/scheme/log': {err}");
|
||||
}
|
||||
|
||||
+ eprintln!("init: starting runtime target");
|
||||
let runtime_target = UnitId("00_runtime.target".to_owned());
|
||||
scheduler.schedule_start_and_report_errors(&mut unit_store, runtime_target.clone());
|
||||
unit_store.set_runtime_target(runtime_target);
|
||||
|
||||
+ eprintln!("init: starting initfs drivers target");
|
||||
scheduler
|
||||
.schedule_start_and_report_errors(&mut unit_store, UnitId("90_initfs.target".to_owned()));
|
||||
scheduler.step(&mut unit_store, &mut init_config);
|
||||
+ eprintln!("init: initfs drivers target step() complete");
|
||||
|
||||
+ eprintln!("init: phase 2 — switchroot to /usr");
|
||||
switch_root(
|
||||
&mut unit_store,
|
||||
&mut init_config,
|
||||
@@ -162,23 +169,64 @@ fn main() {
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
- return;
|
||||
+ Vec::new()
|
||||
}
|
||||
};
|
||||
+ eprintln!("init: scheduling {} rootfs units", entries.len());
|
||||
for entry in entries {
|
||||
+ let name = match entry.file_name().and_then(|n| n.to_str()) {
|
||||
+ Some(name) => name,
|
||||
+ None => {
|
||||
+ eprintln!("init: skipping config entry with non-UTF-8 filename");
|
||||
+ continue;
|
||||
+ }
|
||||
+ };
|
||||
scheduler.schedule_start_and_report_errors(
|
||||
&mut unit_store,
|
||||
- UnitId(entry.file_name().unwrap().to_str().unwrap().to_owned()),
|
||||
+ UnitId(name.to_owned()),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
scheduler.step(&mut unit_store, &mut init_config);
|
||||
+ eprintln!("init: phase 3 — rootfs services started");
|
||||
+
|
||||
+ if let Err(err) = libredox::call::setrens(0, 0) {
|
||||
+ eprintln!("init: failed to enter null namespace: {err}");
|
||||
+ }
|
||||
|
||||
- libredox::call::setrens(0, 0).expect("init: failed to enter null namespace");
|
||||
+ eprintln!("init: boot complete — entering waitpid loop");
|
||||
+
|
||||
+ let mut respawn_map: BTreeMap<u32, UnitId> = BTreeMap::new();
|
||||
+ for (unit_id, pid) in scheduler.respawn_pids {
|
||||
+ respawn_map.insert(pid, unit_id);
|
||||
+ }
|
||||
|
||||
loop {
|
||||
let mut status = 0;
|
||||
- libredox::call::waitpid(0, &mut status, 0).unwrap();
|
||||
+ match libredox::call::waitpid(0, &mut status, 0) {
|
||||
+ Ok(pid) => {
|
||||
+ if let Some(unit_id) = respawn_map.remove(&(pid as u32)) {
|
||||
+ eprintln!("init: respawning {} (pid {} exited)", unit_id.0, pid);
|
||||
+ let mut resp_scheduler = Scheduler::new();
|
||||
+ resp_scheduler.schedule_start_and_report_errors(
|
||||
+ &mut unit_store,
|
||||
+ unit_id.clone(),
|
||||
+ );
|
||||
+ resp_scheduler.step(&mut unit_store, &mut init_config);
|
||||
+ for (uid, new_pid) in resp_scheduler.respawn_pids {
|
||||
+ respawn_map.insert(new_pid, uid);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ Err(err) => {
|
||||
+ // EAGAIN is normal (no child exited yet). Other errors are
|
||||
+ // unexpected but init must never crash — log and continue.
|
||||
+ if err.errno() != syscall::EAGAIN {
|
||||
+ eprintln!("init: waitpid error: {err}");
|
||||
+ }
|
||||
+ std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
}
|
||||
+4578
-140
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1500
-24
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
../../../local/patches/base/P2-acpi-defer-aml.patch
|
||||
@@ -1,7 +1,7 @@
|
||||
[source]
|
||||
git = "https://gitlab.redox-os.org/redox-os/base.git"
|
||||
rev = "463f76b9608a896e6f6c9f63457f57f6409873c7"
|
||||
patches = ["redox.patch", "P2-boot-runtime-fixes.patch", "P2-acpi-i2c-resources.patch", "P2-daemon-ready-graceful.patch", "P2-daemon-hardening.patch"]
|
||||
patches = ["redox.patch", "P2-boot-runtime-fixes.patch", "P2-acpi-i2c-resources.patch", "P2-daemon-ready-graceful.patch", "P2-daemon-hardening.patch", "P2-acpi-defer-aml.patch"]
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
|
||||
Reference in New Issue
Block a user