Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
cef718e
feat(anon): fetch and verify an Anyone consensus
eKisNonos Sep 21, 2026
b8a40b9
test(anon-proofs): the reference vectors, over the capsule's own source
eKisNonos Sep 21, 2026
3e26dca
fix(nym): reach the directory without clearnet dns
senseix21 Sep 27, 2026
0f55fc0
fix(nym): wait for a lease before fetching the directory
senseix21 Sep 27, 2026
4847ba2
fix(nym): sleep between lease polls instead of yielding
senseix21 Sep 27, 2026
f726f7f
fix(css): compare function-name prefixes as bytes, not str slices
eKisNonos Sep 28, 2026
04165b4
perf(css): answer positional selectors from a per-cascade sibling table
eKisNonos Sep 28, 2026
6936fa0
fix(layout): never place a float above an earlier float
eKisNonos Sep 28, 2026
e3f5681
fix(svg): end the path when a number follows closepath
eKisNonos Sep 28, 2026
129f23e
fix(net_core): always send a poll's first frames, so DHCP completes
eKisNonos Sep 28, 2026
1718733
feat(browser): choose Direct, Nym or Anyone; refuse an absent network
eKisNonos Sep 28, 2026
6085e79
Merge anon/anyone-consensus into net/browser-private-networks
eKisNonos Sep 28, 2026
b3c6515
fix(anon-proofs): declare alloc and import Weights so the tests build
eKisNonos Sep 28, 2026
377e34a
fix(net_nym): give each base58 key one spelling and refuse empty text
eKisNonos Sep 28, 2026
988df48
fix(net_nym): take requesters from the validator over TLS, on exits
eKisNonos Sep 28, 2026
47774f6
fix(net_anon): accept the relay's fixed-length NETINFO in the handshake
eKisNonos Sep 28, 2026
82ff1c5
fix(net_anon): build each circuit through the guard the link is open to
eKisNonos Sep 28, 2026
e781632
fix(net.sockets): number reads so a read whose reply was lost is resent
eKisNonos Sep 28, 2026
0aa086f
fix(browser): time a fetch from its last bytes, not from its start
eKisNonos Sep 28, 2026
b5e0c91
feat(browser): report byte counts when a response cannot be read
eKisNonos Sep 28, 2026
dd43e1f
fix(net_core): reset unread closed sockets and remove finished ones
eKisNonos Sep 28, 2026
1c1167d
fix(net_anon): retry the directory after 1 s and 5 s, not in minutes
eKisNonos Sep 28, 2026
5131206
fix(net_anon): scope relay cells to their circuit; credit stream SENDMEs
eKisNonos Sep 28, 2026
705fd2b
fix(layout): keep flex and grid bounds from crossing in narrow boxes
eKisNonos Sep 28, 2026
f69d7da
fix(net.socks5): resend a numbered answer whose reply was lost
eKisNonos Sep 28, 2026
d576e8e
fix(net_anon): run the transport at least every 200 ms under traffic
eKisNonos Sep 28, 2026
b49637b
feat(net_anon): serve SOCKS5 over IPC for the browser's Anyone network
eKisNonos Sep 28, 2026
11e0759
fix(net_anon): move from net.udp's port 4472 to 4484 and 4485
eKisNonos Sep 28, 2026
f1eb0e9
chore(net_anon): drop unused imports and test helpers
eKisNonos Sep 28, 2026
5c98179
fix(net_anon): advance directory fetches per turn instead of blocking
eKisNonos Sep 28, 2026
613001c
fix(browser): route Anyone to net.anon and word refusals per network
eKisNonos Sep 28, 2026
ced5720
feat(desktop): include net.anon in the desktop image
eKisNonos Sep 28, 2026
f425667
fix(net_anon): finish circuit hops as replies arrive, without waiting
eKisNonos Sep 28, 2026
33b2065
refactor: split net_nym, net_anon and mixnet files to 75 lines or fewer
eKisNonos Sep 28, 2026
240542f
chore(net_nym): drop the unused sync path and directory re-exports
eKisNonos Sep 28, 2026
46a6d1b
refactor: write line comments as /* */ outside the browser engine
eKisNonos Sep 28, 2026
d9db142
perf(net): carry 32 KiB per read with a 64 KiB window, end to end
eKisNonos Sep 28, 2026
780b708
perf(net_core): take every waiting frame per poll in one numbered batch
eKisNonos Sep 29, 2026
b447c4e
fix(net_core): hold frames a poll cannot send instead of dropping them
eKisNonos Sep 29, 2026
9ff99f9
sched: run the NIC drivers and net.core in the High band
eKisNonos Sep 29, 2026
cc434df
sched: preempt a lower band when a High process wakes
eKisNonos Sep 29, 2026
384efe6
net.sockets: answer stream connects when the handshake resolves
eKisNonos Sep 29, 2026
f0dd402
refactor: split the five branch files that ran past 75 lines
eKisNonos Sep 29, 2026
43eb8b2
process: hold interrupts off while the process table is written
eKisNonos Sep 29, 2026
0eaf99c
browser: parse HTML with a WHATWG tokenizer and tree builder
eKisNonos Sep 29, 2026
432bff4
browser: match CSS selectors to Selectors Level 4, bounded
eKisNonos Sep 29, 2026
4e7aad3
browser: frame HTTP by RFC 9112 and decode every WHATWG encoding
eKisNonos Sep 29, 2026
1b880b1
browser: stream image decoding and clip paint to the viewport
eKisNonos Sep 29, 2026
a4340c9
toolkit: cache rasterised glyphs and blend text with integers
eKisNonos Sep 29, 2026
0da1d05
browser: lay out from the root box, at true text size, with CSS math
eKisNonos Sep 29, 2026
3348da5
browser: lay out flex and grid items one by one, with real inlines
eKisNonos Sep 29, 2026
813d05b
browser: fetch in parallel and settle TLS in one pass
eKisNonos Sep 29, 2026
d099015
browser: keep empty pseudo slots small so large pages fit the heap
eKisNonos Sep 29, 2026
1dd9e11
browser: pick the Latin font subset and fall back per missing glyph
eKisNonos Sep 29, 2026
bce597b
foreign: end a supervisor's guests when the supervisor exits
eKisNonos Sep 29, 2026
5621b06
sched: give way to a woken High process only when the tick hit user mode
eKisNonos Sep 29, 2026
cb9916d
browser: name a measured page by its size, and use plain test ports
eKisNonos Sep 29, 2026
270bf90
browser: decode WOFF2, JPEG, WebP and ICO, and paint CSS colour fully
eKisNonos Sep 29, 2026
5b2bd38
Merge fix/nym-no-clearnet-dns: reach the Nym directory by pinned address
eKisNonos Sep 29, 2026
d4c3293
browser: paint nothing for a fully transparent image
eKisNonos Sep 29, 2026
75a4952
browser: cascade CSS nesting, @layer, @supports and tables by spec
eKisNonos Sep 29, 2026
cfdc158
browser: repaint only what changed, and give the address bar an editor
eKisNonos Sep 29, 2026
ef00ace
browser: bound selector matching by the bytes it reads
eKisNonos Sep 29, 2026
7cda192
browser: size ratio boxes, intrinsic atoms, grid and bidi runs by spec
eKisNonos Sep 29, 2026
de18fdb
browser: paint by nested stacking contexts, and size SVG without viewBox
eKisNonos Sep 29, 2026
dd150b2
gates: follow the BMP readability check to bmp/readable.rs
eKisNonos Sep 29, 2026
f7115fc
browser: style inline SVG with the page cascade and its variables
eKisNonos Sep 29, 2026
2dc1d06
browser: keep inline-block clips in place when the block moves
eKisNonos Sep 29, 2026
5ace1ea
browser: honour unset, revert, initial and inherit in the cascade
eKisNonos Sep 29, 2026
f2ec58d
browser: paint the visible part of text and images cut by an edge
eKisNonos Sep 29, 2026
f1d7ac7
browser: fetch up to 64 stylesheets a page, their bytes capped as before
eKisNonos Sep 29, 2026
4158b65
browser: break an inline around the block it holds
eKisNonos Sep 29, 2026
f9163aa
browser: draw url mask-image icons through their mask
eKisNonos Sep 29, 2026
5a93ca3
browser: size percentage table columns as a preference, drop position…
eKisNonos Sep 29, 2026
32f57f4
browser: lay floats in an inline out as a row, sized side by side
eKisNonos Sep 29, 2026
fea8a16
browser: draw list markers only for list items laid out as blocks
eKisNonos Sep 29, 2026
1c49741
browser: stop quirks-mode tables inheriting text alignment
eKisNonos Sep 29, 2026
53f0e98
browser: draw the rounded corners of a border
eKisNonos Sep 29, 2026
59dab2d
browser: round the content a rounded box clips
eKisNonos Sep 29, 2026
4c66e27
browser: evict off-screen images before on-screen ones
eKisNonos Sep 29, 2026
9172b08
browser: place background images by background-position and size
eKisNonos Sep 29, 2026
708e98f
browser: draw gradient mask-image layers
eKisNonos Sep 29, 2026
46b66fd
browser: bound images by min and max width and height
eKisNonos Sep 29, 2026
54b741f
browser: apply CSS filter color functions to what a box paints
eKisNonos Sep 29, 2026
b67c6ca
browser: place contained and covering images by object-position
eKisNonos Sep 29, 2026
3c4799e
browser: paint data: url backgrounds and masks
eKisNonos Sep 29, 2026
a074098
kernel: reserve attach and mmap VA only where no page is present
eKisNonos Sep 29, 2026
552839e
kernel: keep surface frames an attach still maps through munmap
eKisNonos Sep 29, 2026
ace8675
toolkit: keep the last four attached surfaces mapped
eKisNonos Sep 29, 2026
7c68f87
browser: draw variable fonts at their CSS weight
eKisNonos Sep 29, 2026
60818cd
kernel: read the page fault address without a canonical check
eKisNonos Sep 29, 2026
67d883d
kernel: park sleepers so a racing wake or tick cannot lose them
eKisNonos Sep 29, 2026
8262cdb
kernel: only try the state lock when waking from the tick or an IRQ
eKisNonos Sep 29, 2026
85d480c
toolkit: kern text by the face's GPOS pair adjustments
eKisNonos Sep 29, 2026
4b4c6ac
browser: commit every row a hovered-link repaint draws
eKisNonos Sep 29, 2026
62b8813
net.anon: state the hex check and SOCKS request type lint-clean
eKisNonos Sep 30, 2026
e75a1b9
tls: box the keyed handshake state and derive Default for readers
eKisNonos Sep 30, 2026
7dd39dc
browser: pass the engine proofs' stable clippy with -D warnings
eKisNonos Sep 30, 2026
8bfec37
browser: refuse a bad entity table row without panic!
eKisNonos Sep 30, 2026
7b407e7
evidence: count the 60 runnable proof crates in EVIDENCE.json
eKisNonos Sep 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,11 @@ jobs:
- theme_proofs
- tls_proofs
- audio_proto_proofs
- browser_http_proofs
- capsule_crypto_proofs
- aes_proofs
- anon_ntor_proofs
- anon_link_proofs
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4.2.2
with:
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ nonos-capsule-net-ntp = []
nonos-capsule-net-core = []
nonos-capsule-net-sockets = []
nonos-capsule-net-nym = []
nonos-capsule-net-anon = []
nonos-capsule-socks5 = []
nonos-capsule-linux = []
nonos-capsule-market = []
Expand Down Expand Up @@ -541,6 +542,7 @@ microkernel-desktop-base = [
"nonos-capsule-net-core",
"nonos-capsule-net-sockets",
"nonos-capsule-net-nym",
"nonos-capsule-net-anon",
"nonos-capsule-socks5",
"nonos-capsule-input-router",
"nonos-capsule-compositor",
Expand Down
1 change: 1 addition & 0 deletions mk/20-build.mk
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@ include userland/capsule_net_dns/Capsule.mk
include userland/capsule_net_ntp/Capsule.mk
include userland/capsule_net_sockets/Capsule.mk
include userland/capsule_net_nym/Capsule.mk
include userland/capsule_net_anon/Capsule.mk
include userland/capsule_socks5/Capsule.mk
include userland/capsule_wallpaper/Capsule.mk
include userland/capsule_attest/Capsule.mk
Expand Down
4 changes: 2 additions & 2 deletions scripts/baselines/stubs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ userland/nonos_qjs/build.rs:23
userland/nonos_qjs/build.rs:77
userland/nonos_qjs/src/lib.rs:26
userland/sdk/nonos_std/src/io/error.rs:28
userland/toolkit/src/image/bmp.rs:27
userland/toolkit/src/image/bmp.rs:35
userland/toolkit/src/image/bmp/readable.rs:22
userland/toolkit/src/image/bmp/readable.rs:26
userland/toolkit/src/image/gif/decoder.rs:58
userland/toolkit/src/image/gif/decoder.rs:68
userland/toolkit/src/image/gif/decoder.rs:69
Expand Down
74 changes: 74 additions & 0 deletions scripts/gen_encoding_index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
# NONOS Operating System
# Copyright (C) 2026 NONOS Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Build the browser's text-decoding tables from the Encoding Standard.
usage: gen_encoding_index.py <dir holding encodings.json and index-*.txt>
Both come from https://encoding.spec.whatwg.org/. Each table is a run of
little-endian u16 code points indexed by pointer (.idx files), 0 where the index has
none; Big5's plane-2 code points keep their low 16 bits and set a bit in
big5_astral.idx. gb18030 ranges are (pointer, code point) u32 pairs.
labels.txt holds one encoding per line, its name first (#n for the n-th
single-byte table), then its labels. SOURCE.txt records each index's
identifier and date."""
import json, struct, sys
from pathlib import Path

OUT = Path("userland/capsule_browser/src/browser/http/response/charset/index")
SRC = Path(sys.argv[1])


def index(name):
rows, meta = {}, []
for line in (SRC / f"index-{name}.txt").read_text(encoding="utf-8").split("\n"):
if line.startswith("# Identifier") or line.startswith("# Date"):
meta.append(line[2:])
elif line.strip() and not line.startswith("#"):
p, c = line.split("\t")[:2]
rows[int(p)] = int(c, 16)
SOURCES[name] = f"index-{name}.txt " + " ".join(meta)
return rows


def u16s(rows, size):
return b"".join(struct.pack("<H", rows.get(p, 0) & 0xFFFF) for p in range(size))


SOURCES, labels, single = {}, [], []
for group in json.loads((SRC / "encodings.json").read_text()):
for e in group["encodings"]:
name = e["name"].lower()
if group["heading"] == "Legacy single-byte encodings":
table = "iso-8859-8" if name == "iso-8859-8-i" else name
single.append(u16s(index(table), 128))
name = f"#{len(single) - 1}"
labels.append(" ".join([name] + e["labels"]))
OUT.mkdir(parents=True, exist_ok=True)
(OUT / "labels.txt").write_text("\n".join(labels) + "\n")
(OUT / "single_byte.idx").write_bytes(b"".join(single))
for name, file in [("jis0208",) * 2, ("jis0212",) * 2, ("gb18030",) * 2, ("euc-kr", "euc_kr")]:
rows = index(name)
(OUT / f"{file}.idx").write_bytes(u16s(rows, max(rows) + 1))
big5 = index("big5")
(OUT / "big5.idx").write_bytes(u16s(big5, max(big5) + 1))
bits = bytearray((max(big5) + 8) // 8)
for p, c in big5.items():
if c > 0xFFFF:
assert c >> 16 == 2, "Big5 leaves plane 2"
bits[p // 8] |= 1 << (p % 8)
(OUT / "big5_astral.idx").write_bytes(bytes(bits))
ranges = index("gb18030-ranges")
(OUT / "gb18030_ranges.idx").write_bytes(b"".join(struct.pack("<II", p, c) for p, c in sorted(ranges.items())))
(OUT / "SOURCE.txt").write_text("https://encoding.spec.whatwg.org/\n" + "\n".join(SOURCES.values()) + "\n")
7 changes: 1 addition & 6 deletions src/arch/x86_64/context/switch/kernel_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,7 @@ pub(super) fn resume_kernel_thread(pcb: &Arc<ProcessControlBlock>, pid: u32) {
let ctx = match INTERRUPT_SAVED_CONTEXTS.write().remove(&pid) {
Some(c) => c,
None => {
// No saved context to resume. Leaving the task Ready let the
// scheduler re-select it every iteration and fail to resume, which
// spins the core. Drop it from the run queue and park it so an
// unresumable task is not re-picked.
crate::process::scheduler::dispatch::remove_from_run_queue(pid);
*pcb.state.lock() = ProcessState::Sleeping;
super::retry_unsaved::retry_unsaved(pid);
return;
}
};
Expand Down
1 change: 1 addition & 0 deletions src/arch/x86_64/context/switch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod dispatch;
mod first_entry;
mod kernel_thread;
mod resume;
mod retry_unsaved;
mod validate_resume;

pub(crate) use dispatch::switch_to_user_pcb_x86_64;
27 changes: 27 additions & 0 deletions src/arch/x86_64/context/switch/retry_unsaved.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

/*
* No saved context yet: the task was woken on another CPU while the CPU it
* runs on had not finished yielding it. Parking it Sleeping with no deadline
* lost it for good, since the wake had already been spent. Park it on a one
* tick deadline instead, so the sweep retries once its context is saved,
* without the core re-picking it in a loop.
*/
pub(super) fn retry_unsaved(pid: u32) {
let retry_ms = crate::time::timestamp_millis().saturating_add(1);
crate::process::scheduler::dispatch::sleep_until(pid, retry_ms);
}
5 changes: 3 additions & 2 deletions src/hardware/broker/irq/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ pub fn on_vector(vector: u8) {
slot.overflow.fetch_add(1, Ordering::AcqRel);
}

/* Try only: code this ISR interrupted may hold the waiter's state. */
let waiter = slot.waiter.swap(0, Ordering::AcqRel);
if waiter != 0 {
crate::sched::wake_process(waiter);
if waiter != 0 && !crate::process::scheduler::dispatch::try_wake::try_wake_process(waiter) {
let _ = slot.waiter.compare_exchange(0, waiter, Ordering::AcqRel, Ordering::Relaxed);
}

crate::interrupts::apic::send_eoi();
Expand Down
26 changes: 26 additions & 0 deletions src/interrupts/handlers/exceptions/fault_address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use x86_64::registers::control::Cr2;

/*
* Raw read: `Cr2::read` panics when CR2 is not canonical, and QEMU TCG
* loads CR2 on a non-canonical access too. The fault is still handled
* (and a user process killed) instead of panicking the kernel.
*/
pub(super) fn fault_address() -> u64 {
Cr2::read_raw()
}
1 change: 1 addition & 0 deletions src/interrupts/handlers/exceptions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod debug;
pub mod device;
pub mod divide;
pub mod double_fault;
mod fault_address;
pub mod floating_point;
pub mod gpf;
pub mod machine_check;
Expand Down
3 changes: 1 addition & 2 deletions src/interrupts/handlers/exceptions/page_fault.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use crate::memory::addr::VirtAddr;
use x86_64::registers::control::Cr2;
use x86_64::structures::idt::InterruptStackFrame;

use super::context::{log_page_fault, ExceptionContext, PageFaultContext, PageFaultErrorCode};
Expand All @@ -32,7 +31,7 @@ use crate::security::observability::redact::redact_address;
pub fn handle(frame: InterruptStackFrame, error_code: u64) {
let _ctx = set_interrupt_context();

let accessed_address = Cr2::read().as_u64();
let accessed_address = super::fault_address::fault_address();
crate::arch::x86_64::diag::dump_trap(b"PF", &frame, Some(error_code), Some(accessed_address));
let exception = ExceptionContext::from_frame(&frame);
let error = PageFaultErrorCode::from_bits(error_code);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,32 @@ use crate::process::core::Priority;
const INTERACTIVE: [&str; 4] =
["driver.ps2_kbd0", "input_router", "compositor", "driver.virtio_gpu0"];

/*
* Capsules that move a frame between the card and TCP: the card drivers and
* the stack, which is where acknowledgements are made. They park the same way
* (`mk_irq_wait`, `mk_ipc_recv_from` with a timeout), so the band stays empty
* on an idle network. In the Normal band a wake queued behind every runnable
* capsule, and a capture of a 92 KB page load showed a median of 230 ms
* between the guest's acknowledgements, which set the pace of every
* slow-start round.
*
* `net.sockets` and `net.tcp` stay Normal: they wait for a connection by
* yielding in a loop, and two of those in this band could hold it for a whole
* connect timeout.
*/
const PACKET_PATH: [&str; 7] = [
"driver.virtio_net0",
"driver.e1000_0",
"driver.rtl8169_0",
"driver.rtl8139_0",
"driver.iwlwifi0",
"driver.rtl8821ce0",
"net.core",
];

/// Scheduling band a freshly installed capsule starts in.
pub(super) fn for_capsule(name: &str) -> Priority {
if INTERACTIVE.contains(&name) {
if INTERACTIVE.contains(&name) || PACKET_PATH.contains(&name) {
Priority::High
} else {
Priority::Normal
Expand Down
1 change: 1 addition & 0 deletions src/kernel_core/surface_registry/attach_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod forget_handle;
mod forget_pid;
mod lookup;
mod record;
pub mod snapshot;
mod state;

pub use forget::forget;
Expand Down
30 changes: 30 additions & 0 deletions src/kernel_core/surface_registry/attach_map/snapshot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use alloc::vec::Vec;

use crate::kernel_core::surface_registry::attach_map::state::ATTACHES;
use crate::kernel_core::surface_registry::types::SurfaceHandle;

/* True when a process other than `owner` holds an attach record for `handle`. */
pub fn has_foreign_holder(handle: SurfaceHandle, owner: u32) -> bool {
ATTACHES.lock().iter().any(|r| r.handle == handle && r.pid != owner)
}

/* (handle, base_va) of every attach record held by `pid`. */
pub fn records_of(pid: u32) -> Vec<(SurfaceHandle, u64)> {
ATTACHES.lock().iter().filter(|r| r.pid == pid).map(|r| (r.handle, r.base_va)).collect()
}
1 change: 1 addition & 0 deletions src/kernel_core/surface_registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod dump;
#[cfg(feature = "input-probe-inject")]
pub mod inject;
pub mod input_ring;
pub mod pin;
pub mod release;
mod ring_math;
pub mod share;
Expand Down
73 changes: 73 additions & 0 deletions src/kernel_core/surface_registry/pin/claim_owned.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use alloc::vec::Vec;

use crate::kernel_core::surface_registry::attach_map::snapshot::{has_foreign_holder, records_of};
use crate::kernel_core::surface_registry::table::SLOTS;
use crate::kernel_core::surface_registry::types::{encode_handle, SurfaceHandle};

use super::frames_of::frames_of;
use super::window::Window;

/*
* Surfaces `pid` registered from [addr, end) stop being attachable: their
* frame list leaves the slot. A fully unmapped window that another process
* still has attached becomes an orphan the registry frees later. A partly
* unmapped attached window stays live and keeps the unmapped frames
* allocated. Caller holds the gate, so no attach runs meanwhile.
*/
pub(super) fn claim_owned(pid: u32, addr: u64, end: u64, out: &mut Vec<Window>) {
let mut hits: Vec<(usize, SurfaceHandle, bool)> = Vec::new();
for (idx, entry) in SLOTS.lock().iter().enumerate() {
let Some(slot) = entry.as_ref() else { continue };
let base = slot.owner_base_va;
let win_end = base.saturating_add(slot.frames.len() as u64 * 4096);
if slot.owner_pid == pid && !slot.frames.is_empty() && base < end && addr < win_end {
let whole = addr <= base && win_end <= end;
hits.push((idx, encode_handle(idx as u32, slot.epoch), whole));
}
}
for (idx, handle, whole) in hits {
let attached = has_foreign_holder(handle, pid);
let mut slots = SLOTS.lock();
let Some(slot) = slots[idx].as_mut() else { continue };
if encode_handle(idx as u32, slot.epoch) != handle || slot.owner_pid != pid {
continue;
}
let base = slot.owner_base_va;
if !attached {
slot.frames = Vec::new();
} else if whole {
let mut w = Window::held(base, core::mem::take(&mut slot.frames));
w.orphan = Some((pid, handle));
out.push(w);
} else {
out.push(Window::held(base, slot.frames.clone()));
}
}
}

/* Windows `pid` attached from other owners that overlap [addr, end). */
pub(super) fn foreign_windows(pid: u32, addr: u64, end: u64, out: &mut Vec<Window>) {
for (handle, base) in records_of(pid) {
let Some((owner, frames)) = frames_of(handle) else { continue };
let win_end = base.saturating_add(frames.len() as u64 * 4096);
if owner != pid && base < end && addr < win_end {
out.push(Window::held(base, frames));
}
}
}
Loading
Loading