Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ members = [
"system/compositor",
"system/compositor/desktop",
"system/compositor/sprite",
"system/compositor/virtio-gpu",
"system/console",
"system/diskserver",
"system/filepicker",
Expand Down
18 changes: 11 additions & 7 deletions build/qemu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,10 @@ pub fn launch(opts: &Options) {
let arch = opts.arch;
// Before anything launches: every virtio machine carries the clipboard's
// channel, and every host's display is the one with its clipboard.
let display = display();
let display = display(shape.virtio_gpu);
let wanted = [
shape.virtio.then(|| toyos_build::vdagent::available(arch)),
(display != "cocoa").then(|| toyos_build::vdagent::gtk(arch)),
(!display.starts_with("cocoa")).then(|| toyos_build::vdagent::gtk(arch)),
];
if let Some(refused) = wanted.into_iter().flatten().find_map(Result::err) {
eprintln!("Error: {refused}");
Expand Down Expand Up @@ -358,11 +358,15 @@ fn iommu_device(arch: Arch) -> Option<&'static str> {

/// The display, with the host's clipboard: QEMU's own on macOS, and GTK's,
/// whose clipboard QEMU leaves off unless asked, everywhere else.
fn display() -> &'static str {
if cfg!(target_os = "macos") {
"cocoa"
} else {
"gtk,clipboard=on"
///
/// On macOS a virtio-gpu guest follows the window (`zoom-to-fit`), so a
/// resize reaches its mode; a firmware framebuffer cannot follow, and its
/// window stays 1:1 with it. GTK has virtio-gpu follow the window already.
fn display(follows: bool) -> &'static str {
match (cfg!(target_os = "macos"), follows) {
(true, true) => "cocoa,zoom-to-fit=on",
(true, false) => "cocoa",
(false, _) => "gtk,clipboard=on",
}
}

Expand Down
5 changes: 3 additions & 2 deletions images/system.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,13 @@ receives = ["power"]
# made in the session, and `vdagent` alone receives it. `input` is where a source
# of keys and pointer reports reaches the owner of input, which it is. `volume`
# is soundserver's master level and mute, which the volume keys set and no
# other row receives.
# other row receives. The display is a virtio-gpu function the compositor
# drives, where the machine has one, and the firmware's framebuffer otherwise.
[programs.compositor]
service = true
serves = ["compositor", "prompt", "clipboard", "input"]
receives = ["soundserver", "filepicker", "volume"]
devices = ["framebuffer", "keyboard", "mouse"]
devices = ["pci:03:80:00/1af4:1050", "framebuffer", "keyboard", "mouse"]
starts = ["terminal", "files", "/apps"]
login = true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,22 @@ opened: 2026-10-08

# A class claim's call in flight acts after the claim's release

A claim on a class this kernel still drives — the framebuffer, HDA audio — is
a per-class flag (`device::TAKEN`), and a call on one checks
the handle and then acts with nothing held across the two:

- `SYS_DEVICE_REG_READ` and `SYS_DEVICE_REG_WRITE` resolve the claim to
`RegTarget::Hda` under the process-data lock (`sys_device_reg`,
`kernel/src/syscall/device.rs`) and reach `drivers::hda::reg_write` after
it;
- `SYS_GPU_PRESENT`, `SYS_GPU_SET_CURSOR`, `SYS_GPU_MOVE_CURSOR` and
`SYS_GPU_SET_RESOLUTION` ask `holds_claim` and then call `crate::gpu`
(`kernel/src/syscall/dispatch.rs`).
A claim on a class this kernel still drives — HDA audio — is a per-class flag
(`device::TAKEN`), and a call on one checks the handle and then acts with
nothing held across the two: `SYS_DEVICE_REG_READ` and `SYS_DEVICE_REG_WRITE`
resolve the claim to `RegTarget::Hda` under the process-data lock
(`sys_device_reg`, `kernel/src/syscall/device.rs`) and reach
`drivers::hda::reg_write` after it.

The flag clears when the claim's last handle goes (`Claim`'s drop,
`kernel/src/device.rs`) and the next claim sets it again. So a call whose
claim a sibling thread closed after the check, and whose class another process
claimed before the act, writes a register of the audio controller, or presents
on the display, that the second process now holds.
claimed before the act, writes a register of the audio controller that the
second process now holds.

A claim on a PCI function, an ISA function or the ACPI fixed hardware does not
have this: its calls borrow what names the device from the claim under the
lock its release takes it with (`DeviceClaim::pci`, `DeviceClaim::isa`). That
borrow is no fix to copy here as it stands: `SYS_GPU_SET_RESOLUTION` allocates,
and the claim's lock is a spinlock.
lock its release takes it with (`DeviceClaim::pci`, `DeviceClaim::isa`).

**A poll has the same shape on these classes.** The watch a poll on an audio
claim or the mouse's registers on is the class's one `static`
Expand Down
12 changes: 7 additions & 5 deletions issues/a-domains-addresses-are-never-given-back.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ opened: 2026-09-03
`Domain::reserve` (`kernel/src/arch/x86_64/vtd/table.rs:213-224`) is a monotonic
bump: an address is never handed out twice, unmapped or not, and `unmap`
returns no range. A driver that maps and unmaps repeatedly therefore consumes
its domain's device address space for good. The display is the first such
driver — every `set_resolution` maps a new scanout and unmaps the old one, and
one boot with three mode changes walked `device=0x400000200000`,
`0x400000800000`, `0x400000a00000`, `0x400000e00000` with nothing between them
reusable. From `first_address = 1 << (bits - 2)` and the `1 << bits` ceiling, a
its domain's device address space for good. The kernel's virtio-gpu driver
was the first such driver — every mode change mapped a new scanout and
unmapped the old one, and one boot with three mode changes walked
`device=0x400000200000`, `0x400000800000`, `0x400000a00000`,
`0x400000e00000` with nothing between them reusable; the compositor lends its
scanouts now, in a window whose addresses are lent again
(`kernel/src/pcidev/mod.rs`). From `first_address = 1 << (bits - 2)` and the `1 << bits` ceiling, a
39-bit unit (the harness's `narrow` shape) has 196,608 leaves to give before a
mode change is refused with `ResourceExhausted`; the refusal is the right
answer to exhaustion and the wrong thing to reach by changing modes.
Expand Down
32 changes: 32 additions & 0 deletions issues/a-lent-scanout-is-one-contiguous-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
status: open
kind: defect
opened: 2026-10-14
---

# A lent scanout is one physically contiguous run

The compositor lends each virtio-gpu scanout as one region
(`system/compositor/src/display.rs`, `Claim::lend`), and a region the kernel
makes is one physically contiguous run of 2 MiB pages
(`kernel/src/object/shm.rs`, `SharedMemObject::create`). A mode at the
largest bound is 64 such pages; on a machine whose memory is fragmented there
may be no run that long, and the mode change is then declined by name and the
old mode stays, though the memory exists.

The window a claim lends in is placed first-fit too (`kernel/src/pcidev/mod.rs`,
`Bound::lend_room`). `toyos-desktop` sizes `LEND_WINDOW` for two scanouts at
the largest mode, which holds only while nothing splits it: boot at 1280x800
(2 MiB pages 0-1), flip to 7680x4320 (pages 2-65, freeing 0-1), then ask for
7680x4300 (63 pages) — the free runs are 2 and 62 pages and the flip is
declined by name, the old mode kept. Only modes past about half the window
reach it.

Virtio 1.2 §5.7.6.1 already takes a
scatter list ("the framebuffer doesn't need to be contignous in guest
physical memory"), and `RESOURCE_ATTACH_BACKING` takes one entry per run.

Owner: the memory track. Exit: a region backed by more than one run, lent as
one mapping in the function's domain or as one entry per run, and a mode
change on a machine with no run of its length served; and a lend window whose
free addresses serve any lend no larger than their total.
16 changes: 8 additions & 8 deletions issues/a-refused-framebuffer-reads-as-no-display.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ the bootloader's line above the second — printed by `query_gop`
metal-sim boot with `mute: true` and the T14 itself do not have. Nothing reaches
the kernel, so nothing reaches `/log`, the panel, or a crash report.

`main.rs:520` then branches on `kernel_args.gop_framebuffer != 0` and takes the
same arm for both. On this harness the second shape is rescued by
`virtio_gpu::init` claiming the same device and logging `GPU: using VirtIO`. On a
machine whose Blt-only display has no kernel driver — which the T14 is until an
Intel display engine driver exists — the branch falls to `main.rs:529` and both
shapes print `GPU: none found, running headless`. That inference is from the
branch, not measured: QEMU cannot stage a Blt-only GOP on a device this kernel
has no driver for.
`kernel/src/main.rs` then branches on `kernel_args.gop_framebuffer != 0` and
takes the same arm for both: both print `GPU: the firmware handed over no
framebuffer`, measured for the Blt-only shape on `Profile::HeadlessVirtioGpu`
(`the_compositor_drives_the_virtio_gpu`'s boot log). There the compositor's
`pci:03:80:00/1af4:1050` claim draws the desktop, but nothing in the kernel's record
tells the two shapes apart, and on a machine whose Blt-only display no
process drives — the T14, until an Intel display engine driver exists — the
record is all there is.

The distinction is the difference between "this machine has no panel" and "this
machine has a panel and something upstream refused it", which are different
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
status: open
kind: defect
opened: 2026-10-14
---

# A restarted display holder lends around its predecessor's scanouts

A virtio function on q35's root bus has no function-level reset and no power
management capability, so `pcidev`'s release cannot reset it: it keeps every
address the last holder's grants and lends were at as the function's residue
(`kernel/src/pcidev/mod.rs`, `Function::residue`), and a residue address is
never lent again (`Bound::lend_room`). A compositor started again on the same
virtio-gpu therefore lends its scanouts in `toyos_abi::pci::LEND_WINDOW` less
what the last one lent: after a holder at the largest mode, which lends two
scanouts' worth, the window has no room for a flip at that mode and it is
declined by name. QEMU keeps the old holder's resources, `max_hostmem`
included, until the next holder's own reset (§3.1.1, which
`toyos-virtio`'s `Offer::acknowledge` makes first).

The supervisor does not restart the compositor, so nothing reaches this
today.

Owner: pcidev, on the small-kernel track. Exit: a residue a holder's own
§3.1.1 reset retires — the function's status
read 0 after the reset is the proof it holds nothing — so the window is whole
again for the next holder.
47 changes: 24 additions & 23 deletions issues/every-driver-is-still-in-the-kernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ opened: 2026-08-02
The owner's target is that `virtio` does not appear anywhere in the kernel. The
strongest check on it is not the driver files but the ABI: no syscall name may
carry a NIC, GPU or audio device operation. `rg '^pub const SYS_(NIC|GPU|AUDIO)_'
sdk/abi/src/syscall.rs` is the check — the three `SYS_NIC_*` names went with
the virtio NIC driver, which is `userland/netd/src/virtio_net.rs` now, and the
`SYS_GPU_*` names are what is left.
sdk/abi/src/syscall.rs` is the check, and it finds none: the three `SYS_NIC_*`
names went with the virtio NIC driver, which is `system/netstack/src/virtio_net.rs`
now, and the four `SYS_GPU_*` names with virtio-gpu's, which is the
compositor's (`system/compositor/virtio-gpu`). The kernel's virtio code left is
its console's.

**Ordering ruling, and it is not negotiable: the IOMMU lands first and
completely, interrupt remapping included, and no driver leaves the kernel before
Expand All @@ -29,32 +31,31 @@ is not built.

What is left of the staged work:

1. **HDA and virtio-gpu, re-scoped.** `drivers/hda.rs` brings its device up
and gates soundserver's register access; `drivers/virtio_gpu.rs` is the only
`Gpu` whose `SYS_GPU_*` calls do anything, since GOP's are all no-ops. Each
leaves when its userland holder claims the function as `pci`, as netstack
and soundserver's virtio-sound driver do, retiring the `hda-audio` class,
its arms of `SYS_DEVICE_REG_READ`/`WRITE`, and `SYS_GPU_*`, which is an ABI
change. GOP stays: it is memory the loader hands over, and the panic console
paints it. HDA leaving feeds soundserver's DLL from a claim's record on
every machine, so that diff lands
1. **HDA, re-scoped.** `drivers/hda.rs` brings its device up and gates
soundserver's register access. It leaves when soundserver claims the
function as `pci`, as it does its virtio-sound function, retiring the
`hda-audio` class and its arms of `SYS_DEVICE_REG_READ`/`WRITE`. HDA
leaving feeds soundserver's DLL from a claim's record on every machine, so
that diff lands
`issues/soundservers-virtio-clock-can-take-a-period-one-notification-early.md`'s
exit.
exit. GOP stays: it is memory the loader hands over, and the panic console
paints it.
A virtio holder stands on `toyos-virtio`, which walks the capability list
too. Before a client ends its device on `UsedRefusal::Written` for a chain
the device only reads, whose bound is 0, what QEMU's device reports as
`len` on such a queue is measured: the NIC's transmit queue is the only
one read so far.
`iommu_virtio_platform`'s decline control, `declining_is_not_free`
(`tests/common/iommu.rs`), declines through the kernel's
`virtio-no-access-platform` actuator, which reaches only a virtio function
the kernel drives; with the NIC and virtio-sound in processes that is the
virtio-gpu `Profile::HeadlessVirtioGpu` adds for it, and virtio-gpu leaving
leaves the control nothing to decline. Exit for the control, in the diff
that moves virtio-gpu out: the decline is made where the features are
negotiated, `toyos-virtio`'s `Offer::accept`, for a function a process
drives, and the control reds when that function keeps `FEATURES_OK`;
`Profile::HeadlessVirtioGpu` goes in the same diff.
Done for virtio-gpu: the compositor drives it through a `pci:03:80:00/1af4:1050`
claim. Its cursor queue's `len` is measured: QEMU writes 0 for a chain
with a writable answer and for one without (PR #895's evidence, `pr-895`
at `7d47ad2b`). The decline control `declining_is_not_free` and the
`virtio-no-access-platform` actuator went with it, by the orchestrator's
ruling on PR #895, rather than the decline moving into `Offer::accept`:
with the kernel driving only its console the actuator reached nothing,
and what ToyOS relies on is `toyos-virtio`'s, host-tested — the bit
accepted wherever offered, and a device that drops `FEATURES_OK`
refused. `Profile::HeadlessVirtioGpu` stays, the suite's one machine whose
display a process drives.
2. Done: **BAR sizing and re-assignment onto 2 MiB boundaries** is
`pcidev::place_bar`, with the overlap refusal kept as the assertion that it
worked rather than as the mechanism.
Expand Down
11 changes: 7 additions & 4 deletions issues/the-desktop-draws-at-the-panels-scale.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ What is left:
drawing its chrome, text, icons and cursor at it and relaying windows out
on a change, terminal, filepicker and `files` at the scale, and the winit
fork's `scale_factor()`.
2. **virtio-gpu leaves the kernel** (stage 1 of
2. Done: **virtio-gpu left the kernel** (stage 1 of
`issues/every-driver-is-still-in-the-kernel.md`): the compositor drives it
through a `pci:1af4:1050` claim and follows the host window's display
events. Blocked on `issues/a-cargo-run-boot-claims-every-pcidev-slot.md`:
the Virtio profile's fifth claim is refused until a slot is free.
through a `pci:03:80:00/1af4:1050` claim, takes the mode the device prefers and
flips to the one each display event asks for, and
`the_compositor_drives_the_virtio_gpu` resizes QEMU's display over VNC and
reads the guest's new mode. The scale is chosen per event and drawn at by
nothing yet, as on GOP; `screen WxH` is still the window protocol's
`MSG_SET_RESOLUTION` until the `display` port of item 1.

Constraints already paid for:

Expand Down
20 changes: 0 additions & 20 deletions issues/the-gpu-driver-asserts-on-what-the-device-answers.md

This file was deleted.

14 changes: 0 additions & 14 deletions issues/the-second-scanout-buffer-is-read-by-nobody.md

This file was deleted.

4 changes: 2 additions & 2 deletions issues/toyos-runs-on-arm64.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,8 @@ Each stage names its exit; "measured" means a number from a run.
stage's SMMUv3 first. Stubbed on AArch64, each owned by the small-kernel
track, which moves the driver out of the kernel:
- a driver in this kernel is given no message (`irqchip::its::msi`), so
the kernel's xHCI (`virt`'s boot stick), HDA, virtio-console and
virtio-gpu drivers each refuse their function by name.
the kernel's xHCI (`virt`'s boot stick), HDA and virtio-console
drivers each refuse their function by name.
- `drivers::gop` refuses a scanout that is not whole 2 MiB pages of its
own, which a `ramfb` scanout carved out of RAM need not be.

Expand Down
4 changes: 2 additions & 2 deletions issues/volatile-composites-on-mmio-dma-structs.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ opened: 2026-08-20
`clippy::volatile_composites` (nursery) measured **10**, kernel-only, all in
driver code that hands a composite type straight to `write_volatile` or
`read_volatile` instead of touching each primitive field on its own. Two were
the NVMe driver's queue entries, which left the kernel with it; the rest:
the NVMe driver's queue entries and one virtio-gpu's EDID answer, which left
the kernel with their drivers; the rest:

- `drivers/xhci/wait/boot.rs:446` — `ErstEntry`.
- `drivers/xhci/mod.rs:560,593,1093` — `Trb`, three sites.
- `drivers/virtio.rs:414,487` — the virtio descriptor.
- `drivers/virtio_gpu.rs:276` — `RespEdid`.

The lint's own docs put the reason plainly: volatile ops are well-defined for
a primitive because the compiler must emit exactly the load/store the source
Expand Down
4 changes: 1 addition & 3 deletions issues/whether-a-region-may-be-lent-is-a-runtime-option.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ virtio-sound's pool, which left the kernel with its driver; the test is out of
the guest suite and staged as a T14 row in
`issues/the-guest-suite-runs-only-what-no-cheaper-tier-reaches.md`'s stage J,
where the kernel driver's pool it can refuse is HDA's. And the field's name
says nothing about lending: the virtio-gpu scanout and cursor carry `Some` and
are lendable today, which is the same authority their holder already has but
was decided by nobody.
says nothing about lending.

**Exit condition.** A region's kind is a type — owned pages, a kernel driver's
pool, an aperture — and `dma_map` takes only the owned-pages kind, so a region
Expand Down
Loading