Release history (technical)
What changed in v10.25
| # | Before (v10.24) | Now (v10.25) | Where |
|---|---|---|---|
| 1 | Right-clicking a file, a folder or a window title had no Properties entry (only desktop icons had a small Properties box), and icons could not be changed. | Files (right-click a file or folder) and window title bars have Properties. The classic Properties window shows the icon, name, type, full location, size and permissions; for folders it counts all files, subfolders and bytes inside; for apps it shows the state and window size. A grid of 12 icons (folder, document, code, picture, archive, settings, program, web page and four coloured folders) changes the icon at once, Default resets it. Choices are kept in /etc/nvx-icons and show in Files, the preview pane, the start menu, the taskbar and title bars. Right-click menus use the classic look. |
graphics/ui/desktop.c, graphics/ui/input.c, apps/system/file_explorer.c (section 11.13) |
| 2 | Settings used the rounded modern look. | Classic Settings: grey window, a white category list, a sunken search field, group boxes, tick boxes, raised buttons and button groups; every page and setting is kept. The Background page still sets any wallpaper and has a Plain grey button for the classic background. | apps/system/settings.c (section 11.13) |
| 3 | The ChatGPT app and the Internet start-menu category existed; the desktop showed six icons. | ChatGPT is removed (app, command and menu entry); Browser is under Utilities; the desktop has no icons. The calculator drawing code that lived in the ChatGPT file moved to calculator.c. |
graphics/ui/desktop.c, apps/office/calculator.c, units.list |
| 4 | Clicking a file on a USB stick always opened it in the text editor. | The file is copied into Downloads and opened with the right app (editor, image viewer, browser, video player, presentation viewer). Files up to 256 MB. | apps/system/file_explorer.c |
| 5 | Name lookups threw away every other packet that arrived while they waited (SYN-ACKs and data of open connections, each costing a 250 ms to 1 s retry), never resent a lost query (12 s wait), and kept only 8 names. A download longer than 5 s set off the freeze report. | Other packets are handed to the socket layer during lookups and ARP, queries are resent after 1 and 2 s, 64 names are cached for their TTL (60 s to 1 h), and the main loop is kept alive during downloads. about:perf shows the average connect time, SYN resends and the slowest new connection (lookup, connect and handshake); the network log has one line per connection. |
net/stack/tcpip.c, sys/linux/lxsock.c, apps/internet/webglue.c, apps/internet/browser.c (section 13.48) |
| 6 | Scripts in srcdoc frames never ran. |
The first srcdoc or src frame of a page runs in the frame engine even when it is hidden or tiny; postMessage works both ways (tested with a ping/pong page). |
web/prelude.js, web/js.c, apps/internet/webglue.c, build.sh (section 13.48) |
| 7 | A page was only shown after the whole HTML had downloaded. | While a large page downloads, the part received so far is shown twice (after 0.3 s and 1.5 s, without scripts and without network access, gzip and chunked pages included), then the full page loads as before. | apps/internet/webglue.c, apps/internet/browser.c, web/engine.c, lib/image/png.h (section 13.48) |
| 8 | replace with a regular expression and a function, Map/Set with string keys and JSON.stringify were slow. |
A direct path for standard regular expressions in replace (6 times faster than before, slightly faster than Node on the test), a mixed hash and load factor 1 for Map and Set (1.7 times faster), JSON.stringify writes strings, numbers and keys straight into the output (28% fewer instructions), and the garbage collector runs when memory doubles instead of growing by half. Results match Node on 43 replace and stringify cases. |
third_party/quickjs/quickjs.c (section 13.48) |
Tested: check.py; the four feature pages (146, 153, 37, 17) and the animation-event page; a slow chunked and gzip page and a srcdoc ping/pong page from a local server in QEMU; Properties, icon changes, the classic menus, Settings and the wallpaper in QEMU.
Not done: opening from a USB stick was not tried with a real stick in this build; only the first frame of a page runs scripts; no JIT and no change to the interpreter core; the real Google sign-in page is still untested here.
What changed in v10.24
| # | Before (v10.23) | Now (v10.24) | Where |
|---|---|---|---|
| 1 | The Performance tab of System Monitor showed thin bar graphs with only a total. | Filled line graphs on white with a dotted grid, like the classic system monitors: CPU usage shows Total (teal) and Kernel (red, busy time not spent in ring-3 programs); Memory usage shows Committed (all heap blocks, grey) and Allocated (used heap, teal) scaled to the committed size, plus the physical memory size. The heap is walked once per second instead of on every redraw. | apps/system/taskmgr.c, mem/heap_api.c (section 11.13) |
| 2 | The Terminal prompt was user@host:/home$ and the title user@host: /home; uname -a printed ? as the release because /sys/kernel/osrelease is not reachable from programs. |
In the classic style the prompt is host:~ $ (# for root) in green with the home folder shown as ~, the title is user@host:~, and uname -a reads the release from /proc/sys/kernel/osrelease. |
shell/terminal.c, shell/commands.c, fs/vfs/unixfs.c, usr/src/nvxbox.c (section 11.13) |
Tested: check.py; the Performance tab and the Terminal in QEMU.
Not done: no js command in the Terminal yet (an interactive JavaScript prompt like in the picture).
What changed in v10.23
| # | Before (v10.22) | Now (v10.23) | Where |
|---|---|---|---|
| 1 | fetch and XMLHttpRequest sent none of the extra headers a page set (such as X-Same-Domain), no Origin and no Sec-Fetch-* headers, and a page could not read response headers: getResponseHeader and response.headers were always empty. Sign-in pages that check these headers waited for a timeout or retried. |
Page headers are sent (forbidden names such as Host or Cookie are dropped), with Accept: */*, Origin for posts and cross-origin requests and Sec-Fetch-Site/Mode/Dest like Chromium; page loads send Sec-Fetch-* and Upgrade-Insecure-Requests, and a typed address sends no Referer. Response headers are kept for scripts (getResponseHeader, getAllResponseHeaders sorted and lower case without set-cookie, response.headers), statusText, upload events and the error path with readyState 4 behave like Chromium. |
apps/internet/webglue.c, apps/internet/browser.c, web/js.c, web/prelude.js (section 13.47) |
| 2 | animationend and transitionend events carried no animationName, propertyName or elapsedTime; transitionstart, transitionrun, transitioncancel and animationcancel were missing, so pages waiting for one of them never continued. |
All eight events fire with the right names, times and order (a transition with a delay starts after the delay; display: none, removal or a new value cancels it); Web Animations no longer start CSS transitions when they finish. A test page gives the same 26 events as Chromium. |
web/anim.c, web/engine.c, web/js.c, web/prelude.js (section 13.47) |
| 3 | An <iframe> never fired load; contentWindow and contentDocument of an empty frame were missing. |
Every frame fires load once per address; an empty or about:blank frame has a working contentDocument (open, write, close) and contentWindow (postMessage, parent, top). navigator.storage.persisted and getDirectory were added. |
web/prelude.js (section 13.47) |
| 4 | The classic desktop had 30-pixel title bars, a gradient wallpaper, large icons and a plain Terminal, Task Manager and browser frame. | A new classic look: 24-pixel title bars with small buttons, a solid grey desktop, small labelled desktop icons (Files, Terminal, Text Editor, Browser, Calculator, Settings), quick launch buttons and CPU and memory graphs on the taskbar, a start menu with categories (Internet added) and Settings, Help and Run. The Terminal has a menu bar, an inset black screen and a scroll bar; System Monitor has tabs, graphs in group boxes and a status bar; the browser has raised buttons, a white address field and a status bar. | graphics/ui/wm.c, graphics/ui/desktop.c, graphics/ui/input.c, shell/commands.c, shell/terminal.c, apps/system/taskmgr.c, apps/internet/browser.c (section 11.13) |
Tested: check.py; the three feature pages (146, 153 and 37 checks) and a class test page (17) still pass; the animation-event page and the request-header test server compared with Chromium; the desktop, start menu, Terminal, System Monitor and browser in QEMU.
Not done: scripts inside srcdoc and remote frames still do not run; no JIT and no change to the interpreter core; the slow connection setup seen on some sign-in pages was not found.
What changed in v10.22
| # | Before (v10.21) | Now (v10.22) | Where |
|---|---|---|---|
| 1 | The ClientHello's session ID field carried the same random bytes that were used as the X25519 private key, so anyone who could see the connection could work out the key exchange and read the HTTPS traffic (all versions since TLS was added). The HTTPS client spoke only TLS 1.3 with X25519: servers that only speak TLS 1.2, or only accept the P-256 curve, refused the connection; a HelloRetryRequest was not understood; a server KeyUpdate during a long download made the next record fail. | The session ID is now a hash of the public client random, and a check confirms it does not give the key share. TLS 1.2 with ECDHE (X25519 or P-256) and AES-128-GCM or ChaCha20-Poly1305, for ECDSA and RSA certificates: the chain and host name are checked as before, the ServerKeyExchange signature is verified (PKCS#1, PSS or ECDSA), extended master secret is used when the server supports it, and the TLS 1.3 downgrade marker is refused. TLS 1.3 also offers P-256: a HelloRetryRequest is answered with the group the server asks for (and its cookie), the server's point is checked to be on the curve, and KeyUpdates are followed. Tested against OpenSSL with seven version, cipher and key combinations and a changed signature; an undefined shift in X25519 was fixed. | lib/crypto/nvxtls.c, lib/crypto/nvxx509.c, lib/crypto/nvxcrypto.c (section 13.46) |
| 2 | querySelector/querySelectorAll parsed the selector on every call; classList.toggle/add/remove split and joined the class string in JavaScript; every style.x = ... ran two regular expressions; inline on... attributes were looked up on every element of an event's path. |
Parsed selectors and results are cached until the DOM changes (200 queries: 95 ms to 3 ms), class list changes run in C (15 000 toggles: 55 ms to 14 ms), style property names are cached (6 000 sets: 30 ms to 14 ms), and event dispatch skips the inline-handler lookup when no element has that attribute. | web/cssparse.c, web/js.c, web/dom.c, web/prelude.js (section 13.46) |
| 3 | There was no list of which JavaScript and DOM features work. | Three test pages (146 language and built-in checks, 153 DOM and Web API checks, 37 behaviour checks) compared with Chromium 141: all pass. New: URL.parse, Atomics.waitAsync, setHTMLUnsafe, Document.parseHTMLUnsafe, moveBefore, CSS.highlights and Highlight, ReportingObserver, a basic navigation object, aria* and role properties, hidden = "until-found", live getElementsByClassName/getElementsByTagName, named form.elements, real form validation (required, type=email/url, pattern, min/max/step, setCustomValidity, invalid events), canvas.toDataURL/toBlob as real PNG files, cssRules/insertRule/deleteRule on <style> sheets and constructed sheets, and new Image(w, h)/new Option(text, value) like Chromium. |
web/prelude.js, web/js.c (section 13.46) |
| 4 | The scroll wheel never reached pages as a wheel event, so maps, carousels and editors with their own scrolling did not react; nothing could scroll sideways (scrollLeft was always 0, so carousel arrows did nothing); scrolled boxes sent no scroll events and getBoundingClientRect ignored their scroll position; horizontal scrolling and the back button of 5-button mice were ignored. |
Each wheel step is sent to the page as a wheel event (deltaY, deltaX) and only scrolls if the page does not cancel it (listeners marked passive, and wheel and touch listeners on window, document and body, cannot cancel, like in Chromium); Shift+wheel and the horizontal wheel or two-finger sideways swipe give deltaX and scroll boxes with overflow-x: auto or scroll; scrollLeft (clamped like Chromium) and scrollWidth work; boxes send scroll events (also after scrollTop/scrollLeft changes from scripts, one per frame, and scrollTop is clamped like Chromium); positions inside scrolled boxes are right (checked against Chromium); PS/2 mice are switched to the 5-button IntelliMouse Explorer mode, and the back button (PS/2 and USB) goes back in the browser. |
web/engine.c, web/prelude.js, apps/internet/browser.c, graphics/ui/input.c, drivers/input/ps2/*.c, drivers/input/input.c, drivers/usb/xhci.c (section 13.46) |
| 5 | Array.prototype.sort was a quicksort that calls the compare function about n log n times even for data that is already in order. |
A natural merge sort (runs found and reversed, short runs extended by binary insertion, then merged): 200 000 objects in reverse order sorted in 17 ms instead of 130 ms; random data about 20% faster; still stable. | third_party/quickjs/quickjs.c (section 13.46) |
| 6 | The gui (or app) Terminal command froze the system: the ring-3 demo waits for a key inside the kernel and the freeze watchdog fired after 5 s. |
Waiting for a key and sleep from ring-3 programs pause the watchdog, so the demo shows its window and exits on a key. |
sys/native/syscall.c (section 13.46) |
Tested: check.py 107 of 107 (11 new TLS checks); the three feature pages on the host against Chromium; wheel events in the host test program and in QEMU; TLS 1.2 and TLS 1.3 with P-256 in QEMU with certificate checks; the gui demo in QEMU.
Not done: no JIT and no change to the interpreter core (plain loops and function calls are still 5 to 15 times slower than Chromium); horizontal scrolling of the whole page (only boxes scroll sideways); TLS 1.2 session resumption and RSA key exchange (without ECDHE) are not supported.
What changed in v10.21
| # | Before (v10.20) | Now (v10.21) | Where |
|---|---|---|---|
| 1 | AES-128-GCM, the cipher Google's servers choose, worked byte by byte with a bit-by-bit GHASH: about 6.6 MB/s on the build machine and far less on slower or emulated CPUs. The same AES also decrypts WPA2 Wi-Fi traffic in software. | Table-based AES (one 1 KB table) and a 4-bit table GHASH: 97 MB/s, the same results on 300 random cases and the standard test vector. HTTPS downloads and WPA2 Wi-Fi decryption both use it. | lib/crypto/nvxtls.c (section 13.45) |
| 2 | Receive windows were limited to 64 KB, which caps a download at 64 KB per round trip (about 1.3 MB/s at 50 ms). | TCP window scaling for outgoing connections with a 256 KB receive buffer, the peer's scale applied to its window, faster buffer moves, and 256 receive descriptors on e1000 and RTL8169. A 3.4 MB download in QEMU went from 17.7 to 23.6 MB/s. | sys/linux/lxsock.c, net/stack/tcpip.c, drivers/net/ethernet/*.c (section 13.45) |
| 3 | about:perf only measured the page load; work started later by timers, animation frames, events and scripts was not counted. A server that handles one connection at a time made the parallel fetches wait 20 s and the OS stopped responding. |
about:perf has an "After the page loaded" part (time since load, engine busy time split into timers and input, the longest single turn, scripts added later, network). Network waits keep the desktop running and can be stopped with Esc; parallel fetches give up after 4 s and skip a server whose connection failed. |
apps/internet/browser.c, web/engine.c, web/js.c, apps/internet/webglue.c (section 13.45) |
| 4 | The page engine had a 6 MB stack and JavaScript 3 MB, and garbage collection started at 256 KB. | 16 MB engine stack, 8 MB for JavaScript (deep framework recursion), first garbage collection at 16 MB. | apps/internet/webglue.c, web/js.c (section 13.45) |
Tested: in QEMU an AES-128-GCM-only HTTPS server (OpenSSL s_server), the keep-alive test page, a 3.4 MB download with curl, and a page with timers and late scripts in about:perf; check.py 96 of 96.
Not done: no JIT and no change to how fast the interpreter runs scripts; the real Google page is still untested here.
What changed in v10.20
| # | Before (v10.19) | Now (v10.20) | Where |
|---|---|---|---|
| 1 | Every request opened a new TCP connection and, for HTTPS, a new TLS handshake, on a single global TCP stream with an 8 KB window; the network card kept only 32 received frames, so bursts were dropped and resent. | Browser requests use the full TCP stack (64 KB windows, several connections at once) with keep-alive: up to 12 connections are kept per session and reused, each with its own TLS session; network cards keep 128 frames. A test page with 26 files over one connection instead of 26. | apps/internet/webglue.c, sys/linux/lxsock.c, drivers/net/ethernet/e1000.c, drivers/net/ethernet/rtl8169.c (section 13.44) |
| 2 | Scripts and style sheets in a page were downloaded one after the other. | Up to 32 scripts, style sheets and preloads found in the HTML are requested in parallel (up to 4 connections per server, 6 at a time) before the page is built. With 150 ms server latency the 26-file test page loads in 2.1 s (HTTP) and 2.4 s (HTTPS); with connection reuse alone it took 6.8 s. | web/engine.c, apps/internet/webglue.c (section 13.44) |
| 3 | The script cache and the file cache were lost at every reboot, and expired files were downloaded again in full. | Compiled scripts and cacheable files (up to 28 MB) are kept in webcache.bin on an installed system and loaded at the first page; expired files are checked with If-None-Match/If-Modified-Since and a 304 answer reuses the cached copy. |
apps/internet/webglue.c, core/power.c, sys/native/syscall.c (section 13.44) |
| 4 | A page's scripts were cut off after 6 s each and skipped after 8 s in total, and the whole load stopped after 45 s including download time, so heavy sign-in pages could lose their scripts on slow machines. | 30 s per script (plus 1 s per 100 KB), 90 s of scripts per page, and 120 s of engine time with download time not counted. | web/js.c, web/engine.c (section 13.44) |
| 5 | There was no way to see why a page was slow. | about:perf shows the last page's time split into network (lookups, connecting, handshakes, waiting, downloading, requests, cache hits, parallel fetches), scripts, compiling, styles, layout, painting and the pointer updates while busy; the status bar shows a one-line summary after each load. |
apps/internet/browser.c, web/engine.c (section 13.44) |
| 6 | The scroll wheel of PS/2 mice and of the QEMU/VMware pointer was read with the wrong sign, so turning it down scrolled up. | Both now follow the USB convention (positive is up), so the wheel scrolls pages, the editor, the Terminal and Settings the right way. | drivers/input/ps2/ps2.c, drivers/input/vm/vmmouse.c (section 13.44) |
| 7 | element.animate finished at once without animating; many interfaces were missing. |
Real Web Animations (keyframes, timing, easing, fill, direction, Animation and KeyframeEffect objects, getAnimations), Trusted Types, ElementInternals for form-associated custom elements, navigator.credentials and PublicKeyCredential (reports no passkeys), navigator.mediaDevices (no devices), a silent Web Audio graph, speechSynthesis (no voices), FontFace loading, OffscreenCanvas, performance marks, measures and observers, URLPattern, cookieStore, ClipboardItem, document.caretPositionFromPoint, elements reachable as window.someId, and about 100 more names. |
web/prelude.js, web/js.c, web/engine.c (section 13.44) |
Tested in QEMU against a local HTTP and HTTPS server with 150 ms added latency, on the host against Chromium for the new interfaces, with 120 DOM mutation seeds and seven app pages.
Not done: a JavaScript JIT (not built); the interpreter itself is unchanged (profile-guided compilation gave no measurable gain); the real Google sign-in, Gmail, Docs, YouTube and ChatGPT could not be loaded from the build machine; the desktop's other windows still do not redraw while a page's scripts run (measured: the pointer updates take well under 1% of the time).
What changed in v10.19
| # | Before (v10.18) | Now (v10.19) | Where |
|---|---|---|---|
| 1 | A click on a field covered by a decoration layer with pointer-events: none (the outline and label layers of Google's and Material sign-in fields) landed on the wrapper, so the field could not be clicked; stacked elements were hit in page order instead of z-index order; a field with height: 100% stayed one line high. |
Clicks pass through pointer-events: none and hidden elements to what is underneath, positioned elements are hit by z-index, and percentage heights of fields and other inline boxes resolve against their container. |
web/layout.c, web/engine.c (section 13.43) |
| 2 | Text fields, buttons and text areas had fixed sizes that did not follow the font size or the size, cols and rows attributes like Chromium. |
Chromium's form control sizes: content-box fields, width from size and the font, height from the line height, text areas with a scrollbar gutter. |
web/cssstyle.c, web/layout.c (section 13.43) |
| 3 | min-width/max-width: fit-content, min-content and max-content used the element's own width; line heights were 1 px taller than Chromium's for most fonts; ch and lh units were estimates. |
Keywords use the content size (also for flex items); normal line height uses Chromium's rounding; ch is the width of "0" and lh the real line height. |
web/layout.c, web/layout2.c, web/font.c, web/cssstyle.c (section 13.43) |
| 4 | The middle mouse button was ignored. | It reaches pages (auxclick), and a middle click on a link opens it in a new tab. |
drivers/input/input.c, graphics/ui/input.c, apps/internet/browser.c, web/engine.c (section 13.43) |
| 5 | Drag-and-drop worked only for draggable elements. |
Selected text on the page or in a field can be dragged into another field or a drop zone (moved from fields, copied from the page), and files dragged from the Files app onto a page arrive as DataTransfer.files or in an <input type=file>. |
web/prelude.js, web/engine.c, web/js.c, apps/internet/browser.c, graphics/ui/input.c (section 13.43) |
| 6 | Held keys repeated only if the keyboard hardware repeated them (and Caps Lock toggled again while held). | NoovexOS repeats held keys itself for PS/2 and USB keyboards (Slow, Normal or Fast in Settings > Language & region), pages see repeat: true, and Caps Lock no longer toggles while held. |
drivers/input/ps2/ps2.c, drivers/usb/xhci.c, net/http/http.c, arch/x86_64/interrupts/interrupts.c, core/init/setup.c, apps/system/settings.c (section 13.43) |
| 7 | A bare addEventListener(...) call at the top of a page script threw an error. |
It registers on window like in Chromium. |
web/prelude.js (section 13.43) |
Tested on the host against Chromium and in QEMU: clicking a field under an overlay, held keys (35 key events in 1.5 s, 34 marked as repeats), a middle click opening a link in a new tab, dragging a selected word into a field, sizing pages; file drops and field-to-field moves on the host.
Not done: the real Google sign-in page could not be loaded from the build machine, so it is untested; the default fonts still differ from Chromium's (serif default, monospace line height).
What changed in v10.18
| # | Before (v10.17) | Now (v10.18) | Where |
|---|---|---|---|
| 1 | Pages only got a click: no press, hold or release, so text could not be selected by dragging, sliders did not move, drag-and-drop did nothing, right-click opened the OS menu and double-click was never seen. | Real mousedown/mousemove/mouseup and pointer events with button, buttons and detail; drag to select text in fields and on the page (with highlight), double-click selects a word, triple-click all; range sliders drag; pointer capture; HTML5 drag-and-drop for draggable elements; contextmenu and dblclick reach the page, and a page that cancels contextmenu gets no OS menu. |
graphics/ui/input.c, web/engine.c, web/paint.c, web/prelude.js (section 13.42) |
| 2 | Shift+Tab, Shift+arrow selection, Ctrl+C/V/X, word moves and Shift+Space were missing in the browser; the page had no access to the OS clipboard. | Shift+Tab, Shift+arrows/Home/End selection, Ctrl+A/C/X/V with the OS clipboard (also navigator.clipboard, copy/cut/paste events and execCommand), Ctrl+arrow and Ctrl+Backspace word moves, Space/Shift+Space/Page Up/Page Down scrolling, Enter submits forms, long fields scroll with the caret. |
web/engine.c, web/js.c, web/prelude.js, apps/internet/webglue.c, apps/office/editor.c (section 13.42) |
| 3 | Only the US keyboard layout existed: on a Swedish or German keyboard Shift+7 gave & instead of /, and the keypad / and Enter did nothing. |
Six layouts (US, Swedish, German, French, Spanish, UK) with AltGr, Caps Lock and accented letters such as a-ring, a-umlaut, o-umlaut and sharp s, chosen in setup or in Settings > Language & region; the keypad / and Enter work everywhere. |
drivers/input/ps2/ps2.c, core/init/setup.c, apps/system/settings.c, graphics/ui/input.c (section 13.42) |
| 4 | Container query units (cqw) used the window width, @container rules were ignored, aspect-ratio ignored a set height, tables gave spare width to fixed columns. |
@container with size features and named containers, cq* units from the real container, aspect-ratio from height (also in flex rows and with auto), table spare width to auto columns; a sizing test page equals Chromium except font-dependent rows. |
web/cssparse.c, web/cssstyle.c, web/layout.c, web/layout2.c (section 13.42) |
| 5 | Many newer JavaScript interfaces were missing. | CompressionStream/DecompressionStream (gzip, deflate, raw), scheduler and TaskController, window.event, navigator.share/userActivation/connection, CloseWatcher, document.getAnimations and about 60 more event classes and interface names. |
web/prelude.js, web/js.c (section 13.42) |
Tested on the host against Chromium and under AddressSanitizer, and in QEMU with the mouse and keyboard: word double-click, slider drag, page text drag selection, drag-and-drop, a cancelled right-click, Shift+End with Ctrl+C and Ctrl+V, Shift+Tab and typing / from both / keys.
Not done: a JavaScript JIT; min-width: fit-content and a few font-dependent line heights still differ from Chromium; middle-click is not passed from the OS mouse driver; key repeat comes from the keyboard hardware.
What changed in v10.17
| # | Before (v10.16) | Now (v10.17) | Where |
|---|---|---|---|
| 1 | Clicking GitHub's Code button could freeze NoovexOS; elements inside hidden parts of a page kept stale style pointers; long engine loops never let the desktop run. | Hidden and removed subtrees are cleared; selector matching stops early; querySelectorAll no longer skips nodes; getElementById is cached; slow engine work stops with "This page stopped" after 45 s or Escape. |
web/dom.c, web/cssstyle.c, web/cssparse.c, web/engine.c (section 13.41) |
| 2 | An empty attribute such as class="" or value="" got the previous attribute's text. |
Parsed correctly; 150 random selectors give Chromium's results. | web/dom.c (section 13.41) |
| 3 | popover and showModal were stubs. |
Popover API with light dismiss and toggle events, popovertarget buttons, :popover-open; modal dialogs with :modal, Escape and requestClose. |
web/prelude.js, web/cssparse.c, web/cssstyle.c (section 13.41) |
| 4 | Ctrl+A in the address bar sometimes typed "a"; closing a background tab reloaded the current one; long Back addresses and links were cut. | Fixed. | apps/internet/browser.c, include/nvx/apps/nwe_api.h, web/nwe.h (section 13.41) |
Tested on the host against Chromium and under AddressSanitizer: 540 random DOM mutation pages, 150 selectors, a GitHub-style page with the Code menu.
What changed in v10.16
| # | Before (v10.15) | Now (v10.16) | Where |
|---|---|---|---|
| 1 | WebAssembly was undefined, so every site or library that ships .wasm (image and video codecs, SQLite, physics, many sign-in and editor tools) failed. |
A real WebAssembly engine (wasm3 interpreter) with the full JavaScript API: Module, Instance, Memory, Table, Global, validate, compile, instantiate, the streaming variants and the three error types; real programs give the same output as Chromium. |
third_party/wasm3, web/wasm.c, web/prelude.js, build.sh (section 13.40) |
| 2 | navigator.serviceWorker and caches were missing. |
Service Workers with registration, install and activate, fetch interception for fetch(), clients, messages, registrations kept across visits, and the Cache API. |
web/prelude.js, web/js.c (section 13.40) |
| 3 | A wasm module with an endless loop would have frozen the browser. | Endless wasm code is stopped after 20 s or when Escape is held, and the desktop keeps drawing meanwhile. | third_party/wasm3/m3_exec.h, web/wasm.c (section 13.40) |
| 4 | Date.now() moved in whole seconds and performance.now() in 10 ms steps; Ctrl+A in the address bar typed an a. |
Millisecond Date.now(), 0.1 ms performance.now(); Ctrl+A selects the address. |
fs/vfs/dirtree.c, web/js.c, apps/internet/webglue.c, apps/internet/browser.c (section 13.40) |
Tested against Chromium on the host and in QEMU: a 40-check WebAssembly page, the c-ray and mal WASI programs (byte-identical output), CoreMark (3,584 on the host interpreter, V8 30,917) and a Service Worker page including a reload that starts controlled.
Not done: a JavaScript JIT (estimate 15-30 sessions) and a WebAssembly JIT; the real Gmail, Google Docs, YouTube and claude.ai remain untested here.
What changed in v10.15
| # | Before (v10.14) | Now (v10.15) | Where |
|---|---|---|---|
| 1 | Custom elements were half done: no upgrade constructor, no connectedCallback on insertion, no disconnectedCallback or attributeChangedCallback. Shadow-root styles from adoptedStyleSheets were lost on the first render. YouTube-, GitHub- and Lit-style components rendered unstyled or not at all. |
Full custom element lifecycle; adopted stylesheets stored on the shadow root; isConnected through shadow trees; mutation events also while inline scripts run at load. |
web/dom.c, web/js.c, web/prelude.js, web/cssparse.c (section 13.38) |
| 2 | Any class change, hover or focus restyled and laid out the whole page; class and id changes even rebuilt the rule index. | Only the changed subtree is restyled, in place, and layout is skipped when only colours or other paint properties changed; CSS rules with only attribute selectors are bucketed. About 8x faster per interaction on a 1,500-row inbox. | web/cssstyle.c, web/engine.c, web/js.c, web/cssparse.c (section 13.38) |
| 3 | SVG text, tspan, foreignObject and friends were HTML elements; no getBBox; D3 axes and Mermaid diagrams broke; SVG with a viewBox was 150 px high. |
Real SVG elements and geometry APIs; inherited text attributes, tspan, em offsets, <style> inside SVG, foreignObject labels; viewBox sizing like Chromium. D3 and Mermaid render like Chromium. |
web/svg.c, web/prelude.js, web/layout.c (section 13.38) |
| 4 | execCommand had only plain-text commands; innerText ran paragraphs together; table rows/cells missing; closed <details> showed content styled with display. |
Rich text commands with undo/redo and queryCommandState; spec-style innerText; table APIs; closed <details> always hides its content. |
web/prelude.js, web/js.c, web/cssstyle.c (section 13.38) |
| 6 | Every visit parsed every script again; tab switches and Back downloaded everything again; indexedDB and Worker were undefined; keyboard shortcuts had no Ctrl/Shift/Alt and no keyup; SVG arrowheads were missing; emoji showed as "?". |
Bytecode cache for scripts (reloads 2-5x faster on the host, the React chat app's engine time in QEMU 1.9 s to 0.58 s), HTTP memory cache, in-memory IndexedDB, same-thread Web Workers, full modifier keys and keyup, SVG markers, 1,413 colour emoji. |
web/js.c, apps/internet/webglue.c, apps/internet/browser.c, web/prelude.js, web/svg.c, web/font.c, assets/emoji32.bin (section 13.39) |
| 5 | Pages with <pre> text and Windows line endings froze the browser; a <link> without href could crash it; huge font sizes overflowed. |
Fixed, found by a sanitizer sweep over 260 real documents, 18 test pages and 557 fuzzed pages. | web/layout.c, web/util.c, web/font.c, web/cssstyle.c (section 13.38) |
Tested against Chromium on the host with real libraries (React 19, KaTeX, highlight.js, Markdown, Headless UI, TanStack Virtual, D3, Mermaid) in a claude.ai-style chat app, a GitHub-style repository page, a YouTube-style grid, a Docs-style editor and chart pages; in QEMU the chat app (typing and sending), the YouTube-style grid and the GitHub-style page run in the Browser.
Not done: the real Gmail, Google Docs, YouTube and claude.ai were not reachable for testing and still need a much faster JavaScript engine (JIT, estimate 15-30 sessions) and more APIs (Service Workers, WebAssembly, real threads for Workers, IndexedDB kept across reboots).
What changed in v10.14
| # | Before (v10.13) | Now (v10.14) | Where |
|---|---|---|---|
| 1 | crypto.subtle was empty, so pages that hash or sign data (OAuth PKCE sign-in, HMAC, password key derivation) failed. |
digest (SHA-256, SHA-1), HMAC importKey/sign/verify/generateKey, PBKDF2 deriveBits, exportKey. |
web/prelude.js (section 13.37) |
| 2 | structuredClone lost Date, Map, Set, typed arrays and cycles. |
Real structured clone with cycles and DataCloneError. |
web/prelude.js (section 13.37) |
Tested: hash, HMAC and PBKDF2 results equal Python's; a test page in the browser in QEMU shows SHA-256, HMAC and structuredClone correct.
JavaScript speed: compiler flags (-O3, -march=x86-64-v2) gave no measurable gain over -O2 and fast opcode dispatch is already on; a real speed-up needs an inline cache or a JIT (estimate 15-30 sessions for a JIT).
What changed in v10.13
| # | Before (v10.12) | Now (v10.13) | Where |
|---|---|---|---|
| 1 | The second page load of a session (Reload, a new address, switching tabs) often showed a white page, so with several tabs most websites seemed not to load. | Fixed: the toolbar redraw no longer wipes the load request before it reaches the engine, and the network reader no longer ends a response early when a packet was missed. | apps/internet/browser.c, net/http/http.c (section 13.36) |
| 2 | Plain grey toolbar, square tabs, the address cut at the start ("tp://..."). | New look: tab strip with site initials and joined active tab, round back/reload/star buttons, rounded address field showing a lock, the host in dark text and the path in grey, pill buttons, a busy bar. | apps/internet/browser.c (section 13.36) |
Tested in QEMU: reload twice, then three tabs with different pages switched back and forth; every page showed correctly.
Not done in this release: the Rust toolchain inside NoovexOS and a bigger kernel image. The real toolchain is about 358 MB, which needs a large writable disk filesystem rather than a bigger kernel image (see v10.11 below); estimate 4-8 sessions.
What changed in v10.12
| # | Before (v10.11) | Now (v10.12) | Where |
|---|---|---|---|
| 1 | <iframe> showed an "Embedded page" box. |
The first iframe of a page shows and runs its real content in a second copy of the web engine linked into the kernel: its own document, JavaScript, timers, clicks, cookies and navigation. Hidden iframes (display: none, 0x0) load and run too. |
build.sh (web2_all.o), apps/internet/webglue.c, apps/internet/browser.c, web/paint.c, web/js.c, web/prelude.js (section 13.35) |
| 2 | iframe.contentWindow was null, and window.parent inside a frame was the frame itself. |
contentWindow and parent/top are window proxies; postMessage crosses between page and frame with the correct origin and source, and target origins are checked. |
web/prelude.js, apps/internet/webglue.c |
Tested in QEMU: a page with an iframe from the test server - the frame's content drawn inside the page, the frame's "ready" message received by the page (isTop: false, right origin, event.source === iframe.contentWindow), the page's reply shown inside the frame, a click on a button inside the frame changing that button and sending a token message to the page; and a page that creates a hidden (display: none) iframe whose script sends a token to the page.
This is what challenge pages such as Cloudflare's need in order to run (their check runs in an iframe and reports to the page with postMessage). Whether Cloudflare then accepts the browser is decided by Cloudflare's own tests and could not be tried from the build machine, which has no route to it.
Limits: one live iframe per page, no keyboard input or wheel scrolling inside it, contentDocument is null, and postMessage data must survive JSON.
What changed in v10.11
| # | Before (v10.10) | Now (v10.11) | Where |
|---|---|---|---|
| 1 | WebSocket was a stub that failed at once. |
Real WebSockets (RFC 6455), ws:// and wss://: masked client frames, text, binary, ping/pong, fragmentation, close codes, up to 8 MiB a message, the handshake with Sec-WebSocket-Accept, cookies and Origin. The protocol runs in the kernel; the JavaScript side polls on a timer. |
apps/internet/websock.c (new), web/js.c, web/prelude.js (section 13.33) |
| 2 | TLS could do one request per connection (tls_get). |
A TLS session API (tls_connect_sess, tls_sess_write, tls_sess_read) keeps a connection open for many messages, which is what wss:// needs; tls_get is now a wrapper over it, so HTTPS is unchanged. |
lib/crypto/nvxtls.c (section 9.9.2) |
| 3 | window.open(url, name, features) navigated the current page away and returned null. |
It returns a detached window with a working postMessage/location/close that reports closed, so popup-based "Sign in with Google" style flows fall back to a full-page redirect (which the browser can follow, keeping cookies). postMessage structure-clones its data and carries the origin. |
web/prelude.js (section 13.34) |
Tested in QEMU: a WebSocket echo page (ws.html) against a local server reached as 10.0.2.2:8090 - open, text echo, binary echo, ping/pong and a clean close.
Not done, with the reason:
rustc/cargorunning inside NoovexOS: attempted and blocked. The smallest usefulrustc(the compiler pluslibrustc_driver.soandlibLLVM.so) is about 358 MB. NoovexOS embeds its sysroot, xz-compressed, inside the kernel image, which must stay under about 101 MiB; adding the pack made the link fail with "kernel image is too large". The only external medium the kernel mounts, a FAT stick at/mnt/usb, holds at most 32 files (ufs[32]) and is read-only with every file loaded into RAM, so the toolchain cannot live there either. Running the real toolchain needs a new large, writable, lazily-loaded disk filesystem first (estimate 4-8 sessions), after which whetherrustcitself runs under the Linux ABI is a second open question.- Getting past Cloudflare's check for claude.ai: the check runs inside an
<iframe>served fromchallenges.cloudflare.com, and the browser does not render<iframe>content yet (section 13.15). That is the real blocker, roughly 3-6 sessions, and it cannot be tested from the build machine, which has no route to Cloudflare or claude.ai.
What changed in v10.10
| # | Before (v10.09) | Now (v10.10) | Where |
|---|---|---|---|
| 1 | <canvas> drew nothing: getContext('2d') returned an object whose methods did nothing. |
A real Canvas 2D context: paths, arcs, curves, fill and stroke with joins, caps and dashes, rectangles, clearRect, linear and radial gradients, transforms, save/restore, text, measureText, drawImage, getImageData/putImageData, Path2D (also from SVG path strings), ImageData, rectangular clip. Anti-aliased by the same method as the font rasterizer. Pages that animate with requestAnimationFrame work. |
web/canvas.c (new), web/js.c, web/prelude.js, web/paint.c (section 13.31) |
| 2 | No Rust anywhere. | Rust is part of the build. The Taffy layout library (Rust, MIT) is compiled no_std into the web engine and does CSS Grid layout; the C grid code stays as a fallback. A build check rejects Rust objects that use the red zone or need unknown symbols. |
rust/nvxlayout, third_party/taffy, rust/shims/smallvec, tools/build_rust.sh, tools/rust_check.py, web/layout2.c (Part 1 section 4.9, section 13.32) |
| 3 | A grid with min-height and an fr row left the row at its content height. |
Taffy gets the container's min-height/max-height, so the fr row fills the grid. |
web/layout2.c |
| 4 | No Rust programs. | rustinfo, a normal Rust std program (threads, channels, files, time), is in /usr/bin; Rust programs built for x86_64-unknown-linux-gnu with crt-static run unchanged. |
rust/rustinfo, tools/build_rust_apps.sh |
| 5 | Canvas natives check every number from the page (NaN, infinity, huge sizes, bad gradient data) before using it; a 400-call random test and the canvas pages ran clean under AddressSanitizer and UBSan on the host. | web/canvas.c, web/js.c |
Tested in QEMU: rustinfo (4 threads, 17984 primes below 200000, file write and read back), the canvas test page, a requestAnimationFrame canvas animation, and a grid page (200px / 1fr / 2fr columns, auto-fill, spans, alignment) in the Browser.
Found and not fixed: in the Browser, the second page load in a session (Reload, a new address, or an about: page) shows a white page. It happens in v10.09 too. The second load gets the same 96-byte body and content type, the engine returns 0 from the paint with the right document height, and the pixel it leaves in the scene buffer is white. The host harness (two loads with paints and ticks in between, with the kernel's own allocator and arena reset, at -O2) renders the second load correctly, and resetting MXCSR and the x87 control word before each engine call did not change it, so the cause is something that differs between the kernel and the host build; it is listed as the first thing to fix next.
What changed in v10.09
| # | Before (v10.08) | Now (v10.09) | Where |
|---|---|---|---|
| 1 | The Text Editor used the old flat look: a plain title bar, square text buttons and the bitmap terminal font. | A header bar like Settings and Task Manager: rounded New, Save and Run buttons with icons, the file name in bold with "Modified", the folder or "Not saved yet" under it, round window buttons. Line-number gutter, current-line band, anti-aliased monospace text, blinking accent caret, overlay scroll bar, and a status bar with "Ln X, Col Y", language, line count and UTF-8. Clicking in the text moves the cursor. | apps/office/editor.c, shell/commands.c (mono_glyph), graphics/ui/input.c, apps/system/logs.c |
| 2 | Run printed "(no output)" for console.log(...); only print and document.write gave output. |
In the Editor, console.log, info, warn and error print their arguments. Run is shown only for .js and .nvx files and the Python IDE (a .py file is no longer run as JavaScript). The output panel has a heading and a round close button, and opening another file closes it. |
lib/js/js.c (js_console), apps/office/editor.c |
Tested in QEMU: edit demo.js with console.log('Hello from', 'NoovexOS', 6*7), typed a second line (subtitle changed to "Modified"), Run showed "Hello from NoovexOS 42" and "sum 5", the close button hid the panel, Save showed "Saved" and the subtitle went back to the folder, New gave "Untitled" / "Not saved yet" without a Run button, and the round close button closed the window.
What changed in v10.08
| # | Before (v10.07) | Now (v10.08) | Where |
|---|---|---|---|
| 1 | A failed HTTPS connection was reported as "No route to gateway" (the code -2 from https_fetch_to means the TCP connection failed, not ARP), and a failed DNS lookup as "Connection failed". The owner's HP machine stopped an update with "The download failed: No route to gateway". |
ap_get says "could not connect to HOST" and "could not find HOST" for HTTPS, "No route to the network gateway" only for plain HTTP, and writes fetch: URL - reason to the kernel log. |
fs/pkg/apt.c |
| 2 | One download attempt. | upd_install tries the ISO download up to three times when the reason is a network error, about 3 s apart, with "The download failed, trying again (2 of 3)" on the update screen and in the log; for "No route ..." it asks DHCP for an address again first. |
core/update.c |
Tested in QEMU: booted from a USB stick, update url pointing at a local manifest offering 10.09 with an ISO address on an HTTPS port where nothing listens: three attempts at ticks 6015, 6317 and 6619, then "Update not installed - The download failed: could not connect to 10.0.2.2", and the computer kept the current version.
What changed in v10.07
| # | Before (v10.06) | Now (v10.07) | Where |
|---|---|---|---|
| 1 | No C compiler inside NoovexOS. | TCC (Tiny C Compiler, mob branch of 16 October 2026) as tcc and cc. It makes position-independent musl programs, so its output runs on NoovexOS; tcc -run file.c compiles and runs in memory. musl's headers and libraries are in /usr/lib/musl, TCC's own headers and runtime in /usr/lib/tcc, and the Linux, zlib, ncurses, curl and mbedTLS headers in /usr/include. |
third_party/tinycc, tools/build_tcc.sh |
| 2 | The command-line programs were small ports (kilo, fetch, sqlite3, ...). | vim 9.2.1225 (vim, vi, view, vimdiff, xxd, with its runtime files), less 712, curl 8.23.0 (HTTP, HTTPS through mbedTLS 3.6.7, FTP; shared libcurl.so.4), git 2.56.0 (local commands, and clone, fetch and push over HTTP and HTTPS through git-remote-http) and Dropbear 2026.94 (ssh, scp, the dropbear server, dropbearkey). All are built from source for musl by tools/build_ports2.sh, with ncurses 6.6 (terminal descriptions built in) and zlib. Python 3.13 and SQLite were already there. |
third_party/{vim,less,curl,git,dropbear,ncurses,mbedtls}, tools/build_ports2.sh, tools/mcc-port |
| 3 | No web browser apart from the built-in one and Firefox through X. | NetSurf (master of 23 October 2026), full screen, started with netsurf [URL] from the text console or a Terminal window: HTML and CSS 2.1 layout (with parts of CSS 3), PNG, JPEG, GIF, BMP and SVG images, HTTPS, forms, history, the DejaVu fonts through FreeType. JavaScript is not included (its build needs flex, which this build machine does not have). New libnsfb surface noovex (/dev/fb0, /dev/input/mice, the tty), and the framebuffer frontend now draws paths (it said "path unimplemented", so SVG images stayed empty). |
third_party/netsurf, tools/build_netsurf.sh, third_party/netsurf/libnsfb/src/surface/noovex.c |
| 4 | Dynamic musl programs could only load libraries by absolute path: musl's search path was /lib plus the build machine's directory. |
Shared musl built with libdir=/usr/lib, and /etc/ld-musl-x86_64.path lists /lib, /usr/local/lib, /usr/lib. |
build.sh, tools/build_ports2.sh |
| 5 | Paths with a trailing slash (mkdir("dir/"), stat("dir/")) failed in the Linux layer, so git init stopped at .git/info/. |
lx_path removes trailing slashes; on a regular file the call fails with ENOTDIR as on Linux. |
sys/linux/linux.c |
| 6 | ioctl compared the full 64-bit request, so musl programs (which pass it as a sign-extended int) never matched requests with the top bit set, for example SNDCTL_DSP_SPEED. |
The request is cut to 32 bits like on Linux. New request 0x80084E50 on /dev/input/mice returns the pointer position. |
sys/linux/linux.c |
| 7 | mkdir name in nvxbox said "File exists" when the name was a program (mkdir vim, mkdir /etc/dropbear), because stat on a bare name found the program in PATH. |
The program fallback of ux_stat2 only answers for /bin, /sbin, /usr/bin and /usr/sbin. |
fs/vfs/unixfs.c |
| 8 | The first network use after start often failed ("Host is unreachable"): the first ARP request was waited for 20 ms only, and a failed DHCP was never tried again. | ARP sends up to four requests (about 1 s in total); if DHCP failed, nic_up tries again up to three times, at least 3 s apart. |
net/stack/tcpip.c |
| 9 | In the text console, programs that read /dev/input/mice got no mouse, and the console cursor was drawn over programs using /dev/fb0. |
live_input keeps the pointer position up to date and passes movements and buttons on; live_present draws nothing while /dev/fb0 is open. |
shell/commands.c |
| 10 | - | EDITOR=vim, PAGER=less, LESS=-R in the default environment; /etc/gitconfig (pager less -FRX, editor vim, branch main, root store), /etc/shells, /etc/dropbear; the Mozilla root store also as /etc/ssl/certs/ca-certificates.crt. Dropbear uses /bin/sh for accounts whose shell is the built-in /bin/nsh. |
sys/native/syscall.c, tools/build_ports2.sh, third_party/dropbear/src/common-session.c |
Tested in QEMU (e1000, local HTTP and HTTPS servers): tcc hello.c -o hello && ./hello, tcc -run; vim editing and saving a file (:wq); git clone over HTTP and HTTPS (http.sslCAInfo), git log, git add, git commit; curl over HTTP, HTTPS with --cacert, and an unknown CA refused; dropbearkey, the dropbear server with key login and ssh ... uname -a, scp to it; less --version; NetSurf from the text console and from a Terminal window: a test page with CSS, fonts, a form, a table, PNG, JPEG and SVG images, mouse hover and click on a link, typing an address, Back, closing with the window button. Not tested: git push, NetSurf's Ctrl+C/Ctrl+V, GIF and BMP images, real hardware, name lookups (the sandbox has no outside DNS; the kernel forwards 127.0.0.1:53 as before), interactive ssh logins into NoovexOS (no pseudo-terminals yet), vim with a real keyboard layout. vim needs about 7 s to start under QEMU without KVM.
What changed in v10.06
| # | Before (v10.05) | Now (v10.06) | Where |
|---|---|---|---|
| 1 | The update screen was dark blue with a turning ring of dots, a progress bar and the current step under it. | A plain blue screen (#2b5fc9) with five dots in a row, "Working on updates NN%" and "Don't turn off your computer" in the anti-aliased Liberation Sans of the word processor, and "Your computer will restart once when it's done" at the bottom. On a failure the reason replaces the second line. |
core/update.c |
| 2 | USB headsets and microphones did not work. | USB Audio Class 1 driver: wireless headsets with a USB dongle, USB headsets, USB speakers and USB microphones. 16-bit PCM, mono or stereo, the device rate closest to 48 kHz (sample-rate control sent when there is a choice), isochronous OUT and IN streams on their own event ring (interrupter 2), software volume, simple rate and channel conversion to the system's 48 kHz stereo. When one is plugged in, sound and recording switch to it ("Headset connected: ..."); when it is unplugged they go back to the built-in sound card. Dongles with volume buttons keep their HID part. | drivers/usb/xhci.c, core/sound.c |
| 3 | mmap and mprotect ignored the protection flags; every user page was readable, writable and executable. |
Real protection: PROT_NONE pages fault on any access, read-only pages fault on write, and code cannot run from pages without PROT_EXEC (NX is switched on with EFER.NXE on every CPU when the CPU supports it). Lazy pages, reserved ranges, copy-on-write and fork keep the protection; the kernel's own copies to and from user memory respect it (EFAULT). |
mem/pmm_vmm.c, sys/linux/linux.c, core/proc/jobs.c, arch/x86_64/user/ring3.c, arch/x86_64/smp/smp_sched.c |
| 4 | sigaltstack always reported "disabled"; signal handlers got no fault address and no register context. |
sigaltstack works (per thread, cleared for new threads and on exec, SS_AUTODISARM), SA_ONSTACK, SA_NODEFER and SA_RESETHAND are honoured, and Linux programs get the Linux signal frame: siginfo with si_code and si_addr (page faults, NX, #GP, divide error, int3, tgkill) and a full ucontext (all registers, fault error code and trap number, the FPU state). rt_sigreturn restores what the handler left there. |
core/proc/jobs.c, sys/linux/linux.c |
| 5 | statx and membarrier returned "not implemented". |
statx (built on newfstatat, AT_EMPTY_PATH) and membarrier (query, global and private expedited barriers). |
sys/linux/linux.c |
| 6 | - | mlibc, a second C library, ported to NoovexOS as its own target (sysdeps/noovex, which starts from mlibc's Linux layer because NoovexOS speaks the Linux system-call interface). It is in the image at /usr/mlibc with the mlibc-gcc wrapper; source in third_party/mlibc, built by tools/build_mlibc.sh with clang and lld (a copy of meson is in tools/meson). |
third_party/mlibc, tools/build_mlibc.sh, build.sh |
| 7 | - | tools/stage_gcc.sh puts GCC into the image only when the build machine's GCC is position independent; Ubuntu's is not, so no gcc is included yet. |
tools/stage_gcc.sh |
Tested: the new kernel test program kfeat (16 checks: guard pages, read-only pages, mprotect, NX, sigaltstack, statx, membarrier) passes in the host harness, which now maps guest pages with their real protection, and inside NoovexOS in QEMU; mlibc's own test suite runs inside NoovexOS: 176 of 242 tests pass (212 on Linux; two of the differences are only QEMU's CPU without AVX2). QEMU usb-audio: found at start ("usb audio: QEMU USB Audio, speakers 48000 Hz 2 ch"), audio tone 440 arrives as a 441 Hz tone in QEMU's recording, unplugging switches back. The update screen during a real update from 10.06 to 10.07 on a USB stick, which then started 10.07. Full tools/check.py 96 of 96. Not tested: real headsets and dongles, USB microphones (QEMU's USB audio device has no input), Bluetooth headsets (not supported).
What changed in v10.05
| # | Before (v10.04) | Now (v10.05) | Where |
|---|---|---|---|
| 1 | There was no way to take a screenshot. | Print Screen opens a screenshot tool: the screen dims, a dark pill at the bottom offers Area (drag a rectangle, with a size label and corner handles), Screen and Window (the window under the pointer is outlined), a round capture button and a close button. Enter or Space captures, Esc or right-click closes, a/s/w switch the mode. Shift+Print Screen saves the whole screen and Alt+Print Screen the window under the pointer straight away. |
apps/system/screenshot.c, graphics/ui/hotkeys.c, graphics/ui/input.c, graphics/ui/desktop.c |
| 2 | - | Each screenshot is saved as a PNG in /home/Pictures (Screenshot-YYYY-MM-DD-HHMMSS.png, written to disk at once) and copied to an image clipboard; a toast says "Screenshot saved to Pictures and copied". Ctrl+V in Paint pastes it. The PNG encoder is new and has no dependencies (Sub/Up row filters, deflate with LZ77 hash chains and fixed Huffman codes, CRC-32, Adler-32); a full 1280 x 800 desktop is about 0.2-0.7 MB. |
apps/system/screenshot.c, apps/accessories/small_apps.c |
| 3 | Print Screen did nothing on PS/2 and USB keyboards. | PS/2 (E0 37, and 54 for SysRq) and USB (usage 0x46) send Print Screen to the hotkey queue with the Shift and Alt variants. | drivers/input/ps2/ps2.c, drivers/usb/xhci.c |
| 4 | - | Shell: screenshot / scrot (screen, window, area, last). |
shell/commands.c |
| 5 | NVMe read and wrote one native block (512 B or 4 KiB) per command. | Reads and writes of several blocks go out as one command of up to 64 KiB (16 pages, or less if the controller's MDTS is smaller) with a PRP list; anything unaligned or a failed big command falls back to the old path. | drivers/storage/nvme.c |
| 6 | The installer wrote and verified the boot partition one sector per call. | 128 sectors (64 KiB) per call for writing and verifying; the log ends with install: done in N ms, ... and the NVMe command counts. The new disktest command reads 32 MB and prints the speed and NVMe command counts. |
core/init/installer.c, shell/commands.c |
Tested in QEMU: Print Screen with an area drag (401 x 301), Shift+Print Screen (1280 x 800), Alt+Print Screen and Window mode (1000 x 680), files listed in /home/Pictures, Ctrl+V in Paint; the PNG encoder on the host with the address and undefined-behaviour sanitizers against a decoder (pixel-exact for five desktop screenshots, sizes from 2 x 2 to 1280 x 800, random noise and an all-black image). Install to an emulated NVMe disk: v10.04 took 136 s, v10.05 13 s (167251 boot sectors in 3056 commands, 3013 of them multi-block); the installed disk starts, disktest reads 32 MB in 512 commands (all multi-block), and a screenshot saved on it is still there after a restart. A new check.py usb test covers the USB Print Screen keys; full tools/check.py 96 of 96. The tool also works with GPU drawing emulated (gpu draw emu). Not tested: real NVMe drives and real keyboards.
What changed in v10.04
| # | Before (v10.03) | Now (v10.04) | Where |
|---|---|---|---|
| 1 | The touchpad's resources were read without asking the firmware which copy is in use. On the ASUS TUF Gaming F17 every copy showed "I2C addr 0x00 on NULL". | _STA and _INI are run first: copies that are off are tried last, the touch panel template XXXX0000 is skipped, and the address that _INI fills in is used. A missing or NULL controller path falls back to the device's parent bus, and ELAN devices try 0x15 first. |
drivers/input/touchpad/touchpad.c, tp_acpi.c |
| 2 | An I2C controller was only found through the pre-scanner, and every Intel PCI function of class 0x0C80 was taken out of reset, including non-I2C ones. | Controllers are also found through the interpreter (_PS0/_PR0, _ADR, memory from _CRS); functions are powered to D0 before their address is read; the LPSS type is checked first, so UART and SPI functions (and device 0x1F, the SPI flash controller) are never written. |
drivers/input/touchpad/touchpad.c, tp_acpi.c |
| 3 | When the firmware named a wrong controller, the touchpad was only found with tpinfo scan. |
When the firmware lists a touchpad but it does not answer, the boot probe tries every known controller and the usual addresses by itself. | drivers/input/touchpad/touchpad.c |
| 4 | tpinfo showed the raw firmware data. |
tpinfo shows per touchpad entry _STA, _INI, the address and controller that were used, and one line per Intel I2C PCI function (ID, BAR, power state, type, what COMP_TYPE read, why it was not used). |
drivers/input/touchpad/touchpad.c |
Tested: the touchpad simulator (59 checks, 3 new: an ASUS-style table with a NULL bus and address 0 inside a XXXX0000 template, and a table with a wrong controller path); QEMU with an SSDT that copies the ASUS layout (TPD0 under I2C1 and I2C2, _STA from a variable, _INI writing address 0x15 into the template): tpinfo picks the copy that is on, shows "I2C addr 0x15 on _SB_.PC00.I2C2" and reports the missing PCI function; full tools/check.py 96 of 96. Not tested: real touchpads on Intel I2C controllers (QEMU has none).
What changed in v10.03
| # | Before (v10.02) | Now (v10.03) | Where |
|---|---|---|---|
| 1 | Task Manager had a Windows-style window frame and a left rail with Processes, Performance, Devices and Services. | Task Manager looks like GNOME System Monitor and matches Settings: its own header bar with a view switcher in the middle (Processes, Resources, Devices, Services), "End task" on the left (red when an app is selected) and round window buttons; the rest of the header still moves the window. | apps/system/taskmgr.c, graphics/ui/input.c |
| 2 | Processes was a list of rows on a gray background. | One boxed list with column titles, an Apps group and a Background group with counts, the selected row in the accent colour, and a summary line (processes, CPU, memory). | apps/system/taskmgr.c |
| 3 | Performance showed tiles and a graph with the fixed-width font. | Resources: a list of CPU, Memory, GPU, Disk and Network with a colour dot, value and small graph, and a card with a 60-second graph and right-aligned details. | apps/system/taskmgr.c |
| 4 | Devices and Services were separate rounded rows. | Devices is one boxed list of expander rows with chevrons and a scroll bar; Services shows each service with its description and a Running or Stopped pill. | apps/system/taskmgr.c |
Tested in QEMU at 1280 x 800: every tab, selecting an app and ending it with "End task", the view switcher in the title zone, dragging by the header, maximize; full tools/check.py.
What changed in v10.02
| # | Before (v10.01) | Now (v10.02) | Where |
|---|---|---|---|
| 1 | NoovexOS running from a USB stick could not update itself ("This copy of NoovexOS runs from the USB stick or the ISO..."). | The updater also updates the USB stick it runs from: same download and checks, the current kernel is kept as kernel64.old, every file is read back, the BIOS and UEFI loaders on the stick count the starts and go back to the previous version after two failed starts, Go back and Use it again work. Settings says "(USB stick)" and "kept on the USB stick". |
core/update.c, apps/system/settings.c |
| 2 | The FAT32 partition in the ISO had 8 MB free, too little for a second kernel. | The ISO's FATs are sized for 512 MB (NVX_GROW_SECTORS, about 8 MB more ISO). On the first update the stick's partition grows into the unused part of the stick (up_grow): the free FAT entries are checked, then the ISO9660 volume size, the MBR entry and the boot sectors are written and read back. Nothing is moved. A stick made from an older ISO is told to be written with 10.02 once. |
boot/mkhybrid.py, core/update.c |
| 3 | USB storage could only read whole sectors one small request at a time and gave up on a command after 0.4 s, which left later commands out of step when a stick (or QEMU) needed longer. | usb_msd_rdn/usb_msd_wrn (up to 64 sectors per command, one retry), usb_msd_sync (SYNCHRONIZE CACHE); data and status phases wait up to about 10 s. |
drivers/usb/xhci.c |
| 4 | On large laptops the touchpad was not found: the ACPI pre-scanner stopped after 160 devices and 768 scopes, so on an ASUS TUF Gaming F17 (11th-gen Intel) tpinfo showed "160 devices, 0 I2C touchpads" and no I2C controller. |
Limits 1024 devices and 8192 scopes; tpinfo says when a limit was reached; the child-device check stops early, so the bigger scan stays fast. |
drivers/acpi/aml_scan.c, include/nvx/drivers/acpi/aml_scan.h, drivers/input/touchpad/touchpad.c |
| 5 | A failed write during an update only said which step failed. | The log names the file, the step (write, read back, compare), the vfat reason and the sector. |
core/update.c, fs/fat/vfat.c |
Tested in QEMU with a 1 GB USB stick image made from the ISO: BIOS update 10.02 to 10.03 (partition grown from 90 MB to 516 MB, restart, confirmed after one minute, fatck clean), Go back to 10.02, a kernel that crashes at start (two crashes, then 10.02 started by itself), a power cut right after the new kernel started to be written (10.02 started, "The last update was interrupted"), the same update with the OVMF UEFI firmware, and a stick made with the old FAT layout (the update stops with the "write 10.02 once" message and the stick is unchanged); full tools/check.py 96 of 96. An SSDT with 420 extra devices and a touchpad after them: 10.01 shows "160 devices, 0 I2C touchpads", 10.02 finds the touchpad. Not tested: real USB sticks and the ASUS laptop itself.
What changed in v10.01
| # | Before (v10.00) | Now (v10.01) | Where |
|---|---|---|---|
| 1 | Settings used the classic window frame, a tinted sidebar with coloured icons and an accent pill for the selected page. | Settings looks like GNOME Settings (libadwaita): its own header bar (bold "Settings" title over the sidebar, page title centred over the content, a back chevron, three round window buttons), a flat gray sidebar with monochrome symbolic icons, a gray pill for the selected category and the user row at the bottom. | apps/system/settings.c |
| 2 | Pages were made of separate cards with shadows. | Boxed lists: a bold group title above a white card with a thin border and full-width separators; category pages are one list with icon, title, subtitle and a chevron per row; content is at most 720 px wide and centred. | apps/system/settings.c |
| 3 | - | Search: a search entry in the sidebar. Typing while Settings has focus searches every page by title and description ("wifi" finds Wi-Fi); Enter opens the first result, Esc clears; "No results found" when nothing matches. | apps/system/settings.c, graphics/ui/input.c |
| 4 | Switches, buttons and segmented controls had shadows and mixed sizes; some labels used the fixed-width font. | Adwaita-style switches (accent when on, gray when off), flat 34-px buttons with bold labels (accent, red text or gray), segmented controls with a white selected segment; every label uses the proportional font. Power shows a status page when there is no battery. | apps/system/settings.c |
| 5 | Every click in the title bar zone moved the window. | settings_title_click handles the back button, the search entry and the user row there first; the rest of the header still moves the window. |
graphics/ui/input.c, apps/system/settings.c |
Tested in QEMU: every Settings page at 1280 x 800, normal and maximized; search for "wifi", "background", "colo", "terminal", "storage", "time", "power" and a word with no result; back button, window buttons and dragging by the header; full tools/check.py 96 of 96.
What changed in v10.00
| # | Before (v9.99) | Now (v10.00) | Where |
|---|---|---|---|
| 1 | A new version had to be written to a USB stick and installed again by hand. | Updates from inside NoovexOS: Settings > Update & recovery > NoovexOS Update and the shell command update ask https://noovexos.site/api/public/update, download the ISO with progress, check its size and SHA-256, take the kernel out of the ISO's FAT32 partition and check its build checksum before anything on the disk changes. |
core/update.c, include/nvx/core/update.h |
| 2 | - | Safe install: the current kernel is kept as /boot/kernel64.old, every written file is read back and compared, the free space is checked first, and /boot/update.txt records each step (PREP, SWAP, TRY0-TRY2, FAIL, OLD, OK), so a power cut at any point leaves a kernel that starts. /boot.cfg gets the entry "NoovexOS 64 (previous version)". |
core/update.c |
| 3 | The boot loaders always started the same kernel. | Both boot loaders count the starts of a new version. If it does not reach one minute of running twice in a row, the previous version starts by itself. The BIOS stage2 writes the state with INT 13h AH=43h (disk_write, fat_file_lba); the UEFI loader uses the file protocol and offers P for the previous version. Either loader falls back to the other kernel file when one cannot be read. |
boot/src/stage2.c, bios.S, disk.c, fat.c, boot/uefi/uefiboot.c |
| 4 | - | After one minute the new version confirms itself and then refreshes the UEFI loader files and the BIOS stage2 on the disk if they differ from its own copies, so boot loader fixes reach installed systems. An interrupted update is reported at the next start, and clusters left by it are freed (vf_reclaim). "Go back" and "Use it again" switch between the two versions. |
core/update.c, fs/fat/vfat.c |
| 5 | - | A full-screen update screen like Windows: turning dots, "Working on updates NN%", "Don't turn off your computer", a bar and the current step; also "Going back to the previous version" and "Restarting". The freeze detector pauses during updates and the installer (g_frz_hold). |
core/update.c, core/crash.c, core/init/installer.c |
| 6 | Settings had no update page; several categories shared icons (Sound used the terminal icon, Update the battery icon). | New NoovexOS Update page (status card with icon and button, "What's new", Safety card with the previous version, automatic fallback and checksum rows). The System and Update & recovery pages open with a status card; new icons for sound, accounts, apps, time, privacy, update, graphics and recovery; a dot in the sidebar when an update is available; settings update, settings recovery and settings about open those pages. |
apps/system/settings.c |
| 7 | Installing to a disk stopped with "The kernel size stamp is invalid": the kernel had grown past the 64 MB snapshot limit, the 128 MB boot partition could not hold two 77 MB kernels, and the 4 MB data snapshot could not hold the 43 MB .data. |
Limit 96 MB; a 512 MB boot partition with one kernel copy (no root KERNEL64.BIN); the embedded WAD, Quake paks, wallpaper data and NVIDIA firmware moved to a new .blob section that is copied from memory; the boot page tables and early IDT are put back to their file contents in the snapshot. |
core/init/installer.c, arch/x86_64/kernel64.ld, kernel64_mb.ld, build.sh, boot64.S, early_fault.c |
| 8 | HTTPS downloads stopped after 512 TLS records (about 8 MB). | The record limit grows with the buffer; g_net_rx reports the bytes received for progress bars; net_get_url gives the redirect-following fetcher to other parts of the kernel. |
lib/crypto/nvxtls.c, net/stack/tcpip.c, fs/pkg/apt.c |
| 9 | vfat read and wrote one cluster per disk command. |
Runs of up to 128 clusters; new vf_size, vf_inplace, vf_reclaim, vf_swap_out/vf_swap_in. |
fs/fat/vfat.c |
| 10 | The first DHCP request after start often got no answer, so the first network use failed. | The update check asks for an address again (twice) before giving up. | core/update.c |
The website gives the update information from its download catalog: GET /api/public/update returns the version, ISO address, size, SHA-256 and release notes as plain JSON.
Tested in QEMU (BIOS and UEFI, NVMe and SATA, local update server): install to an empty disk; update 10.00 to 10.01 with restart and confirmation after one minute; Go back and Use it again; an update to a kernel that crashes at start, which ran twice and then started the previous version by itself with the notice; a power cut while the new kernel was written, after which the previous version started and the state became OLD; the UEFI loader refreshed after confirmation; P in the UEFI loader started the previous version; the boot partition checked with tools/tests/fatck.py after each step.
What changed in v9.99
| # | Before (v9.98) | Now (v9.99) | Where |
|---|---|---|---|
| 1 | A kernel crash showed a red screen with the registers and a bare instruction address. Nothing was saved, so a crash on real hardware had to be photographed and the address looked up by hand. | Crash report: the function name with offset (nvd_poll+0x4c), the call chain (frame pointers, repeated frames folded), the reason (page fault with read/write/execute and the address, double fault, stack overflow, stack protector), the driver being started, the registers and the last 20 log lines. It is saved as CRASH.TXT on the USB stick and crash-report.txt in Files; on the next start a notice says so and the desktop opens it in the Editor. R restarts. The crash screen runs on its own 64 KB stack and draws straight into the framebuffer, so it also works after a stack overflow or a GPU failure. |
core/crash.c, core/panic.c, build.sh |
| 2 | Function names were not in the kernel. | build.sh writes a table of 8,700 text symbols (address and name) into g_ksyms (384 KB reserved in .data) after linking, before the signature is stamped. |
build.sh, core/crash.c |
| 3 | When the main loop stopped for 6 s, a one-line note with the instruction address appeared. | Freeze report: after 5 s the timer interrupt shows a box with the same report (function, call chain, stage, log); after 30 s it is saved; if the system recovers, the box is removed, the report is saved and a message says how long it was frozen. Long NVIDIA start steps and busy pumps count as progress. | core/crash.c, arch/x86_64/interrupts/interrupts.c, graphics/ui/desktop.c |
| 4 | Only the NVIDIA start had a "did not finish last time" flag. | Every start-up driver (VM mouse, ACPI, laptop extras, Intel graphics, AMD GPIO, touchpad, battery, CPU temperature, multi-core start) writes its name to DRVSTART.TXT on the USB stick and driver-start.txt in Files before it starts. If the next start finds a name there, that driver is skipped once and a notice names it. The boot USB stick is mounted at start for this. |
core/crash.c, core/init/main.c |
| 5 | A kernel stack overflow silently overwrote memory below the stack. | A guard page under the 2 MB boot stack (the 2 MB page is split into 4 KB pages, the lowest one unmapped); the double-fault handler runs on its own stack from the start (us_init at boot), so an overflow stops with "a kernel stack overflowed". The 16 KB stacks (per-CPU kernel, job, NMI and double-fault stacks, process stacks, AP start stacks) have a marker at the bottom that the timer interrupt checks. |
core/crash.c, arch/x86_64/cpu/percpu.c, arch/x86_64/smp/smp.c, core/init/main.c |
| 6 | No stack protector in the kernel. | -fstack-protector-strong -mstack-protector-guard=global -fno-omit-frame-pointer for kernel units and built-in modules; __stack_chk_fail stops with a crash report naming the function. |
build.sh, core/crash.c |
| 7 | The heap ignored double frees and frees of bad pointers silently and could not see overruns; block headers were 24 bytes (user memory 8-byte aligned). | Each block remembers the requested size and the caller; 8 guard bytes follow the requested size and are checked on free. Overruns, double frees (the second one is ignored) and bad frees are logged with function names; realloc of a bad pointer returns 0; calloc checks for size overflow. Headers are 32 bytes, so user memory is 16-byte aligned. memcheck walks the heap; memcheck poison on fills freed memory to find use-after-free bugs. |
mem/heap.c, mem/heap_api.c, include/nvx/mem/heap.h, shell/commands.c |
| 8 | The AMD touchpad path trusted the ACPI GPIO pin without checking it, so a wrong pin left the touchpad silent. | AMD GPIO driver (AMDI0030/AMDI0031/AMD0030, Linux pinctrl-amd register layout): the pin must exist and be an input, then its level is compared with real touchpad reports (24 with and 24 without data); only a pin that matches every time is used, otherwise the touchpad stays polled. amdgpio decodes a pin (direction, level, pulls, interrupt type and status, debounce). |
drivers/gpio/amd_gpio.c, drivers/input/touchpad/touchpad.c, i2c_hid.c |
| 9 | No CPU temperature or speed control. | Intel digital thermal sensor (core and package, TjMax from MSR 0x1A2 on Core models) and AMD Zen Tctl (SMN 0x59800); HWP is turned on with the full performance range and a balanced energy setting where the CPU has it; average speed from APERF/MPERF. Shown in the task manager and by cputemp; a warning when the CPU is within 5 C of its limit. Not used inside virtual machines. |
arch/x86_64/cpu/cputemp.c, apps/system/taskmgr.c |
| 10 | Writing a file on a USB stick made from the ISO failed: the free-cluster search stopped after 65,536 clusters, and the kernel fills the clusters before that. This also stopped NVGSPLOG.TXT from being saved on the stick. |
The search covers every cluster of the partition, starts at the FSInfo next-free hint and remembers where it stopped; mkhybrid.py now writes the free count and next-free hint. |
drivers/usb/xhci.c, boot/mkhybrid.py |
| 11 | The kernel's sign-in and cloud files used the address mynexus.site. | They use noovexos.site. | include/nvx/net/http/http.h |
| 13 | A CPU register (MSR) the CPU does not have caused a general protection fault and a crash. | cpu_rdmsr_safe/cpu_wrmsr_safe: while such an access runs, a fault on the rdmsr/wrmsr instruction is caught, the instruction is skipped and the call returns an error; the temperature and HWP code uses them, and memcheck counts refused accesses. |
arch/x86_64/cpu/cpu.h, arch/x86_64/interrupts/interrupts.c, arch/x86_64/cpu/cputemp.c |
| 14 | A failed allocation returned 0 without a trace. | The first 32 out-of-memory failures are logged with the size and the calling function; the count is in memcheck and in crash reports. |
mem/heap.c |
| 12 | memcpy/memset and the main-loop halt used inline assembly outside arch/, and the unity build failed on a duplicate name (g_mq). |
They use cpu_copy, cpu_fill and cpu_idle from cpu.h; the input queue is g_inq. |
lib/base/mem.c, core/init/main.c, drivers/input/input.c |
Tested: QEMU with a USB stick made from the ISO: crash test panic, crash test stack (guard page), crash test smash (stack protector), crash test heap, crash test freeze (5 s box, saved at 30 s, recovered message), report found and shown after a restart, a driver marked as not finished is skipped once; desktop window drag with the performance overlay; full tools/check.py 92 of 92.
What changed in v9.98
| # | Before (v9.97) | Now (v9.98) | Where |
|---|---|---|---|
| 1 | On the owner's GTX 1650, after pressing G in Gears, newly opened apps were not drawn; they could only be reached through the taskbar, where a left click then minimised the (invisible) focused window. Cause: gq_surf_free only marked a GPU drawing surface unused; the memory behind it (about 8 MB per window layer at 1920x1080) was never freed, so every GPU drawing start and stop (G in Gears, Ctrl+Alt+N, a watchdog fallback) leaked all layers and work buffers. When an allocation failed, lay_fail stayed set and no new window got a layer again. |
A free hook (g_gq_free_hook, nd_free) gives the memory back and removes the GPU mapping (nvgsp_ce_unmap, new nvm_unmap) when a surface is freed. If a window layer still cannot be allocated while the GPU draws, the CPU takes over drawing at once, so new windows always show. |
gq.c, nvdraw.c, nvgsp.c, nvmem.c, gfx.c |
| 2 | Mapping a surface for the GPU walked all four page-table levels for every 4 KB page (about 10,000 uncached card-memory reads for one 8 MB window layer). | The walk is done once per 2 MB and physically contiguous runs are mapped in one go, so opening a window under GPU drawing is much faster. | nvmem.c, nvgsp.c |
| 3 | - | Simulator: a freed surface releases its memory and its GPU page-table entries are cleared. 62 checks. | tools/tests/nvidia/nvsim.c |
Tested: NVIDIA simulator 62 checks; USB, touchpad, display, boot emulation and QEMU checks (24 of 24); QEMU with emulated GPU drawing: two apps opened from the start menu are drawn.
What changed in v9.97
| # | Before (v9.96) | Now (v9.97) | Where |
|---|---|---|---|
| 1 | Since v9.94 the desktop lagged on the owner's PC: the pointer moved, but dragged windows, clicks and typed text showed up only every 500 ms. Cause: a line added in v9.94 (clearing the mouse button queue when not on the desktop) sat in the middle of the main loop's else if chain, so on the desktop login_input() ran right after process_input() and threw its result away; redraws then only came from the login screen's 500 ms blink timer. It also stole mouse events and left the login, setup, boot menu, recovery, installer and disk manager screens without input. |
The line is after the chain; each screen gets its own input again and desktop input redraws at once. | core/init/main.c |
| 2 | During long operations busy_pump shifted queued clicks twice, so they could land off target. |
One shift, done with interrupts off together with reading the motion. | desktop.c, drivers/input/input.c |
| 3 | An input interrupt arriving just before the main loop halted waited for the next timer tick (up to 10 ms). | The loop checks mouse, button queue, keyboard and gestures with interrupts off and halts with sti; hlt only when nothing is waiting. |
core/init/main.c, net/http/http.c (kpending) |
| 4 | The kernel memcpy and memset copied one byte at a time; the CPU desktop copies two full screens per frame with them. |
rep movsb / rep stosb (fast string instructions on the i5-10400). In QEMU a desktop frame went from 47 ms to 20 ms. |
lib/base/mem.c |
| 5 | On a desktop PC without a battery the laptop battery chip (EC) was asked every second; each read could wait up to about 0.3 s. | At most three tries, about 2 ms per wait. | desktop.c |
| 6 | The drifting wallpaper light forced a full redraw ten times a second, also while the mouse was moving. | The light pauses while the mouse or keyboard was used in the last 1.5 s. | gfx.c |
| 7 | Once after GPU drawing started, an automatic frame check compared a GPU frame with a CPU frame with interrupts off (up to about 0.8 s, input lost). | Only on request (Settings > Advanced graphics > Run check). | desktop.c |
| 8 | A GPU timeout could bring up to three automatic re-checks, each stalling the desktop; a channel still waiting for timed-out work was re-checked anyway; error reports from other channels were written to the serial log every time. | One re-check per boot, only after 2 s without input, refused while a channel still waits; GSP error reports are logged only the first 16 times. | nvgsp.c |
| 9 | GPU drawing fell back to the CPU only after three frames over 150 ms. | Also when the average over 30 frames is above 40 ms. | nvdraw.c |
| 10 | No way to see frame times without typing. | Ctrl+Alt+P shows frames per second, frame time and worst frame of the last second, how long input waited, who draws (CPU, GPU, NVIDIA), who copies to the screen (CPU or copy engine), whether the screen memory is write-combining, GPU wait, present time and GPU syncs. | desktop.c, core/init/main.c |
Tested: NVIDIA simulator 61 checks; USB, touchpad, display, boot emulation, libc and QEMU checks (28 of 28); QEMU: window drag shows at once, overlay on with Ctrl+Alt+P.
What changed in v9.96
| # | Before (v9.95) | Now (v9.96) | Where |
|---|---|---|---|
| 1 | On the owner's GTX 1650 the desktop became so slow that mouse, window dragging and keyboard hardly responded. Cause: since v9.93 the compositor's GPU-only images (scratch, blur caches, snapshot) were in the card's memory. When a GPU drawing batch cannot be sent (for example a channel still waiting after a timeout, which v9.95 now refuses to overwrite), the draw queue finishes the frame on the CPU, and the CPU then reads and writes those images over PCIe without caching, which takes seconds per frame. | Card memory is off by default: all desktop images are in normal RAM again, as in v9.92. nvgsp cardmem on turns it on for testing. |
nvdraw.c |
| 2 | A failed GPU batch left the desktop on the slow CPU fallback inside GPU drawing; only the first five frames were checked for speed. | Watchdog between frames, for as long as the GPU draws: if the GPU draw queue has failed, or three frames in a row take more than 150 ms, GPU drawing stops at once, the CPU draws the desktop, and the reason is in the log. | nvdraw.c (nvd_poll) |
| 3 | - | Simulator: card memory off by default; both watchdog cases. 61 checks. | tools/tests/nvidia/nvsim.c |
Tested: NVIDIA simulator 61 checks; USB, touchpad, display, boot emulation and QEMU checks; QEMU with emulated GPU drawing: window drag and start menu click.
What changed in v9.95
| # | Before (v9.94) | Now (v9.95) | Where |
|---|---|---|---|
| 1 | The driver did not know which monitors are connected. | First piece of stage 6, read only, like nouveau's r535_disp_oneinit: display instance memory (64 KB VRAM, WRITE_INST_MEM 0x20800A49 once per GSP start on the internal subdevice), NV04_DISPLAY_COMMON (0x73) under its own client, then GET_NUM_HEADS, GET_ALL_HEAD_MASK, GET_SUPPORTED, GET_CONNECT_STATE, OR_GET_INFO (output type: HDMI, DVI, DisplayPort, VGA), DFP_GET_INFO and GET_EDID_V2 (monitor maker, name and up to four native modes, EDID checksum checked). Runs as step 19 after nvgsp all (a failure does not stop the start-up) and with nvgsp monitors; shown in the log and Settings > Display > Screen and monitors (with Read again), next to the mode the card shows now. Nothing on the screen is changed. |
nvgsp.c (nvgsp_monitors), settings.c |
| 2 | Stage 6 (mode setting, tear-free page flips, second monitor). | Not in this release. In Linux it is thousands of lines (display core and window channels, mode timing, DisplayPort/HDMI link set-up through GSP); estimate 3-5 more sessions plus test runs on the card. | - |
| 3 | A GSP-RM reply that came after the 5 s timeout stayed in the queue and was taken as the answer to the next request with the same function, so every later allocation or control got the wrong result. | Like nouveau, a request without an answer is fatal: no more requests are sent until the card is restarted, NVIDIA drawing goes off with the reason. | nvgsp.c (rpc_lost) |
| 4 | After a copy-engine, 2D or compute timeout the same push buffer was rewritten while the GPU might still read it. | A channel takes no new work until the semaphore of the batch that timed out has arrived. | nvgsp.c (ch_ready, ch_hung) |
| 5 | After a channel error (RC) on our channel, nvgsp on reused that channel. |
Like nouveau (r535_fifo_rc_chid kills the channel), it refuses and says to run nvgsp unload and nvgsp all, which make new channels. |
nvgsp.c |
| 6 | A damaged status-queue element blocked the queue forever; a full status queue could block sending; sequence numbers advanced even when a send failed. | Damaged elements are skipped, waiting for room drains GSP events, sequence numbers advance only after a send. | nvgsp.c |
| 7 | Unload left GSP-RM's BAR2 root entry pointing at our page tables; UNRESTRICTED_PRIV_ACCESS_MAP was writable; a half-made display client was not freed; every start allocated a new 24 MB copy of the GSP image and 0.5 MB of buffers. | Like nouveau: BAR2 entry cleared before unload (r535_bar_bar2_fini), the buffer read only like PRIV_ACCESS_MAP; the client is freed; the buffers are reused and cleared. |
nvgsp.c |
| 8 | File Manager (classic style) had a Places list. | SerenityOS-style folder tree with +/- boxes and dotted lines, a Devices part with This PC and the drives, icon view by default. | file_explorer.c |
| 9 | - | Simulator: monitor reading (HDMI monitor with a real EDID, empty DP and DVI outputs, instance memory written once), a request without an answer, a busy channel after a timeout, a channel after RC. 59 checks. | tools/tests/nvidia/nvsim.c |
Tested: NVIDIA simulator 59 checks, USB, touchpad, display, boot emulation and QEMU checks; QEMU File Manager tree, This PC and icon view.
What changed in v9.94
| # | Before (v9.93) | Now (v9.94) | Where |
|---|---|---|---|
| 1 | The desktop compared the mouse button state once per frame. A quick click inside one slow frame was patched in v9.93 by counting presses, but a whole drag (press, move, release) inside one frame still did nothing, a click was handled at the position where the pointer ended up, and the order of several clicks was lost. | Like Linux evdev, every button change goes into an ordered queue (in_btn_raw, 64 entries) with the motion counter at that moment and the time. The desktop replays the queue in order each frame: first the motion up to the change with the old buttons, then the change, so a press lands where it happened and a fast drag still moves the window. Stale entries are dropped when the desktop is not the active screen. |
drivers/input/input.c, graphics/ui/input.c, core/init/main.c |
| 2 | Double-click in Files measured the time when each click was processed, so with slow frames a fast double-click did not open the file. | Double-click uses the time of the click itself (g_click_t). |
file_explorer.c |
| 3 | In QEMU and VMware the absolute pointer also raises the PS/2 interrupt with a fake packet whose buttons are 0; the PS/2 driver took it as a release, so dragging never worked in those VMs (clicks did). | Like Linux's vmmouse driver, PS/2 packets only wake the absolute-pointer driver in that mode; position and buttons come from the backdoor. VirtualBox is unchanged. | drivers/input/ps2/ps2.c |
| 4 | Closing or minimising a window left no window focused. | Focus goes to the next window on the workspace, like SerenityOS. | graphics/ui/wm.c |
| 5 | Classic style looked like Windows 98 (blue title bars, grey #c0c0c0). | SerenityOS-style colours and details (values from SerenityOS's Default theme): window face #d4d0c8, active title gradient #6e2209 to #f4ca9e, inactive #808080 to #c0c0c0, title text with shadow, app icon in the title bar, title stripes between the title and the buttons, darker 3D edges; start menu with a #dbd8d1 stripe and #d2c0b6 selection; taskbar buttons without the dither pattern. Artwork and icons are NoovexOS's own. | wm.c, desktop.c |
| 6 | Files looked like a modern app inside a classic desktop. | In classic style Files is called File Manager and is drawn SerenityOS-style: square corners, grey toolbar, sunken location box, sidebar, file view and status bar, tan folder icons, beveled toolbar buttons and #d2c0b6 selection. | file_explorer.c, framebuffer.c (g_round_off) |
| 7 | Only Intel and NVIDIA were named; other graphics cards showed as a bare PCI ID. | gpu_list / gpu_name detect AMD and NVIDIA cards by PCI ID (GeForce 600 to RTX 50, Radeon GCN to RX 9000 and Ryzen APUs) and say which have a driver: NVIDIA Turing is accelerated, other NVIDIA and all AMD cards are detected and use the firmware framebuffer. Shown in the task manager (device list and GPU page), Settings > Advanced graphics and the new gpus command. |
drivers/video/gpuid.c, taskmgr.c, settings.c, commands.c |
| 8 | Any channel error report from GSP-RM turned all NVIDIA drawing off; a timeout or bad read-back turned a path off until Ctrl+Alt+N. | Like nouveau (r535_fifo_rc_chid), a channel error only counts when it names one of our channels. A compute or 2D timeout, or a bad screen read-back, schedules an automatic re-check 10 s later (the same checks as nvgsp on), at most 3 times per boot; nvgsp status shows the count. |
nvgsp.c |
| 9 | - | Simulator: a channel error on a foreign channel is ignored; an automatic re-check brings the path back and stops after 3. 55 checks. | tools/tests/nvidia/nvsim.c |
Tested: QEMU with emulated GPU drawing: drag a window by the title, open Files from the start menu, double-click a file (opens in the Editor), close a window with a 30 ms click. USB, touchpad, NVIDIA simulator, boot emulation and QEMU checks. The full sanitizer run was not repeated for this release.
What changed in v9.93
| # | Before (v9.92) | Now (v9.93) | Where |
|---|---|---|---|
| 1 | With GPU drawing on, clicks inside app windows were often lost, while dragging windows, typing and moving the pointer worked. The mouse drivers stored only whether a button is down now, and the desktop compared that once per frame; a quick press and release that both fell inside one slower frame were never seen (a held drag always was). The USB mouse driver also did not report a release without movement. | The mouse drivers (PS/2, USB boot protocol, USB HID reports, generic in_buttons) count every press in mouse_lpress / mouse_rpress through one function, in_btn_raw, and report every button change. The desktop turns a press that was released before it looked into a full click: pressed this frame, released the next. Reproduced in QEMU with emulated GPU drawing (click on Settings > Display did nothing after 25 s) and fixed (the page opens). |
drivers/input/input.c, ps2.c, xhci.c, graphics/ui/input.c |
| 2 | Ctrl+Alt+N only turned NVIDIA drawing off; turning it on again needed a restart. | Ctrl+Alt+N toggles. nvgsp on and nvgsp off do the same from the terminal. Turning on checks that the card still answers, then runs the copy-engine read-back again (first 30 frames), the 2D engine test and the compute self-test; only paths that pass come back, and GPU drawing restarts from the main loop. |
nvgsp.c, core/init/main.c |
| 3 | The reason NVIDIA drawing went off was only in the log. | nvgsp status, the task manager and Settings show why and how many seconds ago it went off, plus how often it was turned off and on. |
nvgsp.c, taskmgr.c, settings.c |
| 4 | Once a second BOOT0 was read and timed; a read slower than 2 ms counted as "card gone". | Value only, like the PCI core's "device gone" test: only 0xFFFFFFFF or a wrong BOOT0, read twice in a row 1 ms apart, means the card is gone (in the running check and between start-up steps). Nouveau itself has no periodic poll; it relies on interrupts and RPC timeouts. | nvgsp.c |
| 5 | The task manager's GPU page said "no driver" on machines without Intel graphics. | With an NVIDIA card it shows the driver, GSP state, copy engine, 2D engine, compute shaders, whether the GPU draws the desktop, batches and launches, timeouts and bad read-backs, graphics memory and the off reason. The device list shows the GSP-RM state. | taskmgr.c, nvgsp.c (nvgsp_info) |
| 6 | Settings > Graphics only knew about Intel. | Graphics names the NVIDIA GPU when it draws, and has a new NVIDIA graphics page: start now, NVIDIA drawing on/off with the reason, start by itself after boot, screen through the copy engine, GPU draws the desktop, and "Where the desktop images live" with the note that the desktop images sit in normal RAM and the GPU reads them over PCIe. Display > Resolution opens a new Screen and monitors page: current mode, the heads the NVIDIA display engine is scanning out (read only), and the stage 6 note (no NVIDIA mode setting, no tear-free page flips, no second monitor yet). | settings.c, nvmon.c (nvm_screens) |
| 7 | All drawing buffers were in normal RAM. | Buffers only the GPU touches (the compositor's scratch images, blur caches and snapshot) are put in the card's memory: allocated in VRAM, mapped for the GPU and through BAR1, cleared by the copy engine. Window layers, the scene and the wallpaper, which the CPU also writes, stay in RAM. nvgsp cardmem off keeps everything in RAM. |
nvgsp.c, nvdraw.c, gfx.c, nvmem.c |
| 8 | - | Simulator: Ctrl+Alt+N / nvgsp on brings every path back; the off reason shows in nvgsp status; a card that reads 0xFFFFFFFF is turned off and nvgsp on refuses; a card-memory buffer is mapped to the same VRAM pages by BAR1 and the GPU, and the shader's fill lands there. USB mouse test: a press and release between two frames is counted and the release alone is reported. Display test: the Settings screen list gives -1 for a card that reads 0xFFFFFFFF. |
tools/tests/nvidia/nvsim.c, usb_template.txt, gpu_t.c, tp_template.txt |
Not like nouveau yet: on a channel error (RC_TRIGGERED) nouveau kills only that channel (r535_fifo_rc_chid); NoovexOS turns every NVIDIA path off, because it does not recreate channels yet. The screen mode is still the one set at boot (stage 6).
Tested: NVIDIA simulator 53 checks; full tools/check.py --build --sanitize; QEMU BIOS and UEFI boot; QEMU click test with emulated GPU drawing.
What changed in v9.92
| # | Before (v9.91) | Now (v9.92) | Where |
|---|---|---|---|
| 1 | Restart and power off left GSP-RM running on the NVIDIA card. On a warm restart that does not cut the card's power, WPR2 stays set and the next FWSEC-FRTS fails. | Like nouveau's tu102_gsp_fini (drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c and rm/r535/gsp.c in Linux 7.3), restart and power off first turn every NVIDIA path off, send UNLOADING_GUEST_DRIVER, wait for the GSP to stop, reset the GSP falcon, run FWSEC-SB and booter_unload, and check that WPR2 is cleared (nvgsp_shutdown, called from power_prepare). |
nvgsp.c, core/power.c |
| 2 | A start with WPR2 still set (after a crash or a reset that did not unload the card) failed in step 4. | Before FWSEC-FRTS, a set WPR2 is cleared the way nouveau's fini does it (GSP reset, FWSEC-SB, booter_unload with mailboxes 0xFF), then the start goes on; only if WPR2 is still set does step 4 fail, with "turn the computer fully off and on again". | nvgsp.c |
| 3 | - | Simulator: a second cold start in its own process begins with WPR2 set and must clear it and finish all 18 steps (without the change it fails in step 4); the unload check now goes through nvgsp_shutdown; FRTS with WPR2 already set reports an error like the hardware. |
tools/tests/nvidia/nvsim.c |
Tested: NVIDIA simulator 50 checks; full tools/check.py --build --sanitize; QEMU BIOS and UEFI boot.
What changed in v9.91
| # | Before (v9.90) | Now (v9.91) | Where |
|---|---|---|---|
| 1 | On the owner's GTX 1650 all 18 steps ran: copy engine, screen through the copy engine, 2D engine (13 of 15 test pixels), and compute shaders (362 launches, every pixel equal to the CPU, 10 ms). The log still said "FAILED at step 0" and v9.90 turned every NVIDIA path off. Cause: since v9.89 the result of the run was overwritten with the return value of the "card still answering" check, which is 0 when all is well. | That check no longer touches the result; a run where every step passes ends with "all steps passed", and the desktop switches to GPU drawing with frames through the copy engine. | nvgsp.c |
| 2 | The 2x scaled copy in step 17 put screen pixel 1 on source pixel 1 and screen pixel 63 on source pixel 32 (expected 0 and 31). With ORIGIN_CENTER sampling the 2D engine rounds the source position to the nearest pixel, half a source pixel off from what the driver assumed. | Scaled copies use SET_PIXELS_FROM_MEMORY_SAMPLE_MODE ORIGIN_CORNER and start the source at x + du/2, y + dv/2, so each screen pixel takes the source pixel under its centre; 1:1 copies are unchanged. | nvgsp.c |
| 3 | The simulator's 2D model sampled at pixel centres, so it could not show the card's error, and it tested each step separately, so it could not show the wrong result. | The 2D model rounds like the card in ORIGIN_CENTER mode (and floors in ORIGIN_CORNER mode), clamped to the source; with the v9.90 code it reports exactly the two wrong pixels of the owner's log. A new check runs the whole nvgsp all from a cold start in a separate process, with the BAR windows emulated, and requires "all steps passed" with screen, 2D and compute on; with the v9.90 code it fails with "FAILED at step 0" like the card did. |
tools/tests/nvidia/nvsim.c |
Tested: NVIDIA simulator 49 checks; full tools/check.py --build --sanitize; QEMU BIOS and UEFI boot.
What changed in v9.90
| # | Before (v9.89) | Now (v9.90) | Where |
|---|---|---|---|
| 1 | On the owner's GTX 1650, steps 14-16 passed for the first time (desktop and Gears frames through the copy engine, Gears 41 fps). Then a run ended "not started" while the copy-engine screen path stayed on, and only the pointer still moved. | Any run that ends with a failure turns every NVIDIA path off (nvgsp_all_off): desktop frames through the copy engine, the 2D engine, compute drawing and Gears' copy-engine path (nvgsp_ce_ready returns 0), and the CPU draws everything again. Gears shows "NVIDIA off: |
nvgsp.c, gpu3d.cpp |
| 2 | Pressing G ran nvgsp all again from inside the Gears window's drawing, in the middle of a desktop frame, even when the card was already running. |
G only asks for a start; the main loop runs it between frames (nvd_run_request, nvd_poll). nvgsp all on a running card (state "copy engine verified") starts nothing again and prints the status; nvgsp unload and then nvgsp all start it fresh. Like nouveau, the card is set up once. |
nvdraw.c, nvgsp.c, gpu3d.cpp |
| 3 | After the start-up nothing read GSP-RM's message queue, so channel errors and page faults went unnoticed and the queue could fill up. | Like nouveau's message-queue interrupt handler, the main loop drains up to 16 GSP-RM messages per pass (nvgsp_tick): sequencer requests are run, error logs are logged, and a channel error (RC_TRIGGERED) or a GPU page fault turns every NVIDIA path off and saves the log. Once a second BOOT0 is read and timed; a wrong value or a slow read also turns everything off. |
nvgsp.c |
| 4 | - | Ctrl+Alt+N turns every NVIDIA path off at once, from anywhere. | core/init/main.c |
| 5 | Desktop frames sent by the copy engine were read back on the first 3 frames and every 64th, two pixels per rectangle. | The first 30 frames and every 32nd are read back, three pixels per rectangle (corners and centre) for up to 16 rectangles. | nvgsp.c |
Tested: NVIDIA simulator 48 checks (new: nvgsp all on a running card, a channel error from GSP-RM turning every path off); full tools/check.py --build --sanitize; QEMU BIOS and UEFI boot.
What changed in v9.89
| # | Before (v9.88) | Now (v9.89) | Where |
|---|---|---|---|
| 1 | The NVIDIA card was only started by pressing G in Gears or running nvgsp all; the desktop was drawn by the CPU until then. |
NVIDIA 2D acceleration is on by default: about 3 s after the desktop appears, a Turing card (GTX 16 / RTX 20, PCI IDs 1E00-1FFF and 2180-21FF) is started by itself, and when step 18 passes the desktop is drawn by the GPU. nvgsp auto off turns this off (kept in nvgsp-auto.cfg), nvgsp auto on back on. Intel GPU drawing was already on by default. |
nvdraw.c, core/init/main.c, shell/commands.c |
| 2 | On the owner's GTX 1650 the desktop froze after G, and because the log was only written at the end there was no record of where. | The log is saved (Files and the USB stick) at the start of every step, with the headline STOPPED during step N: <step> until the run ends, and each step's time is logged. A freeze now leaves the step it happened in. |
nvgsp.c, nvflcn.c |
| 3 | A start-up that froze would freeze again on every boot once it starts by itself. | A flag (nvgsp-start.flag in Files and NVSTART.TXT on the USB stick) is set before the automatic start and cleared after it. If it is still set at the next boot, the automatic start is skipped once and the log says why; G or nvgsp all still try by hand. |
nvdraw.c |
| 4 | The run had no overall limit and did not notice a card that stopped answering. | Before every step BOOT0 is read and timed; a wrong value or a read slower than 2 ms ends the run ("the card stopped answering"). The whole run stops after 90 s. While it runs, a panel at the top of the desktop shows the step number and name. | nvgsp.c, desktop.c |
| 5 | When step 18 passed, the desktop switched to GPU drawing inside the G key press. | The switch happens on the next main-loop pass. The first frames are watched: any frame after the first that takes more than 150 ms turns GPU drawing off and the CPU draws again. Test images of a failed self-test are not freed, so a late GPU write cannot land in reused memory. | nvdraw.c |
| 6 | - | Steps 14-18 were compared again with nouveau in Linux 7.3 (nvkm/subdev/gsp/rm/r535: channel allocation flags and memory descriptors, USERD, doorbell, bind and schedule, PROMOTE_CTX entries, context buffer sizes and alignment, external VA space with SET_PAGE_DIRECTORY, golden VA space with COPY_SERVER_RESERVED_PDES, INTx disabled on the card). No difference was found. |
section 10.10.6 |
Tested: NVIDIA simulator 47 checks (new: log saved at each step with the STOPPED headline, a card that stops answering ends the run, switch to GPU drawing from the main loop and turned off by a slow frame, no automatic start after an unfinished start-up); full tools/check.py --build --sanitize; QEMU BIOS and UEFI boot.
What changed in v9.88
| # | Before (v9.87) | Now (v9.88) | Where |
|---|---|---|---|
| 1 | A saved nvgsp-log.txt did not say which version wrote it. A log from the GTX 1650 that failed in step 14 with NV_STATUS 0x1F turned out to come from v9.85 code (no "copy engine for our channel" line, which v9.86 and later print), so it could not show whether the v9.86 step 14 fix works. |
The second line of the saved log is driver: NoovexOS 64 v9.88. |
nvgsp.c |
| 2 | When PROMOTE_CTX failed, the log only gave the status. | Every promote entry is logged before the call: buffer id, GPU virtual address, physical address, size, whether it is initialised and whether it is mapped, as nouveau prints in its debug output. A new failure can be compared field by field with nouveau. | nvgsp.c |
Tested: NVIDIA simulator 44 checks; full tools/check.py --build --sanitize; QEMU BIOS and UEFI boot.
What changed in v9.87
| # | Before (v9.86) | Now (v9.87) | Where |
|---|---|---|---|
| 1 | On NVIDIA the desktop was drawn by the CPU; only Gears frames and the final copy to the screen used the card. | Stage 5 of NVIDIA 2D: the whole desktop (wallpaper, windows, text, rounded corners, soft shadows, glass blur, gradients, the night tint) is drawn by compute shaders on TURING_COMPUTE_A. Every drawing operation of the GPU draw queue (gq.c, the same queue the Intel path uses) becomes one compute launch. |
section 10.10.7 |
| 2 | No shader for NVIDIA. | drivers/video/nvidia/shaders/gqk.c is one kernel with all 12 operations, written to match the CPU reference in gq.c bit for bit. clang makes PTX for sm_75 (gqk.ptx), NVIDIA's ptxas makes Turing machine code (gqk.cubin, 7552 bytes, 44 registers, no stack), and tools/nvcubin.py turns it into gqk_cubin.h. tools/nvshaders.sh redoes the ptxas step on a Linux PC. |
drivers/video/nvidia/shaders/, tools/ |
| 3 | - | Step 18 of nvgsp all: the cubin is checked and loaded (ELF sections, register count, parameter offset, constant bank 2 jump table), the code goes to VRAM, and each operation gets a QMD v2.2 launch descriptor and its own constant bank 0. Dependent operations get a wait-for-idle, independent ones run back to back. The same self-test as on Intel runs: a first fill, a cache test, a coherency test and 400 random operations compared with the CPU pixel for pixel. When it passes, the desktop switches to GPU drawing by itself. |
nvgsp.c, nvdraw.c |
| 4 | Settings > GPU drawing and gpu draw on only knew the Intel GPU. |
Without a ready Intel GPU they start NVIDIA GPU drawing. The task manager shows "NVIDIA GPU (compute shaders)". A batch that does not finish in 500 ms stops GPU drawing with a reason and the CPU takes over. nvgsp status and the log headline say whether the desktop is drawn by the GPU. |
igpu.c, taskmgr.c, nvgsp.c |
| 5 | build.sh used type -P, which only bash has, so tools/check.py --build (which runs it with sh) failed its build check. |
The program lookup for the CPython sysroot searches /usr/bin, /bin and /usr/local/bin itself; sh build.sh works. |
build.sh |
Tested: NVIDIA simulator 44 checks (new: the cubin loader and its refusals, step 18 with a model that reads each QMD and constant bank and runs the shader source on the CPU, wait-for-idle between dependent operations, switching the desktop to the GPU, the 500 ms timeout); full tools/check.py --sanitize; QEMU BIOS and UEFI boot. Not yet seen on the GTX 1650: whether the card runs the launches as the simulator does.
What changed in v9.86
| # | Before (v9.85) | Now (v9.86) | Where |
|---|---|---|---|
| 1 | On the GTX 1650 the driver got through steps 1-13 and stopped in step 14: PROMOTE_CTX for our graphics channel returned NV_ERR_INVALID_ARGUMENT, because the shared PRIV_ACCESS_MAP context buffer was sent as "not mapped" without an address. | As in nouveau, the shared buffer is mapped into every channel after the golden one and sent by GPU address; UNRESTRICTED_PRIV_ACCESS_MAP is mapped writable. The simulator refuses an unmapped entry without an address, which reproduces the card's error with the v9.85 code. | section 10.10.6 |
| 2 | The copy channel used the first copy engine, CE0, which belongs to the graphics engine (same runlist 0). | The first copy engine with its own runlist is used (CE2 on runlist 8 on the GTX 1650). The step 15 copy test also reads GSP events while it waits and stops at the first channel error. The CORE_RESUME wait for SEC2 to halt (2 s, never seen on the card) is gone. | nvgsp.c |
| 3 | No 2D engine. | Step 17: a FERMI_TWOD_A object on the graphics channel, push buffers with fills, copies, scaled copies and blends ending in a host semaphore, checked on 15 pixels in VRAM. API nv2d_fill, nv2d_blit, nv2d_to_screen; three timeouts turn it off. The simulator runs the same methods. |
section 10.10.7 |
| 4 | Every Gears frame redrew the whole desktop on the CPU. | While the Gears window is fully visible, its frames go to the screen alone (gpu3d_fast), through the 2D engine when it works and otherwise by the CPU; a normal frame every 0.5 s keeps the rest current. |
Gears |
Tested: NVIDIA simulator 37 checks (new: PROMOTE_CTX entries and CE choice, step 17, frames to the screen 1:1 and 2x); full tools/check.py --sanitize; QEMU BIOS and UEFI boot, Gears with window-only frames.
What changed in v9.85
| # | Before (v9.84) | Now (v9.85) | Where |
|---|---|---|---|
| 1 | On the GTX 1650, GSP-RM sent a CPU sequencer list that ended with opcode 8 (CORE_RESUME); the driver did not know it, stopped the list, and the halted GSP never sent GSP_INIT_DONE, so step 7 timed out. | CORE_RESUME is done as in nouveau: GSP falcon reset, LibOS arguments in its mailboxes again, SEC2 started again to run the loaded booter, wait for bit 26 of 0x1180F8, SEC2 mailbox 0 must be 0, bootloader version written, GSP RISC-V core must run. CORE_WAIT_FOR_HALT now fails with a reason after 2 s, and a failed sequencer command is named in the step 7 failure. The simulator sends RESET, START, WAIT_FOR_HALT and RESUME and only sends INIT_DONE after a correct resume. | section 10.10.6 |
| 2 | Addresses handed to the GPU were the kernel addresses, which is only right for identity-mapped memory. | Every address goes through kva_to_pa, page by page for page lists; buffers the GPU reads as one block are checked to be physically contiguous. |
nvgsp.c, nvflcn.c |
| 3 | blit_all sent the whole screen every frame. |
Stage 3 of NVIDIA 2D: only changed 64x16 tiles are sent, as rectangles, through the copy engine in one push buffer when the NVIDIA card runs, otherwise by the CPU. fbdamage shows the counts and switches it. |
section 10.10.1, section 10.10.7 |
Tested: NVIDIA simulator 34 checks (new: the sequencer with CORE_RESUME, rectangle present after 5 edits, 300 random frames of damage rectangles); full tools/check.py --sanitize; QEMU BIOS and UEFI boot, Gears drawing continuously and desktop updates with damage tracking.
What changed in v9.84
| # | Before (v9.83) | Now (v9.84) | Where |
|---|---|---|---|
| 1 | Gears ran at 2 frames a second on the owner's NVIDIA desktop although one frame took 10 ms: the main loop only drew a new frame on input, on the Intel compositor's animations or for a fixed list of live apps, and Gears was not in that list. Without the Intel compositor it was redrawn only when something else changed (the caret blink of the Editor, about twice a second). | The main loop asks Gears whether it is running (gpu3d_live) and then draws a frame every refresh interval and arms the precise timer for it, like it does for pointer moves. |
core/init/main.c, Gears |
| 2 | On a TU117 the log said to copy a TU117 booter into Files. | linux-firmware has no separate TU117 booter: nvidia/tu117/gsp is a link to tu116/gsp, so the built-in TU116 booter is the right file and the log says so. TU102, TU104 and TU106 are the chips that need another booter, and the log now says that for them. |
section 10.10.6 |
| 3 | nvgsp-log.txt had the result at the end and lines wider than the Editor window. |
The file starts with nvgsp <command> - RESULT: ... and the state reached, and every line is wrapped at 72 columns. The same file is also written to the USB stick as NVGSPLOG.TXT when one is plugged in, so it can be opened on another computer. Gears shows the step and reason of the last failure instead of "press G". |
nvgsp.c |
| 4 | The copy engine was only used by Gears, into system memory. | Stage 2 of NVIDIA 2D acceleration: nvgsp all has a step 16 that maps the screen's VRAM into the GPU address space, checks a copy-engine fill and a RAM-to-VRAM copy by reading VRAM back, and then sends every desktop frame to the screen through the copy engine (blit_all), with a read-back check of 5 pixels on the first 3 frames and every 64th, and the CPU copy as fallback. nvgsp present on and nvgsp present off switch it. |
section 10.10.7 |
| 5 | - | nvgsp_ce_fill (copy engine with the remap unit writing a constant) for solid fills. After nvgsp unload and a new nvgsp all the copy-engine push buffer and the mappings Gears keeps are made again (nvgsp_ce_gen). |
nvgsp.c, gpu3d.cpp |
Tested: NVIDIA simulator 31 checks (5 new: screen mapping and fill, present with a stride, pitch and read-back refusal, present on/off, log wrapping); full tools/check.py --sanitize; QEMU BIOS and UEFI boot with Gears drawing continuously.
What changed in v9.83
| # | Before (v9.82) | Now (v9.83) | Where |
|---|---|---|---|
| 1 | The code sat under source/kernel/, source/bootloader/, source/tools/ and others, with most kernel folders flat (fs/, net/, apps/, lib/, gui/ and arch/x86_64/ had no subfolders). |
The source tree is flat at the repository root with one folder per part of the system: arch/, boot/, core/, sys/, mem/, fs/, drivers/, graphics/, net/, shell/, apps/, lib/, web/, usr/, lang/, assets/, include/, third_party/, tools/, docs/. Inside them, code is grouped in subfolders: arch/x86_64/{boot,cpu,interrupts,smp,time,user}, fs/{vfs,nvx,fat,ntfs,pkg}, drivers/input/{ps2,touchpad,vm}, drivers/net/{ethernet,wifi}, drivers/video/{fb,intel,nvidia}, graphics/{ui,theme}, net/{stack,http,print,wifi}, lib/{base,cxx,compress,image,js,crypto}, apps/{system,office,internet,media,games,accessories,x11,graphics}, sys/{native,linux}. include/nvx/ mirrors the new tree. Section 2 of Part 1 has the full table, with the old place of every folder. |
section 1 |
| 2 | - | Every top-level folder has a README.md that says what is in it; the repository README.md has the layout. |
section 1 |
| 3 | build.sh ran from source/kernel/. |
build.sh runs from the repository root (bash build.sh); output goes to build/, the boot loader is built in boot/. tools/check.py and tools/emulate.py work from the new root, and the static checks only look at kernel code (not the boot loader or tools). |
section 1 |
The code inside the files did not change apart from paths (#include lines, build and test scripts) and the version number. Tested: tools/check.py --sanitize on an image built from the new tree, 92 of 92 checks; QEMU legacy BIOS and UEFI boot to the live session and run Gears.
What changed in v9.82
| # | Before (v9.81) | Now (v9.82) | Where |
|---|---|---|---|
| 1 | The kernel was C and assembly only. | The kernel is C, C++ and assembly. .cpp units in units.list are compiled with g++ (no exceptions, no RTTI, no SSE); lib/cxx/cxxrt.cpp gives new/delete on the kernel heap and the small C++ runtime; global constructors run from .init_array right after the heap starts (cxx_run_ctors); include/nvx/lib/cxx.h has nvx::KBuf and other helpers. |
section 1 |
| 2 | - | Gears: a glxgears-style 3D demo written in C++ (Start > Drivers & Hardware, or gears). Its own 3D renderer (apps/graphics/r3d/r3d.cpp, a freestanding SSE module) draws three interlocking gears with lighting and a large FPS counter, plus three more scenes (torus knot with shadows, 12 material spheres, 192 cubes) and a wireframe mode. |
section 11 |
| 3 | The NVIDIA copy engine was only used for a 4 KB self-test. | nvgsp_ce_map and nvgsp_ce_copy2d: any kernel buffer can be mapped into the GPU address space and copied as a 2D rectangle with a completion semaphore. The mapping goes page by page through the CPU page tables (kva_to_pa, now shared from igpu.c), so it is right even for pages that early_remap moved. Gears uses it to put every frame on the screen once nvgsp all has run (G in the window). On an Intel iGPU desktop it uses the GPU blitter instead. |
section 10 |
Tested: tools/check.py --sanitize on the v9.82 image, 92 of 92 checks, including the NVIDIA simulator with the new 2D copy check (26 checks) and 0 analyzer findings with the two C++ units in the build. QEMU legacy BIOS and UEFI (OVMF): Gears opens from the terminal, the gears turn, keys 1 to 4 switch scenes and W shows the wireframe; it runs at 1-2 frames a second there because QEMU emulates the CPU without hardware help, and the same renderer takes 8.7 ms per gears frame on the build machine. The NVIDIA copy-engine path has only run in the simulator; on a real card it needs nvgsp all to work first. The Intel blitter path was not tested (QEMU has no Intel iGPU).
What changed in v9.81
| # | Before (v9.80) | Now (v9.81) | Where |
|---|---|---|---|
| 1 | The AHCI, NVMe, xHCI, USB stick, VirtualBox mouse and identify buffers sat at fixed physical addresses (0x09A10000-0x09C80000). early_remap moves a 2 MB page that is not usable RAM to new memory, but only for the CPU: a device that is told the old physical address writes into firmware memory, so disks and USB could fail on a laptop that reserves that range. |
One 2 MB device window (include/nvx/mem/dmawin.h, DMW(off)). dmw_init() runs right after early_remap_fix_mm() and takes a 2 MB block of real RAM below 4 GB from the DMA arena (skipping bad pages with mm_skip_bad); only if that fails does it keep the old address 0x09A00000. All users go through DMW(): AHCI (gdisk.c), the AHCI identify buffers (ahci.c), NVMe, xHCI controller 0, the HID areas and the USB stick buffers (xhci.c), the VirtualBox request (vmmouse.c) and the PCnet rings and buffers (nic.h, the old .bss arrays are gone). xHCI controllers 1-3 get their own pages from dma_alloc_pages. The boot log says where the window is (memory: device buffers in the checked DMA arena at 0x...). check.py static checks that the regions inside the window do not overlap. |
section 3 |
| 2 | The in/out helpers in arch/x86_64/cpu/io.h had no memory clobber, so the compiler could move a store to a DMA buffer past the port write that starts the device. Several drivers had their own inline assembly. |
Every port helper has a "memory" clobber. cpu_mb, cpu_clflush, cpu_copy64 and cpu_vmw_backdoor in arch/x86_64/cpu/cpu.h; igpu.c, vmmouse.c, bench.c, jobs.c and linux.c use them. core/init/early_fault.c moved to arch/x86_64/interrupts/early_fault.c. |
section 4 |
| 3 | The kernel writes to user memory with CR0.WP set, but several Linux and native syscalls only checked that the user address was mapped, not that it was writable. A program that passed a read-only page (for example its own code) to read, pipe, wait4 or poll made the kernel take a page fault. |
Write checks (lx_okw in sys/linux/linux.c, jc_umapped_w in core/proc/jobs.c) before every kernel write to user memory: read, pread/pwrite, getdents, poll, epoll_wait, select, wait4/waitpid, recvfrom, recvmsg, pipe/pipe2, getrandom, socketpair, readlink/readlinkat, futex wake-op, the signal frame, the clear-child-tid write and the native read and pipe calls. The call returns EFAULT. |
section 6 |
| 4 | shmget accepted any size and shmat at a fixed address silently replaced what was mapped there. |
shmget refuses a segment larger than a quarter of the free frames (ENOMEM). shmat at an address that is already mapped needs SHM_REMAP, as on Linux. |
section 6 |
| 5 | Converting a disk to NVX4 formatted it in place; a power cut during the format lost the old first blocks. | n4_conv_backup copies the blocks the format overwrites to a reserved area at the end of the disk with a marker (magic, block count, FNV checksum) in the last block. disk_init restores them if a disk has a valid marker but is not a mountable NVX4 disk; a failed format restores at once. n4_conv_done clears the marker when the new file system is written. |
section 7 |
| 6 | The Elantech PS/2 touchpad could send a two-finger frame before the second finger's head packet, so a scroll started as a jump. | g_etp_wait holds a frame until every finger in the status mask has sent its head packet (elan_ps2.c). |
section 10 |
| 7 | Quake had no sound (snd_null.c). |
WinQuake's mixer (snd_dma.c, snd_mem.c, snd_mix.c) with a NoovexOS back end (snd_nvx.c, a 32768-sample ring). apps/games/quake.c keeps about 70 ms queued in the kernel sound ring. Checked on the host with +play weapons/guncock.wav (peak 22196 of 32767). |
section 11 |
| 8 | Five host test groups did not build any more (usb, nvme, memory, aml, touchpad) and the Linux harness crashed; emulate.py could not find uefi_entry. |
All templates follow the current code (device window, stubs for new helpers). The Linux harness has the X damage-tracking stubs, refetches host page entries after a spurious fault, covers 16 GB and clears SysV segments before the leak check. emulate.py finds the entry after the CET lines. The AML test's own heap moves to 0x60000000 when 0x80000000 is taken (it is inside AddressSanitizer's shadow memory), so --sanitize passes too. All check.py groups pass. |
section 12 |
| 9 | Some places still said 9.80 or older (the terminal said v8.38); uname printed a fixed string. The source zip had no FFmpeg, glibc, CPython or Tcl/Tk sources, so build.sh could not run from it. |
Version 9.81 everywhere. uname reads /sys/kernel/osrelease and knows -r. The source zip contains third_party/ffmpeg (n8.1.2) and third_party_src/ (glibc 2.44, CPython 3.13, Tcl/Tk 8.6). build.sh needs bash and gawk. |
section 1 |
| 10 | The installer's list of kernel areas used fixed indexes and an old boot stack size. | The boot stack area is 0x09800000-0x09A00000; "USB and disk buffers" is listed only while the device window is still at its fixed address; the module area is found by address. | section 8 |
| 11 | NVIDIA cards were only probed read-only. | A GSP-RM driver for Turing (TU102-TU117) with the nvgsp command, see section 10.10.6: VRAM size and an allocator, the WPR2 layout, FWSEC-FRTS from the VBIOS, the signed booter on SEC2, the GSP firmware with its radix3 page tables, the message queues, BAR1 and BAR2 page tables and instance blocks, and the objects client, device, subdevice, address space, channels, TURING_A, TURING_COMPUTE_A and TURING_DMA_COPY_A, with a copy-engine test. The firmware (535.113.01 from linux-firmware) is built in, xz-compressed. tools/tests/nvidia/nvsim.c simulates the card and checks every step (25 checks). |
section 10 |
| 12 | Under UEFI the USB stick was never found (usbls: "no USB stick found"). The firmware (OVMF, and laptops with "Above 4G decoding") places the xHCI registers above 4 GB, and the driver only took 32-bit register addresses, so it gave up on the controller (usbinfo: "registers above 4 GB, cannot be used yet"). Also, a device that showed up on a port after the first scan was marked as already seen and never set up. |
Register addresses are 64-bit (xhci_base, xhci_op and every register pointer in xhci.c); a window above 4 GB is mapped with acpi_hi_map, as NVMe does. msd_autoinit reads the port status again before it looks for a stick, and the first hot-plug scan only marks ports that were set up. Tested: the USB stick in QEMU under UEFI (65 MB FAT32, files listed and read), which failed in v9.80. |
section 10 |
Tested: tools/check.py --sanitize on the final image, all 92 checks (the qemu group, 4-core smptest, musltest 97, featest 52, dyntest 13 and the parking of cores, timed out once while eight other QEMU machines ran and passed when run alone; emulate.py 29 of 29). QEMU legacy BIOS and UEFI (OVMF): boot to the live session, the NVMe controller and disk list, the USB stick (list and read a file) and nvgsp without an NVIDIA card (stops cleanly at step 1). Quake sound was checked on the build host and an HD Audio tone in QEMU was recorded correctly; Quake's own sound in QEMU was not heard because no key or mouse input could start the game there. The NVIDIA driver has only run against the simulator; it is untested on a real card.
What changed in v9.80
| # | Before (v9.79) | Now (v9.80) | Where |
|---|---|---|---|
| 1 | The X compositor compared the whole Xvfb screen with a shadow copy every 2 to 8 ticks to find what changed (about 4 MB at 1280x800, 8 MB at 1080p), even when nothing moved. | Damage tracking with the processor's page dirty bits (apps/x11/xdisplay.c). When the X server maps its screen file for writing, the kernel keeps a pointer to each page table entry (xfb_track). Every tick it reads and clears the dirty bit of each page (xfb_collect), sends a TLB flush to the other processors without waiting, and looks only at the pages that were written, plus the pages from the tick before to cover a write that raced with the flush. Every 3 seconds it does one full compare as a safety net. The tracking stops by itself (and the old full compare is used) when the X server unmaps the screen or exits (xfb_untrack_range, xfb_untrack_cr3). It is used in all three X modes: on the desktop, in the X Display window and full screen. |
section 11 |
| 2 | - | xstat in the terminal shows the compositor numbers since the last call: tracking on or off, dirty pages, scans, partial updates, full redraws, total compositor time and the time spent reading dirty bits. xstat off and xstat on switch between the old full compare and the new tracking for comparison. Measured in QEMU (software emulation, 30 seconds each): idle desktop with X running 1180 ms of compositor time before, 246 ms now; an animating X program 3154 ms before, 1651 ms now, with more frames shown. |
section 11 |
| 3 | Firefox rebuilt the font cache on every boot, had its disk cache off, rewrote its settings file on every start and did not restore tabs after a crash. | The nvx-firefox launcher keeps the font cache in /root/.cache and runs fc-cache only when a font folder has changed since the last run (/root/.cache/nvx-fc-stamp); the same for nvx-xrun. The disk cache is on (256 MB). user.js is written only when it changed, so Firefox's startup cache in the profile stays valid. A profile lock left by a crashed Firefox is removed. Session restore after a crash is on (browser.sessionstore.resume_from_crash, up to 3 crashes in a row, tabs loaded when clicked). The launcher notes in /tmp/ff.log when the last run did not end cleanly and how long Firefox ran. cmp and readlink were added to the system tools for this. |
section 6 |
| 4 | The Firefox crash report listed unimplemented syscalls by number and name but not unsupported options inside supported syscalls, and the report was only on screen. | The Linux layer records every unsupported option it refuses (lx_gap): Unix socket types, socket families, internet socket types, ioctl requests, fcntl commands, prctl options, clone flags and socketpair types, with a count and the order they happened in. The report starts with a "Most likely cause" section: a program that is not installed (exit code 127), or the last missing syscall or unsupported option used before it stopped, skipping syscalls that programs only probe for (rseq, io_uring, landlock, clone3, openat2, faccessat2) and options that are usually harmless (IPv6 and netlink sockets, prctl options), which are still listed and marked. Each report is saved in Files as firefox-crash-N.txt (or app-crash-N.txt). |
section 6 |
| 5 | Cross-process futexes did not work: a futex was found by process group and virtual address, so a process-shared mutex, condition variable or semaphore in shared memory never woke a waiter in another process. Absolute futex timeouts on the real-time clock were cut to 100,000,000 seconds, so every wait like that timed out at once. | Futexes without the private flag are found by physical address (lx_fx_key), so they work between processes; thread exit (CLONE_CHILD_CLEARTID) wakes both kinds. Only relative timeouts are capped now. |
section 6 |
| 6 | Unix sockets only supported stream sockets; there was no /dev/shm; every receive moved the rest of the 256 KB buffer one byte at a time. |
SOCK_SEQPACKET (with socket, connect/accept and socketpair) and SOCK_DGRAM in socketpair, keeping message boundaries with a 64-entry length queue and passing file descriptors with the right message. /dev/shm exists at boot. The receive buffer moves 8 bytes at a time. |
section 6 |
| 7 | Firefox could only run as one process. | nvx-mptest checks what Firefox's multi-process mode needs from the Linux layer: shared memory with memfd, passed over a socket with SCM_RIGHTS, /dev/shm files, a 64 MB buffer in two processes, process-shared mutex, condition variable and semaphore, posix_spawn with descriptor remapping, epoll and eventfd across processes, SIGCHLD, fork and exec from a threaded process, the parent-death signal and SOCK_SEQPACKET. All 12 pass. firefox-mode multi makes the next Firefox start use 4 content processes (dom.ipc.processCount 4, extensions in their own process); firefox-mode single goes back. |
section 6 |
Tested: QEMU legacy BIOS with 2 processors: X on the desktop with nvxwm and xdemo (correct pictures, xstat numbers above), nvx-mptest 12 of 12 (4 failed before the fixes), gthreads, and the crash report with Firefox not installed (report on screen and saved in Files). nvx-mptest was also checked on a normal Linux system (12 of 12). Firefox itself, its start time and multi-process mode are untested until they run on the laptop, because Firefox has to be installed with apt over the network.
What changed in v9.79
| # | Before (v9.78) | Now (v9.79) | Where |
|---|---|---|---|
| 1 | No Quake. | Quake (the original software-rendered engine) in a desktop window: quake in the terminal or Start menu > Games. The engine is quakegeneric (WinQuake source, GPL v2) in third_party/quake, built like DOOM into one relocatable object (quake_blob.o, compiled with SSE, only QG_Create, qsh_tick_us and the qsh_set_* hooks global) with its own small C library (quake_shim.c: heap, printf, RAM files, setjmp/longjmp in assembly, hardware sqrtsd). It renders at 640x480 and is scaled to fill the window. |
section 11 |
| 2 | quakegeneric only built for 32-bit. | 64-bit fixes: QuakeC strings are stored as 32-bit offsets from the progs string block, which breaks when engine strings live far away in a 64-bit address space; every pr_strings + n and p - pr_strings now goes through PR_Str and PR_SetStr, which keep offsets inside the block and give other strings negative indexes into a hashed pointer table (the approach modern source ports use). The video buffers follow the chosen resolution and the warp buffers keep the 320x200 limit. |
section 11 |
| 3 | - | Game data: LibreQuake 0.09 Lite (art BSD-3, QuakeC GPL-2; licences in assets/librequake/), pak0.pak and pak1.pak (50 MB) stored xz-compressed (20 MB, assets/lq_pak*.xz) and unpacked when Quake starts. A pak0.pak (and pak1.pak) from the original game in Files is used instead. The kernel image grows to about 64 MB, inside both boot loaders' 100 MB limit and below the BSS at 256 MB. |
section 11 |
| 4 | - | Controls: WASD or arrows, mouse aim with mouse look after a click in the window (Esc frees the mouse), left click or Ctrl fires, Space jumps, Shift runs, number keys pick weapons, Esc opens the menu. Quake output goes to the kernel log with the prefix quake:. Sound is not connected yet. |
section 11 |
Tested: the engine and the freestanding blob on the build host with LibreQuake (start map, 300 frames, pictures checked); QEMU legacy BIOS and UEFI (Quake loads the start map, renders, walks with the keyboard, scales to the window). Mouse look and speed on the laptop are untested until they run there.
What changed in v9.78
| # | Before (v9.77) | Now (v9.78) | Where |
|---|---|---|---|
| 1 | No word processor; .docx files could not be opened. |
Writer (apps/office/writer.c, writer [file] in the terminal, start menu Productivity): A4 pages with 1-inch margins on a grey desk, word wrap, page breaks and page numbers, a blinking cursor, mouse and Shift selection (with drag and auto-scroll), the wheel, Home/End, Page Up/Down and Ctrl+arrows by word. Three font families (Sans like Arial, Serif like Times New Roman, Mono like Courier New) in 13 sizes from 8 to 48 pt, bold, italic, underline, strikethrough, 8 text colours, left/center/right/justified paragraphs, Heading 1, Heading 2 and Normal, pictures (PNG and JPEG from Files, scaled to the page), undo (24 steps), word count, Save, Save as and Open dialogs, and keyboard shortcuts (Ctrl+B/I/U, Ctrl+E/L/R/J, Ctrl+S, Ctrl+O, Ctrl+N, Ctrl+Z, Ctrl+A). |
section 11 |
| 2 | Only the 8x14 bitmap font. | Real fonts for Writer: Liberation Sans, Serif and Mono (metric-compatible with Arial, Times New Roman and Courier New, SIL Open Font License) pre-rendered by tools/gen_wpfont.py in regular, bold, italic and bold italic at 13 sizes for Latin-1 (including the Swedish letters a-ring, a-umlaut and o-umlaut), stored as 4-bit anti-aliased glyphs (assets/wpfont.c metrics, assets/wpf_data.bin 5.5 MB linked as wpf_data.o). |
section 11 |
| 3 | - | Word format: Writer saves real .docx files (Office Open XML in a stored ZIP: [Content_Types].xml, relationships, word/document.xml with runs, fonts, sizes, bold, italic, underline, strike, colours, alignment and inline pictures in word/media). It opens .docx files from Word and other programs (paragraphs, runs, alignment, tabs, line breaks, fonts, sizes, colours, Title/Heading/Subtitle styles, pictures through the relationships file) and plain .txt. Opening a .docx in Files opens Writer. |
section 11 |
| 4 | Ctrl shortcuts could arrive as plain letters when Ctrl was released before the app read the key. | The key queue records Ctrl and Shift with every key at the moment it is pressed (g_key_ctrl, g_key_shift). |
section 10 |
Tested: QEMU legacy BIOS and UEFI (typing, formatting, alignment, selection, shortcuts, saving); the saved file passes a ZIP check in NoovexOS Python, is read correctly by python-docx and lays out the same in LibreOffice; a .docx made by python-docx with a heading, Times, Courier, colour, right alignment, Swedish letters and a picture was opened by Writer's code and saved again without losing anything (host test harness around apps/office/writer.c), and the page drawing was checked in the same harness.
What changed in v9.77
| # | Before (v9.76) | Now (v9.77) | Where |
|---|---|---|---|
| 1 | Volume, mute, media and other special keys did nothing. | The PS/2 keyboard driver recognises the extended (E0) keys: volume up and down, mute, play/pause, next, previous, stop, calculator, web browser, mail, my computer, sleep, media select and the browser keys. They go to a 32-entry hotkey queue (hk_push) that the desktop handles (graphics/ui/hotkeys.c): volume in 6% steps, mute remembers the old volume, media keys control the Video Player, calculator, browser and Files open their apps. |
section 10 |
| 2 | Brightness and volume changes showed a small text toast. | A Windows-style on-screen display: a glass panel at the bottom with an icon (speaker with level waves or a cross when muted, sun, microphone, plane, camera, screen, touchpad) and a level bar with the value, for volume, brightness, microphone mute, airplane mode, camera, display switch and touchpad. | section 11 |
| 3 | On Lenovo IdeaPad laptops the special Fn keys reported through the VPC2004 ACPI device were ignored. | The IdeaPad VPC events are read like Linux ideapad-laptop does (VPCW/VPCR, registers 0x10 and 0x1A): display switch, camera, microphone mute, airplane mode, touchpad on/off (state from register 0x1B) and firmware brightness (registers 0x11 and 0x12) all work. ACPI video brightness events also show the new display, and display-switch events (0x80, 0x82) are handled. |
section 10 |
| 4 | Only Win+R and Win+E did something. | Win+L locks (login screen), Win+P shows the display choice, Win+I opens Settings, Win+A opens quick settings. | section 11 |
| 5 | No way to see what a key sends. | keys in the terminal lists the last 16 special keys and laptop events (E0 scan codes, unknown keys, ACPI events and Lenovo VPC bits), so a key that still does nothing can be reported and added. |
section 11 |
Tested: QEMU legacy BIOS with the QEMU volume up, mute and calculator keys (on-screen display, muting, Calculator opens) and the keys list. The Lenovo VPC events and real Fn keys are untested until they run on the laptop.
What changed in v9.76
| # | Before (v9.75) | Now (v9.76) | Where |
|---|---|---|---|
| 1 | The HD Audio driver only played sound. | Recording. The driver finds the input path from an analog-to-digital converter to a microphone pin (built-in microphone first, then a microphone jack, then line in) through mixers and selectors, sets the connection selects, microphone bias (VRef 80% or 50%), pin boost and input amplifiers, and runs the first input stream (stream tag 2, 48 kHz, 16-bit stereo, 32 x 4 KB buffer list). struct nvx_snd gained rec_start, rec_read, rec_stop, rec_gain and rec_name. |
section 10 |
| 2 | No capture path in the sound core. | core/sound.c moves recorded audio from the driver into a 1 MB buffer every 20 ms (snd_poll), tracks the input level and counts dropped data. snd_rec_start, snd_rec_stop, snd_rec_gain, snd_cap_read, and audio mic <0-100> in the terminal. |
section 10 |
| 3 | /dev/dsp could only play. |
/dev/dsp records too: read() returns 48 kHz 16-bit stereo, and the OSS calls SNDCTL_DSP_GETISPACE, GETBLKSIZE, GETCAPS (duplex), SETTRIGGER and SETDUPLEX are answered, so Linux programs can record (tested with CPython reading /dev/dsp). |
section 6 |
| 4 | No recording app. | Voice Recorder (apps/media/recorder.c, recorder in the terminal): a big record button (Space also starts and stops), timer, live waveform, input level meter, microphone volume slider, play and pause with a moving play head, click the waveform to jump, and every recording saved as recording-N.wav (48 kHz mono) in Files. Settings > System > Sound has a new Input card with the microphone, its volume and a level test. The app is in the Internet & Media and Drivers & Hardware start menu groups. |
section 11 |
Tested: QEMU legacy BIOS and UEFI with an emulated HD Audio codec (line in): recording runs at the right speed, the WAV file is saved, Python reads 96,000 bytes from /dev/dsp, and the Settings input card works. QEMU only gives silence on its input, so the real microphone level and sound quality are untested until they run on the laptop. On laptops whose built-in microphone is wired to the Intel audio DSP instead of the HD Audio codec, the microphone is not found yet.
What changed in v9.75
| # | Before (v9.74) | Now (v9.75) | Where |
|---|---|---|---|
| 1 | A USB camera was listed in Task Manager as "Camera" but nothing could use it. | USB Video Class (UVC) camera driver inside the xHCI driver (drivers/usb/xhci.c, API in include/nvx/drivers/usb/uvc.h). When a device has a Video interface, the full configuration descriptor (up to 16 KB) is read and parsed: Video Control and Video Streaming interfaces, YUY2, NV12 and MJPEG formats with their frame sizes, and the isochronous IN endpoints of each alternate setting (with high-bandwidth packets and SuperSpeed companion descriptors). Starting the stream: probe and commit (VS_PROBE_CONTROL, VS_COMMIT_CONTROL, 26, 34 or 48 bytes by UVC version), the smallest alternate setting that carries the camera's payload size, a Configure Endpoint command for an isochronous IN endpoint and SET_INTERFACE. Transfers use a 1000-entry isochronous ring with "start as soon as possible" and their completions go to a second event ring on interrupter 1, so mouse, keyboard and USB stick traffic is not disturbed. Payload headers (frame id, end of frame, error bit) assemble frames; YUY2 and NV12 are converted to RGB, MJPEG frames get the standard Huffman tables when the camera leaves them out and are decoded by the JPEG decoder. If a size is refused (for example not enough USB bandwidth) the next size is tried. |
section 10 |
| 2 | No camera app. | Camera app (apps/media/camera.c, camera in the terminal): live picture, Start/Stop, SD (YUY2 up to 640x480) or HD (MJPEG up to 1280x720), mirror, Take photo (saves photo-N.png; Space also takes a photo) and a details panel with the camera name, USB id and speed, every supported size, frames per second, dropped frames, USB packet errors and the last completion code. Without a camera it lists the USB devices that were found. |
section 11 |
| 3 | No benchmark. | Benchmark app (apps/system/bench.c, bench cpu or bench gpu). CPU test: four single-core passes counting primes up to 100,000 (must be 9,592), 16 million integer hash steps (checked against a known result), a 64 MB memory write, copy and read-back check, all other cores counting primes up to 150,000 at once (must be 13,848 each) with the speed-up over one core, the clock under load from APERF/MPERF and the temperature from the digital thermal sensor. GPU test: fill, blend and copy speed on a 1024x1024 GPU surface (three passes each), a correctness check of GPU-drawn pixels against the expected values, the GPU clock under load and the chip temperature. The verdict is Healthy, Works with warnings (speed changing between passes, low clock, heat, throttling since power-on, poor core scaling) or Problem found (any wrong answer, memory errors, a core not finishing, wrong GPU pixels, GPU falling back to the CPU). |
section 11 |
| 4 | The start menus had four groups. | A fifth group, Drivers & Hardware, with Camera, Benchmark and Task Manager, in both the modern and the classic start menu. | section 11 |
Tested: QEMU legacy BIOS and UEFI boot; the CPU test (healthy, 4 cores) and the GPU test with the queue emulated on the CPU (all pixels correct); the Camera app without a camera and with a QEMU USB keyboard, which still works. QEMU has no USB camera and no Intel graphics, so the camera stream and the GPU test on the real chip are untested until they run on the laptop.
What changed in v9.74
| # | Before (v9.73) | Now (v9.74) | Where |
|---|---|---|---|
| 1 | Nine built-in wallpapers, five of them made for NoovexOS. | Exactly six wallpapers, the pictures supplied by the owner: Porsche, Crane, Wireframe, Peaks, Earth and Yosemite (THN[NWALL], NWALL is 6). Each is cover-cropped to 1920x1080 with Lanczos; the three small originals (Peaks, Earth, Yosemite) are lightly denoised and sharpened. The default is Crane (g_theme=1). `wallpaper porsche |
crane |
| 2 | The drifting light was off by default. | Every graphics option is on by default: GPU drawing (when the Intel driver is ready), visual effects, partial redraws, wallpaper effects and the drifting light (g_wall_light=1). Each can still be turned off in Settings > System > Display > Graphics. |
section 10 |
| 3 | DOOM only started when the BIOS loader found /doom/DOOM1.WAD or a FAT disk held a WAD, so on the release ISO it showed "Could not load the WAD". |
The shareware DOOM1.WAD (id Software, freely redistributable) is built into the kernel (assets/doom1.wad, linked by ld -r -b binary as doom_wad.o), so DOOM works on BIOS and UEFI with no disk. A full WAD you own on a FAT disk is used first (DOOM2.WAD, DOOM.WAD, FREEDOOM.WAD, FREEDOO1.WAD), then the boot module, then the built-in one; the WAD is registered under its real name so DOOM II is detected. The doom terminal command opens it (also from the text console) and prints the controls; the window title shows them too. |
section 11 |
Tested: QEMU legacy BIOS (1280x800) and UEFI (1920x1080) boot; doom from the console starts the game with the built-in WAD on both. QEMU has no Intel graphics, so the drifting light on the GPU is untested until it runs on the laptop.
What changed in v9.73
| # | Before (v9.72) | Now (v9.73) | Where |
|---|---|---|---|
| 1 | On the Lenovo the taskbar was a blank strip. The frame check showed the picture in memory was right; it appeared with gpu flip off and with gpu draw off, so the GPU shader copy to the screen lost the bottom rows. |
The picture is copied to the screen by the GPU blitter, only the changed area, with a CPU cache flush first when the blitter does not snoop. The shader copy is still available (gpu present shader, or Settings > System > Display > Graphics > Advanced graphics) and the render engine now also flushes its render cache at the end of every batch. |
section 10 |
| 2 | The built-in wallpapers were 640x360 JPEGs (about 12 KB) stretched three times, so they looked blurry and blocky. | Nine wallpapers at 1920x1080: Aurora, Mountains, Waves, Sunset and Crane glow are made for NoovexOS; Crane, Porsche, Wireframe and Peaks are the pictures supplied by the owner, scaled with Lanczos. The JPEG decoder accepts pictures up to 1920x1200. Smaller pictures (for example a custom wallpaper) are enlarged with Catmull-Rom (bicubic) filtering instead of bilinear. wallpaper <name> sets one from the terminal. |
section 11 |
| 3 | Task Manager showed one process list with fixed numbers. | New Task Manager (apps/system/taskmgr.c) with four tabs: Processes (open apps, status, End task, kernel services), Performance (CPU from the scheduler's idle counters on cores 1 and up, memory, GPU busy time and clock, disk, network, 60-second graphs), Devices (Processor, Graphics, Memory, Power supply and battery with health and cycles, Storage, Network, Sound, USB, Display, Input and all PCI devices, opened by clicking, scrolled with the wheel) and Services. taskmgr opens it. |
section 11 |
| 4 | Settings had one flat list of nine categories. | Windows-style Settings: categories (System, Personalization, Network & internet, Bluetooth & devices, Apps, Accounts, Time & language, Privacy & security, Update & recovery) open pages of tiles, tiles open sub-pages and some of those open further pages (System > Display > Graphics > Advanced graphics). A clickable path and a back button at the top. New pages: Graphics (GPU drawing, effects, partial redraws, wallpaper effects, drifting light), Advanced graphics (GPU details, screen copy engine, tear-free, frame check), Sound (volume, mute), Network details, Mouse & pointer, Installed apps, Your info, Date & time, Language & region, Recovery. | section 11 |
Tested: QEMU legacy BIOS and UEFI (1920x1080) boot, the desktop, every new wallpaper, Task Manager tabs and Settings pages; host GPU emulator tests. QEMU has no Intel graphics, so the blitter screen copy with partial areas is untested until it runs on the laptop.
What changed in v9.72
| # | Before (v9.71) | Now (v9.72) | Where |
|---|---|---|---|
| 1 | Every desktop frame rebuilt the whole screen (wallpaper copy, every window, shadows, glass) even when one line of text changed. On the Lenovo that was 21 ms per frame. | The operations that draw the screen are recorded instead of run, compared with the previous frame's list (lined up so inserted or removed operations do not shift the rest), and only the area that really changed is rebuilt, in order, clipped to that area. Scratch buffers used by the glass blur are re-run only where their result is needed; persistent buffers such as the blur cache are kept in step. A frame where nothing changed is skipped. The copy to the cursor-free buffer and the GPU screen copy cover only the changed area (with tear-free double buffering, the area changed since each buffer was last written; a full copy at least once a second). If anything touches the screen directly while it is being drawn, that frame is rebuilt in full. gpu partial off turns this off for comparison. |
section 10 |
| 2 | The taskbar showed as a blank light strip on the Lenovo. In QEMU it was correct, so the cause could not be found from here. | gpu check (and once automatically at desktop start) draws one frame on the GPU and the same frame on the CPU and compares every pixel. gpuinfo shows the result: how many pixels differ, where, the GPU and CPU values and the last operation that drew the first wrong pixel. If the frames differ, the check runs again with a wait between every operation; if that fixes it, drawing stays in that mode. The idle clock update on the GPU now goes through the normal (partial) frame instead of a separate taskbar path. |
section 10 |
| 3 | Wallpapers looked the same as with CPU drawing. | Wallpaper effects, made once when the wallpaper is set: colour bands removed (smooth areas are averaged in higher precision and dithered), a soft glow where bright areas meet darker ones, a soft depth blur toward the top, a light vignette and a gentle light from the upper left. gpu wallfx off shows the plain picture. Optional drifting light drawn by the GPU every frame (gpu light on, off by default because it redraws part of the screen ten times a second). |
section 11 |
| 4 | Parts of the mouse pointer could stay behind on the screen; the pointer area was smaller than the pointer. | The pointer area covers the whole pointer. | section 11 |
| 5 | Text drawn by a window in one frame was one operation, so typing one letter changed the whole window. | Text is batched in bands of at most 40 rows, so a change redraws only its band. The Welcome heading now fits its window. | section 10 |
Tested: host GPU emulator (partial screen rebuild equals a full rebuild in 24,000+ random frames with glass chains, caches, layers, text and outside writes, on the CPU and the emulator with delayed writes); QEMU legacy BIOS and UEFI boot, the desktop with CPU drawing and with the queue emulated, including a build that compared every partial frame with a full rebuild during typing, scrolling, dragging and window switching. QEMU has no Intel graphics, so the partial GPU screen copy and the frame check on real hardware are untested until they run on the laptop.
What changed in v9.71
| # | Before (v9.70) | Now (v9.71) | Where |
|---|---|---|---|
| 1 | The GPU ran at whatever clock the firmware left it at. | While the desktop is drawn by the GPU, the clock request is raised to the maximum the chip allows (read from RP_STATE_CAP); it is put back when GPU drawing stops. gpuinfo shows the current, requested and allowed frequencies. |
section 10 |
| 2 | Every text run was its own operation, with an ordering wait before most of them (284 waits per frame in the photo). | All text a window draws in one frame is collected and drawn with one masked operation (QEMU: 5 text operations for 556 characters, 0 ordering waits). | section 10 |
| 3 | Every window was redrawn completely every frame, and shadows and glass blur were recalculated every frame. | Each window's operations are recorded and compared with the previous frame; only the changed area is redrawn, and an unchanged window is not drawn. Shadows come from a precomputed 9-piece image (8 operations instead of one per pixel row). The glass blur is cached per window and reused until the window moves, resizes, or what is behind it changes. In QEMU a desktop frame went from 467 to about 311 operations. | section 10 |
| 4 | The wallpaper was scaled by picking the nearest pixel (blocky edges). | Bilinear scaling (smooth). This is done once on the CPU when the wallpaper or screen size changes, not with a new shader: a shader would need a per-pixel gather that the existing shaders do not do and that could not be verified on the hardware before release. It costs nothing per frame. | section 11 |
| 5 | gpuinfo showed one "last frame" time. |
The frame is split into CPU recording time, GPU drawing time and screen copy time, with submissions, operations, ordering waits, text operations, windows redrawn (full, partly, unchanged, by the CPU), blur cache hits and misses, and fast and slow shadows. | section 10 |
Tested: host GPU emulator (partial redraw gives exactly the same pixels as a full redraw in 300 random cases, on the CPU, the emulator and the emulator with delayed writes; self-test with 4000 random operations); QEMU legacy BIOS and UEFI boot, desktop with CPU drawing and with the queue in emulated mode. QEMU has no Intel graphics, so the clock and the real GPU path are untested until they run on the laptop.
What changed in v9.70
| # | Before (v9.69) | Now (v9.70) | Where |
|---|---|---|---|
| 1 | gpuinfo on the Lenovo showed "subslices 3 x 5 EUs" and the render context asked for 5 EUs per subslice. |
The EU fuse byte per subslice (0x9134) has 8 slots; on Gemini Lake slots 3 and 7 do not exist and read as disabled (0x88). EUs are now counted as 8 minus the set bits, as Linux does: 3 x 6 = 18 EUs, all powered. | section 10 |
| 2 | The boot self-test failed on the Lenovo ("wrong pixel: blur h op 15 146,90"): replayed on the host, op 13 (an "over" copy) had read its source 32 bytes too early, from the only test image whose rows were not a multiple of 64 bytes (544 bytes). The host emulator gave the right value, so the difference is hardware behaviour. | Test images now have rows padded to a multiple of 64 bytes (the screen and window buffers already were). Every operation is preceded by MEDIA_STATE_FLUSH before its constant data and interface descriptor are loaded. |
section 10 |
| 3 | A self-test mismatch turned GPU drawing off with only the last operation of the batch named. | The self-test keeps a copy of the images before each batch of 16 operations. On a mismatch it restores them and runs the batch again one operation per submission, comparing after each. If one at a time is correct, GPU drawing stays on in a slower mode with a wait between every operation; otherwise the first wrong operation is named. gpuinfo prints both results (operation, image, pixel, value read and expected). The host emulator now also holds back every GPU write until the next pipeline stall, which models overlapping operations; the queue passes with it, and planted faults produce the expected diagnosis. |
section 10 |
Tested in QEMU: legacy BIOS and UEFI boot to the live session and the desktop.
What changed in v9.69
| # | Before (v9.68) | Now (v9.69) | Where |
|---|---|---|---|
| 1 | The desktop was drawn by the CPU; the GPU only copied the finished picture to the screen. | On Intel gen 9 the GPU draws everything by default: 12 drawing shaders (fill, blend, masked colour for text and rounded corners, copy, masked copy, horizontal and vertical gradients, night-light tint, horizontal and vertical blur, premultiplied "over" with and without a mask), a draw queue that batches operations and inserts ordering waits only where two operations touch the same 8x8 blocks, and a drawing layer that turns rectangles, rounded rectangles, circles, shadows, text runs, gradients, wallpaper and single pixels into queue operations. Every CPU access to the screen buffer first waits for queued GPU work, so code that still draws pixels itself stays correct. Machines without Intel gen 9 graphics keep CPU drawing exactly as before. | section 10 |
| 2 | Moving or raising a window redrew its contents. | GPU compositor: every window draws into its own full-screen layer (premultiplied alpha). Each frame the layers are combined on the GPU with soft shadows, rounded corners, a translucent blurred title strip, transparency and glass behind the window. Dragging a window only re-combines its layer; its contents are not drawn again. | section 10 |
| 3 | No GPU effects. | Blurred glass behind the terminal, menus and panels, open/close/minimise animations (fade, slide, fall), smooth scrolling in the terminal and File Explorer (old and new contents slide over each other for 140 ms), all done by the GPU. `gpu fx on | off` switches the effects. |
| 4 | gpuinfo showed "3 x 5 EUs" without the fuse values; the screen was always copied by the blitter. |
gpuinfo prints the raw fuse registers (0x9120 and 0x9134). The CPU, blitter and shader screen copies are timed at start-up and the fastest one is used. |
section 10 |
| 5 | - | Safety: a self-test at boot runs 400 random operations on all shaders and compares every pixel with the CPU result, plus tests that the CPU sees what the GPU drew and the GPU never reuses stale cached data. If any test fails, or a drawing batch does not finish within 250 ms, GPU drawing turns off for the session and the CPU draws. gpu draw off, gpu draw on, Ctrl+Alt+G and gpu draw emu (the same queue run by the CPU, for testing on machines without Intel graphics). |
section 10 |
Tested: every shader in a host-side GPU emulator that executes the exact command batches and instruction bits (thousands of random operations, pixel-exact against the CPU); all shaders checked against Intel's assembler (IGA); QEMU legacy BIOS and UEFI boot, the desktop with CPU drawing, and the full queue and compositor in emulated mode (glass terminal, dragging, scrolling, switching modes). QEMU has no Intel graphics, so the real GPU path is untested until it runs on the laptop.
What changed in v9.68
| # | Before (v9.67) | Now (v9.68) | Where |
|---|---|---|---|
| 1 | On the Lenovo the blitter stopped at step 9: the GPU read the same old pixel (0x005B6198) with and without a CPU cache flush, so it was reading a different copy of the screen buffer than the CPU wrote, not a stale cache line. The buffer was an 8 MB array in the kernel image, in an area that start-up can move to other memory. | The screen buffer the desktop draws into (SCENE) is now a pointer. On gen 9 the GPU driver allocates it from the kernel heap, maps every page into the GPU's tables itself, copies the picture over and switches SCENE to it. Read tests on three buffers (test buffer, new screen buffer, old kernel array), each flushed and unflushed, show in gpuinfo which memory the GPU reads correctly, with the physical addresses and whether the old array was moved at boot. |
section 10 |
| 2 | The render engine was started but ran no programs. | Stage 2 of GPU drawing: a small assembler for native gen 9 GPU instructions (drivers/video/intel/gen9asm.c) and the first two compute shaders, a gradient fill and a block copy, run on the render engine through GPGPU_WALKER with surface states, binding tables, interface descriptors and constant data. Results are checked pixel by pixel (256x128 test images, then a full-screen gradient that is never shown). gpuinfo shows "Shaders: ready" with the times, or the step, the first wrong pixel and the reason. The render context now asks for all subslices and EUs to be powered, as Linux does on Apollo Lake and Gemini Lake. Both shaders were checked bit for bit against Intel's own assembler. QEMU has no Intel graphics, so only the fallback was tested. |
section 10 |
Tested in QEMU: legacy BIOS and UEFI boot to the live session and the desktop.
What changed in v9.67
| # | Before (v9.66) | Now (v9.67) | Where |
|---|---|---|---|
| 1 | On the Lenovo (Gemini Lake) the blitter passed its fill and copy tests (steps 7 and 8) but stopped at step 9: the GPU read old memory instead of what the CPU had just drawn. gpuinfo showed the firmware's GPU memory types as 0x0303030303030303 (no snooping), but not whether the new values were taken. |
gpuinfo shows the memory types before and after they are set and the wrong value read. If the GPU does not snoop, the test is repeated after flushing those lines from the CPU caches; if that works, every GPU screen copy flushes the scene first. The CPU and GPU copy times (the GPU time including the flush) decide which one draws. |
section 10 |
| 2 | gpu blit gpu printed "no NVIDIA GPU found": the older NVIDIA gpu command came first in the shell. |
gpu, gpu flip ..., gpu blit ... go to the Intel driver; the NVIDIA tool answers to nvidia and to other gpu subcommands when an NVIDIA card is present. |
section 11 |
| 3 | Only the blitter engine was used. | Stage 1 of GPU drawing: the render engine is started with the same execlist submission (23-page render context, PIPE_CONTROL completion), runs a store test, selects the compute pipeline, sets the state base addresses and the compute front end. gpuinfo shows "Render engine: ready" or the step and reason where it stopped. The submission code is now shared by both engines, and after the first run a context restores the state the hardware saved. QEMU has no Intel graphics, so only the fallback was tested. |
section 10 |
Tested in QEMU: legacy BIOS and UEFI boot to the live session and the desktop.
What changed in v9.66
| # | Before (v9.65) | Now (v9.66) | Where |
|---|---|---|---|
| 1 | On the Lenovo (Intel Gemini Lake, UHD 605) the screen was copied by the CPU only (2.1 ms per frame with write-combining and tear-free drawing on, per gpuinfo on the laptop). |
The GPU's blitter engine is started on gen 9 (Skylake to Comet Lake, Apollo Lake, Gemini Lake): forcewake, private PAT on Apollo/Gemini Lake, a page table for the GPU, a logical ring context, execlist submission, fill and copy self-tests and a test that the GPU sees the CPU's latest drawing. The CPU and GPU full-screen copies are timed and screen updates use the faster one. Every stage reports a step number and reason in gpuinfo if it stops; then drawing stays on the CPU. gpu blit gpu and gpu blit cpu choose by hand. QEMU has no Intel graphics, so only the fallback was tested. |
section 10 |
| 2 | The touchpad's interrupt pin was mapped directly only for Sunrise Point/Kaby Lake GPIO (INT344B); on Gemini Lake it fell back to learning or polling. |
Apollo Lake (INT3452) and Gemini Lake (INT3453) GPIO controllers are one ACPI device per community, so the GpioInt pin number is the pad number inside that community; it is mapped directly and checked (GPIO mode, input enabled). |
section 10 |
Tested in QEMU: legacy BIOS and UEFI boot to the live session and the desktop.
What changed in v9.65
| # | Before (v9.64) | Now (v9.65) | Where |
|---|---|---|---|
| 1 | This PC showed one tile for the system and one for a USB stick ("USB Drive (E:)"), with sizes estimated from the files. | A This PC page modelled on Windows 11: tiles for every volume on every disk and on the USB stick with drive letters (C: is the system, then D:, E: ... in disk order), volume labels, usage bars that turn red above 90 percent, real free space for FAT12/16/32, NTFS, exFAT and ext2/3/4, a command bar (Properties, Open, Refresh, Open Settings), the drives under This PC in the side bar, item and selection counts, and a Properties dialog with a used/free ring chart. EFI, Microsoft reserved, recovery, BIOS boot and swap partitions are hidden, as in Windows. Win+E opens File Explorer, and the Win key no longer types its letter into the focused window. Tested in QEMU with a SATA disk (NTFS "Windows", ext4 "Data", FAT32), an NVMe disk with GPT (EFI system plus FAT32) and a USB stick: all volumes appear with the right labels and free space, and the EFI partition is hidden. | section 11 |
| 2 | Legacy BIOS boot always asked for 1280x1024 (video=1280x1024), so a laptop panel showed a stretched, blurry picture. |
video=auto (now the default in the ISO and on installed disks) reads the screen's EDID through VBE 4F15h and sets the panel's native mode when the BIOS offers it (up to 1920x1080), then falls back through 1920x1080, 1600x900, 1366x768, 1280x1024 and smaller. Tested in QEMU with a 1600x900 EDID: the session started at 1600x900. |
section 2 |
| 3 | The framebuffer was mapped with the default caching, which firmware MTRRs normally make uncached, so every pixel write was a separate bus write. | The page attribute table gets a write-combining entry on every CPU and the framebuffer pages use it, so screen writes go out in bursts. gpuinfo shows the mapping and the time of a full-screen copy. |
section 10 |
| 4 | Intel graphics were only used through the firmware framebuffer. | New Intel driver (drivers/video/intel/igpu.c): finds the GPU (Sandy Bridge to Meteor Lake), reads the panel's native size, the current mode, whether the picture is scaled, and the display plane. On gen 8 to 12 it adds a second screen buffer in the graphics address table and switches between the two at the screen's vertical blank, so a frame is never shown half drawn (tear-free). It checks the plane format, that the framebuffer is the visible surface, that the new buffer keeps data and that the display really switches, and falls back to normal drawing otherwise; gpu flip off, gpu flip on and Ctrl+Alt+F switch it by hand. New gpuinfo command. QEMU has no Intel graphics, so this part was only tested for its fallback. |
section 10 |
Tested in QEMU: legacy BIOS and UEFI boot to the live session and the desktop.
What changed in v9.64
| # | Before (v9.63) | Now (v9.64) | Where |
|---|---|---|---|
| 1 | On the Lenovo (ELAN469D) the touchpad was read every 10 ms because the Intel GPIO interrupt pad was never set up (tpinfo had no "Intel GPIO interrupt" line). |
igp_setup dereferences a _CRS that returns a reference, records why it failed (reason code in tpinfo), and maps the ACPI GpioInt pin directly to its pad for Sunrise Point/Kaby Lake (INT344B: communities 0-47, 48-119, 120-151, in _CRS order). The pad must be in GPIO mode with RX enabled (checked in PADCFG DW0); then the pin is read and reports are fetched only when the pad asserts it, up to 6 per call. Other Intel controllers still use the learning method. Without a pin, polling now reads up to 4 reports per 10 ms tick and stops at an empty or repeated report. tpinfo shows reports per second. |
section 10 |
| 2 | Hybrid-mode pads (one finger per report, Contact Count only in the first report) could mix two frames: the first report of a new frame was added to the unfinished one, and a lifted finger delayed the frame. | A report with Contact Count > 0 starts a frame; it needs ceil(count / fingers per report) reports; reports with count 0 continue it. An unfinished frame is sent when the next one starts (counted as incomplete). Lifted fingers count toward completion. | section 10 |
| 3 | Taps were recognised, but there was no tap-and-drag, no software button areas, no palm or typing rejection, and acceleration depended on the report rate. | Gesture engine (tp_gesture.c, used by all touchpad drivers): tap-to-click (1 finger left, 2 right, 3 middle; at most 230 ms and 3 mm); tap-and-drag and double tap (a touch within 260 ms after a tap holds the left button); clickpad zones (a press in the bottom 12 mm clicks left or right by half; a thumb resting there does not move the pointer or turn one-finger motion into scrolling); palm rejection (contacts with Confidence 0 are dropped and cancel taps for that touch); typing rejection (taps and motion are ignored for 400 ms after a key on the PS/2 or USB keyboard); pointer ballistics by speed in mm/s from the high-resolution timer (about 4 px/mm below 25 mm/s rising to 16 px/mm at 250 mm/s, scaled by the speed setting); two-finger scrolling faster on quick strokes. New touchpad command: tap, speed 1-10, scroll natural/classic, zones, typing. Checked on the host with simulated touches (tap, drag, double tap, two-finger tap and scroll, zone clicks, resting thumb, typing, palm). |
section 10 |
| 4 | Windows could only be resized from an 18-pixel square in the bottom-right corner. | All four edges and corners resize (6 px outside and 4 px inside the frame, corners 14 px), with a two-headed arrow cursor while hovering or resizing. Minimum 240x160. Maximised windows are not resized. Tested in QEMU: dragging the left edge of the Terminal widened it from 90 to 102 columns with the right edge fixed. | section 9 |
| 5 | The installed boot partition held only boot/, boot.cfg, KERNEL64.BIN and EFI/BOOT/. |
It is laid out like a Windows system drive, with long file names: EFI/BOOT/BOOTX64.EFI, EFI/NoovexOS/ (copy of the loader and boot.cfg), NoovexOS/System/ (Drivers, Config, version.txt), NoovexOS/Boot/, NoovexOS/Fonts, Logs, Temp, Program Files, ProgramData, Users/Public, Users/Default/ (Desktop, Documents, Downloads, Pictures, Music, Videos) and README.txt. boot/, boot.cfg and KERNEL64.BIN keep their names (the loaders need them) and are marked hidden and system. The root directory now has 4 clusters. Checked on the host: the image from this code passes the repository FAT checker (tools/tests/fatck.py) with valid long-name checksums and no cross-linked clusters. |
section 8 |
Tested in QEMU: legacy BIOS and UEFI boot to the live session.
What changed in v9.63
| # | Before (v9.62) | Now (v9.63) | Where |
|---|---|---|---|
| 1 | On a Lenovo laptop with an ELAN469D touchpad (vendor 04F3, product 3081, 381-byte report descriptor) tpinfo showed "report desc not understood". tp_parse stopped at the first Main item with a Report Size above 64 or a Report Count above 1024, and at the first item that ran past the end of the data, so one vendor report or one damaged byte rejected the whole descriptor. |
tp_parse skips Main items it cannot use (Report Size outside 1..32 or Report Count above 4096) and still counts their bits, stops at a truncated item and keeps what it found before it, and tracks collection nesting errors. It records the item count, the skipped items, the first skipped byte and the cut-off byte. Tested on the host: the 11 Linux self-test touchpad descriptors give the same results as before; the same descriptors with a vendor feature report of 4096 bytes in front (rejected by v9.62) and cut to two thirds are recognised. |
section 10 |
| 2 | The report descriptor was read once at the bus speed from _CRS. A long read that arrived damaged could not be told apart from a descriptor the parser did not accept. |
It is read twice and compared (up to 4 attempts). If no two reads match, the read fails, or no touchpad or mouse layout is found, the bus is set up again at 100 kHz and the descriptor is read and parsed again. | section 10 |
| 3 | The ELAN protocol fallback for HID pads with vendor 0x04F3 never ran: every later descriptor register that failed wrote its invalid values into t->d, so the saved vendor was no longer 0x04F3. |
ih_get_desc writes t->d only after the descriptor is valid. After a valid descriptor no other register is tried at that address. Any device whose _HID starts with ELAN is a candidate and gets the ELAN protocol if HID setup fails. |
section 10 |
| 4 | tpinfo dump and tpinfo scan printed the plain tpinfo report: the shell compares only the first word. The descriptor buffer could also be overwritten by the ELAN probe. |
tpinfo scan, tpinfo dump and the new tpdump work. The last descriptor read is copied to its own buffer and printed in hex (32 bytes per line) with the vendor, product, address, register, whether the two reads matched, the bus speed, the parser counts and the layout found. tpinfo lines also show the read check, speed, item count, skipped items, cut-off byte and layout. |
section 10 |
| 5 | The _CRS address was put in front of the static resource list that already held it, so the same address was tried twice. |
Duplicates of the _CRS entry (same address, same controller path) are removed. |
section 10 |
Tested in QEMU: legacy BIOS boot to the live session. QEMU has no I2C touchpad, so the new probe paths are checked on the laptop with tpinfo and tpdump.
What changed in v9.62
| # | Before (v9.61) | Now (v9.62) | Where |
|---|---|---|---|
| 1 | Every ACPI method added in v9.54-v9.56 checked g_aml_ok, which nothing ever set. _CRS, _DSM, _PS0, power resources, SSCN/FMCN, the Intel GPIO pad table and all Lenovo/ASUS laptop functions (hotkeys, keyboard light, charge limit) therefore never ran. Found through tpinfo on a Lenovo laptop ("_CRS not used, _DSM HID reg none, SSCN none, FMCN none"). |
g_aml_ok is set when the AML interpreter has started (acpi_aml_init). Tested in QEMU: a method call through the touchpad code returns _HID of \_SB.PCI0 as "PNP0A08". |
section 10 |
| 2 | Touchpads whose _HID is a method or an EISA number showed "?" and ELAN pads listed only as PNP0C50 were never driven with the ELAN protocol. |
_HID is evaluated (string or compressed EISA ID). Any ELAN06xx ID matches the ELAN list. ELAN pads are tried with the ELAN I2C protocol first and HID over I2C second, as Linux does; a HID pad whose descriptor reports vendor 0x04F3 (ELAN) also gets the ELAN protocol if HID setup fails. |
section 10 |
| 3 | After the first HID descriptor register that answered, a failed setup ended the attempt at that address. | Each descriptor register (the _DSM one first, then 0x20, 0x01, 0x00, 0x10, 0x30) gets the full setup; an address that does not acknowledge is skipped at once. |
section 10 |
| 4 | tpinfo showed only the last failure. |
One line per attempt: address, protocol, descriptor register, vendor and product, report descriptor length and the exact failure (no device, descriptor invalid, report descriptor not read, not understood, no touchpad or mouse layout, ELAN step). New tpinfo dump prints the report descriptor in hex. |
section 10 |
The HID report descriptor parser was checked on the host against the 11 precision-touchpad descriptors in the Linux kernel HID self-tests (including ELAN 04f3:300b, 04f3:3045 and 04f3:313a): all are recognised as precision touchpads with correct ranges and sizes.
What changed in v9.61
| # | Before (v9.60) | Now (v9.61) | Where |
|---|---|---|---|
| 1 | NoovexOS uses fixed physical addresses: boot stack and disk/USB buffers at 0x09800000-0x09C80000, the module area at 0x0E000000 and .bss from 0x10000000 (about 107 MB). On a Lenovo laptop part of that range is not usable RAM. Evidence: under UEFI a boot page-table entry at 0x10002440 read back as all ones (page fault, error 0xB, reserved bits set); under legacy BIOS the kernel warned "the firmware uses memory inside a kernel area" and then jumped to 0xFFFFFFFFFFFFFFFF (page fault at RIP and CR2 all ones), the value memory returns when nothing keeps it. Writes there survive only while they stay in the CPU cache. |
early_remap() runs first in kmain, before .bss is cleared. It reads the firmware map the loader left at 0x7000 (UEFI summary or E820) and checks every 2 MB page of 0x09800000-0x09E00000 and 0x0E000000 up to the end of .bss. Every page that is not fully inside usable RAM gets the same virtual address but new physical memory, taken from the top of the largest usable region above 4 GB: the pages are copied through a temporary window (PDPT slot 4 with a page directory in .data), and their page-directory entries are rewritten. The page that holds the running stack is copied and switched in one assembly sequence that does not touch the stack. The stolen range is removed from the memory map after mm_load, so the page allocator never hands it out. Process page tables copy the kernel's PML4 entries, so they see the same mapping. The boot log says how many MB were moved and where. Tested in QEMU (6 GB) with 0x09800000-0x09A00000 and 0x10000000-0x11000000 marked as firmware memory: UEFI and legacy BIOS boot to the live session. If there is no RAM above 4 GB to move to, the boot log says so. |
section 3 |
| 2 | The warning "the firmware uses memory inside a kernel area" did not say which area. | One line per affected area with the firmware type and the range. | section 3 |
Fixed DMA buffers that are moved this way (disk and USB buffers at 0x09800000-0x09C80000) are still handed to devices by their old physical address, so AHCI, NVMe and USB storage may not work on a machine where that range is not RAM.
What changed in v9.60
| # | Before (v9.59) | Now (v9.60) | Where |
|---|---|---|---|
| 1 | On a Lenovo laptop (UEFI and legacy BIOS) start-up finished ("interrupts on, entering main loop") and then nothing happened: no live screen. In QEMU it worked. | Two interrupt problems that only real firmware exposes were fixed. (a) The I/O APIC was never touched, so redirection entries left unmasked by the firmware (ACPI SCI, USB and others) could still deliver interrupts. Every I/O APIC listed in the ACPI MADT (or 0xFEC00000 if there is no MADT) now has all entries masked before interrupts are enabled; the boot log reports how many were unmasked. (b) Vectors 48-255 went to isr_ignore, which sent no end-of-interrupt. One stray vector kept its in-service bit set in the local APIC, which blocked the timer and every lower-priority interrupt, and the main loop then waited in hlt forever. Stray vectors now send the local APIC EOI (xAPIC register 0xB0 or x2APIC MSR 0x80B), and the highest in-service vector and a counter are recorded. Tested in QEMU with a self-sent vector 0x50: it was recorded and the timer kept running. |
section 2 |
| 2 | A hang inside the main loop gave no information. | The main loop sets a stage number before each part (touchpad, PS/2 pads, VM mouse, ACPI, sound, USB mouse, input, USB hotplug) and counts passes. The timer interrupt checks the count; if it has not changed for 6 seconds it prints a screen with the stage, the interrupted RIP, the number of stray interrupts and the last stray vector, the I/O APIC count and how many lines were masked. Tested in QEMU with a deliberate endless loop. | section 2 |
What changed in v9.59
| # | Before (v9.58) | Now (v9.59) | Where |
|---|---|---|---|
| 1 | The boot page tables (PML4, PDPT, four page directories, two framebuffer page directories) were at the start of .bss, physical 0x10000000. On a Lenovo laptop (UEFI) the v9.58 early fault screen showed: page fault, error code 0xB (present, write, reserved bit set), address 0x11000000, CR3 0x10000000, step 1 (clearing kernel memory). The entry that maps 0x11000000 had been overwritten after it was built, by something outside the kernel: a device still writing to memory after the firmware hand-over, or firmware memory at that address. |
The boot page tables are in .data (about 0x1F39000 in the current build), inside the kernel image the loader copies to 1 MB; kernel code already runs from that memory on the same laptop. kmain clears .bss from __bss_start. The legacy BIOS path uses the same tables. Tested in QEMU: UEFI and legacy BIOS boot to the live session. |
section 2 |
| 2 | The early fault screen started at the top and covered the UEFI loader's own messages (including its warnings about firmware memory overlapping kernel areas). | It starts a third of the way down. For a page fault it also prints the PML4, PDPT and PD entries used for the address and where the PD entry is, and for the fault address, for 0x10000000 and for the page tables it prints which UEFI memory-map entry (type and range) they fall in. | section 2 |
What changed in v9.58
| # | Before (v9.57) | Now (v9.58) | Where |
|---|---|---|---|
| 1 | A CPU exception in the first steps of kmain was invisible: no interrupt table was loaded yet, so a UEFI boot froze inside the firmware's handler (all coloured boot marks except the white one) and a legacy BIOS boot triple-faulted and restarted. Reported on a Lenovo laptop in both modes. |
kmain first loads a small early interrupt table (core/init/early_fault.c, stubs eexc0-eexc31 in isr64.S, table in .data so clearing .bss does not erase it). Any exception before the normal table is loaded prints a screen with the exception number and name, error code, RIP, CR2, CR3, stack, the boot mode, and the start-up step, then halts instead of restarting. Tested in QEMU with a deliberate page fault at step 3: the screen appears under UEFI (OVMF) and legacy BIOS. |
section 2 |
| 2 | Only one mark (white) was drawn inside kmain. |
Four more marks after each early step (6 purple: memory cleared, 7 cyan: per-CPU set up, 8 pink: kernel snapshot taken, 9 grey: memory map read), then the white mark after the FPU setup, so a photo shows exactly where start-up stops. | section 2 |
| 3 | The UEFI entry kept whatever CR4 the firmware left. | CR4.CET (bit 23) is cleared on entry in case the firmware left control-flow enforcement on. | section 2 |
What changed in v9.57
| # | Before (v9.56) | Now (v9.57) | Where |
|---|---|---|---|
| 1 | Settings > Devices showed a fixed set of rows (keyboard, touchpad, USB list, printer, laptop). | The page has one tab per subsystem, each filled only from detected hardware: Input (PS/2 keyboard, the touchpad driver actually in use, USB keyboards and mice), USB (every xHCI controller from the PCI scan and every enumerated device with name, vendor:product, port and a NEW mark, USB drive state), Storage (every disk with size and which one is in use, plus every storage controller including RAID/Intel RST), Network (every Ethernet and Wi-Fi card by name with link state, and the IP address), Media (graphics cards by vendor and ID, the framebuffer mode, sound cards with driver state, cameras), Printers (network printer by model name and status, USB printers) and Laptop (vendor interface, keyboard light and charge limit controls). A coloured dot shows the state: working, attention or off. | section 11 |
| 2 | printer <ip> only stored the address. |
It sends Get-Printer-Attributes (IPP operation 0x000B) and reads printer-make-and-model and document-format-supported. The status says whether the printer takes PWG raster (ready), only Apple raster (AirPrint, not supported yet) or neither; the model name is shown in Settings. |
section 9 |
| 3 | The Wi-Fi link state was private to the Wi-Fi code. | g_wnd_up is shared so Settings can show it. |
section 9 |
Cameras still have no video: streaming needs isochronous USB transfers in the xHCI driver.
What changed in v9.56
| # | Before (v9.55) | Now (v9.56) | Where |
|---|---|---|---|
| 1 | A USB mouse or keyboard plugged in while running worked, but nothing showed which devices were connected. | Every USB device that is enumerated is recorded (usbdev_note: vendor and product ID, product name read from its string descriptor, kind from its interface classes: keyboard, mouse, camera, printer, audio, hub and others) and removed again when it is unplugged. A notification appears when one is connected. New Settings page "Devices" lists the keyboard, the touchpad type and every USB device with a "New" badge for 10 seconds; new command usbdevs (alias lsusb). Tested in QEMU: a USB mouse added while running appeared as "Mouse QEMU USB Mouse 0627:0001 port 5". |
section 11 |
| 2 | No Lenovo or ASUS laptop extras. | New drivers/acpi/laptop.c, using the AML interpreter. ThinkPad (HKEY device LEN0068/IBM0068/LEN0268): hotkeys enabled with MHKM/MHKC, events read with MHKP (brightness 0x1010/0x1011, volume 0x1015-0x1017, keyboard light 0x1012, touchpad 0x1008/0x1009), keyboard light with MLCG/MLCS, charge limit 80% with BCSS. IdeaPad (VPC2004): conservation mode with SBMC 3/5, keyboard light with SALS/HALS. ASUS (WMI device PNP0C14 with _UID "ATK"): INIT, events through _WED(0xFF) (brightness 0x11-0x2E, volume 0x30-0x32, keyboard light 0xC4/0xC5/0xC7, touchpad 0x6B), keyboard light (device 0x00050021) and charge limit (device 0x00120057) through DEVS/DSTS; older ASUS ATK0100 events directly. The touchpad toggle stops all touchpad drivers. Settings > Devices shows keyboard light and charge limit controls; command laptop, kbdlight 0-3, chargelimit on/off. Not tested on real laptops. |
section 10 |
| 3 | No printing. | New net/print/ipp.c: IPP Everywhere network printing. Text is rendered with the anti-aliased font into an A4 PWG raster (300 dpi, 8-bit grey, PackBits line compression with line repeat) and sent as a Print-Job (IPP 2.0, document-format image/pwg-raster) by HTTP POST to port 631, /ipp/print. New sock_send_rel sends with a window of 8 segments, waits for ACKs and resends after a timeout, so large jobs are not lost. Commands printer <ip>, print (test page) and print <file>; Settings > Devices has a test page button. Tested on the host: the job decodes to a valid 2480 x 3508 page (21 KB for the test page). Not tested against a real printer. |
section 9 |
| 4 | - | USB cameras (video class 0x0E) are detected and listed by name. Streaming needs isochronous USB transfers, which the xHCI driver does not have yet, so there is no picture. | section 10 |
What changed in v9.55
| # | Before (v9.54) | Now (v9.55) | Where |
|---|---|---|---|
| 1 | On Intel laptops the I2C touchpad was read every 10 ms because there was no driver for the Intel GPIO controller, whose pad numbering differs between CPU generations. | New drivers/input/touchpad/tp_igpio.c. It needs no per-generation table: it evaluates _CRS of the GPIO controller named in the touchpad's GpioInt resource, takes every Memory32Fixed window as a pin community, reads the pad table offset (PADBAR, community offset 0x0C) and the pad size (16 bytes if the revision at offset 0 is 0x94 or newer, otherwise 8), and lists every pad. While polling, it samples the RX state (PADCFG0 bit 1) of every pad before each read and compares it with whether the touchpad had a report. When exactly one pad has matched every sample (at least 24 with data and 24 without), with either polarity, that pad is the interrupt line: from then on the touchpad is read only while the pin is active, checked on every pass of the main loop instead of every 10 ms. If no pad matches, polling continues. tpinfo shows the learning state. |
section 10 |
| 2 | Synaptics PS/2 touchpads worked only as a basic mouse. | New drivers/input/ps2/syn_ps2.c. After the ELAN check, mouse_init sends the Synaptics identify query (sliced command 0x00, magic byte 0x47), reads the capabilities (0x02) and switches to absolute mode with W value (mode byte 0xC1). The 6-byte packets are checked and parsed (X, Y, pressure, W). W 0 means two fingers, W 1 three or more, W 3 a pass-through packet from a TrackPoint, which is sent as relative movement and buttons. The frames go through the same gesture engine as the other touchpads: tap to click, two-finger scrolling, three-finger swipes. If any step fails, the standard mouse setup runs as before. |
section 10 |
| 3 | - | Checked the Wi-Fi changes from v9.54 again (separate transmit buffers, full-ring check, missing-card check); no change needed. | section 9 |
Not done: ALPS absolute mode and Synaptics RMI4 over SMBus. Neither the Synaptics driver nor the GPIO learning could be tested here (QEMU has neither device); QEMU boot and the standard PS/2 mouse fallback were tested.
What changed in v9.54
| # | Before (v9.53) | Now (v9.54) | Where |
|---|---|---|---|
| 1 | Wi-Fi used one DMA buffer for every transmit slot. When several frames were queued quickly (TCP bursts, DNS plus HTTP), a new frame overwrote one the card had not sent yet, so frames went out corrupted and connections stalled. The transmit ring was also never checked for free space. | Every ring slot has its own 2 KB DMA buffer (up to 128). Before queuing, the driver checks the card's read index; if the ring is full it waits up to about 1 ms and then drops the frame instead of overwriting one. If the card stops answering (registers read 0xFFFFFFFF, for example after a PCIe link drop), sending and receiving stop touching the rings instead of processing garbage. wifi status shows the new counters ring-full and card-gone. |
section 9 |
| 2 | Touchpad I2C controllers above 4 GB were rejected (dw_present and the PCI scan accepted only 32-bit addresses). |
64-bit BARs are read in full and mapped with acpi_hi_map; dw_present accepts addresses below 128 TB. |
section 10 |
| 3 | Intel LPSS controllers were only taken out of reset when they did not answer, and the private remap registers were never written. | For every Intel LPSS I2C controller the driver sets memory and bus-master enable, makes sure the reset register (private offset 0x204) is 7 and writes the controller's own address to the remap registers 0x240/0x244, as Linux does. | section 10 |
| 4 | I2C timing used whatever the BIOS left or defaults for a 100 MHz clock; the SDA hold time was never set. | SSCN and FMCN are evaluated with the AML interpreter under the controller's ACPI node and written to the high/low count registers, and their third value to the SDA hold register (0x7C). Without them, empty registers are computed from the controller clock (AMD0010 133 MHz, AMDI0010/AMDI0510 150 MHz, Intel assumed 216 MHz, which gives a slower, safe bus), and an empty SDA hold is set to 300 ns. |
section 10 |
| 5 | The touchpad's I2C address and interrupt pin came from a static scan of the tables, without running BIOS code, so a _CRS method that chooses between two addresses could give the wrong one. |
_CRS is evaluated and its I2cSerialBus (address, speed, controller) and GpioInt (pin, polarity, controller) descriptors are tried first; the static scan and address guessing remain as fallbacks. The touchpad probe now runs after the AML interpreter has started. |
section 10 |
| 6 | The HID descriptor register was guessed (0x20, 0x01, 0x00, 0x10, 0x30). | _DSM with the HID-over-I2C UUID 3CDFF6F7-4267-4555-AD05-B30A3D8938DE, revision 1, function 1 gives the register; it is tried first, then the guesses. |
section 10 |
| 7 | Touchpads that need the OS to power them on stayed off. | Before probing, the power resources in _PR0 are switched on (_ON) and _PS0 is run, followed by a 30 ms wait. |
section 10 |
| 8 | tpinfo did not show any of this. |
Two new lines: which ACPI methods were used (_CRS, _DSM register, _PS0, power resources) and the I2C timing (SSCN, FMCN, SDA hold, Intel LPSS setups). |
section 10 |
Tested in QEMU only: boot, tpinfo scan (QEMU has no I2C touchpad, so the new ACPI paths ran without finding one). The Intel GPIO interrupt driver and Synaptics/ALPS multi-finger support are not in this version.
What changed in v9.53
| # | Before (v9.52) | Now (v9.53) | Where |
|---|---|---|---|
| 1 | NVMe disks behind Intel VMD ("Intel RST" with NVMe on 11th-generation and newer Intel CPUs) were invisible: the VMD controller hides them in its own PCI domain, so no disk was found. | vmd_find_nvme (drivers/storage/nvme.c) finds the VMD controller (PCI class 01:04, Intel device IDs 9A0B, 467F, A77F, 7D0B, AD0B and others), enables it, reads its bus restriction (VMCAP 0x40 / VMCONFIG 0x44: first bus 0, 128 or 224), and scans its configuration window (BAR0, one MB per bus) for an NVMe device (class 01:08:02). The NVMe driver then uses that device's own configuration space through the window. If the firmware left the NVMe address empty, it is placed at the start of VMD memory window 1 (BAR2) and the parent bridge's memory window and command bits are set. The NVMe driver polls and needs no interrupts, so VMD's interrupt remapping is not needed. Not tested on real VMD hardware (QEMU has no VMD). |
section 8 |
| 2 | The RAID-class fallback in ahci_init could pick the VMD controller and treat it as SATA. |
VMD device IDs are skipped, and a RAID-class controller is used only if its AHCI version register reads 1.0 to 1.3.1. | section 8 |
| 3 | disks printed "NVMe present - read/identify not implemented yet" even when the NVMe driver worked. |
It says whether the driver is active, and prints the VMD device and what was found behind it. The installer shows the same reason when no disk is found. | section 8 |
What changed in v9.52
| # | Before (v9.51) | Now (v9.52) | Where |
|---|---|---|---|
| 1 | When no disk was found, the installer showed "/dev/hda - 2048 MB", a made-up size. | It shows "no disk found". | section 8 |
| 2 | SATA controllers in RAID mode (PCI class 01:04, for example Intel RST on many laptops) were ignored, so their disks were not found. | ahci_init also accepts class 01:04 controllers and drives them through their AHCI registers. Not tested on real RAID-mode hardware. |
section 8 |
| 3 | The installer's target page showed only disks. | Below the disks it lists the partitions of the selected disk with name, size and file system (from dm_load, GPT or MBR). |
section 8 |
| 4 | The volume name was "NOOVEXOS"; MBR partitions of type 0x7F showed as "NoovexOS data". | The default volume name is "NoovexOS" (FAT stores it in capitals; NTFS keeps "NoovexOS"), and type 0x7F partitions show as "NoovexOS". | section 8 |
What changed in v9.51
| # | Before (v9.50) | Now (v9.51) | Where |
|---|---|---|---|
| 1 | Firefox opened as a window the size of the whole screen, so it looked like a full-screen black background and could not be moved. | nvxwm shrinks any window wider than the screen minus 40 pixels (or taller than the screen minus the title bar and 40 pixels) to 3/4 of the screen and centres it, and limits later resize requests the same way. Firefox draws no tabs in the title bar (browser.tabs.inTitlebar 0), so the window manager's title bar is there to drag it. Tested on a host X server: a 1280x1024 window became 960x768 at (160,106) with a 22-pixel title bar. |
section 11 |
| 2 | nvx-xinput sent every mouse movement to X one by one and woke every 20 ms. |
Movements in one batch are merged into one (the last position) before any click or key, and it wakes every 8 ms. | section 11 |
| 3 | Firefox started extra helper processes (network, media decoder, extensions, fork server) and wrote a disk cache. | user.js turns those processes and the disk cache off, uses software WebRender and skips the blank start window and the bookmark import. When X starts in overlay mode, the Firefox libraries are read once in the background so the first start reads less from disk. The start time was not measured. |
section 6 |
| 4 | "This PC" showed RAM size as the size of C: when the data partition was not NVX4 (for example 2048 MB on a 2 GB machine) and never showed the real disks. | C: uses the data partition size (g_nvx_secs); in the live session the tile is called "Live RAM disk (C:)". A line lists every physical disk with its real size. |
section 11 |
What changed in v9.50
| # | Before (v9.49) | Now (v9.50) | Where |
|---|---|---|---|
| 1 | Only the first SATA port with a drive was used. A 500 GB disk on another SATA port was invisible to the installer, which then tried the small disk and said "The disk is too small." | Every SATA port with a disk is its own disk (ahci_select), and IDE master and slave are both listed. The installer's target page lists every disk with its size (click it or use Up/Down). Tested in QEMU: SATA ports 0, 1 and 2 plus NVMe all listed, install to the 500 GB disk on port 2, boot from it. |
section 8 |
| 2 | The Disk Manager only understood MBR. | It reads GPT (up to 256 entries) and MBR, shows partition names and file systems (NTFS, exFAT, FAT32 with label, FAT16, NVX4, ext4), deletes partitions (GPT: primary and backup tables rewritten with correct CRC32s), erases the whole disk and switches disks (Next disk). Every delete or erase asks for confirmation (Y/N). Tested on a 500 GB GPT disk with EFI, MSR, Windows data and Linux partitions: the table stays valid after a delete. | section 8 |
| 3 | Whole-disk install used a fixed small data partition and left the backup GPT on 4K-sector disks. | The data partition uses the rest of the disk (NoovexFS up to 64 GB, FAT32 up to 32 GB, FAT16 up to 2 GB, NTFS up to 2 TB). Both GPT copies are wiped first. | section 8 |
| 4 | "The disk is too small." gave no details. | The message names the disk, its size and the size needed, and points to Next disk. | section 8 |
| 5 | IDE used 28-bit addresses only, so sectors above 128 GB wrapped around. | IDE reads and writes above 128 GB use LBA48 commands. Not yet tested in QEMU. | section 8 |
What changed in v9.49
| # | Before (v9.48) | Now (v9.49) | Where |
|---|---|---|---|
| 1 | Firefox and other Linux GUI apps opened in a full-screen copy of the whole X screen (black around the window) or in the "X Display" box. | They open on the NoovexOS desktop: the X root window is painted with the key colour 0x010203 and only real X windows are drawn over the desktop (overlay mode). The launcher switches to overlay mode before starting the app, closes the X Display box if it is open and gives the keyboard to the app as soon as its window appears; clicking the desktop or a NoovexOS window takes the keyboard back. | section 11 |
| 2 | The overlay was refreshed every 60 ms by sampling 1/24 of the pixels, and any change redrew the whole desktop. | A shadow copy of the X screen is compared pixel by pixel every 20 ms (every 80 ms after 25 quiet checks). Only the changed rectangle is copied to the screen; the whole desktop is redrawn only when part of an X window disappears (a pixel turns into the key colour) or a menu, popup or drag is active. Tested with xdemo: 436 partial updates against 3 full redraws. |
section 11 |
| 3 | Keys sent to X apps had no Ctrl or Alt, so shortcuts such as Ctrl+L, Ctrl+T or Ctrl+C did nothing; nvx-xinput waited up to 50 ms between reads. |
Every key carries the Ctrl and Alt state (byte 3 of the event record) and nvx-xinput presses Control_L / Alt_L around the key. It wakes at least every 20 ms. |
section 10 |
| 4 | Every Firefox start ran fc-cache -f (rebuilds the whole font cache) and ld.so --list on libxul.so, and started with a random new profile. |
The font cache is updated once per boot without -f; the library check only runs after a failed start. Firefox uses the fixed profile /root/.nvx-firefox with a user.js that turns off telemetry, studies, Safe Browsing downloads, Pocket, the welcome page, the default-browser check, IPv6 lookups, prefetching, smooth scrolling and animations, limits Firefox to one content process without preloading, and sets 30 frames per second. |
section 6 |
| 5 | The X server started only when an X app was opened. | 3 seconds after the desktop appears, if Firefox is installed, the X server starts in the background in overlay mode, so opening Firefox does not wait for it. The wait after starting X for a new app is 1 second (was 2). | section 11 |
What changed in v9.48
| # | Before (v9.47) | Now (v9.48) | Where |
|---|---|---|---|
| 1 | The installer refused kernels larger than 15 MB ("The kernel size stamp is invalid."). The kernel is about 32 MB since the Linux sysroot is built in, so nothing could be installed. The boot-time copy of the kernel (g_kimg_snap, a 15 MB static array) and the boot signature check had the same limit. |
Limit 64 MB everywhere. At boot only the writable part of the kernel (.data, about 320 KB) is copied, plus a 32-bit FNV-1a checksum of every 4 KB page of the code part; the full image is assembled from memory and that copy when the installer or kcheck needs it. The boot partition is 128 MB on 512-byte-sector disks (was 64 MB; two kernel copies need about 62 MB) and the installer checks that the image fits. |
section 8 |
| 2 | Writing the "installed" marker could hit the old disk. When the target disk was blank, the live system had it mounted as a scratch disk at LBA 2048, and any file sync during installation overwrote the new boot partition (found in testing: "Sector 2080 differs"). | Installation stops all writes to the previous mount first (disk_ok=0, pending write-back cleared); the new data partition is mounted when it is formatted. |
section 8 |
| 3 | Booting an installed disk showed the live terminal and installer every time. | The installer writes /etc/nvx-installed. On the first boot from the installed disk NoovexOS goes straight to setup; setup writes /etc/nvx-setup-done (user name, accent, theme, language, keyboard, time zone), and every later boot goes straight to the login screen. The ISO without an installed disk still starts the live session. Tested: install, first boot to setup, second boot to login. |
section 8 |
| 4 | NVMe controllers with their registers above 4 GB (64-bit BAR, common with UEFI firmware and on new laptops) were rejected. acpi_hi_map could only map addresses below 512 GB. |
64-bit BARs are read in full and mapped with acpi_hi_map, which now maps any physical address below 128 TB (except the user-space range) with uncached 2 MB pages, creating page-table levels as needed. Tested in QEMU with the NVMe registers at 0xC00000000: installation to NVMe, then UEFI boot from that NVMe disk, mount of the data partition and setup. |
section 8 |
| 5 | With several disks, NoovexOS used the first one it found (IDE, then AHCI, NVMe, VirtIO). | All disks are probed and the one with a NoovexOS data partition (type 0x7F) is used; otherwise the first one. Tested: IDE disk with the ISO plus an installed NVMe disk boots into the installed system. | section 8 |
| 6 | No mouse integration in virtual machines. | New drivers/input/vm/vmmouse.c. Detects the hypervisor (CPUID leaf 0x40000000). VirtualBox: VMMDev (PCI 80EE:CAFE) - reports guest info, sets "guest can use absolute pointer" and reads the host pointer position every timer tick; buttons keep coming from the emulated PS/2 mouse. VMware and QEMU: VMware backdoor (port 0x5658) vmmouse protocol with absolute position, buttons and wheel. The position is turned into the usual pointer movement, so every screen and the X server see it. New command vminfo. Tested in QEMU: three absolute positions sent from the host moved the pointer exactly to them. VirtualBox could not be tested here. |
section 10 |
What changed in v9.47
| # | Before (v9.46) | Now (v9.47) | Where |
|---|---|---|---|
| 1 | arch_prctl knew only ARCH_SET_FS and ARCH_GET_FS. Firefox runs some libraries (fonts, spelling, XML) inside an RLBox WebAssembly sandbox built with wasm2c, which points the GS segment base at the sandbox memory ("Segue") with arch_prctl(ARCH_SET_GS). The call returned EINVAL, the log showed wasm_rt_syscall_set_segue_base error: Invalid argument, and Firefox aborted (exit code 139). |
ARCH_SET_GS (0x1001) and ARCH_GET_GS (0x1004) work. The value is stored per thread (struct jproc, field gsbase) and written to IA32_KERNEL_GS_BASE (MSR 0xC0000102) when the scheduler switches to the thread; the kernel keeps its own per-CPU pointer in IA32_GS_BASE and swapgs exchanges the two on every user/kernel transition, so they never collide. Threads and fork children inherit the value, execve clears it. ARCH_GET_CPUID (0x1011) returns 1. Tested inside NoovexOS on 2 CPUs: a mov rax, gs:[0] instruction returns the main thread's value while another thread uses a different GS base, both before and after the switch, and a forked child reads the inherited value. |
section 6 |
| 2 | The crash report's "Reason" line could show a fragment such as ). |
A decoded reason needs at least 6 characters, 4 of them letters. | section 6 |
Found through the v9.45 Firefox report: 158 arch_prctl -22 x1 and the wasm_rt_syscall_set_segue_base log line.
What changed in v9.46
| # | Before (v9.45) | Now (v9.46) | Where |
|---|---|---|---|
| 1 | Linux GUI apps installed with apt (for example AbiWord) started without the settings GTK needs here and wrote their errors nowhere visible. Typing their name in the terminal did not start the X server. | New launcher /usr/bin/nvx-xrun NAME COMMAND...: sets DISPLAY, HOME, GTK_A11Y=none, NO_AT_BRIDGE=1, GSETTINGS_BACKEND=memory (settings kept in memory, no dconf or D-Bus), GDK_BACKEND=x11, QT_QPA_PLATFORM=xcb, LIBGL_ALWAYS_SOFTWARE=1, SDL_AUDIODRIVER=dummy and XDG_RUNTIME_DIR; creates the config folders; waits for the X server; lists missing libraries; builds the font cache once per boot; writes all output to /tmp/NAME.log and the exit code to /tmp/NAME.done. |
section 6 |
| 2 | Only Firefox had a failure report. | Every app started from its icon or by typing its name gets the same watcher: full screen when its window appears, and the report (log tail, crash address and library, failed syscalls, process list) if it exits with an error or shows no window within 2 minutes. A normal exit (code 0) just closes full screen. | section 6 |
| 3 | - | Typing the name of an installed GUI app in the terminal (any program with a .desktop file in /usr/share/applications, for example abiword or abiword report.docx) starts the X server if needed and runs the app through nvx-xrun. Shells, interpreters, sudo and apt are never redirected. If the app is missing, the report shows the apt install command to run. |
section 6 |
Tested inside NoovexOS with test .desktop entries: abiword (not installed) was recognised as a GUI app and reported "not installed - run: sudo apt install abiword" with code 127; python3 was not redirected; xdemo started through nvx-xrun and connected to the X server, with its output in /tmp/xdemo.log.
What changed in v9.46
| # | Before (v9.45) | Now (v9.46) | Where |
|---|---|---|---|
| 1 | Linux GUI apps installed with apt (for example AbiWord) started without the settings GTK needs here and wrote errors nowhere visible. Typing their name in the terminal did not start the X server. | New launcher /usr/bin/nvx-xrun NAME COMMAND...: sets DISPLAY, HOME, GTK_A11Y=none, NO_AT_BRIDGE=1, GSETTINGS_BACKEND=memory (settings kept in memory, no dconf or D-Bus), GDK_BACKEND=x11, QT_QPA_PLATFORM=xcb, LIBGL_ALWAYS_SOFTWARE=1, SDL_AUDIODRIVER=dummy, XDG_RUNTIME_DIR; creates the config folders; waits for the X server; lists missing libraries; builds the font cache once per boot; writes all output to /tmp/NAME.log and the exit code to /tmp/NAME.done. |
section 6 |
| 2 | Only Firefox had a failure report. | Every app started from its icon or by typing its name gets the same watcher: full screen when its window appears, and the report (log tail, crash address and library, failed syscalls, process list) if it exits with an error or shows no window within 2 minutes. A normal exit (code 0) just closes full screen. | section 6 |
| 3 | - | Typing the name of an installed GUI app in the terminal (any program with a .desktop file in /usr/share/applications, for example abiword or abiword report.docx) starts the X server if needed and runs it through nvx-xrun. Shells, interpreters, sudo and apt are never redirected. If the app is missing, the report says which apt install command to run. |
section 6 |
Tested inside NoovexOS with test .desktop entries: abiword (not installed) was recognised as a GUI app and reported "not installed - run: sudo apt install abiword" with code 127; python3 was not redirected; xdemo started through nvx-xrun and connected to the X server, with its log in /tmp/xdemo.log.
What changed in v9.45
| # | Before (v9.44) | Now (v9.45) | Where |
|---|---|---|---|
| 1 | A MAP_SHARED mapping of a file was a one-time copy. write() to the file did not change the mapping, and changes made through the mapping never reached the file. SQLite (Firefox history, bookmarks, settings) reads through a shared mapping and writes with write(). |
Shared file mappings are tracked (256 slots). write and pwrite64 copy the new bytes into every shared mapping of that file at once. Changed pages go back into the file on msync, on munmap and when the process exits. fork copies the tracking. Tested inside NoovexOS: pwrite seen through the mapping, a write through the mapping read back with pread after msync. |
section 6 |
| 2 | System V shared memory (shmget, shmat, shmdt, shmctl) returned ENOSYS. X clients use it (MIT-SHM) so the X server reads images straight from shared memory instead of through the socket. |
Implemented for Linux programs: 64 segments up to 1 GB each, backed by memfd frames, private or by key, IPC_CREAT/IPC_EXCL, SHM_RDONLY, SHM_RND, IPC_RMID (freed after the last detach), IPC_STAT/SHM_STAT, IPC_INFO/SHM_INFO, IPC_SET, SHM_LOCK. Attachments are inherited by fork and detached at exit. Tested inside NoovexOS: two attachments of one segment see the same bytes. |
section 6 |
| 3 | A file name part longer than 63 characters returned ENAMETOOLONG (the NoovexOS file system stores names in 64 bytes). | Parts up to 255 characters are accepted, as on Linux. A part longer than 63 is stored under a stable 63-character name: the first 46 characters, ~, and a 64-bit FNV-1a hash of the full name in hex. Open, create, stat and delete with the long name all reach the same file; a directory listing shows the stored name. Tested inside NoovexOS with a 120-character name. |
section 6 |
| 4 | When Firefox showed no window, the report could not say what each process was doing. | The report lists every running Linux process: pid, name, state (run, wait, sleep), its most recent syscall and how many syscalls it has made, so a stuck process stands out. | section 6 |
What changed in v9.44
| # | Before (v9.43) | Now (v9.44) | Where |
|---|---|---|---|
| 1 | A large anonymous mmap built page-table entries for the whole range up front (about 32 MB of real memory for each 16 GB) and was refused when the free-frame check failed. Firefox reserves 16 GB of address space 30 times (JavaScript and WebAssembly memory) and got ENOMEM every time. |
Private anonymous mappings larger than 256 MB are stored as reserved ranges (512 slots). No page tables are built; a page gets its entry on the first touch, from the page-fault handler or when the kernel copies data into it. munmap and MAP_FIXED trim or split ranges, fork copies them, process exit drops them, and the gap finder skips them. Anonymous mappings up to 1 TB are accepted. Tested inside NoovexOS: 30 reservations of 16 GB each, writes at both ends, values seen by a forked child, untouched pages read as zero. |
section 6 |
| 2 | newfstatat(fd, NULL, buf, AT_EMPTY_PATH) returned EFAULT. |
Treated as fstat(fd), as on Linux 6.11 and later. |
section 6 |
| 3 | GLib looked for an accessibility bus and tried to run dbus-launch, which is not installed. |
The Firefox launcher sets GTK_A11Y=none and points DBUS_SESSION_BUS_ADDRESS at an unused socket, so no session bus is started. |
section 6 |
Checked and left as is: the repeating poll/read/recvmsg lines of pid 5 come from nvx-xinput; its poll waits 50 ms each time (it returns 0), so it wakes 20 times a second and is not a busy loop.
What changed in v9.43
| # | Before (v9.42) | Now (v9.43) | Where |
|---|---|---|---|
| 1 | execve accepted at most 32 arguments, 32 environment variables and 2 KB of text for each. Firefox has more than 32 environment variables, so every program it started failed with E2BIG ("Argument list too long"): dbus-launch and, more importantly, its content processes, which draw the web pages. Firefox kept running but never drew a window. |
Up to 4096 arguments and 4096 environment variables with 96 KB of text each. The initial stack image is 320 KB; only the pages it uses are mapped up front, the rest of the 1 MB stack stays lazy. A request that does not fit still returns E2BIG. | section 6 |
| 2 | fallocate (285) returned EOPNOTSUPP (28 times in the Firefox report). |
Mode 0 grows a regular file or memfd to offset + len (memfd seals respected); FALLOC_FL_KEEP_SIZE succeeds; hole punching still returns EOPNOTSUPP. |
section 6 |
| 3 | A MAP_PRIVATE mapping of a memfd wrote straight into the memfd, so other processes saw the private changes. |
Pages are mapped read-only copy-on-write; the first write makes a private copy. | section 6 |
| 4 | The report showed only counts for failed syscalls (30 mmap ENOMEM with no sizes). |
For mmap, mremap and execve the report shows the arguments of the last failing call. |
section 6 |
Found through the v9.42 Firefox report: Failed to execute child process "dbus-launch" (Argument list too long) and 59 execve -7 x3.
What changed in v9.42
| # | Before (v9.41) | Now (v9.42) | Where |
|---|---|---|---|
| 1 | fork turned every writable page into a copy-on-write page, including MAP_SHARED mappings. After a fork, the first write made a private copy, so parent and child stopped sharing. The X server forks (for example to run xkbcomp); its screen buffer, mapped MAP_SHARED from /tmp/Xvfb_screen0, was then silently copied, and the frames NoovexOS displays were freed when the child exited. With Firefox this showed a black full-screen window and then a jump back to the desktop. |
Pages of MAP_SHARED mappings carry a software page-table bit (PTE_SHR, bit 11). fork shares them writable (reference count +1) instead of marking them copy-on-write. Shared anonymous mappings up to 256 MB are allocated up front so that parent and child share real frames. |
section 6 |
| 2 | MADV_DONTNEED could drop pages of a shared mapping that only one process had mapped, and unmapping any copy of a screen frame switched the X display off. |
MADV_DONTNEED leaves shared pages alone. The X display is switched off only when the last reference to one of its frames goes away. |
section 6 |
| 3 | If the Firefox window appeared and full screen ended while Firefox kept running, nothing was reported. | 15 seconds after full screen ends without an exit, the report opens: it says whether the X display was lost or full screen was closed, shows the log tail and the last syscalls. Reports without a crash now list the last syscalls too. | section 6 |
| 4 | /etc/machine-id and /var/lib/dbus/machine-id were missing; GLib and Firefox warned "Cannot spawn a message bus without a machine-id". |
Both files are in the system overlay. | section 6 |
Found through the v9.41 tests: Firefox no longer crashes; it opened a window (the screen went to full-screen X), then the desktop came back with no report.
What changed in v9.41
| # | Before (v9.40) | Now (v9.41) | Where |
|---|---|---|---|
| 1 | Opening /proc/self/fd/N did not reopen file N. Every path under /proc/ matched the procfs driver, so the open "succeeded" with a file that had no content. Firefox shares read-only copies of memory by reopening its memfd through /proc/self/fd/N; the copy it mapped was empty and the check MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic) in libxul.so failed (exit 139). |
/proc/self/fd/N, /proc/thread-self/fd/N, /proc/<own pid>/fd/N and /dev/fd/N open the same object again with the new access mode: a memfd (shared frames, seals kept), a regular file, a sysroot file or a directory get a new open file with its own offset; sockets, pipes and event files are shared like dup. Opening read-write is refused for read-only files. |
section 6 |
| 2 | readlink("/proc/self/fd/N") failed. |
Returns the file path, /memfd:nvx (deleted), socket:[n], pipe:[n] or anon_inode:[eventfd] / anon_inode:[timerfd], as on Linux. |
section 6 |
| 3 | Any unknown /proc/... path opened as an empty file. |
Unknown paths return ENOENT. The procfs driver also serves the files from the Unix layer (/proc/loadavg, /proc/filesystems, /proc/self/status and others) to Linux programs. |
section 6 |
Found through the v9.40 Firefox failure report: the crash stack started in a mapping named 29 (the reopened /proc/self/fd/29), unmapped just before the assertion.
What changed in v9.40
| # | Before (v9.39) | Now (v9.40) | Where |
|---|---|---|---|
| 1 | fcntl(F_ADD_SEALS) and fcntl(F_GET_SEALS) returned EINVAL. Firefox seals the memfd blocks it shares between its processes; the failure ended in MOZ_RELEASE_ASSERT(result.isOk()) in libxul.so (exit 139). |
memfd seals work: F_SEAL_SEAL, F_SEAL_SHRINK, F_SEAL_GROW, F_SEAL_WRITE, F_SEAL_FUTURE_WRITE, F_SEAL_EXEC. They are enforced by write, pwrite64, ftruncate and writable shared mmap. A memfd made without MFD_ALLOW_SEALING starts with F_SEAL_SEAL, as on Linux. MFD_NOEXEC_SEAL and MFD_EXEC are accepted. |
section 6 |
| 2 | Other fcntl commands returned EINVAL: open file description locks, signal owner, lease, notify and write-hint commands. |
F_OFD_GETLK, F_OFD_SETLK, F_OFD_SETLKW, F_SETOWN, F_GETOWN, F_SETSIG, F_GETSIG, F_SETOWN_EX, F_GETOWN_EX, F_SETLEASE, F_NOTIFY and the read/write hint commands succeed. |
section 6 |
| 3 | prctl knew only PR_SET_NAME and PR_GET_NAME (117 EINVAL results in the Firefox report). |
Also the death signal, dumpable, keep-caps, seccomp query, capability bounding set, timer slack, child subreaper, no-new-privs, ambient caps, speculation control, PR_SET_PTRACER and PR_SET_VMA (glibc names its memory areas with it). |
section 6 |
| 4 | mmap(MAP_FIXED) and munmap could replace the lower part of the main thread's 1 MB stack (the guard covered only the top 64 KB). |
The whole stack range is protected. | section 6 |
| 5 | The crash report listed only the first argument of each recent syscall. | It shows the first two, so an fcntl line names the command. |
section 6 |
Found through the v9.39 Firefox failure report: two fcntl calls on a memfd returned -22 just before the assertion.
What changed in v9.39
| # | Before (v9.38) | Now (v9.39) | Where |
|---|---|---|---|
| 1 | /proc/self/maps did not exist for Linux programs. glibc's pthread_getattr_np() reads it to find the main thread's stack; it failed, and Firefox's JavaScript engine treats that as fatal (MOZ_CRASH, a write to address 0, exit code 139). This is the most likely cause of the v9.38 crash. |
/proc/self/maps, /proc/thread-self/maps and /proc/<own pid>/maps are generated on open: stack, program image, heap, dynamic loader and every file mapping of the process, sorted, Linux format. |
section 6 |
| 2 | read, write, lseek, pread64 and pwrite64 on a memfd failed (only mmap and ftruncate worked). |
All five work; writes past the end grow the memfd. | section 6 |
| 3 | pread64 on files from the system overlay (the read-only sysroot) returned ESPIPE. |
Works. | section 6 |
| 4 | The crash line had no library name: the table of file mappings held only 96 entries and Firefox maps hundreds. | 2048 entries, adjacent segments of one file are merged, offsets are counted from the start of the library, and entries of exited processes are kept for the report but left out of /proc/self/maps. |
section 6 |
| 5 | The Firefox report showed only the fault and code address. | The report now also shows: whether it was MOZ_CRASH, the crash reason string and source line (decoded from the code around the fault), the instruction bytes, a call stack of library return addresses, the last 8 syscalls before the crash, and every syscall that returned an error (with errno and count). |
section 6 |
Found through the v9.38 Firefox failure report (fault at address 0, no library name).
What changed in v9.38
| # | Before (v9.37) | Now (v9.38) | Where |
|---|---|---|---|
| 1 | Anonymous mmap was capped at 1 GB and also refused when the request was larger than the number of free physical frames. Firefox's JavaScript engine reserves about 2 GB of address space for compiled code at startup; the reservation failed, Firefox called MOZ_CRASH (a deliberate write to address 0) and exited with code 139 (SIGSEGV). |
Anonymous reservations up to 64 GB are accepted. Anonymous memory is lazy, so a reservation only costs page-table space; physical frames are taken when a page is first touched. File and memfd mappings keep the free-frame check. | section 6 |
| 2 | madvise (28) returned 0 but did nothing. MADV_DONTNEED must give back zero-filled pages on the next touch; allocators in Firefox (mozjemalloc) rely on this and read stale data otherwise. |
MADV_DONTNEED and MADV_REMOVE drop private pages in the range and remap them lazily, so the next touch sees zeros. Shared frames (memfd, other processes) are left alone. |
section 6 |
| 3 | readahead (187) returned ENOSYS (seen 11 times in the Firefox report). |
Returns 0 (it is only a hint). | section 6 |
| 4 | The Firefox failure report showed only the exit code and missing syscalls. | When a Linux program crashes, the report adds a Crash line: the faulting address, the instruction address, and the library it was in with the offset (for example libxul.so+0x1a2b3c). |
section 6 |
clone3 (435) still returns ENOSYS on purpose: glibc falls back to clone, which works. Found through the v9.37 Firefox failure report (exit 139).
What changed in v9.37
| # | Before (v9.36) | Now (v9.37) | Where |
|---|---|---|---|
| 1 | readlink("/proc/self/exe") failed with ENOENT. Firefox uses it to find its own install folder (/usr/lib/firefox-esr/), so it quit with "Couldn't find the application directory" (exit code 255). Many other programs (Python's sys.executable, Chromium, Electron apps, anything that locates its own files) use the same call. |
Every process records the real path of its program when it starts, whether launched by the NoovexOS shell or by Linux execve. Symlinks are followed (so /usr/bin/firefox-esr reports /usr/lib/firefox-esr/firefox-esr). readlink and readlinkat answer /proc/self/exe, /proc/thread-self/exe and /proc/<pid>/exe for the calling process. Verified inside NoovexOS: /usr/bin/python3 reports /usr/bin/python3.13. |
section 6 |
The path is stored in struct jproc (exe[128]), set from jc_cmdname() through lx_set_exe() (PATH search plus symlink resolution) and inherited across fork. Found through the v9.35 Firefox failure report.
What changed in v9.36
| # | Before (v9.35) | Now (v9.36) | Where |
|---|---|---|---|
| 1 | Firefox failed to start with libgcc_s.so.1: cannot open shared object file. apt treats libgcc-s1 and libcrypt1 as provided by NoovexOS and never downloads them, but the system image did not actually contain libgcc_s.so.1 or libcrypt.so.1. Any C++ program from Debian (Firefox, and most GTK/Qt apps) hit this. |
libgcc_s.so.1 and libcrypt.so.1 are in the system image (/lib64), so the "provided by NoovexOS" promise in apt is true. Verified inside NoovexOS in QEMU: a glibc test program linked against libgcc_s loads it and walks its stack. |
section 6.16 |
Found through the v9.35 Firefox failure report, which printed the dynamic loader's error on screen.
What changed in v9.35
| # | Before (v9.34) | Now (v9.35) | Where |
|---|---|---|---|
| 1 | Clicking Firefox switched straight to the full-screen X session. If Firefox crashed or was still loading, the screen just went black with no explanation. | The desktop stays visible with a "Starting Firefox" notice. The display switches to full-screen only once Firefox's window has actually been drawn in the X framebuffer. If Firefox exits, NoovexOS leaves X, opens the terminal and prints the exit code, the last lines of /tmp/ff.log, and every unimplemented Linux syscall Firefox called. If no window appears within 2 minutes, the same report is shown. |
section 6.21 |
| 2 | eventfd, eventfd2, timerfd_create, timerfd_settime and timerfd_gettime returned "not implemented". GLib falls back, but Rust I/O libraries inside Firefox do not. |
All five are implemented (eventfd as dev 20, timerfd as dev 21), with read/write/poll/epoll support and blocking and non-blocking modes. | section 6 |
| 3 | Unimplemented syscalls failed silently. | The kernel records each syscall number that returned ENOSYS, with a count, and the Firefox report lists them by name. | section 6 |
The launcher (/usr/bin/nvx-firefox) now also checks that firefox-esr is installed, waits for the X server, lists any shared libraries the loader cannot find for firefox-esr and libxul.so, and turns off the RDD and socket-process sandboxes and the crash reporter. Verified in QEMU: afutest passes 6/6 (socketpair, memfd, SCM_RIGHTS, cross-process shared memory, eventfd, timerfd), and launching Firefox with no Firefox installed produces the exit-code-127 report through the new watcher.
What changed in v9.34
| # | Before (v9.33) | Now (v9.34) | Where |
|---|---|---|---|
| 1 | Running Firefox meant typing nine export lines, fc-cache -f, startx f and the launch command by hand. |
Clicking the Firefox icon in the start menu (or typing firefox / firefox-esr in the terminal) does it all: starts X, switches to the full-screen X session, sets the sandbox-off and software-rendering variables, refreshes the font cache and launches firefox-esr --no-remote --new-instance about:blank. |
section 6.21 |
The settings live in /usr/bin/nvx-firefox (built into the system image by build.sh). The start menu routes any app whose Exec= contains firefox to firefox_launch() in apps/x11/xapps.c, which runs that script through x_launch and sets g_xfull_pending so the display switches to full-screen X as soon as the server is up. Firefox's output is appended to /tmp/ff.log.
What changed in v9.33
| # | Before (v9.32) | Now (v9.33) | Where |
|---|---|---|---|
| 1 | The filesystem had only 1024 file slots (FS_N) and 512 directory slots (DN_MAX). A big sudo apt install (Firefox pulls thousands of files across hundreds of directories) ran out of slots, not RAM, and failed with out of space or memory while unpacking - which is why Task Manager showed only ~7 percent memory used. The on-disk NVX4 filesystem used the same limits, so installing to disk hit the identical wall. |
FS_N is 16384 and DN_MAX is 4096, so the filesystem holds enough files and directories for Firefox and its full GTK/X dependency set. Verified in QEMU: 3000 files created where the old build stopped at 1024. |
section 3 |
The earlier out-of-memory on large installs was never raw RAM (the v9.30 high-memory heap already made all installed RAM usable, and ramfs file bytes allocate from it). The real limit was the fixed-size file and directory tables. Both the live-ISO ramfs and the on-disk NVX4 filesystem share FS_N/DN_MAX, so this is what blocked Firefox either way. Note: on the live ISO the installed file bytes still live in RAM, so a very large install also needs enough RAM; installing NoovexOS to the SSD/NVMe moves those bytes to disk. All the FS_N/DN_MAX arrays are static or heap-allocated (the NVX4 metadata image is kmalloc-backed and scales automatically), so nothing moved to the stack. A disk formatted by an older build is not read by v9.33 (the inode count in its metadata no longer matches), so re-run the installer for a fresh on-disk filesystem.
What changed in v9.32
| # | Before (v9.31) | Now (v9.32) | Where |
|---|---|---|---|
| 1 | Only TCP and UDP sockets existed. socket(AF_UNIX, ...) failed, so multi-process Linux apps (Firefox, D-Bus, local X) could not talk between their processes. |
Full AF_UNIX stream sockets: socket/bind/listen/connect/accept/socketpair, filesystem and abstract names, blocking and non-blocking, poll/epoll. socketpair() is the channel Firefox's parent/content/GPU processes rely on. |
section 9 |
| 2 | No way to pass an open file descriptor between processes. | sendmsg/recvmsg carry SCM_RIGHTS control messages, duplicating a descriptor from the sender into the receiver. This is how Firefox hands a shared-memory buffer to a child process. |
section 9 |
| 3 | mmap of a file copied bytes into private per-process pages, so two processes could not share writable memory. |
memfd_create returns an anonymous, growable object (ftruncate sizes it); mmap(MAP_SHARED) maps its actual physical frames, reference-counted. Pass the memfd over SCM_RIGHTS and both processes map the same frames: real cross-process shared memory. |
section 3 |
These are Stages 1 and 2 of the Firefox-enablement plan: the kernel groundwork that unblocks the multi-process IPC and shared-memory handoff every GTK/X/Firefox process tree depends on. Verified in QEMU by a bundled test (afutest, runnable from the shell): socketpair data transfer, memfd_create+ftruncate+mmap, SCM_RIGHTS fd-passing, and, the Firefox-critical case, a child process receiving a memfd over a socketpair across fork() and writing to shared memory the parent reads back. All four pass. New kernel code lives in sys/linux/linux.c (dev 18 = AF_UNIX via un_*, dev 19 = memfd via mfd_*, SCM_RIGHTS in lx_un_msg) with close cleanup in core/proc/jobs.c. Remaining for a running Firefox: install the GTK/X stack via apt and software-render it (Steps 3-5), which run on real hardware against the Debian archive.
What changed in v9.31
| # | Before (v9.30) | Now (v9.31) | Where |
|---|---|---|---|
| 1 | Only one display lit up. The kernel used the single UEFI GOP framebuffer the firmware handed it at 0x5000, and the NVIDIA driver's own modeset is an unverified, disabled placeholder. A second connected monitor stayed dark. |
The UEFI loader now enumerates every GOP output the firmware exposes (LocateHandleBuffer) and records any extra framebuffer in the boot-info block, with a per-display flag saying whether the kernel's page tables already cover it. When a second firmware-lit head is present, the compositor mirrors the desktop onto it each frame (blit_mirror2, nearest-neighbour scaled). |
section 3 |
Scope and limits, stated plainly: this lights the second monitor only if your firmware initializes both outputs at power-on (many do when both are connected at boot). If the firmware lights only the primary, LocateHandleBuffer returns one output and the second stays dark - that case needs a real display-engine modeset, which is a separate effort. The change is safe and additive: it writes no GPU registers, only uses framebuffers the firmware already set up, and only touches a second framebuffer the loader has confirmed is mapped, so a single-display boot (BIOS or one-GOP UEFI) behaves exactly as before. The boot log prints display: second head WxH (mirroring) when a second head is active. New code: loader enumeration in boot/uefi/uefiboot.c, extra-display table at 0x5040, second-framebuffer globals and mirror in drivers/video/fb/framebuffer.c, read in core/init/main.c.
What changed in v9.30
| # | Before (v9.29) | Now (v9.30) | Where |
|---|---|---|---|
| 1 | kmalloc served only the sub-4 GB region (capped at 0xFC000000). On a real laptop that region is squeezed by the PCI hole, the kernel and the sysroot overlay, while gigabytes of RAM above 4 GB sat unused. A large sudo apt install (VLC, Firefox) exhausted the low region and failed with E: out of space or memory while unpacking, even with Task Manager showing about 7 percent memory used. |
When the low heap is full, kmalloc pulls contiguous, identity-mapped pages from the high-memory pool above 4 GB (the pages the frame allocator already manages) and keeps serving allocations. All installed RAM is now usable for installs, decompression buffers and the package index, so big installs no longer run out of memory. |
section 3 |
Root cause of the v9.29 install failure: the direct-to-disk unpacker still needed transient RAM per package (download buffer, decompressed data.tar, the roughly 45 MB package index), and on the live ISO the writable filesystem is itself RAM-backed. All of that competed for the small sub-4 GB heap. v9.30 removes the 4 GB ceiling on the kernel heap. New primitives: hp_alloc_run(pages) in mem/pmm_vmm.c hands out a contiguous run of high frames; kmalloc in mem/heap.c adds extra heap arenas from it on demand, and hblk_ok validates pointers across every arena. Booted and verified at 6 GB; the high-memory pool self-test passes at startup.
What changed in v9.29
| # | Before (v9.28) | Now (v9.29) | Where |
|---|---|---|---|
| 1 | apt built each package entirely in RAM (download + decompress + a copy of every file + a resident in-memory layer), so a big package could run the machine out of memory mid-install. | apt unpacks each file straight to the filesystem (the SSD/NVMe on an installed system). Peak memory stays small regardless of package size. Each package records a /var/lib/nvpkg/<pkg>.list manifest; dpkg -L and apt remove use it. |
section 6.16 |
| 2 | Default sources had only Debian main. |
main contrib non-free non-free-firmware, and installs resolve up to 4096 packages. A "run sudo apt update first" hint shows when no list is downloaded yet. |
section 6.16 |
| 3 | Start-menu app scan only read the in-kernel image. | It also reads /usr/share/applications on disk, so apt-installed apps appear with their icons. |
section 6.21 |
Tested against a 20,000-package Debian-format mirror: apt update reads the full index, install writes real files to disk, dpkg -L and apt remove work off the manifest.
What changed in v9.29
| # | Before (v9.28) | Now (v9.29) | Where |
|---|---|---|---|
| 1 | The default /etc/apt/sources.list had only Debian main. |
It has main contrib non-free non-free-firmware, so sudo apt install can reach every package Debian offers (after sudo apt update). |
section 6.16 |
| 2 | apt install <pkg> on a package not in the list just said "Unable to locate package". |
When no Debian list has been downloaded yet, it adds a note telling you to run sudo apt update first. |
section 6.16 |
| 3 | An install transaction was capped at 1024 packages. | 4096, so large dependency sets resolve. | section 6.16 |
Tested with a 20,000-package Debian-format mirror: apt update downloads and parses the full index, and apt install finds and installs any package in it. The real deb.debian.org download is only tested on real hardware, since this build environment has no route to Debian.
What changed in v9.28
| # | Before (v9.27) | Now (v9.28) | Where |
|---|---|---|---|
| 1 | X programs were shown inside the X Display window. | X windows appear directly on the desktop like other apps (transparent full-screen overlay); mouse and keyboard go to them when you click them. | section 6.21 |
| 2 | Start menu entries for X apps used NoovexOS icons. | IDLE shows Python's IDLE icon; apps installed with apt appear in the start menu with their own icons from their .desktop files (for example VLC's cone). |
section 6.21 |
| 3 | .py files opened in the NoovexOS Editor. |
A double click opens them in IDLE. | section 6.21 |
What changed in v9.27
| # | Before (v9.26) | Now (v9.27) | Where |
|---|---|---|---|
| 1 | No Tkinter. | Tcl/Tk 8.6 and CPython's _tkinter are built from source; import tkinter works and Tk windows appear on the X display. |
section 6.20 |
| 2 | idle and idle3 opened the NoovexOS Python IDE. |
They run the real IDLE (idlelib) from CPython 3.13 on X11, with its shell, editor, colouring and separate code process. IDLE (Python 3.13) is in the start menu. python3-ide (the NoovexOS IDE) keeps its own name. |
section 6.20 |
| 3 | X windows had no frames. | nvxwm: title bars, close buttons, moving and click-to-focus. |
section 6.20 |
| 4 | X programs needed startx first. |
idle3, wish, xdemo and commands that use tkinter start the X server when needed. |
section 6.20 |
| 5 | The ISO held the kernel twice (76 MB). | The UEFI CD boot entry points at the main FAT32 partition, so the kernel is stored once. | section 3.15 |
What changed in v9.26
| # | Before (v9.25) | Now (v9.26) | Where |
|---|---|---|---|
| 1 | X programs could not be used: keyboard and mouse did not reach them. | Mouse movement, left and right buttons, the wheel and typed keys in the X Display window reach X programs (through the new /dev/nvxxin and the XTEST helper nvx-xinput, started by startx). |
section 6.19 |
| 2 | X only ran in a window. | startx full runs a full-screen X session at the display's size, with the classic X background, /root/.xinitrc or xdemo started, all input going to X; F12 or Ctrl+Alt+Backspace returns to the desktop. |
section 6.19 |
| 3 | xdemo only drew. |
It shows the pointer position, drops dots where you click, has a button that toggles, shows typed text, and quits with Esc. | section 6.19 |
What changed in v9.25
| # | Before (v9.24) | Now (v9.25) | Where |
|---|---|---|---|
| 1 | No X11. | The X.Org X server (Xvfb) runs on the glibc layer. startx starts it on :0; X programs connect over local TCP (DISPLAY=127.0.0.1:0) and appear in the new X Display window. xdemo is an Xlib test program. Keyboard and mouse input to X programs is not done yet. |
section 6.19 |
| 2 | No epoll. |
epoll_create1, epoll_ctl, epoll_wait, epoll_pwait and epoll_pwait2. |
section 6.19 |
What changed in v9.24
| # | Before (v9.23) | Now (v9.24) | Where |
|---|---|---|---|
| 1 | No Python IDE. | sudo apt install python3-ide (or ide, idle3) installs a Python IDE from a package repository built into the system. It appears in the start menu (Development, or Productivity in the modern menu) and in search; it colours Python code and its Run button runs the program with CPython 3.13 in the Terminal. |
section 6.18 |
| 2 | apt could only fetch over HTTP and HTTPS. |
file: sources, and a local repository (/usr/share/noovex/repo) that is always available. |
section 6.18 |
What changed in v9.23
This release adds real Python 3 and Debian package installation: sudo apt install PKG downloads .deb files from a Debian mirror, resolves their dependencies and unpacks them, and python3 is CPython 3.13. Each item links to the section that explains it.
| # | Before (v9.22) | Now (v9.23) | Where |
|---|---|---|---|
| 1 | No Python 3 (Ubuntu's python3 is a fixed-address program and cannot run). |
CPython 3.13 built from source as a position-independent program, with its standard library, extension modules and their libraries in the sysroot. python3, python3 -c, script files and the interactive prompt work. |
section 6.17 |
| 2 | pkg install only copied files out of gzip .deb files into /pkg/NAME. |
apt update, apt install, remove, autoremove, upgrade, search, show, list, and dpkg -i, -l, -L, -s, -r. Packages come from /etc/apt/sources.list (Debian trixie by default), dependencies are resolved, every package is checked against its SHA256, and installed programs run at once. |
section 6.16 |
| 3 | The kernel could decompress gzip only. | xz (xz-embedded) and zstd decoders in the kernel (lib/compress/xzk.c, lib/compress/zstdk.c, lib/compress/unpack.c). |
section 6.16 |
| 4 | The sysroot was one fixed file tree with deflate per file, looked up by scanning every entry. | A stack of layers (the kernel's own plus one per installed package) with a hash index; the kernel's layer uses xz blocks (glibc and GNU tools: 7.0 MB before, 4.8 MB now). | section 6.15 |
| 5 | glibc programs never saw a terminal (glibc 2.44 asks with TCGETS2). |
TCGETS2 and TCSETS2 are answered, so prompts and line editing work. |
section 6.15 |
| 6 | Scripts with #! lines could not be started. |
#!/usr/bin/env python3, #!/bin/sh and other interpreter lines work from the Terminal and from programs (execve); /bin/sh and /bin/bash are GNU bash. |
section 6.15 |
| 7 | A command line given to a program was cut at 255 characters. | 2047 characters. | section 6.15 |
| 8 | Bare program names were looked up in /opt/gnu/bin only. |
Also /usr/local/bin, /usr/bin, /bin, /usr/sbin and /sbin of the sysroot and installed packages. |
section 6.15 |
The source zip now holds the CPython 3.13 source (third_party/src/cpython-3.13.tar.xz); the glibc source is shipped next to it as glibc-2.44.tar.xz.
What changed in v9.22
This release runs programs built for glibc, the C library of Debian, Ubuntu and most Linux systems, and raises the kernel image limit from 15 MiB to 100 MiB. Each item links to the section that explains it.
| # | Before (v9.21) | Now (v9.22) | Where |
|---|---|---|---|
| 1 | The kernel image had to end below 16 MiB (15 MiB of kernel). | Up to 100 MiB. The kernel's static data (BSS) moved to 256 MiB, the heap to 384 MiB, the fixed USB, SATA and NVMe buffers and the boot stack to 152-157 MiB. All three boot paths (BIOS, UEFI from disk or USB, UEFI from CD) load it; a 70 MiB test kernel booted on each. User programs now need 500 MiB of RAM (was 272 MiB). | section 3.15 |
| 2 | Only musl programs ran. | glibc 2.44 is built from source and shipped in the kernel as a compressed file tree (/lib64, with /lib/x86_64-linux-gnu, /usr/lib/x86_64-linux-gnu and /usr/lib64 linking to it). Programs that ask for /lib64/ld-linux-x86-64.so.2 run: threads, mutexes, malloc, libm, clocks and uname were checked. |
section 6.15 |
| 3 | No GNU tools. | GNU bash 5.2, coreutils, grep, sed, awk, find, xargs, diff and others are in /opt/gnu/bin, with the libraries they need. PATH starts with /opt/gnu/bin, so bash in the Terminal is real bash. |
section 6.15 |
| 4 | The Terminal expanded $NAME inside single quotes. |
Text in single quotes is passed on unchanged, so bash -c '...$i...' works. |
section 6.15 |
| 5 | The CD's UEFI boot image was limited to 32 MiB. | Any size: the El Torito entry uses sector count 0 (the rest of the disc) and the FAT16 image grows its cluster size. | section 3.15 |
| 6 | No way to see what a Linux program asked the kernel. | lxtrace N logs the next N Linux system calls (number, first two arguments, result) to the kernel log. |
section 6.15 |
The source zip now also contains FFmpeg and the glibc 2.44 source (third_party/src/glibc-2.44.tar.gz).
What changed in v9.21
| # | Before (v9.20) | Now (v9.21) | Where |
|---|---|---|---|
| 1 | Since v9.15 each script could run for 10 seconds, plus 1 second per 40 KB of source (up to 2 minutes more), and every timer or event callback for 10 seconds. A page with a few large or stuck scripts took 40 seconds or more. | Back to 6 seconds per call, plus 1 second per 200 KB of source (at most 8 seconds more). All page scripts together get at most 8 seconds during a load; scripts after that are skipped and named in about:console. |
section 13.30 |
| 2 | Forced layouts for scripts could use 150 ms of every second. | 60 ms of every second. | section 13.30 |
| 3 | The pointer pump ran (and polled USB controllers) on every millisecond of network waiting. | At most once per timer tick (10 ms). | section 13.30 |
| 4 | No way to see where load time went. | After each page load the status bar and about:console show "Loaded in N ms: page A ms, F files B ms, engine C ms". |
section 13.30 |
What changed in v9.20
| # | Before (v9.19) | Now (v9.20) | Where |
|---|---|---|---|
| 1 | Since v9.17 every size or position query from a script laid the whole page out again when anything had changed. A page that adds an element and measures it 200 times took 1.4 seconds in the host harness. | Forced layouts share a budget of 150 ms per second; past it, queries get the last layout. box_rect no longer lays out by itself. The same page now takes 152 ms. |
section 13.29 |
| 2 | Text inputs were always 153 px wide and ignored size. |
The width follows size (20 characters by default), as in browsers: 184 px where Chromium gives 185 px. Range inputs are 16 px tall. textarea sizing reads cols and rows. |
section 13.29 |
| 3 | Enter sent as code 13 did nothing in a text field. | Code 13 is treated as Enter, so it submits forms. | section 13.29 |
What changed in v9.19
A probe page with 31 layout cases (box sizing, inline-block, floats, flex, grid, positioning, margins, tables, multi-column, sticky, transforms, pseudo-elements, logical properties) and 20 script checks was run in the engine and in Chromium, and the results compared number by number. 29 of the 31 layout cases and all 20 script checks already matched; the differences found are fixed below, apart from one.
| # | Before (v9.18) | Now (v9.19) | Where |
|---|---|---|---|
| 1 | grid-template-areas was ignored, so items placed with grid-area: name were auto-placed into one cell. |
Named areas are resolved to row and column lines from the parent's grid-template-areas. |
section 13.28 |
| 2 | scrollHeight of an element whose content fits included its borders. |
It is the padding box height, as in browsers. | section 13.28 |
| 3 | The new tab page was a purple card design. | A Google-style page: the multicoloured Noovex logo, a rounded search box, "Noovex Search" and "I'm Feeling Lucky" buttons, round shortcut tiles, links at the top right and a footer bar. Shortcuts are hidden when the window is too short for them. | section 13.28 |
Still different: an inline <span> is 19 px tall where Chromium makes it 17 px (font metrics).
What changed in v9.18
Bugs found by rendering a modern landing page (dark theme, CSS variables, color-mix(), clamp(), auto-fit grid, inline SVG logo) in the engine and in Chromium side by side.
| # | Before (v9.17) | Now (v9.18) | Where |
|---|---|---|---|
| 1 | SVG <text> was not drawn, so logos and labels made of SVG text were missing. |
<text> and nested <tspan> text is drawn with x, y, font-size, font-weight, font-style, font-family, text-anchor, dominant-baseline, fill and opacity, scaled by the SVG transform. |
section 13.27 |
| 2 | color-mix() with transparent (or any translucent colour) mixed the colour channels toward black, so muted text came out too dark. |
Mixing uses premultiplied alpha, as the CSS specification requires. | section 13.27 |
| 3 | repeat(auto-fit, minmax(min(100%, 420px), 1fr)), a common responsive grid, made one column, because the comma inside min() split the arguments. |
Arguments are split at top-level commas only, and a min(percent, length) minimum uses the length. |
section 13.27 |
| 4 | getComputedStyle(el).fontSize and other values were empty strings while the page was loading. |
Styles are computed on demand, as for the layout queries in v9.17. | section 13.27 |
What changed in v9.17
| # | Before (v9.16) | Now (v9.17) | Where |
|---|---|---|---|
| 1 | Scripts that asked for an element's size or position while the page was loading (getBoundingClientRect, offsetWidth, clientHeight, scrollHeight and others) got 0, because layout ran only after scripts. Sites that measure themselves at start-up built wrong layouts. |
These calls now lay the page out first when it has changed, as browsers do. At most 30 forced layouts per 50 ms, so a script that reads and writes in a loop cannot freeze the page. | section 13.26 |
| 2 | After 64 contained engine crashes the error screen came back, even across many pages. | The count is lowered at each new page load, so only a page that keeps crashing in a loop reaches it. | section 13.22 |
What changed in v9.16
This release stops the browser from freezing the desktop. Each item links to the section that explains it.
| # | Before (v9.15) | Now (v9.16) | Where |
|---|---|---|---|
| 1 | The browser engine runs inside the desktop's main loop, so a long script, a slow download or a large layout froze the whole screen, including the mouse pointer. | While the engine or the network is busy, the kernel keeps reading the mouse and redrawing the pointer about every 10 ms (busy_pump). This is called from the script interrupt check, from the style and layout loops, and from every network wait. |
section 13.25 |
| 2 | A page stuck in a loop could only be stopped by the time limit. | Pressing Esc while one engine call has run for more than 1.5 seconds stops the page and shows "This page stopped". | section 13.25 |
| 3 | One engine tick ran up to 400 due timers whatever the cost, and every growth of the timer list leaked the old list. | A tick stops running timers after 40 ms and continues on the next one; at most 50,000 timers; the old list is freed. | section 13.25 |
| 4 | data: addresses were looked up as host names. |
data: addresses (plain or base64) open in the browser. |
section 13.25 |
| 5 | Missing script APIs. | scheduler.postTask and scheduler.yield, AbortSignal.timeout, AbortSignal.any and AbortSignal.abort, navigator.scheduling.isInputPending, navigator.getBattery, MediaMetadata, document.timeline, Path2D, createImageBitmap, XPathResult and document.evaluate (empty results), and reportError. |
section 13.25 |
What changed in v9.15
This release works toward running the real youtube.com in the browser. The built-in YouTube pages from v9.14 are switched off, so YouTube addresses load the real site. This could not be tested against youtube.com from the development machine.
| # | Before (v9.14) | Now (v9.15) | Where |
|---|---|---|---|
| 1 | YouTube addresses opened a built-in player page instead of the site. | They load the real site. The player page code is kept but not used. | section 13.23 |
| 2 | There was no Intl object (QuickJS does not have one), so scripts that format numbers, dates or plurals stopped with a ReferenceError. |
Intl with NumberFormat (decimal, percent, currency, compact), DateTimeFormat (with formatToParts, dateStyle and timeStyle), PluralRules, Collator, RelativeTimeFormat, ListFormat, Segmenter, Locale and DisplayNames, plus toLocaleString and localeCompare that use it. English rules only. |
section 13.24 |
| 3 | Each script had 6 seconds and the JavaScript heap at most about 96 MiB, too little for multi-megabyte application bundles. | Scripts get 15 seconds plus 1 second per 40 KB of source (at most 2 minutes more). The engine arena can be up to 768 MiB when the kernel heap has room for it plus 320 MiB, the JavaScript heap 70 percent of the arena, the JavaScript stack 3 MiB and the engine stack 6 MiB. | section 13.24 |
What changed in v9.14
This release lets NoovexOS use up to 128 GiB of RAM, finds every partition on every disk, keeps the system running when a web page crashes the engine, and plays YouTube videos through a lightweight player page. Each item links to the section that explains it.
| # | Before (v9.13) | Now (v9.14) | Where |
|---|---|---|---|
| 1 | At most 4032 MiB of RAM was recognised, and only memory below 4 GiB was mapped and used. BIOS boots had no memory map at all. | BIOS boots pass the E820 map to the kernel (through Multiboot or the native loaders). All RAM is counted, everything above 4 GiB (up to 512 GiB) is identity-mapped with 2 MiB pages, and it becomes a second frame pool that user programs use first. Tested at 8, 16 and 128 GiB with BIOS and UEFI. | section 3.14 |
| 2 | Partitions were found only on the install disk, only as four MBR primaries or the first 128 GPT entries, and only when they held FAT or NTFS. | A partition scanner reads MBR primaries, extended and logical partitions, GPT (up to 1024 entries, partition names, backup header) and whole-disk file systems, on every IDE drive, every SATA port, the NVMe or virtio disk and USB sticks. It names 13 file systems. parts (also lsblk) lists them; the root file system search uses it, so a NoovexOS install on a logical partition is found. |
section 8.11 |
| 3 | A CPU exception inside the web engine (a bad pointer while rendering a page) stopped the whole system with the red error screen. | The exception is caught: the engine call is unwound, the browser shows "This page crashed", and the browser and the desktop keep working. about:crash triggers this on purpose. |
section 13.22 |
| 4 | YouTube's own site is too large for the engine, so watch pages did not play. | youtube.com/watch, youtu.be, /shorts/, /embed/ and /live/ addresses open a player page that asks YouTube's player API for the video (trying four clients) and plays the best progressive MP4 or WebM up to 720p in the browser's <video>. |
section 13.23 |
What changed in v9.13
This release makes the web engine draw and move pages the way modern sites expect: real 2D transforms, CSS animations and transitions, downloadable fonts including icon fonts, Shadow DOM for web components, and a much faster style system. Each item links to the section that explains it.
| # | Before (v9.12) | Now (v9.13) | Where |
|---|---|---|---|
| 1 | Only translate was applied; rotate, skew, scale and matrix were ignored. |
Every 2D transform is drawn: the element and everything in it is painted into an off-screen buffer and mapped with bilinear filtering, with transform-origin and the translate, rotate and scale properties. |
section 13.18 |
| 2 | Animations and transitions jumped to the end state, and their events never fired. | @keyframes with every animation-* property and timing function (ease, cubic-bezier(), steps()), and transition on opacity, colours, transforms, sizes, insets, margins, paddings and visibility. animationstart, animationiteration, animationend, transitionrun, transitionstart and transitionend are dispatched. |
section 13.18 |
| 3 | @font-face was ignored; pages used the three built-in families. |
Web fonts in TTF, WOFF and WOFF2 (with a new Brotli decompressor and the WOFF2 glyph-table rebuild), chosen by family, weight, style and unicode-range. Ligatures from the font's GSUB table are applied, so icon fonts that turn words into icons work, as do icon fonts that use private code points. |
section 13.19 |
| 4 | attachShadow returned the element itself, so a component's template replaced its children. |
Real shadow roots: the shadow tree is rendered in place of the children, <slot> elements show the assigned children (or their fallback), slotted content inherits from the slot, styles inside a shadow root are scoped to that component, :host, :host() and ::slotted() work, events bubble from the shadow tree to the host, plus assignedNodes, adoptedStyleSheets and constructable CSSStyleSheet. |
section 13.20 |
| 5 | Every new <style> element made the engine re-parse and re-index every stylesheet on the page, and each element was checked against every rule with a matching tag. |
Parsed stylesheets are cached, the rule index is extended instead of rebuilt when sheets are only added, an ancestor filter skips rules whose ancestors cannot match, and @media results are cached per style pass. A test page that adds 60 style elements to 18,000 rules runs 8.6 times faster. |
section 13.21 |
What changed in v9.12
This release puts video everywhere: a media service in the kernel, a Video Player window on the desktop, and <video>, <audio> and MediaSource Extensions (MSE) in the browser, so pages that stream video in segments, as YouTube does, can play. Each item links to the section that explains it.
| # | Before (v9.11) | Now (v9.12) | Where |
|---|---|---|---|
| 1 | play could only take over the whole screen, and only for a local file. |
A kernel media service (core/media.c, /dev/media) runs one play --embed process per video. The process reads its data from a local file, from an HTTP or HTTPS URL (with Range requests and an 8 MB cache), or from data the kernel was handed (one or two streams); it sends decoded frames, its status and takes commands through ioctl. |
section 6.13 |
| 2 | No video window. | A Video Player window: play and pause, a seek bar, time, mute, loop and maximize; keyboard control (space, arrows, j/l, m, f, 0-9). Double-click a video or audio file in Files, or type video FILE or video URL. |
section 6.14 |
| 3 | <video> and <audio> were grey boxes with the word "Video", and the media API was a stub. |
Real playback with the standard API: play(), pause(), currentTime, duration, volume, muted, loop, buffered, readyState, videoWidth, canPlayType, the media events (loadedmetadata, canplay, playing, timeupdate, seeking, seeked, ended and others), <source> selection, autoplay, native controls drawn by the engine, and the element's size taken from the video. |
section 13.17 |
| 4 | No MediaSource. | MediaSource and SourceBuffer with one or two buffers (split video and audio), isTypeSupported (H.264, H.265, VP8, VP9, AAC, MP3, Opus, Vorbis; AV1 is refused so sites fall back), appendBuffer, remove, endOfStream, duration, and buffered ranges computed from the fragmented MP4 (tfdt/trun) and WebM (Cluster) timestamps. |
section 13.17 |
| 5 | fetch and XMLHttpRequest returned every body as text, so binary data (video segments, images, protobuf) was corrupted, and Blob held only text. |
Bodies are binary-safe: fetch gets the bytes as an ArrayBuffer and decodes text only when asked; XHR with responseType arraybuffer or blob gets the raw bytes; request bodies may be ArrayBuffers or typed arrays; Blob holds bytes. TextEncoder and TextDecoder run in C, which is much faster. |
section 13.8 |
| 6 | A full-screen request was refused. | requestFullscreen and exitFullscreen make the element cover the page and fire fullscreenchange. |
section 13.17 |
| 7 | While any program ran, the desktop redrew the whole screen after every scheduler slice, which left programs about a third of the CPU. | The desktop redraws when a program marks the screen dirty, the terminal changes, or twice a second. New video frames are copied only into their rectangle when the window is on top (present_rect). |
section 6.14 |
| 8 | Four checker groups had not compiled or run since earlier releases (USB, NVMe and xHCI test templates used the old memory map; the AML test allocated above 4 GiB, which the interpreter's pointer check rejects). | All updated; the checker passes 106 of 106. | section 12.2 |
What changed in v9.11
This release adds video and audio playback with FFmpeg, raises the kernel image limit from 8 MiB to 15 MiB, and fixes the HD Audio driver on controllers that pace codec responses. Each item links to the section that explains it.
| # | Before (v9.10) | Now (v9.11) | Where |
|---|---|---|---|
| 1 | No way to play video or compressed audio. | play FILE plays MP4, MKV/WebM, Ogg, MP3 and WAV fullscreen with sound: H.264, H.265, VP9, VP8, AAC, MP3, Opus and Vorbis, decoded in software on all cores with FFmpeg's SIMD code. Space pauses, the arrow keys seek 10 or 60 seconds, q quits; play -b benchmarks the decoder and play -s SECONDS out.ppm saves a frame. |
section 6.12 |
| 2 | The kernel image had to end below 0x900000 (8 MiB); the boot stack was at 0x980000, the USB/AHCI/NVMe buffers at 0x990000-0xC00000 and .bss at 0xC00000. |
The image may use 0x100000-0x1000000 (15 MiB). Everything above moves up by 7 MiB: boot stack top 0x1080000, xHCI memory 0x1090000, mass-storage rings 0x1150000, AHCI 0x1200000, NVMe 0x1230000, .bss at 0x1300000. stage2 reads up to 15 MiB, and the UEFI loader, installer snapshot, integrity check and linker scripts use the same limit. |
section 2.13 |
| 3 | The HD Audio driver never set the RIRB interrupt-enable bit. On controllers that count responses against it (QEMU's intel-hda does), the codec answered only the first verb, so no output path was found ("hda: no usable output path"). |
RIRBCTL is set to 0x03; the global interrupt stays off, so nothing fires, but the response counter is reset each time the driver acknowledges a response. The error message now lists what was found. |
section 10.9.3 |
| 4 | /dev/fb0 had no way to tell a program the screen size, assumed a stride equal to the width, and copied byte by byte. |
FBIOGET_VSCREENINFO (0x4600) and FBIOGET_FSCREENINFO (0x4602) report the size, 32 bpp and the real line length; reads and writes use the stride and copy with memcpy. When a fullscreen program closes /dev/fb0, the live terminal repaints completely. |
section 6.12 |
What changed in v9.10
This release makes the web engine handle more of the modern web: rich-text editors, streamed responses, modern colour syntax, nested CSS and several layout fixes. Each item links to the section that explains it.
| # | Before (v9.09) | Now (v9.10) | Where |
|---|---|---|---|
| 1 | margin: 0 auto (and margin-inline: auto) never centred a block: the parent placed the box before its auto margins were computed. |
lay() moves the box by the auto margin that block_width computes, so centred page wrappers are centred. |
section 13.6 |
| 2 | contenteditable elements could not be focused or typed into, and Selection, Range and MutationObserver were stubs. |
Editing hosts take focus, a click places the caret at the clicked character, and keys go through beforeinput, a DOM edit (insert text, delete, split paragraph) and input. Selection and Range are real (anchor and focus, setBaseAndExtent, deleteContents, insertNode, getBoundingClientRect), MutationObserver reports child, attribute and text changes, and document.execCommand('insertText') works. The caret is drawn by the engine. |
section 13.8 |
| 3 | response.body was always null and there were no streams. |
ReadableStream, WritableStream, TransformStream, TextDecoderStream and TextEncoderStream; Response.body is a stream, and a Response can be built from one. |
section 13.8 |
| 4 | oklch(), oklab(), lab(), lch(), light-dark(), color() and system colours were not understood, so sites built with Tailwind v4 lost most of their colours. |
All of them are parsed, including inside color-mix() and gradients. |
section 13.5 |
| 5 | Nested CSS rules (.a { & .b {} }) were dropped. |
Nested rules, &, and nested @media/@supports/@layer are flattened while the sheet is parsed. |
section 13.5 |
| 6 | Visually hidden elements (clip: rect(0 0 0 0), clip-path: inset(50%), 1-px boxes) were painted, for example a "Choose File" box over ChatGPT's message field. |
They are not painted. | section 13.7 |
| 7 | text-overflow: ellipsis, -webkit-line-clamp, position: sticky, -webkit-background-clip: text and -webkit-text-fill-color had no effect, and auto grid rows did not stretch to the container height. |
All implemented (gradient text is drawn in the gradient's first colour); place-items: center in a fixed-height grid centres vertically. |
section 13.6 |
| 8 | Text inputs sent input events without data and never sent beforeinput. |
beforeinput (cancelable) and input carry inputType and data. |
section 13.8 |
What changed in v9.09
| Before (v9.08) | Now (v9.09) | Where |
|---|---|---|
The web engine's allocator (web/krt.c) served requests of 60,593 to 65,536 bytes from its largest size class, which is only 60,592 bytes. Up to about 5 KB was written past the block, which corrupted QuickJS data (seen as a page fault in __JS_NewAtom with RAX = 0xF000FF53, a value read from the real-mode interrupt table at address 8). |
Only requests that fit the largest class use the size classes; everything else uses the large-block path. A host test checks every size up to 300,000 bytes. | section 13.3 |
What changed in v9.08
This release replaces the browser with a real web engine: an HTML parser and DOM, the CSS cascade, a layout engine with flexbox and grid, the QuickJS JavaScript engine with the browser API, and a cookie jar that is saved to disk. It also adds the qjs command. Each item links to the section that explains it.
| # | Before (v9.07) | Now (v9.08) | Where |
|---|---|---|---|
| 1 | The browser was a single-pass tag scanner that turned a page into lines of text with some colours. | A layout engine in web/: HTML parser with the usual implied-tag and table rules, a DOM, selectors up to :has(), the full cascade with !important, custom properties and calc(), block and inline layout, floats, positioning, z-index, overflow and scrolling boxes, tables, flexbox and grid, anti-aliased TrueType text, gradients, shadows, rounded corners, SVG. |
Part 13 |
| 2 | An integer-only JavaScript subset with document.write and no DOM. |
QuickJS-ng (ES2023) with DOM bindings and a browser API written in JavaScript: events, classList, style, fetch, XMLHttpRequest, localStorage, history, timers, observers, modules. React 19 and jQuery 3.6 run. Scripts are limited to 6 s per run and to 60% of the engine arena. |
section 13.8 |
| 3 | Cookies were kept in a small table in net/http/http.c. |
A cookie jar (net/http/cookies.c) with domain and path matching, expiry, Secure, HttpOnly and SameSite, saved to cookies.txt; localStorage saved to webstorage.txt; an about:cookies page and a Cookies button. |
section 13.10 |
| 4 | No way to run modern JavaScript from the shell. | qjs, the QuickJS command-line interpreter, as an embedded musl program. |
section 13.13 |
| 5 | browse did not exist, and opening the browser from the live terminal left the window hidden behind the terminal. |
browse <url> (also www) opens the browser and, in the live terminal, switches to the desktop first; desktop switches without opening anything. |
section 13.11 |
| 6 | The boot stack started at 0x700000, directly above a kernel that could be at most 5.75 MB. The 6.7 MB kernel was overwritten by its own stack and the integrity check stopped the boot. |
The stack top is 0x980000, the kernel may be 8 MiB (0x100000-0x900000), the UEFI loader's KERNEL_MAX matches, and both linker scripts fail the build if the image is too large. |
section 13.12 |
| 7 | ChaCha20-Poly1305 used a fixed scratch buffer at 0x900000. |
It uses a heap buffer (aead_mac), so it no longer depends on that memory being free. |
section 13.12 |
Other changes:
| Change | Why |
|---|---|
The browser's string copies now always end the string (sset). |
The tab title showed the start of the new host name followed by the end of the old title, and Back went to the wrong page. |
| Page fonts include about 80 symbols from DejaVu Sans (check marks, arrows, stars, menu and warning icons), used by all faces. | Liberation has none of them, so they showed as ?. |
The stack-switch trampoline moved to arch/x86_64/user/stack_call.c. |
The static checker allows inline assembly only under arch/. |
The UEFI simulator (tools/tests/pe_run.c) emulates 32-bit port I/O. |
The loader's quiet_dma reads PCI configuration space with in/out on eax, which the simulator treated as a crash. |
The mhello check accepts any version number. |
It looked for "8.97" in the program's output. |
What changed in v8.98 to v9.07
These releases fix the installer crash on real hardware, speed up USB mice, give the browser better CSS and images, and replace the desktop with a classic taskbar and start menu. Each item links to the section that explains it.
| # | Before (v8.97) | Now (v9.07) | Where |
|---|---|---|---|
| 1 | On some UEFI laptops the kernel was already corrupted when it started: kcheck said code DIFFERS, the installer refused to copy it, and the system could page-fault (seen in anode_child with a pointer of all ones). The firmware's USB controller kept writing into memory that the loader had just filled with the kernel. |
The UEFI loader stops DMA right after ExitBootServices and before it copies the kernel: it halts every xHCI controller (clears USBCMD.RS, waits for USBSTS.HCH) and clears Bus-Master Enable on storage, network, multimedia, wireless and serial-bus devices (quiet_dma). |
section 2.4 |
| 2 | ahci_init and nvme_init turned on bus mastering before checking the BAR and before stopping the controller, so a rejected or still-running controller could resume DMA into reclaimed memory. |
Both enable memory space first. AHCI stops every implemented port and clears its interrupts; NVMe disables the controller. Only then is bus mastering switched on. | section 8.3 |
| 3 | A corrupt AML namespace link crashed the kernel. | anode_child checks every node pointer (below 4 GiB, above 1 MiB, 8-byte aligned) and stops the walk instead of faulting. |
section 10.7.3 |
| 4 | A USB mouse or keyboard had one read queued at a time. A 1000 Hz gaming dongle lost most reports during a redraw, so the pointer was slow and lagged. | 16 reads are queued per HID endpoint, each with its own 64-byte buffer; every completed report is decoded from its own buffer and immediately re-queued. | section 10.6.5 |
| 5 | The browser understood hex colours and a few names, one page background colour, PNG/JPEG/WebP/BMP, and 600 lines per page. | About 140 CSS colour names, rgb()/rgba()/hsl()/hsla(), 4- and 8-digit hex; linear-gradient page backgrounds; block backgrounds, text-align and font-size from rules, inline style and bgcolor/align; paragraph spacing; GIF images; alpha blending; 2400 lines. |
section 11.7 |
| 6 | The JavaScript interpreter had six string methods and small tables. | Many more string methods (split, replace, trim, slice, padStart and others), array shift, unshift, reverse, slice, includes, concat, Math.pow/sign/trunc/hypot, and larger limits. |
section 11.9 |
| 7 | A top bar, a floating dock, desktop icons and a centred launcher. | A classic desktop: a gray 3D taskbar with a NoovexOS start button, one button per open window, a tray and a clock; a classic start menu with Development, Games, Graphics and Utilities submenus and Exit; no desktop icons. | section 11.3 |
| 8 | Windows had rounded light frames and a 40-px title bar. | Classic windows: a raised gray frame, a navy-to-blue gradient title bar (gray when inactive), square 3D caption buttons, and a 30-px title bar (TITLEH 30). |
section 11.2 |
| 9 | The tray had Wi-Fi, quick settings (wallpaper and accent) and a bell. | A Windows 11 style tray (show more, Wi-Fi, volume, battery, notifications) and a Quick Settings panel with Wi-Fi, Airplane, Night light, Battery saver, Do not disturb and All settings, plus brightness and volume sliders. | section 11.3 |
| 10 | Most text used the 1-bit pixel font. | dchar draws the anti-aliased fontaa glyphs at the same 8-px width, so every app gets smooth text without layout changes. |
section 11.11 |
| 11 | Aurora was the default wallpaper. | Crane is the default on every boot and is preselected in setup. | section 11.3 |
Other changes:
| Change | Why |
|---|---|
| Settings opens larger (up to 1000 x 800, sized to the screen), with a wider sidebar, more space between cards, smooth text, bigger toggles, rounded buttons and a softer segmented control. | The control center was cramped. |
The kcheck investigation found the Lenovo boot copy differed from the build while nothing had changed since boot. |
That pointed at the loader, not the kernel (item 1). |
What changed in v8.97
This release adds threads, dynamic linking, a real terminal, timers, server sockets and TLS for programs, and raises the old fixed limits. Each item links to the section that explains it.
| # | Before (v8.96) | Now (v8.97) | Where |
|---|---|---|---|
| 1 | clone with CLONE_THREAD returned ENOSYS and futex never waited, so SQLite, xz and zstd were built single-threaded. |
Real threads: a shared address space, descriptor table and signal handlers, threads running in parallel on all cores, futex wait and wake (with requeue, wake-op and bitsets), gettid, tgkill, set_tid_address, exit versus exit_group, execve from a threaded program. musl's pthreads work unchanged; SQLite is thread-safe and xz -T and zstd -T compress in parallel. |
section 5.15 |
| 2 | Only static programs ran. | Dynamic linking: the kernel loads PT_INTERP, the shared musl libc.so is the dynamic linker /lib/ld-musl-x86_64.so.1, and dlopen loads libraries from disk. The ports are now dynamic programs. |
section 6.10 |
| 3 | The console dropped escape sequences, so colours and cursor movement vanished, and termios settings were ignored. | An xterm-style terminal: colours (16, 256 and true colour), cursor movement, erase, insert and delete, scroll regions, the alternate screen, cursor position reports, cursor hiding; termios raw mode, echo control, VMIN/VTIME, arrow and function keys as escape sequences. The grid is now up to 240 x 72 cells. The kilo editor is included. |
section 11.12 |
| 4 | alarm and setitimer were stubs. |
alarm, setitimer and getitimer with SIGALRM (ITIMER_REAL). |
section 5.16 |
| 5 | 32 MiB of user memory for everything, 32 descriptors per process, 32 open files and 12 pipes system-wide, 64 processes, a 64 KiB stack. | The frame pool grows with RAM (32 MiB plus half the free heap, up to 2 GiB more), 256 descriptors per process, 1024 open files, 128 pipes, 256 process/thread slots, 128 sockets, a 1 MiB stack, and select on any descriptor. |
section 3.7, section 5.9 |
| 6 | listen and accept returned EOPNOTSUPP, and loopback traffic was refused. |
Server sockets (listen, accept, accept4, a listen queue with backlog) over the network card and over a new loopback path for 127.0.0.1 and the machine's own address. |
section 9.7.3, section 9.7.4 |
| 7 | No TLS in user space. | BearSSL (upstream, MIT) for musl programs, a helper that loads the kernel's 120-root store (written to /etc/ssl/nvx-anchors.pem at boot) and /etc/ssl/local-ca.pem, and fetch, an HTTP/HTTPS downloader with certificate checks. |
section 6.11 |
Other changes:
| Change | Why |
|---|---|
| Embedded programs and the shared C library are stored compressed (raw DEFLATE) and inflated on first use. | The kernel went from 5.46 MB to 3.86 MB, which made room for the new programs; it is now about 4.28 MB, 1.75 MB under the UEFI limit (section 6.6). |
getrandom, /dev/random and /dev/urandom return output of the kernel's ChaCha20 generator. |
They were an xorshift or LCG sequence, not suitable for TLS keys (section 9.8). |
| The kernel's random generator checks CPUID before using RDSEED or RDRAND. | It used RDSEED unconditionally, which crashes on CPUs without it (found when programs started asking for random bytes in QEMU). |
TLB shootdowns that wait for the other cores, for copy-on-write, fork and munmap in a process with threads. |
Threads of one address space run on several cores at once (section 4.15). |
User memory is limited to PML4 slot 8 (0x40000000000-0x47FFFFFFFFF) everywhere; mmap searches the whole slot for a gap. |
jc_uok allowed addresses up to 0x7FFFFFFFFFF, but only slot 8 is cloned on fork and freed on exit. |
Native programs' writes no longer sync the file to disk on every write; the file is written back when it is closed. |
seq 1 30000 >file took more than 10 s in QEMU (section 5.12). |
| The program table had room for 80 entries but 81 were listed. | The compiler dropped the end marker; the table's size now comes from its contents. |
The system-call fuzzer no longer calls futex, accept, alarm and setitimer. |
With real waiting these can legitimately block the fuzzer forever or end it with SIGALRM. |
New tests: featest (52 checks for descriptors, memory, timers, server sockets, threads and the terminal), dyntest (13 checks for the dynamic linker and dlopen), the fetch checker group (BearSSL and fetch on the host under sanitizers against local TLS 1.2 servers), and in the net group fetch against the test peer and a connection from the host into a musl server program (section 12.2).
What changed in v8.96
The kernel is now built from separate modules instead of one giant source file.
| Before (v8.95) | Now (v8.96) |
|---|---|
kernel64.c included every kernel .c file into one translation unit; nearly everything was static and visible to everything after it. |
104 compilation units listed in units.list, each compiled on its own. |
No interfaces: the include order in kernel64.c decided what a file could use. |
One interface header per unit under include/nvx/, with shared types, constants, macros, extern variables, prototypes and small inline helpers. About 1,850 names are shared; the other 2,400 stay static in their file. |
| Every build recompiled the whole kernel (about 20 s). | tools/kbuild.py compiles only changed units, in parallel, using gcc -MMD dependency files. A full build takes about 12 s on 2 cores; changing one .c file recompiles one unit. |
| Units silently depended on whatever came earlier. | A unit includes only the headers it uses (10 on average), and each header includes its own dependencies, so a missing include is a compile error. |
.h files that really held code or data (include/percpu.h, arch/x86_64/simd.h, lib/js.h, lib/crypto/*.h, the fonts). |
They are units now: arch/x86_64/cpu/percpu.c, arch/x86_64/cpu/simd.c, lib/js/js.c, lib/crypto/*.c, assets/font.c, assets/fontaa.c. |
The behaviour of the system is unchanged: the full checker passes 86 of 86 checks (including the QEMU multi-core, socket and HTTPS runs), and the image boots on BIOS and UEFI. The kernel is about 24 KB larger because the compiler no longer inlines across files. Part 1, section 3 explains the layout and how to add a unit; section 12.2 explains how the test generators read the split sources.
What changed in v8.95
This release fixes the problems found in the v8.94 review and adds the features those fixes needed. Each item links to the part that explains it in full.
| # | Problem in v8.94 | What v8.95 does | Where |
|---|---|---|---|
| 1 | The installer copied the running kernel into a 3 MB buffer, but the kernel is about 5 MB, so the copy failed and every installation stopped. | The boot-time snapshot holds up to 8 MB (the build limit), and the check works on a heap copy instead of a second static buffer. kcheck reports code MATCHES whole file MATCHES. |
section 2.12, section 8.7 |
| 2 | disks re-pointed the AHCI port's command list and FIS buffers at its own probe buffers and never pointed them back, so disk access failed afterwards. |
ahci_identify saves the port registers and restores them (stopping and restarting the port) after the probe. |
section 8.3 |
| 3 | The integrity check only warned. | A hash mismatch stops the boot at the "WRONG VERSION" screen. R restarts, C continues at your own risk. Unstamped developer builds are not blocked. | section 2.12 |
| 4 | Linux programs could not use the network: socket() returned EAFNOSUPPORT. |
New socket layer (sys/linux/lxsock.c, device 14): TCP and UDP over IPv4 with connect, send/recv(from/msg), bind, shutdown, poll/select, non-blocking mode, retransmission and flow control. musl's getaddrinfo works without configuration. |
section 9.7, section 6.9 |
| 5 | Sockets and HTTPS read only from the wired card and checked a wired-only flag, so they could not work over Wi-Fi. | Every receive path goes through the active link (g_rx), every sender checks nd_up(), and nic_up() reports the Wi-Fi link when Wi-Fi is active. |
section 9.5 |
| 6 | TLS did not verify the server at all. | Full server authentication: X.509 chain to one of 120 built-in Mozilla roots (or your own in /etc/ssl/local-ca.pem), host name (SAN, wildcards, IP), validity dates, the CertificateVerify signature (ECDSA P-256/P-384, RSA-PSS) and the server Finished. https -k skips the checks for one request. |
section 9.9 |
| 7 | The Disk Manager could not be reached. | diskmgr (or partitions) opens it, and P opens it from the installer. |
section 8.8 |
| 8 | smp_tlb_shootdown and smp_kick_idle were never called, and a stale comment in kmain said the cores start only on demand. |
High-MMIO mapping triggers a TLB shootdown, new processes wake idle cores at once, and the comment is gone. | section 4.14, section 4.15 |
Problems found and fixed while testing these changes:
| Problem | Fix |
|---|---|
| The kernel never answered ARP requests, so after the gateway's entry for us expired it dropped our SYN-ACKs. Every second HTTPS connection failed. | arp_auto_reply in every receive loop (section 9.6.3) |
Wired TCP/UDP sent nothing until wifi eth had been run once (nd_tx had no fallback). |
nd_tx/nd_rx fall back to the wired card (section 9.5) |
| The TLS transport acknowledged a whole TCP segment even when it had copied only part of it, losing data. | tcps_rd acknowledges only what it copied (section 9.6.7) |
| No MSS option and a fixed 8 KiB window made downloads slow (300 KB in about 6 s in QEMU). | SYNs carry MSS 1400; Linux sockets advertise their free buffer space (300 KB in about 0.05 s) |
| The terminal cut commands at 127 characters. | 254 characters (section 11.5) |
>>file, <file and 2>file written without a space were passed to programs as arguments; >> on built-in commands truncated. |
Both fixed (section 11.8) |
SHA-256 shifted a byte left by 24 bits as a signed int (undefined behaviour found by UBSan). |
Cast to u32 first |
New tests: the tls checker group (117 Mozilla root signatures plus 31 generated chain and signature cases, under ASan/UBSan) and the net group (a QEMU guest on a tap device against a scripted peer: nettest, HTTP, and HTTPS with good, unknown, wrong-name and -k cases). See section 12.2.
Note: The Wi-Fi changes were checked by reading the code and by the wired tests, which use the same paths; they have not been run on an RTL8821CE yet. Please report what netlog shows if HTTPS over Wi-Fi fails on your laptop.
