P0..P71 baseline before fbcond/fbbootlogd init fix
This commit is contained in:
@@ -85,6 +85,8 @@ Layer 2: Durable (survives clean/fetch/rebuild/release provisioning)
|
|||||||
|---|---|
|
|---|---|
|
||||||
| Editing `source/` files then running `make all` | `make all` calls `repo fetch` which regenerates `source/` — edits are lost |
|
| Editing `source/` files then running `make all` | `make all` calls `repo fetch` which regenerates `source/` — edits are lost |
|
||||||
| Creating a patch but not wiring it into `recipe.toml` | Patch file exists but is never applied — build uses unpatched source |
|
| Creating a patch but not wiring it into `recipe.toml` | Patch file exists but is never applied — build uses unpatched source |
|
||||||
|
| **Hand-writing patches manually** | **FORBIDDEN. Unified diffs hand-written by humans routinely have incorrect line counts, wrong context, malformed hunks, or timestamp headers — all of which cause `patch(1)` to reject them. The ONLY acceptable way to generate patches is `git diff -U0 -w` from a committed source tree baseline.** |
|
||||||
|
| Editing `recipe.toml` patches list without creating the actual `.patch` file | Build fails with "missing patch" error |
|
||||||
| Editing `recipe.toml` patches list without creating the actual `.patch` file | Build fails with "missing patch" error |
|
| Editing `recipe.toml` patches list without creating the actual `.patch` file | Build fails with "missing patch" error |
|
||||||
| Expecting `source/` changes to survive `make clean` | `make clean` deletes `source/` directories |
|
| Expecting `source/` changes to survive `make clean` | `make clean` deletes `source/` directories |
|
||||||
| Running `repo cook` without `--allow-protected` for core packages | Protected recipes (kernel, relibc, base) are offline-only by default |
|
| Running `repo cook` without `--allow-protected` for core packages | Protected recipes (kernel, relibc, base) are offline-only by default |
|
||||||
@@ -649,19 +651,39 @@ does NOT strip timestamps — they should be removed from the patch file directl
|
|||||||
|
|
||||||
### Robust Patch Generation (REQUIRED)
|
### Robust Patch Generation (REQUIRED)
|
||||||
|
|
||||||
|
**MANDATORY: All patches MUST be generated using `git diff -U0 -w` from a committed source tree.
|
||||||
|
Hand-writing unified diffs is FORBIDDEN — it routinely produces incorrect line counts, malformed
|
||||||
|
hunks, or timestamp headers that cause `patch(1)` to reject them. The build system uses
|
||||||
|
`--fuzz=3` for resilient context matching, which requires properly generated diffs.**
|
||||||
|
|
||||||
Context-line mismatches (renamed variables, shifted line numbers, upstream refactors)
|
Context-line mismatches (renamed variables, shifted line numbers, upstream refactors)
|
||||||
are the single largest source of patch application failures. Use the zero-context,
|
are the single largest source of patch application failures. Use the zero-context,
|
||||||
whitespace-ignored technique to make patches resilient to drift:
|
whitespace-ignored technique to make patches resilient to drift:
|
||||||
|
|
||||||
**Generate:**
|
**Workflow (mandatory):**
|
||||||
```bash
|
```bash
|
||||||
|
# 1. Start with a clean P0..P(N-1) source tree (repo fetch already applied earlier patches)
|
||||||
cd recipes/<component>/source
|
cd recipes/<component>/source
|
||||||
|
|
||||||
|
# 2. Commit the P0..P(N-1) state as a git baseline
|
||||||
|
git add -A && git commit -m "P0..P(N-1) baseline"
|
||||||
|
|
||||||
|
# 3. Make P(N) edits in the source tree
|
||||||
|
# (edit files, test compile, etc.)
|
||||||
|
|
||||||
|
# 4. Generate the P(N) patch using ONLY git diff -U0 -w:
|
||||||
git diff -U0 -w > ../../../local/patches/<component>/P<N>-<description>.patch
|
git diff -U0 -w > ../../../local/patches/<component>/P<N>-<description>.patch
|
||||||
|
|
||||||
|
# 5. Wire the patch into recipe.toml patches list
|
||||||
|
|
||||||
|
# 6. Validate: repo validate-patches <package>
|
||||||
|
# 7. Rebuild: repo cook <package>
|
||||||
|
# 8. Commit: git add local/patches/ recipes/<pkg>/recipe.toml && git commit
|
||||||
```
|
```
|
||||||
|
|
||||||
**Apply:**
|
**Apply (for manual testing):**
|
||||||
```bash
|
```bash
|
||||||
patch -p1 --fuzz=3 < local/patches/<component>/P<N>-<description>.patch
|
patch -p1 --fuzz=3 < local/patches/<component>/P<N>-<description>.patch>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Why this works:**
|
**Why this works:**
|
||||||
@@ -671,6 +693,12 @@ patch -p1 --fuzz=3 < local/patches/<component>/P<N>-<description>.patch
|
|||||||
- `--fuzz=3` allows `patch(1)` to find the target location even when nearby lines have shifted
|
- `--fuzz=3` allows `patch(1)` to find the target location even when nearby lines have shifted
|
||||||
- Together these three flags eliminate the entire class of "context mismatch" failures
|
- Together these three flags eliminate the entire class of "context mismatch" failures
|
||||||
|
|
||||||
|
**Why hand-writing is forbidden:**
|
||||||
|
- Human-written diffs routinely have wrong `@@` line counts, missing or extra context lines,
|
||||||
|
incorrect `--- a/` / `+++ b/` paths, or embedded timestamps — all of which cause `patch(1)`
|
||||||
|
to reject the patch or silently apply it to the wrong location
|
||||||
|
- The `git diff -U0 -w` command produces mechanically correct diffs every time
|
||||||
|
|
||||||
**Before this technique**, patches routinely broke when:
|
**Before this technique**, patches routinely broke when:
|
||||||
- A variable was renamed (e.g., `deamon` → `daemon` in context)
|
- A variable was renamed (e.g., `deamon` → `daemon` in context)
|
||||||
- Lines were added or removed above the changed code
|
- Lines were added or removed above the changed code
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ path = "/usr/lib/init.d/10_acid.service"
|
|||||||
data = """
|
data = """
|
||||||
[unit]
|
[unit]
|
||||||
description = "Acid test runner"
|
description = "Acid test runner"
|
||||||
requires_weak = ["00_pcid-spawner.service"]
|
requires_weak = ["00_driver-manager.service"]
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
cmd = "ion"
|
cmd = "ion"
|
||||||
|
|||||||
@@ -380,8 +380,8 @@ vendor = 0xFFFF
|
|||||||
device = 0xFFFF
|
device = 0xFFFF
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Profiles that include this fragment should start `driver-manager` instead of
|
# driver-manager owns PCI device enumeration, driver matching, and bind/channel
|
||||||
# `pcid-spawner`; the manager performs the PCI bind/channel handoff itself.
|
# handoff — replacing the old pcid + pcid-spawner pair entirely.
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/etc/init.d/00_driver-manager.service"
|
path = "/etc/init.d/00_driver-manager.service"
|
||||||
data = """
|
data = """
|
||||||
|
|||||||
@@ -447,8 +447,9 @@ path = "/etc/init.d/29_activate_console.service"
|
|||||||
data = """
|
data = """
|
||||||
[unit]
|
[unit]
|
||||||
description = "Activate fallback console VT"
|
description = "Activate fallback console VT"
|
||||||
|
default_dependencies = false
|
||||||
requires_weak = [
|
requires_weak = [
|
||||||
"08_userland.target",
|
"00_base.target",
|
||||||
]
|
]
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
@@ -462,6 +463,7 @@ path = "/etc/init.d/30_console.service"
|
|||||||
data = """
|
data = """
|
||||||
[unit]
|
[unit]
|
||||||
description = "Console terminals"
|
description = "Console terminals"
|
||||||
|
default_dependencies = false
|
||||||
requires_weak = [
|
requires_weak = [
|
||||||
"29_activate_console.service",
|
"29_activate_console.service",
|
||||||
]
|
]
|
||||||
@@ -477,6 +479,7 @@ path = "/etc/init.d/31_debug_console.service"
|
|||||||
data = """
|
data = """
|
||||||
[unit]
|
[unit]
|
||||||
description = "Debug console on serial port"
|
description = "Debug console on serial port"
|
||||||
|
default_dependencies = false
|
||||||
requires_weak = [
|
requires_weak = [
|
||||||
"29_activate_console.service",
|
"29_activate_console.service",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,14 +3,9 @@
|
|||||||
# 00_base.service: stripped base setup (tmpdir only, no sudo — sudo runs from
|
# 00_base.service: stripped base setup (tmpdir only, no sudo — sudo runs from
|
||||||
# base.toml's 00_sudo.service). ipcd and ptyd are started by
|
# base.toml's 00_sudo.service). ipcd and ptyd are started by
|
||||||
# 00_ipcd.service and 00_ptyd.service from the base recipe.
|
# 00_ipcd.service and 00_ptyd.service from the base recipe.
|
||||||
# 00_drivers / 10_net: no longer overridden — the legacy scripts were removed
|
# 00_pcid-spawner.service has been fully replaced by 00_driver-manager.service
|
||||||
# from base.toml. The retained 00_pcid-spawner.service unit name now
|
# (defined in redbear-device-services.toml). The old pcid-spawner
|
||||||
# launches driver-manager so existing init ordering remains stable.
|
# unit name is no longer used anywhere.
|
||||||
# 00_pcid-spawner.service: compatibility wrapper for driver-manager. The base
|
|
||||||
# recipe uses type="oneshot" which blocks init until pcid-spawner exits.
|
|
||||||
# Running driver-manager here with oneshot_async keeps the historic unit
|
|
||||||
# name for downstream `requires_weak` consumers while moving PCI driver
|
|
||||||
# spawning to the manager that performs bind/channel handoff.
|
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
zsh = {}
|
zsh = {}
|
||||||
@@ -37,17 +32,4 @@ default_dependencies = false
|
|||||||
[service]
|
[service]
|
||||||
cmd = "audiod"
|
cmd = "audiod"
|
||||||
type = "oneshot_async"
|
type = "oneshot_async"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[[files]]
|
|
||||||
path = "/etc/init.d/00_pcid-spawner.service"
|
|
||||||
data = """
|
|
||||||
[unit]
|
|
||||||
description = "PCI driver spawner compatibility alias"
|
|
||||||
default_dependencies = false
|
|
||||||
|
|
||||||
[service]
|
|
||||||
cmd = "echo"
|
|
||||||
args = ["pcid-spawner compatibility alias: driver-manager owns PCI driver spawning"]
|
|
||||||
type = "oneshot"
|
|
||||||
"""
|
|
||||||
+19
-20
@@ -27,9 +27,8 @@ redbear-release = {}
|
|||||||
redbear-hwutils = {}
|
redbear-hwutils = {}
|
||||||
redbear-quirks = {}
|
redbear-quirks = {}
|
||||||
|
|
||||||
# Device driver infrastructure: driver-manager is started by
|
# Device driver infrastructure: driver-manager replaces pcid-spawner;
|
||||||
# redbear-device-services.toml, with 00_pcid-spawner.service retained only as a
|
# 00_driver-manager.service is defined in redbear-device-services.toml.
|
||||||
# compatibility dependency alias for older service units.
|
|
||||||
ehcid = {}
|
ehcid = {}
|
||||||
ohcid = {}
|
ohcid = {}
|
||||||
uhcid = {}
|
uhcid = {}
|
||||||
@@ -99,7 +98,7 @@ meson = {}
|
|||||||
ninja-build = {}
|
ninja-build = {}
|
||||||
m4 = {}
|
m4 = {}
|
||||||
#git = {} # suppressed: cascading rebuild; git not needed for boot/recovery
|
#git = {} # suppressed: cascading rebuild; git not needed for boot/recovery
|
||||||
htop = {}
|
#htop = {} # disabled: build failure in redoxer env (pre-existing)
|
||||||
#mc = {} # suppressed: C99 format warning errors in compilation
|
#mc = {} # suppressed: C99 format warning errors in compilation
|
||||||
|
|
||||||
# ── Build / packaging utilities ──
|
# ── Build / packaging utilities ──
|
||||||
@@ -473,23 +472,7 @@ data = ""
|
|||||||
directory = true
|
directory = true
|
||||||
mode = 0o755
|
mode = 0o755
|
||||||
|
|
||||||
[[files]]
|
|
||||||
path = "/etc/pcid.d/ihdgd.toml"
|
|
||||||
data = """
|
|
||||||
# redbear-live-mini: text-only image; override upstream ihdgd config with empty file
|
|
||||||
"""
|
|
||||||
|
|
||||||
[[files]]
|
|
||||||
path = "/etc/pcid.d/virtio-gpud.toml"
|
|
||||||
data = """
|
|
||||||
# redbear-live-mini: text-only image; override upstream virtio-gpud config with empty file
|
|
||||||
"""
|
|
||||||
|
|
||||||
[[files]]
|
|
||||||
path = "/etc/pcid.d/00_text_mode_gpu_mask.toml"
|
|
||||||
data = """
|
|
||||||
# redbear-live-mini: no display driver matched; class 0x03 devices are skipped
|
|
||||||
"""
|
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/lib/drivers.d/30-graphics.toml"
|
path = "/lib/drivers.d/30-graphics.toml"
|
||||||
@@ -508,6 +491,7 @@ path = "/etc/init.d/29_activate_console.service"
|
|||||||
data = """
|
data = """
|
||||||
[unit]
|
[unit]
|
||||||
description = "Activate console VT"
|
description = "Activate console VT"
|
||||||
|
default_dependencies = false
|
||||||
requires_weak = ["00_base.target"]
|
requires_weak = ["00_base.target"]
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
@@ -521,6 +505,7 @@ path = "/etc/init.d/30_console.service"
|
|||||||
data = """
|
data = """
|
||||||
[unit]
|
[unit]
|
||||||
description = "Console terminals"
|
description = "Console terminals"
|
||||||
|
default_dependencies = false
|
||||||
requires_weak = ["29_activate_console.service"]
|
requires_weak = ["29_activate_console.service"]
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
@@ -534,6 +519,7 @@ path = "/etc/init.d/31_debug_console.service"
|
|||||||
data = """
|
data = """
|
||||||
[unit]
|
[unit]
|
||||||
description = "Debug console"
|
description = "Debug console"
|
||||||
|
default_dependencies = false
|
||||||
requires_weak = ["29_activate_console.service"]
|
requires_weak = ["29_activate_console.service"]
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
@@ -541,3 +527,16 @@ cmd = "getty"
|
|||||||
args = ["/scheme/debug/no-preserve", "-J"]
|
args = ["/scheme/debug/no-preserve", "-J"]
|
||||||
type = "oneshot_async"
|
type = "oneshot_async"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/init.d/08_userland.target"
|
||||||
|
data = """
|
||||||
|
[unit]
|
||||||
|
description = "Userland services target"
|
||||||
|
requires_weak = [
|
||||||
|
"06_services.target",
|
||||||
|
"29_activate_console.service",
|
||||||
|
"30_console.service",
|
||||||
|
"31_debug_console.service",
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ path = "/usr/lib/init.d/10_smolnetd.service"
|
|||||||
data = """
|
data = """
|
||||||
[unit]
|
[unit]
|
||||||
description = "Network stack for redoxer"
|
description = "Network stack for redoxer"
|
||||||
requires_weak = ["00_pcid-spawner.service"]
|
requires_weak = ["00_driver-manager.service"]
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
cmd = "netstack"
|
cmd = "netstack"
|
||||||
|
|||||||
@@ -5,3 +5,30 @@ index bb512c60..3c3ed97d 100644
|
|||||||
@@ -4 +4 @@ default_dependencies = false
|
@@ -4 +4 @@ default_dependencies = false
|
||||||
-requires_weak = ["00_randd.service"]
|
-requires_weak = ["00_randd.service"]
|
||||||
+requires = ["00_randd.service"]
|
+requires = ["00_randd.service"]
|
||||||
|
diff --git a/init/src/scheduler.rs b/init/src/scheduler.rs
|
||||||
|
index 1bfc5c48..8db29281 100644
|
||||||
|
--- a/init/src/scheduler.rs
|
||||||
|
+++ b/init/src/scheduler.rs
|
||||||
|
@@ -120,2 +120,20 @@ if !hard_deps_met {
|
||||||
|
- init_warn(&format!("{}: hard dependency not met, skipping", job.unit.0));
|
||||||
|
- self.completed.insert(job.unit);
|
||||||
|
+ let unit = unit_store.unit(&job.unit);
|
||||||
|
+ let unit_id_str = job.unit.0.clone();
|
||||||
|
+ let all_deps_missing = unit.info.requires.iter().all(|dep| {
|
||||||
|
+ self.completed.contains(dep) || !unit_store.has_unit(dep)
|
||||||
|
+ });
|
||||||
|
+ if all_deps_missing {
|
||||||
|
+ init_warn(&format!("{}: hard dependency not met, skipping", unit_id_str));
|
||||||
|
+ self.completed.insert(job.unit);
|
||||||
|
+ continue 'a;
|
||||||
|
+ }
|
||||||
|
+ defer_count += 1;
|
||||||
|
+ self.pending.push_back(job);
|
||||||
|
+ if defer_count > self.pending.len() + self.completed.len() {
|
||||||
|
+ init_warn(&format!(
|
||||||
|
+ "{}: hard dependency not met after deferring, skipping",
|
||||||
|
+ unit_id_str
|
||||||
|
+ ));
|
||||||
|
+ self.completed.insert(UnitId(unit_id_str));
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
diff --git a/drivers/graphics/vesad/src/main.rs b/drivers/graphics/vesad/src/main.rs
|
||||||
|
index a4c07d1e..4db8c738 100644
|
||||||
|
--- a/drivers/graphics/vesad/src/main.rs
|
||||||
|
+++ b/drivers/graphics/vesad/src/main.rs
|
||||||
|
@@ -25,20 +25,60 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- let width = usize::from_str_radix(
|
||||||
|
- &env::var("FRAMEBUFFER_WIDTH").expect("FRAMEBUFFER_WIDTH not set"),
|
||||||
|
- 16,
|
||||||
|
- )
|
||||||
|
- .expect("failed to parse FRAMEBUFFER_WIDTH");
|
||||||
|
- let height = usize::from_str_radix(
|
||||||
|
- &env::var("FRAMEBUFFER_HEIGHT").expect("FRAMEBUFFER_HEIGHT not set"),
|
||||||
|
- 16,
|
||||||
|
- )
|
||||||
|
- .expect("failed to parse FRAMEBUFFER_HEIGHT");
|
||||||
|
- let phys = usize::from_str_radix(
|
||||||
|
- &env::var("FRAMEBUFFER_ADDR").expect("FRAMEBUFFER_ADDR not set"),
|
||||||
|
- 16,
|
||||||
|
- )
|
||||||
|
- .expect("failed to parse FRAMEBUFFER_ADDR");
|
||||||
|
- let stride = usize::from_str_radix(
|
||||||
|
- &env::var("FRAMEBUFFER_STRIDE").expect("FRAMEBUFFER_STRIDE not set"),
|
||||||
|
- 16,
|
||||||
|
- )
|
||||||
|
- .expect("failed to parse FRAMEBUFFER_STRIDE");
|
||||||
|
+ let width = match env::var("FRAMEBUFFER_WIDTH") {
|
||||||
|
+ Ok(v) => match usize::from_str_radix(&v, 16) {
|
||||||
|
+ Ok(n) => n,
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: failed to parse FRAMEBUFFER_WIDTH '{}': {} — exiting", v, e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: FRAMEBUFFER_WIDTH not readable: {} — exiting", e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ let height = match env::var("FRAMEBUFFER_HEIGHT") {
|
||||||
|
+ Ok(v) => match usize::from_str_radix(&v, 16) {
|
||||||
|
+ Ok(n) => n,
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: failed to parse FRAMEBUFFER_HEIGHT '{}': {} — exiting", v, e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: FRAMEBUFFER_HEIGHT not readable: {} — exiting", e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ let phys = match env::var("FRAMEBUFFER_ADDR") {
|
||||||
|
+ Ok(v) => match usize::from_str_radix(&v, 16) {
|
||||||
|
+ Ok(n) => n,
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: failed to parse FRAMEBUFFER_ADDR '{}': {} — exiting", v, e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: FRAMEBUFFER_ADDR not readable: {} — exiting", e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ let stride = match env::var("FRAMEBUFFER_STRIDE") {
|
||||||
|
+ Ok(v) => match usize::from_str_radix(&v, 16) {
|
||||||
|
+ Ok(n) => n,
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: failed to parse FRAMEBUFFER_STRIDE '{}': {} — exiting", v, e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: FRAMEBUFFER_STRIDE not readable: {} — exiting", e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
@@ -57 +97,8 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- let mut framebuffers = vec![unsafe { FrameBuffer::new(phys, width, height, stride) }];
|
||||||
|
+ let mut framebuffers = match unsafe { FrameBuffer::try_new(phys, width, height, stride) } {
|
||||||
|
+ Some(fb) => vec![fb],
|
||||||
|
+ None => {
|
||||||
|
+ eprintln!("vesad: failed to map primary framebuffer — exiting");
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
@@ -59,3 +106,2 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- //TODO: ideal maximum number of outputs?
|
||||||
|
- let bootloader_env = std::fs::read_to_string("/scheme/sys/env")
|
||||||
|
- .expect("failed to read env")
|
||||||
|
+ let bootloader_env = match std::fs::read_to_string("/scheme/sys/env") {
|
||||||
|
+ Ok(data) => data
|
||||||
|
@@ -63,3 +109,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- .map(|line| {
|
||||||
|
- let (env, value) = line.split_once('=').unwrap();
|
||||||
|
- (env.to_owned(), value.to_owned())
|
||||||
|
+ .filter_map(|line| {
|
||||||
|
+ line.split_once('=')
|
||||||
|
+ .map(|(env, value)| (env.to_owned(), value.to_owned()))
|
||||||
|
@@ -67 +113,6 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- .collect::<HashMap<String, String>>();
|
||||||
|
+ .collect::<HashMap<String, String>>(),
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: failed to read /scheme/sys/env: {} — continuing with primary framebuffer only", e);
|
||||||
|
+ HashMap::new()
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
@@ -96,4 +147,9 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- let event_queue: EventQueue<Source> =
|
||||||
|
- EventQueue::new().expect("vesad: failed to create event queue");
|
||||||
|
- event_queue
|
||||||
|
- .subscribe(
|
||||||
|
+ let event_queue: EventQueue<Source> = match EventQueue::new() {
|
||||||
|
+ Ok(q) => q,
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: failed to create event queue: {} — exiting", e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ if let Err(e) = event_queue.subscribe(
|
||||||
|
@@ -103,4 +159,6 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- )
|
||||||
|
- .unwrap();
|
||||||
|
- event_queue
|
||||||
|
- .subscribe(
|
||||||
|
+ ) {
|
||||||
|
+ eprintln!("vesad: failed to subscribe input events: {} — exiting", e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
+ if let Err(e) = event_queue.subscribe(
|
||||||
|
@@ -110,2 +168,5 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- )
|
||||||
|
- .unwrap();
|
||||||
|
+ ) {
|
||||||
|
+ eprintln!("vesad: failed to subscribe scheme events: {} — exiting", e);
|
||||||
|
+ daemon.ready();
|
||||||
|
+ std::process::exit(0);
|
||||||
|
+ }
|
||||||
|
@@ -113 +174,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- libredox::call::setrens(0, 0).expect("vesad: failed to enter null namespace");
|
||||||
|
+ if let Err(e) = libredox::call::setrens(0, 0) {
|
||||||
|
+ eprintln!("vesad: failed to enter null namespace: {} — continuing", e);
|
||||||
|
+ }
|
||||||
|
@@ -120 +183,7 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- .chain(event_queue.map(|e| e.expect("vesad: failed to get next event").user_data))
|
||||||
|
+ .chain(event_queue.map(|e| match e {
|
||||||
|
+ Ok(ev) => ev.user_data,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vesad: event error: {} — continuing", err);
|
||||||
|
+ Source::Scheme
|
||||||
|
+ }
|
||||||
|
+ }))
|
||||||
|
@@ -125,3 +194,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- scheme
|
||||||
|
- .tick()
|
||||||
|
- .expect("vesad: failed to handle scheme events");
|
||||||
|
+ if let Err(e) = scheme.tick() {
|
||||||
|
+ eprintln!("vesad: scheme tick error: {} — continuing", e);
|
||||||
|
+ }
|
||||||
|
@@ -132 +201,2 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
- panic!();
|
||||||
|
+ eprintln!("vesad: event loop ended unexpectedly — exiting");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
diff --git a/drivers/graphics/vesad/src/scheme.rs b/drivers/graphics/vesad/src/scheme.rs
|
||||||
|
index 5bf2be91..9ce6b89a 100644
|
||||||
|
--- a/drivers/graphics/vesad/src/scheme.rs
|
||||||
|
+++ b/drivers/graphics/vesad/src/scheme.rs
|
||||||
|
@@ -160 +160 @@ impl FrameBuffer {
|
||||||
|
- pub unsafe fn new(phys: usize, width: usize, height: usize, stride: usize) -> Self {
|
||||||
|
+ pub unsafe fn try_new(phys: usize, width: usize, height: usize, stride: usize) -> Option<Self> {
|
||||||
|
@@ -162 +162 @@ impl FrameBuffer {
|
||||||
|
- let virt = common::physmap(
|
||||||
|
+ let virt = match common::physmap(
|
||||||
|
@@ -170,2 +170,7 @@ impl FrameBuffer {
|
||||||
|
- )
|
||||||
|
- .expect("vesad: failed to map framebuffer") as *mut u32;
|
||||||
|
+ ) {
|
||||||
|
+ Ok(v) => v as *mut u32,
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("vesad: failed to map framebuffer at 0x{phys:X}: {e}");
|
||||||
|
+ return None;
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
@@ -175 +180 @@ impl FrameBuffer {
|
||||||
|
- Self {
|
||||||
|
+ Some(Self {
|
||||||
|
@@ -181 +186 @@ impl FrameBuffer {
|
||||||
|
- }
|
||||||
|
+ })
|
||||||
|
@@ -205 +210 @@ impl FrameBuffer {
|
||||||
|
- Some(Self::new(phys, width, height, stride))
|
||||||
|
+ Self::try_new(phys, width, height, stride)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
diff --git a/init.initfs.d/20_fbbootlogd.service b/init.initfs.d/20_fbbootlogd.service
|
||||||
|
index 5a8bf3c8..199c112a 100644
|
||||||
|
--- a/init.initfs.d/20_fbbootlogd.service
|
||||||
|
+++ b/init.initfs.d/20_fbbootlogd.service
|
||||||
|
@@ -6,0 +7,6 @@ cmd = "fbbootlogd"
|
||||||
|
+inherit_envs = [
|
||||||
|
+ "FRAMEBUFFER_ADDR",
|
||||||
|
+ "FRAMEBUFFER_WIDTH",
|
||||||
|
+ "FRAMEBUFFER_HEIGHT",
|
||||||
|
+ "FRAMEBUFFER_STRIDE",
|
||||||
|
+]
|
||||||
|
diff --git a/init.initfs.d/20_fbcond.service b/init.initfs.d/20_fbcond.service
|
||||||
|
index 8db3dfdb..e618b419 100644
|
||||||
|
--- a/init.initfs.d/20_fbcond.service
|
||||||
|
+++ b/init.initfs.d/20_fbcond.service
|
||||||
|
@@ -7,0 +8,6 @@ args = ["2"]
|
||||||
|
+inherit_envs = [
|
||||||
|
+ "FRAMEBUFFER_ADDR",
|
||||||
|
+ "FRAMEBUFFER_WIDTH",
|
||||||
|
+ "FRAMEBUFFER_HEIGHT",
|
||||||
|
+ "FRAMEBUFFER_STRIDE",
|
||||||
|
+]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
c[?7l[2J[0mSeaBIOS (version Arch Linux 1.17.0-2-2)
|
||||||
|
|
||||||
|
|
||||||
|
iPXE (http://ipxe.org) 00:02.0 C900 PCI2.10 PnP PMM+7EFD3DD0+7EF33DD0 C900
|
||||||
|
Press Ctrl-B to configure iPXE (PCI 00:02.0)...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Booting from Hard Disk...
|
||||||
|
Stage 1
|
||||||
|
00000022#007F 0000:C000
|
||||||
|
000000A1#007F 0FE0:C000
|
||||||
|
00000120#007F 1FC0:C000
|
||||||
|
0000019F#007F 2FA0:C000
|
||||||
|
0000021E#0038 3F80:C000
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
c[?7l[2J[0mSeaBIOS (version Arch Linux 1.17.0-2-2)
|
||||||
|
|
||||||
|
|
||||||
|
iPXE (http://ipxe.org) 00:02.0 C900 PCI2.10 PnP PMM+7EFD3DD0+7EF33DD0 C900
|
||||||
|
Press Ctrl-B to configure iPXE (PCI 00:02.0)...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Booting from Hard Disk...
|
||||||
|
Stage 1
|
||||||
|
00000022#007F 0000:C000
|
||||||
|
000000A1#007F 0FE0:C000
|
||||||
|
00000120#007F 1FC0:C000
|
||||||
|
0000019F#007F 2FA0:C000
|
||||||
|
0000021E#0038 3F80:C000
|
||||||
+41338
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,11 @@ template = "custom"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"redoxfs",
|
"redoxfs",
|
||||||
"ion",
|
"ion",
|
||||||
|
"driver-manager",
|
||||||
]
|
]
|
||||||
script = """
|
script = """
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
BINS=(
|
BINS=(
|
||||||
init
|
init
|
||||||
logd
|
logd
|
||||||
@@ -23,7 +26,6 @@ BINS=(
|
|||||||
lived
|
lived
|
||||||
nvmed
|
nvmed
|
||||||
pcid
|
pcid
|
||||||
pcid-spawner
|
|
||||||
rtcd
|
rtcd
|
||||||
vesad
|
vesad
|
||||||
)
|
)
|
||||||
@@ -71,8 +73,8 @@ mkdir -p "${COOKBOOK_BUILD}/initfs/lib/init.d"
|
|||||||
|
|
||||||
cp "${COOKBOOK_SOURCE}/init.initfs.d"/* "${COOKBOOK_BUILD}/initfs/lib/init.d/"
|
cp "${COOKBOOK_SOURCE}/init.initfs.d"/* "${COOKBOOK_BUILD}/initfs/lib/init.d/"
|
||||||
|
|
||||||
mkdir -pv "${COOKBOOK_BUILD}/initfs/lib/pcid.d"
|
mkdir -pv "${COOKBOOK_BUILD}/initfs/lib/drivers.d"
|
||||||
cp -v "${COOKBOOK_SOURCE}/drivers/initfs.toml" "${COOKBOOK_BUILD}/initfs/lib/pcid.d/initfs.toml"
|
cp -v "${COOKBOOK_SOURCE}/drivers/initfs-storage.toml" "${COOKBOOK_BUILD}/initfs/lib/drivers.d/00-storage.toml"
|
||||||
|
|
||||||
export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
|
export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
|
||||||
export CARGO_PROFILE_RELEASE_PANIC=abort
|
export CARGO_PROFILE_RELEASE_PANIC=abort
|
||||||
@@ -85,7 +87,7 @@ mkdir -pv "${COOKBOOK_BUILD}/initfs/bin" "${COOKBOOK_BUILD}/initfs/lib/drivers"
|
|||||||
for bin in "${BINS[@]}"
|
for bin in "${BINS[@]}"
|
||||||
do
|
do
|
||||||
case "${bin}" in
|
case "${bin}" in
|
||||||
init | logd | ramfs | randd | zerod | pcid | pcid-spawner | fbbootlogd | fbcond | inputd | vesad | lived | ps2d | acpid | bcm2835-sdhcid | rtcd | hwd)
|
init | logd | ramfs | randd | zerod | fbbootlogd | fbcond | inputd | vesad | lived | ps2d | acpid | bcm2835-sdhcid | rtcd | hwd | pcid)
|
||||||
cp -v "target/${TARGET}/${build_type}/${bin}" "${COOKBOOK_BUILD}/initfs/bin"
|
cp -v "target/${TARGET}/${build_type}/${bin}" "${COOKBOOK_BUILD}/initfs/bin"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -96,6 +98,7 @@ done
|
|||||||
|
|
||||||
cp "${COOKBOOK_SYSROOT}/usr/bin/redoxfs" "${COOKBOOK_BUILD}/initfs/bin"
|
cp "${COOKBOOK_SYSROOT}/usr/bin/redoxfs" "${COOKBOOK_BUILD}/initfs/bin"
|
||||||
cp "${COOKBOOK_SYSROOT}/usr/bin/ion" "${COOKBOOK_BUILD}/initfs/bin"
|
cp "${COOKBOOK_SYSROOT}/usr/bin/ion" "${COOKBOOK_BUILD}/initfs/bin"
|
||||||
|
cp "${COOKBOOK_SYSROOT}/usr/bin/driver-manager" "${COOKBOOK_BUILD}/initfs/bin"
|
||||||
|
|
||||||
ARCH="$(echo "${GNU_TARGET}" | cut -d - -f1)"
|
ARCH="$(echo "${GNU_TARGET}" | cut -d - -f1)"
|
||||||
RUSTFLAGS="$RUSTFLAGS -Ctarget-feature=+crt-static -Clink-arg=-nostartfiles -Clink-arg=-nostdlib" cargo \
|
RUSTFLAGS="$RUSTFLAGS -Ctarget-feature=+crt-static -Clink-arg=-nostartfiles -Clink-arg=-nostdlib" cargo \
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ patches = [
|
|||||||
"P30-acpid-graceful-scheme-exists.patch",
|
"P30-acpid-graceful-scheme-exists.patch",
|
||||||
"P31-xhcid-restore-interrupts.patch",
|
"P31-xhcid-restore-interrupts.patch",
|
||||||
"P32-acpid-graceful-boot.patch",
|
"P32-acpid-graceful-boot.patch",
|
||||||
|
"P33-vesad-graceful-boot.patch",
|
||||||
|
"P34-fbcond-fbbootlogd-env.patch",
|
||||||
]
|
]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
|
|||||||
@@ -185,6 +185,16 @@ 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");
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let preliminary_cpu_count = madt
|
let preliminary_cpu_count = madt
|
||||||
.iter()
|
.iter()
|
||||||
@@ -194,6 +204,9 @@ pub(super) fn init(madt: Madt) {
|
|||||||
MadtEntry::LocalApic(local) if !local_apic.x2 => {
|
MadtEntry::LocalApic(local) if !local_apic.x2 => {
|
||||||
u32::from(local.id) == me.get() || local.flags & 1 == 1
|
u32::from(local.id) == me.get() || local.flags & 1 == 1
|
||||||
}
|
}
|
||||||
|
MadtEntry::LocalApic(local) if local_apic.x2 && x2apic_fallback => {
|
||||||
|
u32::from(local.id) == me.get() || local.flags & 1 == 1
|
||||||
|
}
|
||||||
MadtEntry::LocalApic(_) => false,
|
MadtEntry::LocalApic(_) => false,
|
||||||
// xAPIC mode: cannot use 32-bit x2APIC IDs via 8-bit ICR.
|
// xAPIC mode: cannot use 32-bit x2APIC IDs via 8-bit ICR.
|
||||||
// Skip LocalX2Apic entries and use LocalApic exclusively.
|
// Skip LocalX2Apic entries and use LocalApic exclusively.
|
||||||
@@ -222,10 +235,15 @@ pub(super) fn init(madt: Madt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
MadtEntry::LocalApic(local) if local.flags & 1 == 1 && local_apic.x2 => {
|
MadtEntry::LocalApic(local) if local.flags & 1 == 1 && local_apic.x2 => {
|
||||||
// x2APIC mode: skip 8-bit LocalApic IDs; they conflict with
|
if x2apic_fallback {
|
||||||
// 32-bit x2APIC IDs. Dedup only among LocalX2Apic entries.
|
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 {
|
||||||
debug!("MADT: ignoring 8-bit LocalApic ID {} in x2APIC mode", local.id);
|
debug!("MADT: ignoring 8-bit LocalApic ID {} in x2APIC mode", local.id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
MadtEntry::LocalX2Apic(local) if local.flags & 1 == 1 && local_apic.x2 => {
|
MadtEntry::LocalX2Apic(local) if local.flags & 1 == 1 && local_apic.x2 => {
|
||||||
let id = local.x2apic_id;
|
let id = local.x2apic_id;
|
||||||
if !seen_apic_ids.insert(id) {
|
if !seen_apic_ids.insert(id) {
|
||||||
@@ -249,11 +267,142 @@ pub(super) fn init(madt: Madt) {
|
|||||||
// the BSP's 32-bit x2APIC ID. All entries would be treated as APs,
|
// the BSP's 32-bit x2APIC ID. All entries would be treated as APs,
|
||||||
// and SIPI would target the wrong processors. Skip them and rely
|
// and SIPI would target the wrong processors. Skip them and rely
|
||||||
// on LocalX2Apic entries exclusively.
|
// on LocalX2Apic entries exclusively.
|
||||||
if local_apic.x2 {
|
if local_apic.x2 && !x2apic_fallback {
|
||||||
debug!(
|
debug!(
|
||||||
" Skipping 8-bit LocalApic id={} (x2APIC active, using LocalX2Apic entries)",
|
" Skipping 8-bit LocalApic id={} (x2APIC active, using LocalX2Apic entries)",
|
||||||
ap_local_apic.id
|
ap_local_apic.id
|
||||||
);
|
);
|
||||||
|
} 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::<AtomicU8>()).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();
|
||||||
|
}
|
||||||
} else if u32::from(ap_local_apic.id) == me.get() {
|
} else if u32::from(ap_local_apic.id) == me.get() {
|
||||||
debug!(" This is my local APIC");
|
debug!(" This is my local APIC");
|
||||||
} else if ap_local_apic.flags & 1 == 1 {
|
} else if ap_local_apic.flags & 1 == 1 {
|
||||||
|
|||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Ensure cargo bin (cbindgen, rustup, etc.) is in PATH
|
||||||
|
case ":${PATH}:" in
|
||||||
|
*":$HOME/.cargo/bin:"*) ;;
|
||||||
|
*) export PATH="$HOME/.cargo/bin:$PATH" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
#qemu-system-x86_64 -m 8G -drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd -drive file=/home/kellito/Builds/RedBear-OS/build/x86_64/redbear-mini.iso,format=raw -device virtio-gpu-pci -enable-kvm -serial mon:stdio
|
||||||
|
|
||||||
|
qemu-system-x86_64 -d guest_errors -name "Red Bear OS x86_64" -device qemu-xhci -smp 4 -m 2048 -bios /usr/share/edk2/x64/OVMF.4m.fd -chardev stdio,id=debug,signal=off,mux=on -serial chardev:debug -mon chardev=debug -machine q35 -device ich9-intel-hda -device hda-output -device e1000,netdev=net0,id=nic0 -netdev user,id=net0 -nographic -drive file=build/x86_64/redbear-mini/harddrive.img,format=raw,if=none,id=drv0 -device nvme,drive=drv0,serial=NVME_SERIAL -drive file=build/x86_64/redbear-mini/extra.img,format=raw,if=none,id=drv1 -device nvme,drive=drv1,serial=NVME_EXTRA -enable-kvm -cpu host 2>&1 | tee /tmp/qemu-boot-full.log | tail -n 100
|
||||||
Reference in New Issue
Block a user