The ramdisk build embedded a full second copy of ramdisk.tar at
boot/ramdisk.tar inside the ramdisk itself. Nothing consumes it (the
Installer copies the live 0:/ tree file-by-file and explicitly skips it),
so it was pure dead weight that doubled the image. Build the tar once.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Route draw_rounded_frame through a new fill_round_rect_aa that fills the
straight interior solid and 4x4-supersamples only the four corner arcs,
so button/text-field/panel/list-frame corners are smooth instead of
pixelated, at negligible cost.
Consolidate the checkbox's private AA fill/blend helpers onto the shared
aa_blend_px / fill_round_rect_aa primitives.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Rewrite the Installer app on the Montauk Toolkit (Canvas + gui/mtk
widgets, theme, hover states, scrollbar) replacing the hand-rolled px_*
renderer and bespoke TrueType usage.
Add a new "Software" step: an expandable checkbox tree for optional
components (Montauk SDK with gcc/g++, binutils, tcc, lua sub-items;
Games; Office; Internet apps; Printing; experimental httpd and SDR).
Unchecked components' paths are excluded from the install copy, with a
longest-path ownership rule so e.g. sdk/tcc installs even when the rest
of the SDK is deselected. The update flow refreshes only the components
the target already has.
Add tri-state checkbox and disclosure-arrow widgets to the MTK toolkit
(anti-aliased, supersampled).
Merge tcc and lua into 0:/sdk (0:/sdk/tcc, 0:/sdk/lua) and drop 0:/lib:
update tcc config.h, lua luaconf.h, both Makefiles, the devkit clean
scope, man pages and montaukos.org notices.
Make printing optional: init skips printd when 0:/os/printd.elf is
absent.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
stat() synthesized path-hash inodes for regular files but left
st_ino = 0 for directories. GCC's include-path setup deduplicates
directories by (st_dev, st_ino), so every directory on MontaukOS
compared equal and cc1plus silently dropped all C++ include dirs
except the first - <iostream> resolved but the target-specific
bits/c++config.h directory was gone. Directories now get the same
path-hash inodes as files. (Different spellings of one directory
hash differently; the harmless direction - a dir may be searched
twice, never dropped.)
Native GCC relinked against the fixed libc.
Co-Authored-By: Claude Fable 5 <[email protected]>
The GCC driver relocates its install prefix from argv[0]. crt1
hardcoded argv[0] as "prog", so make_relative_prefix cwd-joined it
and computed exec prefixes relative to the current directory
(0:/users/admin/../libexec/gcc/...), and because the computed
gcc_exec_prefix is non-NULL the standard /sdk prefixes were never
searched. cc1plus was unreachable from anywhere except (by accident
of the path arithmetic) 0:/sdk/bin. sdk-diag proved the kernel and
libc layers all worked; only the driver's self-relocation was lost.
New SYS_GETEXECPATH (151) returns the absolute path the process was
spawned from (Process::name); crt1 uses it for argv[0] with a "prog"
fallback. With a real argv[0], make_relative_prefix computes
0:/sdk/bin/../libexec/gcc/ from any cwd. Native GCC relinked against
the new crt1; sdk-diag ships in the SDK as a permanent probe; the
montauk.h TCC mirror gains the wrapper (checker enforced it).
Co-Authored-By: Claude Fable 5 <[email protected]>
ElfLoad read the entire binary into one kernel heap allocation before
copying segments out. The heap grows through ReallocConsecutive, so a
40 MB cc1plus required 10k physically contiguous pages - effectively
impossible after boot with a 368 MB ramdisk module resident, and the
failed spawn surfaced as posix_spawnp ENOENT in the gcc driver.
The loader now reads the ELF header and program header table (bounded
at 64 entries), then copies each PT_LOAD page and the PT_TLS template
directly from the VFS into freshly mapped process pages. Peak kernel
memory per load drops from fileSize to one page regardless of binary
size. Boot-smoke verified: all userspace loads through this path.
Co-Authored-By: Claude Fable 5 <[email protected]>
Two ramdisk fixes exposed by shipping the C++ header tree:
Paths longer than 100 characters (libstdc++'s pb_ds detail headers)
are split by USTAR across the name field and the 155-byte prefix
field at offset 345. The parser only read the name field, so the
tails of 7 deep header paths appeared as bogus root-level entries
while the real paths were missing. The parser now joins
prefix + '/' + name, and MaxNameLen grows to 260 to hold the full
combined path.
SYS_CHDIR validated non-root targets by opening them as files, which
only ever worked because directories used to be openable. It now uses
the ReadDir probe, which fails for nonexistent paths and regular
files on all backends (ext2/fat32 already validate the inode type).
Co-Authored-By: Claude Fable 5 <[email protected]>
SYS_OPEN succeeded on directory tar entries, so userspace stat() -
which tries open-as-file first - reported every directory as a
regular file. GCC's include-path setup stats each search directory
and rejected all the real ones with "not a directory" warnings.
Directories now fall through Open to -1 and stat() classifies them
via the (recently fixed) ReadDir existence probe.
Co-Authored-By: Claude Fable 5 <[email protected]>
ReadDirAt returned 0 (empty listing) for paths that do not exist at
all, so the libc's stat()/access() - which probe directory-ness via
SYS_READDIR - reported every nonexistent path as an existing
directory. Harmless for two years of callers, fatal for GCC's driver:
it access()-tests the optional specs file, got a false positive for
0:/sdk/lib/gcc/x86_64-montauk/14.2.0/specs, then died failing to read
it.
A path now has to have a directory entry or at least one child to
list as a directory; everything else returns -1. Real empty
directories (tar dir entries, runtime Mkdir) still list as empty.
Co-Authored-By: Claude Fable 5 <[email protected]>
Cross-build GCC 14.2.0 with --host=x86_64-montauk: the driver, cc1,
cc1plus and collect2 now link as static Montauk ET_EXEC binaries
against the hosted libstdc++, and ship in the SDK. Together with the
native binutils, TCC and the target sysroot, MontaukOS carries a
complete self-contained C and C++ toolchain at 0:/sdk.
libc: anonymous mmap/munmap over SYS_ALLOC (GCC's page allocator
wants mmap; SYS_ALLOC memory is page-aligned by construction),
getpagesize, minimal sysconf, MB_CUR_MAX, isascii/toascii, POSIX id
typedefs (ino_t, dev_t, nlink_t, ...), struct stat fields switched to
proper POSIX types (st_size is a signed off_t now), and a
conventional _STDIO_H guard marker so GMP's FILE detection works.
Build recipe and the five host-build gotchas (bundled config.subs,
gettext removal, stdio guard sniffing, endianness cache preset,
sysroot sdk symlink) are documented in toolchain/README.md. The
devkit stages the driver as gcc.elf/g++.elf/cpp.elf, the backends in
the libexec layout the driver expects, a copy of ld where collect2
searches, and a 0:/tmp scratch dir. Ramdisk: 1604 files, 368 MB.
Co-Authored-By: Claude Fable 5 <[email protected]>
The cross toolchain now builds real hosted C++: std::string, vector,
unique_ptr, iostreams and exceptions all work in a plain
x86_64-montauk-g++ invocation with no special flags. This is the
library that native cc1plus will link against for the GCC self-host.
crt1 now runs .preinit_array/.init_array before main and hooks
.fini_array through atexit (main returns via exit(), so destructors
run on both exit paths). Global constructors and libgcc's eh_frame
registration both ride this. GCC is reconfigured with
--enable-initfini-array (modern arrays instead of .ctors),
--with-newlib (satisfies libstdc++'s crossconfig for unknown hosts),
--disable-wchar_t and --disable-libstdcxx-pch; the montauk gcc patch
grows a hunk keeping os/generic ctype under --with-newlib (the
Montauk libc is not newlib).
libc additions harvested by the libstdc++ build: mbtowc/wctomb,
strxfrm, strtok/strtok_r, a real vfscanf/fscanf/scanf (character-
driven with widths and l/ll; also unblocks gprof later), fgetpos/
fsetpos/setbuf/fpos_t, modf plus the C99 float math variants
(acosf..tanhf, hypot/hypotf as wrappers), FP_* classification macros,
the full POSIX errno vocabulary, complete DT_* dirent types, and
mbstate_t.
The SDK ships the real libstdc++.a (was an empty stand-in), the C++
headers at 0:/sdk/include/c++, and cxx-test.elf (global ctor, string,
vector+sort, unique_ptr, throw/catch; exits 0 on 5/5). Ramdisk sits
at 1571 files, within the 2048 table.
Co-Authored-By: Claude Fable 5 <[email protected]>
With the TLS runtime landed (PT_TLS loading + per-thread FS base) and
tls-test.elf passing on the OS, the sysdep.h hunk that degraded BFD's
_Thread_local error state to plain globals is no longer needed.
Native binutils is rebuilt with genuine thread-locals: the shipped
tools now carry a PT_TLS segment and %fs-relative accesses, exercising
the new runtime in every invocation.
Co-Authored-By: Claude Fable 5 <[email protected]>
MontaukOS binaries could not use thread-local storage: the ELF loader
ignored PT_TLS and no FS base was ever programmed, so any %fs access
(initial-exec/local-exec TLS, e.g. libbfd's _Thread_local error state)
page faulted at address 0. This is the kernel prerequisite for
cc1plus/libstdc++ in the GCC port.
ELF loader: parse PT_TLS and build the main thread's TLS block above
the loaded image (x86-64 variant II ABI: block below the thread
pointer, TP = base + align_up(memsz, align), .tdata copied from the
template, .tbss zeroed, ABI self-pointer stored at [TP]). The
template description is kept on the process for thread spawns.
Scheduler: fsBase per thread, loaded into IA32_FS_BASE at every
dispatch site through a per-CPU cache - TLS-free processes cost one
MSR write per CPU ever, and userspace cannot desync the cache since
CR4.FSGSBASE stays off. SYS_THREAD_SPAWN gives each new thread its
own TLS block copied from the template (allocated from the process
heap; reclaimed at process teardown).
Ships tls-test.elf in the SDK (built by the devkit target): checks
.tdata values, .tbss zeroing, and the %fs:0 self-pointer, exiting
nonzero on failure. Boot-smoke verified; existing TLS-free programs
are unaffected.
Co-Authored-By: Claude Fable 5 <[email protected]>
Groundwork for the GCC driver: a compiler driver must spawn cc1/as/ld
and know whether each stage succeeded.
Kernel: the scheduler keeps an exit-code ledger (pid -> code; pids are
monotonic so entries never alias), published during teardown right
before waiters wake. SYS_EXIT records main()'s return value, SYS_KILL
records 256+SIGKILL, and the exception handler records 256+signal
mapped from the fault vector (#PF/#GP -> SIGSEGV, #DE/FP -> SIGFPE,
#UD -> SIGILL). SYS_WAITPID now returns the code: 0..255 for a normal
exit, 256+signal for a violent death. Process args grow from 256
bytes to 4 KiB (cc1 invocations do not fit in 256), with crt1 now
parsing up to 255 argv entries from a static buffer.
libc: new spawn.h with posix_spawn/posix_spawnp over SYS_SPAWN -
libiberty's pex layer has a posix_spawn backend, so GCC's driver works
without fork. argv is joined into the kernel args string (spaces in
arguments rejected; no kernel quoting), envp is not transferred, and
non-empty file actions fail loudly with ENOTSUP until the kernel can
redirect stdio on spawn. waitpid() now decodes real POSIX status and
the sys/wait.h macros distinguish exited from signaled children.
Shell: prints [exit code N] after nonzero exits and [terminated by
signal N] for killed or crashed children.
Verified on the OS: cat on a missing file reports exit code 1; a
window-close (clean exit 0) stays silent as it should.
Co-Authored-By: Claude Fable 5 <[email protected]>
0:/usr was a Unix transplant in an otherwise Montauk-native layout.
The SDK now lives at 0:/sdk/{bin,include,lib} (+ ldscripts under
0:/sdk/x86_64-montauk): Montauk-flavored, and a clean unit for a
future per-component installer tickbox. Native binutils is rebuilt
with --prefix=/sdk so ld's compiled-in search paths follow; future
ports configure with --prefix=/sdk.
Also add pathconf() with _PC_* names to the libc: now that realpath
exists, libiberty's lrealpath.c compiles its pathconf fallback path
unconditionally.
Shell command resolution and tab completion updated to 0:/sdk/bin.
Boot-verified in QEMU: setup, login, desktop on the new image with
zero usr/ remnants in the ramdisk.
Co-Authored-By: Claude Fable 5 <[email protected]>
Every libbfd-based native tool (objdump, as, ld, ar, nm, ...) page
faulted at startup: binutils 2.43 keeps BFD's error state in
_Thread_local variables, and MontaukOS has no TLS runtime (no FS-base
setup, no PT_TLS handling), so the %fs:0 access in bfd_init trapped at
address 0. elfedit survived only because it does not link libbfd.
Patch bfd/sysdep.h to define the TLS storage macro to empty under
__montauk__ (binutils has no fallback of its own; ac_cv_tls=none does
not even compile). Single-threaded tools lose nothing.
Confirmed on the OS: objdump -S disassembles hello.elf in the
terminal. Real TLS support (FS base + PT_TLS in the ELF loader) stays
on the roadmap for the GCC/libstdc++ milestone.
Co-Authored-By: Claude Fable 5 <[email protected]>
The 0:/usr devkit pushed the base image to ~650 files, past the
ramdisk's MaxFiles = 512. The USTAR loader silently dropped the tail
of the archive and, worse, every runtime Create/Mkdir failed once the
table was full - so first-boot account creation could not write the
user database and every login failed with "invalid username or
password".
Raise MaxFiles to 2048 (256 KiB static table) and log loudly both
when archive entries are dropped at load and when Create/Mkdir hit
the cap, so a full table can never masquerade as an auth failure
again.
Verified in QEMU end to end: setup -> create account -> log in ->
desktop.
Co-Authored-By: Claude Fable 5 <[email protected]>
The devkit target in programs/GNUmakefile stages the native binutils
(as, ld, ar, nm, objcopy, objdump, readelf, ranlib, strip, size,
strings, addr2line, c++filt, elfedit as *.elf) into the image at
0:/usr/bin, plus a target-side sysroot: libc headers, montauk/ and
Api/ SDK headers and the freestanding C++ set at 0:/usr/include, and
libc.a + crt1/crti/crtn objects (with empty libm/libstdc++ stand-ins)
at 0:/usr/lib. ld's ldscripts ship under 0:/usr/x86_64-montauk.
The /usr prefix matches the binutils configure prefix, and the kernel
already resolves driveless absolute paths against the cwd drive, so
compiled-in /usr/lib search paths work natively on drive 0.
The shell resolves commands from 0:/usr/bin (after 0:/os) and tab
completion lists it. When toolchain/native/ has not been built, the
devkit step skips cleanly and the image builds as before.
Co-Authored-By: Claude Fable 5 <[email protected]>
Grow the Montauk libc enough to cross-build binutils 2.43.1 with
--host=x86_64-montauk. gas, ld, ar, nm, objcopy, objdump, readelf,
ranlib, strip and friends now link as native ET_EXEC Montauk binaries
(staged stripped in toolchain/native/, not yet shipped in the image).
New libc surface: full Linux errno and signal sets, O_* flags with
real O_EXCL/O_APPEND semantics, unlink/rmdir/dup/dup2/getpid/_exit,
kill (SYS_KILL), fcntl, fileno/fdopen, putc/getchar/rewind,
ctime/asctime, strtoll/strtoull/atoll, bsearch, mkstemp/mktemp,
realpath (lexical, drive-prefix aware), mbstowcs/mblen, chmod/fchmod/
umask/utime no-ops (VFS has no modes or settable times), full struct
stat with fake inodes, sscanf field widths (bounded conversions),
SCN*/PRI* completions, wait/waitpid over SYS_WAITPID, and new headers
sys/wait.h, sys/param.h, utime.h, wchar.h, memory.h.
fork/exec/pipe are declared but fail with ENOSYS: binutils never
spawns, and real process plumbing is the posix_spawn milestone, which
needs kernel support (exit status reporting, fd redirection).
TCC's montauk_compat.h shims (unlink, chmod, execvp, realpath, fdopen,
strtoll, strtoull) are retired in favor of the libc versions.
Co-Authored-By: Claude Fable 5 <[email protected]>
Switch all program and library Makefiles from host g++ (or the
bare-metal x86_64-elf compiler) to x86_64-montauk-g++/gcc and drop the
flags the target now owns: -nostdinc + kernel freestanding -isystem
paths, -m64/-march, -fno-PIC, -mno-red-zone, -mcmodel=small, -static,
-Wl,-m,elf_x86_64, -z max-page-size, --build-id=none. Deliberate
policy flags stay (-ffreestanding, per-app SSE, -nostdlib + link.ld).
Program builds now require the montauk toolchain and fail with a
pointer to build-montauk-toolchain.sh; lib/ and libs/ keep the host
fallback since the toolchain script bootstraps libc through it.
Userspace is also now pinned to the cross GCC instead of drifting
with the distro compiler.
Co-Authored-By: Claude Fable 5 <[email protected]>
Add build-montauk-toolchain.sh: builds an OS-aware cross GCC/binutils
(same versions and prefix as the bare-metal x86_64-elf toolchain) whose
x86_64-montauk target links static ET_EXEC binaries against the Montauk
libc sysroot with no special flags: 4 KiB max page size for the kernel
ELF loader, -mno-red-zone by default, crt1/crti/crtn + -lc from the
sysroot, __montauk__ defined. Target patches are checked into
toolchain/patches/, the GCC target header into toolchain/files/.
sys/types.h now includes <stdint.h> so hosted code (libgcov and
friends) sees intptr_t via the stdio include chain.
Co-Authored-By: Claude Fable 5 <[email protected]>
Add tear-free scanout to the Intel GPU driver: a second kernel-allocated
scanout buffer, DSPASURF flips latched at vblank, and a vblank interrupt
delivered over MSI (Gen 11+ master/display/pipe IRQ chain) with a
monotonic vblank counter and WaitVblank().
Expose it as SYS_FBFLIP (150): index selects the front buffer, -1
queries support, flags bit0 waits for the flip to latch. fb_map() now
maps buffer 1 right after buffer 0 when flipping is available, and
gui::Framebuffer draws to the off-screen buffer and flips with vsync,
falling back to the direct copy when unsupported.
Scanout is restored to buffer 0 when the flip-owning process exits and
on panic, so the terminal and panic box never land on the invisible
buffer.
Co-Authored-By: Claude Fable 5 <[email protected]>
Restricting the IntelHDA PCI match to subclass 0x03 (e0c02f7) hid the
speakers on modern laptops, where the same HDA controller enumerates
as 0x01 (Multimedia audio controller) when the Smart Sound DSP is
enabled. Match any multimedia subclass instead; legacy AC'97 devices
also report 0x01 but expose an I/O-space BAR0, which Probe already
rejects via the ReadBar0 check.
Co-Authored-By: Claude Fable 5 <[email protected]>
The A2DP output is a single unmixed PCM stream. A second process
opening audio while a stream was active would reconfigure the SBC
encoder and media clock under the owner and interleave both apps' PCM
into one ring, garbling playback (e.g. launching DOOM destabilized
Music). Add ClaimOutput/ReleaseOutput pid ownership: the first opener
gets the BT sink, later openers fall back to the HDA mixer, and only
the owner can tear the stream down. The scheduler releases ownership
on process exit so a killed app cannot leak the claim.
Co-Authored-By: Claude Fable 5 <[email protected]>
Deleting a big file froze the desktop for seconds: ext2 FreeBlock did 4
synchronous disk I/Os per data block (bitmap + BGDT read/write), and the
file manager deleted single files inline on the desktop main thread.
- Ext2: batch block frees per block group; keep the bitmap resident,
clear bits in memory, flush bitmap + BGDT once per group transition.
Also covers the truncate-on-overwrite path.
- Ext2: refuse to mount volumes with block size > 4096; temp buffers
throughout the driver are single 4 KiB pages, so larger blocks would
overflow them (our mkfs always uses 4K).
- Files: route single-file deletes past 4 MiB to the background worker
+ progress dialog; refuse (instead of stalling inline) when another
file operation already owns the worker.
Co-Authored-By: Claude Fable 5 <[email protected]>
Cut the 0.1.7 release (docs/tutorials, HTML man pages, updated man pages,
libc floating-point formatting, DOOM missing-WAD warning, ramdisk copy fix).
Archive the 0.1.7 ISO, publish it to the website, refresh release notes and
the downloads page, and open the 0.1.8 dev cycle (apiVersion 9, BuildNo reset).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
VirtualBox default VMs expose an AC'97 audio controller (8086:2415,
class 04/subclass 01) whose BAR0 is an I/O-space BAR. The IntelHDA
driver matched any Intel multimedia subclass, and ReadBar0 masked the
I/O BAR's port number into a bogus 'physical address' that MapMMIO
panicked on (non-page-aligned), halting boot.
Two fixes:
- IntelHDA now matches only subclass 0x03 (HD Audio); AC'97 (0x01) is
a different programming model and is not claimed.
- ReadBar0 returns 0 (no usable MMIO BAR) for I/O-space BARs instead
of handing a port number to MapMMIO.
Co-Authored-By: Claude Fable 5 <[email protected]>
Ship a default wallpaper (blossoms by Nikhil Kumar, Unsplash License) in
a new 0:/os/wallpapers/ directory for OS-provided imagery, staged from
programs/data/wallpapers/. login.elf falls back to
0:/os/wallpapers/default.jpg when the desktop config names no wallpaper
or the configured file cannot be opened. Attribution added to
THIRD-PARTY-NOTICES.txt and man legal.
Co-Authored-By: Claude Fable 5 <[email protected]>
- THIRD-PARTY-NOTICES.txt gains sections for the Mozilla CA certificate
bundle (0:/os/certs/ca-certificates.crt; Mozilla CA Certificate
Program root store as packaged by Debian/Ubuntu ca-certificates,
MPL-2.0) and the Intel Bluetooth firmware blobs
(0:/os/firmware/intel/ibt-1040-0041.*, from linux-firmware, Intel
redistributable firmware license).
- MPL-2.0 full text and Intel's firmware license (with its required
copyright notice and disclaimer) ship at 0:/os/licenses/.
- Both added to the license's third-party list and man legal.
Co-Authored-By: Claude Fable 5 <[email protected]>
- THIRD-PARTY-NOTICES.txt gains sections for the Tiny C Compiler
(0.9.28rc, LGPL-2.1, modified for MontaukOS; source in
programs/src/tcc/) and Lua (5.4.8, MIT, Lua.org PUC-Rio).
- LGPL-2.1 full text ships at 0:/os/licenses/LGPL-2.1.txt (copied from
the vendored TCC COPYING); Lua's MIT text is reproduced in the
notices file.
- Both components added to the license's third-party list and man legal.
Co-Authored-By: Claude Fable 5 <[email protected]>
- Ship each bundled font's license at 0:/os/licenses/: OFL-1.1 copies for
JetBrains Mono, Noto Serif, and Roboto, and AGPLv3 + font-embedding
exception for C059 (URW Base 35).
- THIRD-PARTY-NOTICES.txt gains sections for all four font families and
now describes MontaukOS as source-available.
- license.txt rewritten as the MontaukOS Software License: use, study,
modify, and redistribute freely with attribution; selling or bundling
into commercial products still requires permission. Replaces the
previous no-copy/no-modify/no-redistribute EULA.
- The MontaukOS license itself now also ships on the ISO
(0:/os/licenses/LICENSE.txt, sourced from the website copy).
- man legal updated: license pointer + full third-party component list.
Co-Authored-By: Claude Fable 5 <[email protected]>
- New 0:/os/licenses/ directory on the ISO with GPL-3.0.txt (Flat Remix
icon theme), GPL-2.0.txt (doomgeneric DOOM engine), and NOTICES.txt
(mirrors montaukos.org/THIRD-PARTY-NOTICES.txt).
- THIRD-PARTY-NOTICES.txt gains a Flat Remix section (GPLv3, attribution,
modification and corresponding-source statements; the shipped SVGs are
the source form).
- man legal now lists third-party components and points at 0:/os/licenses/.
- doom1.wad removed from the repo and the build: the shareware WAD is
proprietary id Software content. doom.elf (GPLv2) still ships; users
must supply their own WAD.
Co-Authored-By: Claude Fable 5 <[email protected]>
bdAddr bytes are stored LSB-first (HCI wire order) throughout the stack,
but the bluetooth app and btbonds tool formatted them LSB-first too, so
every MAC displayed byte-reversed. Print addr[5] down to addr[0] instead.
Display-only change; wire-order parsing and syscalls untouched.
Co-Authored-By: Claude Fable 5 <[email protected]>
Four fixes, each a root cause verified on hardware (AX211 + Bose QC Ultra):
1. Link Key Request Reply TRUNCATED: the pending-command queue's params
buffer was 16 bytes; the reply is 22 (addr 6 + key 16). The controller
got 10 key bytes -> every stored-key reconnection failed authentication
(status 5) since 2026-06-03 (0f16785). Fresh pairings never touch this
path, which kept the bug perfectly disguised as a headset quirk.
2. Secure Connections host support (0x0C7A) now enabled: bonds are minted
as P-256 (Type=7), interoperable with BlueZ's, and SC-bonded peers can
actually authenticate us.
3. Never write the BD_ADDR override (0xFC31) with the factory address:
it desyncs the firmware's crypto address from the on-air one and ALL
SSP pairing fails with status 5. (The spoofing feature itself was
already known-cosmetic: the baseband answers pages on the factory
address regardless.) import-bluez-bond.sh now removes the override.
4. A2DP channel setup: wait for Encryption Change before dialing L2CAP
(post-SSP sinks ignore unencrypted CONN_REQ), and LISTEN 2.5s first --
on reconnection the sink dials AVDTP itself and ignores our dials while
doing so. Ends the historical connRsp=FFFF retry-then-give-up failures.
Plus: queued security replies now log delivery + controller status.
Co-Authored-By: Claude Fable 5 <[email protected]>
- On Authentication Failure (Auth Complete status!=0 -- previously swallowed
silently -- or disconnect reason 0x05), drop the stale local link key so
the next connect falls back to fresh SSP pairing instead of failing
identically forever (BlueZ behavior). Log the link-key exchange.
- scripts/import-bluez-bond.sh: copy a BlueZ link key into the MontaukOS
key store on the installed root. Root cause: the AX211 BD_ADDR override
(0xFC31) is cosmetic -- the baseband answers pages on the FACTORY address,
so peers see Linux and MontaukOS as ONE device with ONE key slot, and each
OS's pairing clobbers the other's key. Sharing identity + key ends the
fight: both OSes reconnect (incl. autoconnect) without re-pairing.
Co-Authored-By: Claude Fable 5 <[email protected]>
The BT firmware download now runs from the idle loop after boot (zero boot
stall), completing the async goal. What made every earlier deferral attempt
fail was a months-latent HCI-layer bug, not the deferred environment:
WaitCommandComplete returned after the FIRST USB packet of an event, but
events larger than the 64-byte interrupt max-packet (like the AX211's
96-byte FC05 TLV version response) span several packets. Sending the next
command while the tail of the previous response was still in flight wedges
the AX211 bootloader into permanently ignoring commands. Boot-time flanterm
rendering added milliseconds between commands and accidentally paced the
protocol past the race -- which is why the synchronous bring-up always
worked and every log-suppressed (deferred) bring-up went mute at FC05 #2,
regardless of scheduling/MSI/xHCI fixes.
Fix: reassemble multi-packet Command Complete/Status events in the
transfer callback; the mailbox is marked ready only when the declared
event length has fully arrived. This inherently paces command flow and,
as a bonus, the TLV version read now sees the full response (sbe_type
present -> ECDSA/RSA selection is no longer a guess).
Also: per-slot EP0 completion tracking in the xHCI (a waiting ControlTransfer
can no longer be released early by another device's EP0 completion).
Verified on the AX211: instant boot, background download, real BD_ADDR.
Co-Authored-By: Claude Fable 5 <[email protected]>
Two fixes, verified on the AX211 (8087:0033), ibt-1040-0041.sfi, 720 KB:
1. TryHeader waited 1500+2000 ms for secure-send results after the CSS and
key/signature sends, but on success this controller stays SILENT until
the end of the whole download (traced) -- both timeouts always burned in
full. A rejection arrives within milliseconds, so 250 ms windows lose
nothing and save ~3 s per cold boot.
2. The payload now goes over the bulk OUT endpoint with up to 7 fragments
in flight (the btusb bootloader path for 0xFC09), replacing ~2900
synchronous 3-stage EP0 control transfers. Headers stay on EP0; the
ACL TX DMA ring is reused (no ACL header, no NOCP credit accounting).
DrainBulkTx() ensures all bytes reach the controller before waiting for
the download-complete result.
Also in this branch since main: IRQ-safe BT-TRACE ring (KernelLogStream in
TransferCallback deadlocked on the terminal Mutex from MSI context), xHCI
interrupt-IN ZLP length fix, always-re-arm of the BT event pipe,
InPollContext same-core owner check, TLV version read retry.
Co-Authored-By: Claude Fable 5 <[email protected]>
Bump the selected character's name (16->19) and metadata (14->15) sizes, and
lay them out as a two-line block vertically centered on the preview glyph box
instead of top-aligned.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Render the button in its disabled state during the post-copy confirmation
window, matching the Bluetooth app's greyed-out "Scanning..." button.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Instead of a separate "Copied to clipboard" line beside the Copy button, flip
the button's own label to "Copied" for ~1.6s after a copy, then back to "Copy".
Simpler and keeps the footer uncluttered.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The hand-rolled Copy button (bold, custom-shaded accent fill) and the floating
accent "toast" pill both looked out of place. Replace the button with the
canonical mtk::draw_button (BUTTON_PRIMARY), matching the Refresh button in
Devices and buttons elsewhere. Replace the floating pill with a plain
"Copied to clipboard" status line in the footer, right-aligned beside the Copy
button, that fades after ~1.6s — no overlay drawn over the character grid.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The scrollbar was placed inside the padded tile grid, leaving a gap above it
(below the tabs) and beside it (before the window edge). Give the scrollbar
its own viewport spanning the full panel between the tabs and footer, flush to
the right window edge, like the Devices/Music apps. The tile grid keeps its
margins and simply reserves the scrollbar width plus a gap on its right.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Replace charmap's hand-drawn scrollbar with the mtk scrollbar widgets
(scrollbar_track_rect / scrollbar_thumb_rect / draw_scrollbar), matching the
Devices and Music apps: standard 12px track colored SCROLLBAR_BG/FG with a
draggable thumb. Adds thumb drag, track-click-to-jump, and hover highlight
(mirroring devexplorer's handling); the tile grid reserves an SB_GUTTER on the
right sized from mtk::SCROLLBAR_W. The bar auto-hides when the set fits.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Replace charmap's bespoke tab drawing/hit-testing with mtk::draw_tab_bar /
mtk::hit_tab_bar so the top tabs match the canonical MontaukOS style used by
the Desktop Settings panel: a surface-colored bar with a bottom border, the
active tab cut out in window_bg with a 3px accent underline, active label in
accent and inactive labels in text_subtle. Tab height is now 36 (theme.tab_h).
Drops the custom hover tint (the canonical style has none).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The top header bar duplicated the window-manager title bar. Drop it: tabs
now sit at the top of the content area (window shrinks by the header's
height). The "Copied" confirmation moves from the header to a floating
accent toast anchored to the lower-right of the grid, drawn last.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
A standalone Window Server app for browsing curated sets of characters
and copying them to the clipboard for pasting into other apps.
- Four sets (Latin, Punctuation, Currency, Symbols) shown as a responsive,
scrollable tile grid with category tabs, a live preview/detail footer,
and a Copy button.
- Click a tile (or use arrow keys + Enter/Space) to select and copy; a
transient toast confirms the copy. Mouse wheel scrolls; Tab cycles sets.
- Styled with the MTK theme (accent, surfaces, rounded tiles) to match the
other Montauk apps, modeled on the calculator app.
The character set is deliberately the single-byte Windows-1252 range the
system font can render (the glyph cache only holds codepoints 0-255).
Copying writes the raw byte, so glyphs render identically here and in every
other single-byte-text Montauk app (copying UTF-8 would render as mojibake).
Registered in programs/GNUmakefile and scripts/install_apps.sh alongside
calculator; menu category "Applications", icon accessories-character-map.svg.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Deferring the Intel BT firmware download off the boot path made the AX211
bootloader stop answering after the first FC05; even the final synchronous
revert freezes boot, so one of the 'neutral' fixes kept in this diff breaks
the bring-up on its own (candidates: BT-TRACE logging inside TransferCallback,
unconditional interrupt-IN re-queue after error completions on a halted EP,
xHCI interrupt-IN ZLP len fix interacting with HID, InPollContext owner
check). Full history + next experiments in memory notes, 2026-07-05/06.
Co-Authored-By: Claude Fable 5 <[email protected]>