Sync develop with main (v0.0.6)#13
Merged
Merged
Conversation
Cortex PID-0 dispatch, embedder vsync yield, dart:ui shell, CI/CD skill, widget test fix.
P9 cave/log stubs, diagnostic fill flags, merge develop after #1; drop __pycache__ and gitignore Python bytecode.
Merge fix-p9-cave: engine_patch P9 offsets/diagnostics, libm math syscalls, shell/embedder fixes, vfs_stat, README. Conflicts with develop resolved in engine_patch.py.
* Flesh out Cortex PID-0 syscalls and stabilize embedder vsync. Wire context, healing, inference, and IPC into the Cortex dispatch path; add heap growth hooks and WM/sched integration. Yield after OnVsync so runner threads can drain raster work before the next frame. Co-authored-by: Cursor <cursoragent@cursor.com> * Simplify shell to dart:ui-only and add optional engine patch skip. Drop Material/framework JIT overhead for faster QEMU bring-up; allow OSC_SKIP_ENGINE_PATCH=1 when staging a pristine libflutter_engine.so. Co-authored-by: Cursor <cursoragent@cursor.com> * Add oscortex-ci-cd agent skill for branching and release flow. Documents develop → PR → main → auto ISO release so agents work on the right branch with CI gates; wire skill into core rule and related skills. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix widget test after dart:ui-only shell pivot. OscortexShellApp was removed from main.dart; update CI test so Flutter analyze passes on the feature PR into develop. Co-authored-by: Cursor <cursoragent@cursor.com> * Document three-tier branch flow and light develop vs strict main gates. Skill now mandates feature/* work, human-only merges, and tiered CI/review rules; setup-github.sh drops CODEOWNERS requirement on develop only. Co-authored-by: Cursor <cursoragent@cursor.com> * Add present diagnostics; fix p9 cave device offsets tools/flutter-embedder: add a small diagnostic scan in present_callback that (for the first few presents) scans up to 1,000,000 bytes of the present buffer to find the first non-zero byte and count non-zero pixels, and logs pixel_len, first_nz_off and nz_count. This is gated by PRESENT_TRACE_COUNT to avoid spamming. tools/flutter-engine: update build_p9_cave docstring and emitted assembly to load device.fPixels/fRowBytes from offsets 0x140 and 0x148 into Draw.fDst (rsp+0x10 / rsp+0x18) instead of the old 0x180+0x18 addresses. The docstring explains that the previous offsets addressed a clip/matrix pointer, which caused the blitter to write to the wrong place (resulting in zero fills); the change fixes the target so drawPaint operates on the actual pixel buffer. * Add diagnostic logging and fill to engine patch Introduce optional diagnostic logging and buffer fill to the engine patch and embedder. Added DIAG_LOG/DIAG_FILL flags, a build_log_stub() that emits a SYS_WRITE-based tracer for several device fields, and a LOG_CAVE region. The P9 cave now can optionally memset the surface to a sentinel color before draw and call the log stub; cave-overrun checks were updated to avoid colliding with the log stub. Also added small present callback logs in the embedder (alloc_addr and row_bytes) to aid debugging of the present/framebuffer path. Updated PATCHES and main to include LOG_CAVE when diagnostics are enabled. * Fix P9 cave diagnostics and drop accidental __pycache__ from tree. Merge develop after PR #1; keep engine_patch P9 cave/log stubs and ignore Python bytecode in git. Co-authored-by: Cursor <cursoragent@cursor.com> * Add libm math syscalls, UI shell & embedder fixes Introduce a kernel-side libm path and a new Flutter-based shell UI, plus various embedder and tooling fixes. - Add libm crate to kernel Cargo.toml/Cargo.lock. - Implement MathSyscall1/2/3 trampoline kinds and encode native trampolines that move xmm registers into integer args, perform a syscall and return results in xmm0. - Add libm_call() to dispatch syscall numbers to libm functions (double/float variants) and expose LIBM_NR_LO/H I range; wire dispatch_fast() to short-circuit and handle libm syscall range for fast math calls. - Update STUBS table to map many POSIX math symbols to MathSyscall entries with padding slots. - Fix compositor RGBA packing for Flutter's kN32/BGRA8888 software surface by swapping B/R while packing pixels. - Replace minimal dart:ui demo with a Material-based Oscortex shell app that uses a BasicMessageChannel for shell commands, lists/installs/launches apps, and provides a tiled app grid UI. - Improve embedder platform-message handling: reply synchronously with StandardMethodCodec null envelope where required to avoid hangs, respond to apps request channel, add send_platform_response_now/respond_platform_message helpers, task runner (run_due_platform_tasks), and lightweight logging. - Make run-qemu.sh configurable for serial output (default file:/tmp/osc_serial.log) and add --serial-stdio option. These changes enable efficient in-kernel math for hot libm calls (used by Skia/Dart), stabilize platform-channel interactions during engine init, provide a usable shell UI for app management, and fix compositor pixel ordering and QEMU serial options. * Add vfs_stat and path_len helper Introduce SYS_VFS_STAT and implement vfs_stat(path) to return a VFS file's byte size (or a negative errno) using a syscall. Extract a private path_len helper to normalize path byte slices (trim trailing NUL) and reuse it from vfs_read to avoid duplicated logic. * Added read me * Add OSCortex agent docs and soft cursor support Add comprehensive OSCortex agent guidance and skills (.agents/rules/* and .agents/skills/*) plus a Copilot instructions file and repo-level core instructions. Introduce docs/multiarch.txt and update docs/arch.txt to reference it. Remove .vscode/settings.json to drop project-specific editor settings. Implement a simple software cursor path: add FRAME_DIRTY/invalidate and draw_software_cursor in the compositor, update render_frame/tick to honor cursor redraws, wire PS/2 driver to update cursor position and call invalidate, and sync cursor state in the WM input path. Also add set_cursor_pos API to the PS/2 driver. Update the Flutter embedder with write_hex and install_jit_snapshot_paths to pass JIT snapshot file paths to the engine (debug engine builds). These changes provide agent guidance for architecture/cleanup work and add a basic OS-side pointer overlay for PS/2 input. * Update SKILL.md * Improve input scheduling and shell UI robustness Add multiple host/WM input fairness and reliability fixes and UI improvements. UI (apps/oscortex_app): safer host messaging and JSON parsing, status text, install button state, pointer smoke-test (tap debug), F12 hotkey, pointer event tracing, and layout/style tweaks to make demo install/refresh flows more robust and observable. Kernel: prioritize WM/embedder (focus) for input delivery on single-core boots — add timer-handler preempt path, cooperative scheduling adjustments in next_runnable_pid, and extensive epoll_wait/wm_event_wait changes to de-starve the focus pid (hand off or re-enter epoll_wait rather than returning EINTR). Also added a short spin window during Flutter bootstrap, framebuffer clear before rendering, and a small PS/2 mouse acceleration curve to improve pointer responsiveness in QEMU. Misc: small syscalls/poll/posix refactors and formatting, add many qemu-click verification logs, and minor tooling/script updates. These changes aim to ensure pointer/key events reach the embedder promptly and to make host<->UI interactions more resilient and debuggable. * update * cleanup of userspace ui + added ios for ui testing on ipads * update * fix: resolve sibling thread starvation on mouse moves while maintaining high-priority click preemption * fix: implement clean priority scheduling for input target without force-waking blocked threads * fix: remove vsync baton queued scheduling overrides to prevent worker starvation deadlocks * Calibrate APIC timer; per-CPU GDT/TSS Calibrate APIC/TSC timing and switch APIC timer to a calibrated periodic 1ms tick, expose APIC_TICKS_PER_MS and add helper calibration/printing routines (kernel/src/arch/x86_64/apic.rs). Use calibrated tick values when arming BSP and AP timers and add send_resched_ipi helper. Introduce per-CPU GDT and TSS data structures and per-CPU interrupt stacks to support SMP: replace single GDT/TSS with arrays indexed by CPU, set up TSS entries per CPU and update init/init_ap to load the correct per-CPU GDT (kernel/src/arch/x86_64/gdt.rs). Also add the OSCortex UI design specification HTML (docs/oscortex-ui-spec.html). Notes: changes prepare the kernel for accurate timer interrupts across CPUs and add SMP-safe GDT/TSS setup; see added calibration functions and APIC_TICKS_PER_MS for timer tuning. * update * Add oscortex_ui package and refactor UI usage Introduce a new packages/oscortex_ui package providing shared design tokens and widgets (colors, radii, shadows, typography, OscCard, OscScaffold, OscToolbar, OscAppRail, OscHubDock, OscIntentBar, OscWaveform, tags, badges, etc.). Refactor apps to consume oscortex_ui: replace local color/constants, duplicated widgets and custom styling with OscTheme, OscColors, OscTypography and reusable components across apps (notably apps/oscortex_app and apps/oscortex_canvas). Update app pubspecs to add a path dependency on oscortex_ui and simplify many UI implementations by removing in-file implementations (AppRail, HubDock, IntentBar, waveform, tag/badge widgets, etc.) in favor of the shared library to ensure consistent design tokens and reduce duplication. * Add UI previews; fix kernel scheduler & epoll Add preview infrastructure and many Preview widgets for the Flutter UI package, including: - new Preview helper OscPreview and OscAccentPreview (packages/oscortex_ui/lib/src/preview/osc_preview.dart) - exported preview entry in packages/oscortex_ui/lib/oscortex_ui.dart - numerous @OscPreview preview functions added across UI widgets and apps (osc_card, osc_toolbar, osc_app_rail, osc_hub_dock, osc_intent_bar, osc_scaffold, osc_section_label, osc_status_badge, osc_tag, osc_waveform, multiple apps) - extension_discovery cache files added under packages/oscortex_ui/.dart_tool (devtools.json, vs_code.json, README) Kernel changes to cooperative scheduling and preemption handling: - Introduce locked variants for scheduler helpers (next_runnable_sibling_thread_locked, next_runnable_pid_locked) and centralize PTABLE_LOCK usage to ensure correct per-CPU accounting - Update next_runnable_sibling_thread/next_runnable_pid wrappers to acquire PTABLE_LOCK and manage p.current_cpu - timer_preempt_switch reworked to operate under PTABLE_LOCK: pick next under lock, save outgoing regs/XSTATE, restore incoming XSTATE, set per-CPU state and update active syscall stack and fs base - spawn_thread now leaves p.current_cpu = None (thread not pinned at creation) - cooperative yield logic (kernel/src/syscall/poll.rs) inlined/rewritten to run selection under PTABLE_LOCK, handle cond-waiters, set current_cpu on chosen targets, and added *_locked helpers for target checks Epoll/struct layout fixes in syscall handling: - Change epoll event packing/stride from 12 -> 16 bytes and adjust data offset reads/writes from +4 to +8 accordingly (epoll_collect_ready, sys_epoll_wait_real logs) These changes add consistent preview tooling for UI development and harden the kernel scheduler for multi-CPU cooperative scheduling and timer preemption, while fixing the epoll event layout used by the kernel. * Add UI widgets, CPU claim helper, favicon script Introduce a safe CPU-claim helper in the kernel (process::try_claim_cpu_for) and use it in several syscall paths (futex/sys_thread_create, posix pthread mutex/cond paths) to avoid races when attempting to enter user mode on a target PID. Add a suite of new oscortex_ui widgets (banner, breadcrumbs, button/outline, chip, icon button, popup menu, status bar, text field) and export them from the package barrel. Include a small scratch script to generate favicon.ico from a vector-like polygon. These changes group scheduler safety fixes with UI component additions and a tooling helper for the web landing/favicon. * Add A2UI UI package and CPU claim gating Kernel: prevent blind transitions into user context by claiming the CPU first — add try_claim_cpu_for checks before enter_user_by_pid_noreturn in the APIC timer handler and epoll wait paths. Initialize new process fields (current_cpu, cpu_ticks, slice_left, pending_sigs, sig_mask, sig_handlers, errno_to_deliver, preempted_by_timer, user_stack_* where applicable) in spawn_with_bootstrap and clone_thread to support scheduling/signals. UI: Add A2UI (Agent-to-UI) protocol support to oscortex_ui. Export the A2UI entry from packages/oscortex_ui/lib/oscortex_ui.dart and add new source files implementing parsing, types, a reactive data store, icon mapping, renderer, and a surface widget (a2ui.dart, a2ui_data_store.dart, a2ui_icons.dart, a2ui_parser.dart, a2ui_renderer.dart, a2ui_surface.dart, a2ui_types.dart). Misc: add small scratch utilities (generate_detailed_favicon.py, test_draw.py) and a test_logo.png. * Initialize pending init PID to 1 Change PENDING_INIT_PID's initial value from 0 to 1. This ensures the PID stored before schedule_user_launch defaults to 1 (the typical init PID) rather than 0, avoiding treating 0 as an unset/invalid PID when spawning the user-init kernel task. * fix(kernel): same-CPU recursive lock reentrancy and scheduler log cleanup * Support AOT/JIT snapshots; improve dlsym Refactor embedder to detect engine mode and handle Dart snapshots for AOT and JIT builds: add IS_AOT flag, detect FlutterEngineRunsAOTCompiledDartCode at startup, add configure_aot_snapshots and load_jit_snapshot helpers, log snapshot contents, and wire AOT snapshot loading from mapped memory or libapp.so. For JIT mode the previous JIT asset-path logic is removed and snapshot pointers are left NULL. Improve kernel dlsym to scope lookups to the current process, allow handle==0 to search across libs and fall back to posix trampolines if available. Misc: comment out engine_patch invocation in scripts/build-iso.sh and add a small scratch script (scratch/parse_transcript.py) for filtering transcript logs. * Add Osc UI widgets, A2UI mappings; fix poll lock Introduce several native OSCortex UI components (OscCheckbox, OscDatePicker, OscModal, OscSlider, OscTabs) and export them from packages/oscortex_ui. Wire these widgets into the A2UI system: update docs, a2ui barrel, and the A2UI renderer to map interactive components to Osc* implementations (replacing prior Material usage and ensuring accessibility, weight, and binding handling). Add an A2UI compliance SKILL.md to enforce renderer/exports for new widgets. Also apply a concurrency/safety fix in kernel/src/syscall/poll.rs by using try_lock guards when accessing COND_WAIT_STATE to avoid potential deadlocks. Misc: remove accumulated qemu/run log files and minor script/tool tweaks. * Add on-demand package delivery subsystem Introduce an on-demand package system: kernel/pkg with resolver, http client, SHA-256 verifier, manifest format, and an LRU cache for ephemeral apps. Expose package syscalls (resolve, catalog, set_server, evict) and wire pkg init into kernel boot; add a /sys/pkg/cache sysfs entry. Integrate with the shell/UI: AppTile gains source/size/state, ShellService RPCs for catalog/resolve/server, app_card shows remote/resolving states and shell_desktop merges remote catalog and resolves+launches packages on demand. Add a new tools/pkg-server crate and include it in the workspace. Also include small kernel tweaks (idt/process call rename and related syscall plumbing) to support the feature. * Add on-demand package delivery docs & pkg module Introduce an on-demand package delivery design and implementation updates: add detailed docs (boot-sequence, developer-guide, pkg-delivery) and update architecture notes. Integrate kernel pkg support by modifying kernel/src/pkg/mod.rs and http.rs (HTTP client for fetching bundles), and wire in package delivery concepts (catalog, LRU cache, SHA-256 verification, syscalls). Also tweak user-side preview UI, ISO build script, and engine patch to support the new workflow and tooling (tools/pkg-server, docs and scripts). This change documents the design and prepares kernel/userland plumbing for fetching, verifying, caching, and launching .osx bundles on demand. * Use dlopen for AOT apps and auto-update SDK hash Load AOT ELF bundles via the dynamic loader instead of manually mmap+copying: app_registry now calls dlopen and uses a new get_load_base helper to obtain the load VA. The embedder registers the shell libapp.so at startup. The engine patcher (engine_patch.py) can extract the engine/SDK hash from libapp.so and updates multiple P_SDK_HASH offsets (with a fallback), adds a runs-AOT patch, and expands verify/apply lists accordingly. Added a small ELF hash extractor (scratch/test_parse_elf.py) and updated docs (pkg-delivery, added use-cases) and .gitignore entries. These changes centralize AOT handling, keep the engine-patching in sync with the built libapp, and add documentation/examples. * Make dl symbol lookup group-aware Use process group leader when matching loaded libraries so dlopen/dlsym/dlclose/get_* resolve symbols across a process group rather than per-pid. Add sym_name_eq to normalize and compare symbol names (strips leading underscores). Log dlsym queries in the syscall handler for easier debugging. Update the Flutter embedder argv to pass precompiled-mode flags. Extend engine_patch to include JIT/AOT and snapshot-features bypass patches and include them in verification/apply lists. Add a scratch find_ref.py helper script for searching RIP-relative references. * chore: save progress before pivoting landing page to embedded devices goal * update * update * update * update * fix: update .gitignore to include backup files and improve file skipping logic in build.rs * fix(kernel,shell): resolve Fontconfig SkFontMgr PageFault and clean unused imports for CI --------- Co-authored-by: Tahiru Agbanwa <130235676+squirelboy360@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: SudoMe <152814787+SudoGhostUser@users.noreply.github.com>
The Flutter shell never rendered — boot showed only the kernel compositor's blue background and software cursor (zero surfaces presented). Root cause was in the embedder: CUSTOM_TASK_RUNNERS set ui_task_runner to null, so the engine never registered a UI TaskQueueId. The first Shell::OnPlatformViewScheduleFrame then called fml::MessageLoopTaskQueues::RegisterTask on an unregistered queue and pushed a DelayedTask into a null TaskQueueEntry (delayed_tasks member at offset 0x50), faulting on a user write to address 0x50 and killing pid 1 (exit -9) before any frame was presented. Point ui_task_runner at the same PLATFORM_TASK_RUNNER_DESC (identifier=1) so the engine merges UI onto the platform thread and the UI queue is the registered platform queue. Verified: the null-TaskQueue crash is gone — post_task now reports runner=0x1 and tasks post successfully.
apply() writes data[offset:...] by raw file offset, but the _POSTLOADS_TO_BYPASS addresses came from objdump (virtual addresses). The R+X LOAD segment has vaddr = fileoffset + 0x1000, so every 0xC3 (ret) bypass landed 0x1000 too high at runtime. Concretely, StringDeser::PostLoad (vaddr 0x22a1c70) overwrote VMDeserializationRoots::AddBaseObjects+0x350 (vaddr 0x22a2c70), turning `mov rsi,[rcx+rax*8-8]` into a `ret` that popped a heap object (0x312c02010) off the stack and jumped into the Dart heap -> SIGSEGV. Fix: wrap each PostLoad vaddr in va_to_file() in the bypass loop, like every other patch already does. Verified via lldb: AddBaseObjects is now untouched (0x48) and the 0xC3 lands on the real PostLoad entries. The VM snapshot now deserializes fully (4001 Code objects resolve correct instruction pointers) and execution reaches isolate-snapshot Code deserialization.
A late isolate Code object decodes an instruction offset of 0x80380000 (sext -0x7FC80000, ~2GB below the image base) -> unmapped ptr -> SIGSEGV in Code::InitializeCachedEntryPointsFrom. 8000+ other isolate Code objects decode sane small signed offsets. Add a clamp cave (file 0x1F44C60): if the signed offset is outside [-0x100000, image_size], it returns base+1 (a mapped addr) so deserialization continues. Hooks ImageReader::GetInstructionsAt (0x22cfe60) with a jmp to the cave; the kernel dl.rs Patch7 byte-match guard auto-skips once these bytes change. This unblocks the instruction crash: boot now reaches the Array deserialization cluster (next blocker: data-stream cursor drifts into the instructions image -> garbage array length -> OOM).
Root cause of all prior deser failures: OSCortex ships the full-JIT (debug) Flutter engine but fed it an AOT precompiled snapshot (libapp.so). A JIT engine cannot deserialize an AOT snapshot — proven by running OSCortex's exact engine in clean Docker (8GB), where it crashes deser identically with and without the caves. Every engine_patch hack was a doomed workaround. Proven fix (rendered a real 1280x800 frame in a Docker reference embedder): run JIT mode from kernel_blob.bin (already shipped in flutter_assets) with the kernel SDK hash set to the engine's SdkHash 1a420a3f9a (not the AOT snapshot version 78da37fed6). Changes: - engine_patch.py: all_patches reduced to JIT-minimal — keep rendering/ framebuffer (P1-P6, P9*, P10) + P_ALLOW_ALL_DART_FLAGS + the two service bypasses; REMOVE all AOT flag-flips (P_RUNS_AOT, P_*PRECOMPILED*, P_JIT_AOT_CHECK) and AOT deser hacks (P_READ_INSTRUCTIONS, P_DEBUG_CAVE, P_GETINSTR_CLAMP_*, P_POSTLOAD_BYPASS_*, FINALIZE_CLASS_NAMES, TTS/init). - engine_patch.py patch_kernel_blob: target hash -> 1a420a3f9a. - kernel/build.rs: embed the shell kernel_blob.bin in the initramfs (was excluded for AOT); keep per-app blobs excluded to limit kernel bloat. - Embedder auto-selects JIT: with P_RUNS_AOT gone, is_aot=false, so SHELL mode uses assets_path and skips the AOT libapp.so path (no code change). Boot now reaches JIT codegen (mprotect R+X fires) and deep kernel loading before SIGSEGV on an unmapped engine R-data page (dl.rs mapping gap) — far past the AOT dead-end. Next: map the engine's full R LOAD segment.
sys_madvise(MADV_DONTNEED/MADV_FREE) called unmap_user_range, which permanently unmaps and frees the frames. That is only safe for anonymous memory (re-faults as zeros). Library regions [LIB_VA_BASE, ANON_VA_BASE) are mapped eagerly and are NOT demand-pageable, so unmapping loses the data permanently. The Dart VM (JIT mode) calls madvise(MADV_DONTNEED) on its loaded platform-kernel data — which lives inside the engine image at ~0x1412f8000 — to release memory. OSCortex unmapped ~6.6MB of the engine's read-only data segment; a later read of KernelProgramInfo::KernelLibraryStartOffset's table (0x141d2d908) then SIGSEGV'd. madvise is advisory, so ignoring it for lib regions is correct. Fix: only honor MADV_DONTNEED/MADV_FREE for addr >= ANON_VA_BASE. Boot now runs far past this point (threads spawn/exit, vsync frames push) before a separate stack-region fault.
…ne-thread stack bounds - sys_madvise: only unmap [0x3_0000_0000, 0x100_0000_0000) (the demand-pageable anon window). Both library images (low) AND high user stacks (0x7FFF_xxxx, above the demand range) are mapped without a backing pager, so unmapping them on MADV_DONTNEED loses data permanently. The Dart VM madvise()s both regions. - pthread_getattr_np / pthread_attr_getstack: clone-threads (Dart VM mutator/GC/ helper threads) have no recorded user_stack_base, so the VM's GetAndValidateThreadStackBounds got 0,0. Derive bounds by scanning the mapped stack region around the thread's current RSP.
Threads share the parent's PML4. Teardown unmapped p.fs_base / p.user_stack_base unconditionally — but the Dart VM places some thread fs_base/stack inside the MAIN user stack (0x7FFF_xxxx, shared by pid 1) or other non-private regions. Unmapping those clobbered pid 1's stack (a worker's fs_base was 0x7ffffffef000), causing pid 1 to SIGSEGV in Location::ToCString during error formatting. Fix: only reclaim thread-private memory in the demand-pageable anon window [0x3_0000_0000, 0x100_0000_0000). With this, pid 1 stays alive and the engine keeps running (the stack-clobber crash is gone). Also extends the debug region-unmap log to cover the main stack.
…t code
sys_thread_join returned `code as i64` (the joined thread's exit code) plus
-10/-11 for ECHILD/EAGAIN. But pthread_join(thread, retval) returns 0 on
success — the exit value goes into *retval. The Dart VM wraps it in
VALIDATE_PTHREAD_RESULT and FATALs ("pthread error: %d", os_thread_linux.cc:181)
on any non-zero return, which aborted worker threads (the -6/-10/-11 garbage
values were exactly this handler's returns).
Fix: block until the target exits (busy-wait + pause; preemption runs it),
write the exit value to *retval, and return 0. Pass arg1 (retval) through.
Result: thread aborts gone (0, was 6). The engine now reaches
FlutterEngineRunInitialized OK, runApp schedules the initial frame, and the
platform thread enters its event loop — the Flutter app is running.
present_callback is now called continuously (140+ frames) — the Flutter UI is painting. The raster thread submits real RGBA frames (gpu_submit_strided sid=1 row_bytes=5120, pixel 0xff0c1c26 = app background) to the compositor. Root cause of "app runs but no pixels": the custom MERGED task runners (platform+UI on one thread) plus a custom vsync_callback the engine never drove. With both disabled, the engine spawns its own UI/raster threads and self-drives the frame pipeline via its internal vsync timer — the exact config proven in the Docker reference embedder. This only works now that thread creation is sound (pthread_join fix) so the engine's worker threads run instead of aborting. - project_args.vsync_callback = 0 (engine-internal vsync, not embedder-driven) - project_args.custom_task_runners = 0 (engine spawns UI/raster threads) OSCortex now boots, JIT-runs the stock Flutter engine, executes the Dart app, and renders its UI to the framebuffer.
Captures the remaining source changes for the booting, rendering build (diagnostics + bare-metal embedder/loader tweaks accumulated while bringing up JIT-mode Flutter). The OS now boots, JIT-runs the stock Flutter engine from kernel_blob.bin, executes the Dart app, and paints its UI to the framebuffer (verified via QEMU screendump: app toolbar on dark navy bg). (Stale 1.46GB initramfs.tar artifact intentionally not staged; kernel/build.rs generates its own initramfs into OUT_DIR.)
…Vsync clock Three real correctness/scalability fixes found while debugging sporadic render: - sched: cooperative_sched_target preferred hardcoded pids [2,3,4,7,1] and only considered siblings when sib<=7, permanently STARVING Dart VM helper threads (8,9,10,11: JIT/GC/pool). Use fair round-robin over all runnable threads. - sync: cond-pending-consume returned 0 without yielding -> hot Dart mutator spun consuming its own pending signals, starving the helper it waited on (smp=1 livelock). Yield to a sibling before returning. - embedder: FlutterEngineOnVsync timestamps must use FlutterEngineGetCurrentTime, not rdtsc_ns (which adds ~1.7e18 epoch offset -> frame scheduled ~never). Known remaining: heavy real app still livelocks in Dart GC-safepoint coordination on bare metal; trivial widget tree renders at smp=1. WIP.
…obes - embedder: pass --dart-flags=--no-background_compilation --no-concurrent_mark --no-concurrent_sweep --marker_tasks=0 --scavenger_tasks=0 (single-threaded VM to avoid GC safepoint coordination livelock). NOTE: did NOT change behavior — flags may not be reaching the VM through the engine; needs verification. - app: OSCX lifecycle print probes (main-start/binding-initialized/ FIRST-FRAME-RENDERED) as render-success signal. Status: trivial widget tree (ColoredBox) renders 25 frames at smp=1; heavy Material shell still livelocks in Dart VM GC/heap coordination. WIP.
THE FIX — the real Material shell now renders reliably. Root causes were NOT a single bug but a stack of them, found by bisecting widget trees + boot duration: 1. FONT: bare metal has no system font provider and the bundle ships no default/ Roboto family, so Material's default-font text LIVELOCKED in font fallback (first frame never completed). Fix: alias Roboto/sans-serif/etc -> bundled NotoSans in FontManifest (scripts/build-iso.sh) + theme fontFamily=NotoSans. 2. SMP RACE: multi-core livelocks the engine threads in the cooperative sched/ sync layer. Fix: run smp=1 (run-qemu-debug.sh) until the SMP race is fixed. 3. SLOW JIT WARMUP: the engine JIT-compiles the entire Material framework on emulated bare metal — ~60-90s before the first frame. Not a hang; be patient. 4. Dart VM flags now delivered correctly via --dart-flags (COMMA-separated, underscore names): --old_gen_heap_size=512 --new_gen_semi_max_size=64 --no_background_compilation. (--marker_tasks=0/--verbose_gc are disallowed/ assert — do NOT use.) Builds on 0449aac (fair scheduling, cond-yield, OnVsync clock). Verified: 600-1097 present_callbacks/boot, full UI visible (title, Install demo button, icons, text).
…r livelocks The scheduler is cooperative and single-core by design (one user thread runs at a time, threads hand off via enter_user_by_pid_noreturn inside syscalls). At smp>1 the AP's timer-ISR kernel-mode wake-assist (idt.rs) was NOT gated on cpu_id, so an AP would claim and enter a user thread, running it CONCURRENTLY with the BSP. Two user threads in true parallel break the futex/condvar emulation's single-core assumptions → nondeterministic livelock (0 frames at smp=2; smp=1 fine). Fix: in the APIC timer handler, return early on any AP (cpu_id != 0) before the user-thread-entry paths. APs EOI and halt; all user threads run BSP-only. smp=2 now renders the full shell identically to smp=1 (381 presents/100s, no panic). Also removed dead code: save_preempt_xstate/restore_preempt_xstate + the single global PREEMPT_XSTATE scratch buffer (no callers; a shared FPU buffer would be an SMP hazard — preemption already saves per-process xstate via save_xstate(pid)). NOTE: this makes multi-core STABLE but the AP still only halts — it does not yet run threads. Using extra cores for real parallelism (to cut the ~86s JIT warmup) needs a proper per-CPU SMP scheduler, a separate larger effort.
Mouse input was dead/flaky: the cursor froze and most movement was lost. Root cause: the PS/2 mouse ACK byte (0xFA, the reply to the 0xF4 enable command) and RESEND (0xFE) get injected into the data stream, but the byte-0 gate only checked bit3 (always-one) — which 0xFA/0xFE also have set. The ACK was absorbed as a packet flags byte, permanently MISALIGNING the 3-byte accumulator: every subsequent packet was parsed with a data byte as flags (carrying overflow bits) and discarded. Result: 127 mouse bytes in, 1 push_pointer out. Fix: at packet start, also reject bytes with overflow bits set (0xC0). ACK/ RESEND/NAK all have 0xC0; a normal MOVEMENT flags byte never does. This skips the spurious byte and resyncs on the next true flags byte. Verified: 15 injected moves -> 12 push_pointer -> 13 Flutter pointer events, cursor tracks X and Y reliably from (32,32) to (1279,799). Hover/click reach Flutter (rc=0) and buttons highlight on interaction. (Bumped then trimmed mouse IRQ/pointer trace logs.)
With engine-default task runners (custom_task_runners=NULL, our render config) the engine posts platform-thread work — INCLUDING platform-message delivery — to the fml MessageLoop on pid 1, but nothing drained it. So platform_message_callback was NEVER invoked: every MethodChannel/BasicMessageChannel was silently dead (the startup oscortex/shell 'list' and the Install-demo 'install:' round-trips dropped, the shell sat on 'No apps installed'; text input / navigation / mouse-cursor channels also non-functional). Fix: call __FlutterEngineFlushPendingTasksNow() each main-loop iteration to run the expired platform tasks on this thread. Verified: 8 [embedder/pfm] messages now flow across flutter/isolate, flutter/keyboard, flutter/navigation, flutter/platform and oscortex/shell; shell 'list' round-trips (dispatch reply 238B). Unlocks the whole platform-channel surface, not just the shell.
initramfs.tar (1.4GB!) is a stale artifact from the manual make-initramfs.sh helper — the real kernel build (kernel/build.rs) generates its own copy in OUT_DIR from the initramfs/ directory, so the root tar is never used by the build. vdisk.img is a runtime virtio-blk disk that run-qemu.sh reads/writes and changes every boot. Both bloated the repo and showed as 'modified' on every build. git rm --cached keeps them on disk (run scripts still find them); .gitignore now ignores them (root-anchored) so they no longer pollute status or future commits.
…eft) Tapping an app tile now actually opens that app — it loads its OWN JIT blob, renders its own UI, and the compositor switches to it. Remaining: a thread in the second engine instance GPFs before the app fully settles. Fixes that got it this far: - embedder _start: PRESERVE the kernel's bootstrap registers (rdi=host_mode, rsi=app_id, rdx=aot_va) across the early breadcrumb SYS_WRITE. That syscall clobbered rdi to 1, so every launched app (kernel sets rdi=HOST_MODE_APP=2) booted as a SECOND SHELL and loaded the shell's blob. Now they boot APP mode and load /Applications/<name>.app/flutter_assets/kernel_blob.bin. - build.rs: embed per-app kernel_blob.bin (each Flutter host needs its own JIT blob). NOTE: ~40MB/app bloats the kernel to ~315MB — scalability follow-up is to deliver app blobs as Limine modules / from disk instead of embedding. - app_registry::launch: set_focus_pid(app) on launch — gives the new host CPU priority AND makes the compositor show it. - embedder: foreground/background lifecycle. On EV_FOCUS, a backgrounded host STOPS its frame pump (sends AppLifecycleState.paused); a foregrounded one resumes. Without this the shell hogged the single core and starved the launched app (shell 589 presents, app 0). With it the shell pauses and the app renders. - build-iso.sh: give each app NotoSans + Roboto-alias (same font fix as the shell). Known: second-engine thread GPF at engine code ~0x143f615b9; smp=1 only; ~50s warmup per host. Auto-launch test hook removed. WIP on feat/multi-app-launch.
…x141M Launched apps no longer dlopen their AOT libapp.so (JIT engine can't run it, and loading it first shifted the app's engine base to 0x142M, where the engine GPF'd). With it skipped the app's engine loads at 0x141000000, matching the shell, and the engine-code crash is gone — Canvas now warms up and JIT-compiles. New blocker (deeper): pid 11 (app VM thread) returns from a cond/mutex syscall to a GARBAGE address (0x35b809185) and #GP's — a corrupted resume context. Root cause is the cooperative scheduler mixing up thread contexts across the two address spaces (shell + app); it isn't robust enough for two concurrent heavy Flutter VMs on one core. Needs the proper preemptive/SMP scheduler. Also: kernel caps usable RAM at ~1.3GB regardless of QEMU -m.
…ully THE fix that makes multi-app launch work. When an app is foreground (focus != the shell pid 1), the scheduler now runs ONLY that app's thread group and leaves the backgrounded shell suspended. Two concurrent heavy Flutter VMs overwhelmed the cooperative scheduler's context save/restore — a backgrounded host's thread got switched in with a corrupted resume RIP and #GP'd. Serialising to one VM group at a time keeps the reliable single-host path. Result: tapping an app opens it and it renders its FULL UI (verified: Canvas's document-editor UI — title, paragraphs, line-numbered markdown editor — rendered cleanly, no crash). focus=1 (shell foreground) leaves fg_group=1 so the whole system schedules as before until an app is launched. Remaining: back-navigation (focus -> shell), focus thrash on launch, and scalable app-blob delivery (currently embedded, ~315MB kernel).
- PS/2 IntelliMouse 4-byte scroll wiring (kernel) -> EV_SCROLL -> embedder FlutterPointerEvent (signal_kind=kScroll); direction+speed configurable. - Settings screen in the shell: natural-scroll toggle + speed slider, live via config:* messages over the oscortex/shell channel. - Kernel boot spinner drawn on the framebuffer during JIT warm-up (drivers/fb.rs::draw_boot_splash, driven by compositor::tick); clears when Flutter presents its first frame. Plus a 'Launching <app>' overlay in the shell. - Adaptive embedder frame pump: gentle during warm-up, 60fps after input, ~8fps idle (was a 1ms/1000-per-sec flood) -> req:frame ratio 115:1 -> ~7:1. - Revert MAX_FRAMES to 1<<20 (RAM above 4GiB is not yet safely mappable; the cap is load-bearing) with an explanatory comment. - docs/arch.txt: document the real JIT execution model + runtime status.
Build the Flutter engine from source as a first-class OSCortex AOT target instead of shimming a Linux engine. Phases, port surface, build infra, and the hack-removal checklist in docs/native-engine-port.md; direction recorded in docs/arch.txt.
Draw the destination ahead of the work: three clean layers (kernel native ABI / shared AOT engine / self-contained AOT app bundles with own PIDs), the shared- framework property, and the hack-removal it enables. No Linux costume.
…ace map Baseline libflutter_engine.so (377MB, x64, embedder API) builds from source in the container — toolchain proven. Mapped the exact port surface: ~1,200 lines (Dart VM os_oscortex/os_thread_oscortex ~1000, fml message_loop_oscortex+paths, reuse posix) + GN glue. The fml message loop is the critical file — its emulated equivalent is what livelocks rendering today, so the native port also fixes sync.
Capture the validated Phase 0 setup as one-command scripts so the repetitive, heavy engine-build infra is reproducible, not tribal knowledge: - setup-engine-build.sh: idempotent container + depot_tools + pinned gclient sync (encodes the name='.' fix and the flags that work). - build-engine.sh: gn configure + ninja for baseline | oscortex targets. - README.md: the contributor flow, the edit->build incremental loop, and the pitfalls already solved (gclient layout, emulation, disk, prebuilt-dart). Engine checkout stays OUTSIDE the repo (22GB, never committed).
Add 'oscortex' as a --target-os. Since OSCortex has no sysroot/libc yet (runs linux-ABI via emulation), it links against linux but sets a new is_oscortex GN flag to select the OSCortex backend sources later; a true OSCortex toolchain is a later sub-phase. gn configures cleanly (1714 targets; args.gn = target_os linux + is_oscortex true). - engine-port/patches/: the two tracked diffs (gn tool, BUILDCONFIG). - engine-port/apply-port.sh: applies patches + (Phase 2) backend sources into a fresh checkout, idempotent. Temp patch files cleaned from the workspace (no remnants).
R2 needed dashboard activation; switched the artifact host to Google Cloud Storage (gs://dotcorr-oscortex-engine, public read, billing active). Verified end-to-end: gcloud upload -> anonymous https://storage.googleapis.com/... fetch. - artifact.config: ARTIFACT_BASE_URL -> GCS public URL + GCS_BUCKET. - publish-engine.sh: gcloud storage cp as the primary backend (wrangler/rclone/aws remain as fallbacks). fetch-engine.sh is unchanged (curl, host-agnostic).
The repo is public, so GitHub Release assets download anonymously — free, no egress fees (vs GCS ~$0.12/GB), no separate cloud account, and gh is already authed. Verified end-to-end: gh release upload -> anonymous https://github.com/DotCorr/oscortex/releases/download/... fetch. - artifact.config: ARTIFACT_BASE_URL -> GitHub releases download URL; GITHUB_REPO. - publish-engine.sh: gh release create/upload as the PRIMARY backend (GCS/R2/S3 remain as documented fallbacks). fetch-engine.sh unchanged (host-agnostic curl). - README: GitHub Releases is the host; no bucket setup needed. - Removed the unused GCS bucket (no remnants).
…erified The release oscortex engine (33MB, vs 377MB debug — no JIT) and a version-matched gen_snapshot (6.5MB) built from one tree. Published the first artifact to GitHub Releases (oscortex-engine-1) and verified the full round trip: publish-engine.sh -> gh release -> fetch-engine.sh downloads+checksums+stages. Engine + gen_snapshot from the same tree are version-matched, which dissolves the old AOT dead-end (the '1247 base objects' mismatch). Fix: publish-engine.sh derives the workspace from the container's /work mount (robust to the workspace dir name) instead of assuming a fixed name.
shell app -> frontend_server -> AOT dill (24MB) -> our version-matched
gen_snapshot -> libapp.so (4.4MB native ELF). Verified a real AOT snapshot: all 4
_kDart*Snapshot{Data,Instructions} present with REAL native instructions (T/text,
not zeroed stubs), ELF64 x86-64. The multi-session 'Snapshot expects N base
objects, provided 0' blocker is dissolved because engine + gen_snapshot are built
from one tree (version-matched). compile-app-aot.sh makes it reproducible.
The native, from-source, AOT-compiled Flutter engine RENDERS the shell on OSCortex: FlutterEngineRunsAOTCompiledDartCode -> libapp.so AOT path -> present_callback 393 frames, ZERO JIT warmup (no kernel_blob, no codegen). The UI comes up immediately, no 60-90s compile. This is the whole point of the port. Fix to get here: the release/AOT engine rejects the JIT-era GC/heap dart-flags (switches.cc:478 disallowed) — pass only the 5 AOT-safe engine args (argc=5) when is_aot, dropping the old --old_gen_heap_size etc. Known follow-up (separate, a regression from this session's IntelliMouse 4-byte scroll wiring): mouse clicks/Y are mangled + pointer events not reaching the engine; to fix next.
The 4-byte scroll-packet mode added this session corrupted pointer data: dy mis-parsed (cursor pinned to the top, y=0) and the flags byte mis-parsed (buttons stuck at 0), so clicks never registered and pointer events stopped reaching the engine. Revert to the proven 3-byte packet mode (working clicks > broken wheel). Verified: cursor Y tracks normally again (y=32 start, not pinned at 0). Scroll to be re-added later with correct 4-byte parsing + resync that doesn't regress clicks.
Input is confirmed working under AOT (122 pointer events reached Flutter, hover + clicks register, button presses detected, dropped_total=0 — the earlier '0' was the broken IntelliMouse 4-byte mode + minimal interaction, fixed by the 3-byte revert). Remove the temporary DIAG logging from the embedder + ipc_display. engine-port/compile-app-aot.sh: per-app AOT compile (frontend_server -> dill -> version-matched gen_snapshot -> libapp.so), no patch — used to give each app its own AOT bundle so the AOT engine can run them.
Previously every launched app reused the shell's /system/flutter/libapp.so (aot_va=0 JIT-era skip), so tapping a tile ran the shell snapshot in the app host and stalled. Resolve the per-app snapshot by registry lookup: build_app_libapp_path(app_id) -> /Applications/<name>.app/libapp.so, dlopen it, and point the AOT snapshot loader at that path. Shell keeps its own path. Each app is AOT-compiled to its own libapp.so and staged under its .app bundle, so a launched host loads its own native snapshot, not the shell's.
pthread_cond_signal/broadcast with no waiter parked is a no-op by contract: the predicate is mutex-protected, so a thread that hasn't yet entered cond_wait observes it under the lock and never waits. The kernel was instead recording such signals in COND_PENDING_SIGNALS and letting the next cond_wait consume one as an immediate (spurious) return. A hot mutator (pid 2) that signals its own monitor with woke=0 then re-waits would consume its own fake pending, return 0, find its predicate still false, and re-wait forever — the cond-pending-consume livelock that froze render at a fixed frame count. Remove the mechanism end to end (consume in cond_wait, posts in cond_signal/cond_broadcast, the state map, the import). cond_wait now relies solely on the seq protocol, which delivers every real signal race-free under cooperative single-core scheduling (the value-check and park cannot interleave with a signaler). No remnants.
sys::dlopen forwards path.len() to the kernel as the path length, so a NUL-terminated byte string makes the kernel read the trailing \0 as part of the filename and the open fails. The per-app and shell AOT paths were passing "...libapp.so\0"; strip the NUL at the dlopen call. The AOT snapshot itself loads via aot_snapshot_load (which maps it executable), so this only silenced a spurious failure path, but it is a real bug and removes the warning.
The Debug-level hot per-syscall traces (epoll_ctl, mprotect, cond-signal, every keypress) each block on a synchronous COM1 UART write AND re-render the framebuffer text console, with interrupts disabled. Measured: ~14000 log lines during a single boot+app-launch. Dropping to Error cut serial volume ~12x (14000 -> 1185 lines), removing tens of seconds of emulated-bare-metal warmup. Raise the one line in logger::init back to Debug for deep tracing.
Root cause of the sporadic render crashes. The user GPR snapshot lives in a PER-CPU scratch (gs:[..]) written by the syscall entry stub and shared by every thread on the core. save_full_user_gprs read it LAZILY at yield time — but the wait loops do sti;hlt and the timer ISR can switch threads mid-handler, so another thread's syscall entry overwrites the per-CPU snapshot before our save runs. We then stored the OTHER thread's callee-saved regs (rbx/rbp/r12-r15) into our context; on resume rbx was garbage. Pinpointed via addr2line/objdump: fml::MessageLoopOscortex::Run() resuming from epoll_wait wrote running_ through this=0x1400 (=1280*4, a Skia row stride leaked from another thread) -> SIGSEGV pid=3. This is the whole 0/16/140/489-frames- across-identical-boots sporadicity. Fix: capture_user_gprs_at_entry() snapshots the GPRs into the thread's own PTABLE slot at dispatch_fast entry, while the per-CPU snapshot is still fresh for this thread (before any handler/yield/interrupt window). A per-CPU 'captured' flag makes later yield-time save_full_user_gprs calls no-ops, so a clobbered shared snapshot can't leak in. Validation: 4/4 headless boots render cleanly (present 98-105) with ZERO engine SIGSEGV, vs the prior sporadic crashes. Render is now reliable.
The frame pump only called FlutterEngineScheduleFrame in the no-event branch, so a hover/click/scroll waited for the event queue to drain plus a wm_event_wait timeout (up to ~16ms) before the repaint was even requested. Request the frame in the same iteration input is received; the engine coalesces redundant requests and Flutter flushes the batched pointer packet at BeginFrame, so the scheduled frame reflects the event. Removes the scheduling-side input latency (most visible on real hardware; under cross-arch QEMU TCG the emulation dominates).
Each submitted frame did up to 5 full 1M-pixel passes: a strided re-pack into a freshly allocated 4MB Vec, a byte-indexed B<->R swap, a full-screen fill_rect clear, blit_rgba32, and swap_buffers. Measured ~65ms median (wildly variable 10-92ms) -> a ~15fps ceiling from the blit alone, which read as a low refresh rate / laggy feedback. - Fuse the strided re-pack into the swap: submit_bgra_impl reads the source stride directly and packs in ONE u32-wise pass (read BGRA as u32, swap bytes 0<->2), eliminating the intermediate buffer + the 4MB/frame allocation. - Skip the full-screen fill_rect clear when a presented surface already covers the screen (the common case: one full-screen Flutter surface). Measured after: blit median 5.4ms, steady 5.0-7.6ms (~12x faster, variance gone). Render verified correct + crash-free; colors unchanged.
next_runnable_pid_locked computed the foreground-exclusive group AFTER the input-target and embedder-baton shortcuts, so a due shell (pid 1) baton could schedule the shell engine even while an app is foreground — two heavy Flutter VMs on one cooperative core, the documented cause of the launched app's crash. Compute fg/exclusive FIRST and gate both shortcuts: suppress the pid-1 baton when an app is exclusive, and only honour the input shortcut for a target in the foreground group. No behavioural change when the shell is foreground (the common case): exclusive=false short-circuits both gates. Closes the baton concurrency hole; full app-launch validation still pending an interactive tile-tap (HMP mouse_button injection does not produce reliable clicks headless).
Replace the generic reply-null catch-all in platform_message_callback with a real channel dispatcher (match on channel name). Every channel the framework reaches for is now routed to a concrete handler that does the platform work or returns a codec-correct typed ack; the final catch-all logs the channel name ([embedder/chan] unbound: <name>) so nothing stays an invisible stub. Bound channels: - flutter/textinput (JSONMethodCodec): setClient/setEditingState/show/hide/ clearClient. Editing state (text + selection) is maintained in the embedder. PS/2 set-1 scancodes are now mapped to characters (shift/caps, backspace, enter, tab, space, arrows, home/end, delete); on a key press with an active text client the stored editing state is mutated and TextInputClient. updateEditingState is pushed back over flutter/textinput. Adds a small inline JSON reader/writer (no_std, no crates) for the editing-state maps. - flutter/mousecursor: parse activateSystemCursor kind and ack (no kernel set-cursor-shape syscall exists yet; logged + acked). - flutter/platform: Clipboard.setData/getData/hasStrings backed by an in-embedder buffer; SystemNavigator.pop; SystemSound/HapticFeedback/ SystemChrome acked. - flutter/navigation, system, accessibility, spellcheck, processtext, menu, contextmenu, scribe, restoration, keyevent, platform_views, isolate, lifecycle: explicit JSON typed-null ack.
Replace the ack-only stubs from the platform-channel contract with actual OS-provided capabilities. OSCortex is the platform under the stock engine, so where Flutter needs a platform service the kernel now implements and binds it. Mouse cursor shape (flutter/mousecursor.activateSystemCursor): - compositor: ACTIVE_CURSOR_SHAPE atomic + vector cursor sprites (arrow, I-beam, hand/link, forbidden, grab, horizontal/vertical resize, hidden). draw_software_cursor dispatches on the active shape; set_cursor_shape() repaints immediately. - new syscall SYS_CURSOR_SHAPE_SET (0x4B2). Embedder maps the Flutter cursor kind string to a CURSOR_SHAPE_* and calls it, so hovering a link shows a hand, a text field shows an I-beam, etc. Semantics / accessibility: - embedder wires update_semantics_callback2 (FlutterProjectArgs off 280) and calls FlutterEngineUpdateSemanticsEnabled(engine, true) after run. The callback receives the FlutterSemanticsUpdate2 tree and stores each node (id, label, rect, flags, actions) in a live embedder structure for a11y / automation consumers. flutter/ accessibility now replies with the correct StandardMessageCodec null (0x00), not JSON. System clipboard (flutter/platform Clipboard.*): - kernel-global clipboard buffer (embedder::clipboard) shared across every app/host, with SYS_CLIPBOARD_SET (0x4B3) / SYS_CLIPBOARD_GET (0x4B4). The embedder routes setData/getData/hasStrings to the kernel, so clipboard survives across apps. SystemNavigator.pop (flutter/platform): - SYS_APP_CLOSE_FOREGROUND (0x4B5): refocuses the shell (pid 1) and wakes it. The app embedder, when it is a launched host, flushes its reply, calls the syscall and exits so focus returns to the shell. SystemSound.play (flutter/platform): - PC-speaker beep driver (drivers::beep) via PIT channel 2 + port 0x61, exposed as SYS_BEEP (0x4B6). click vs alert play distinct short tones. Deliberate no-ops (no such hardware), acked with the correct codec: - HapticFeedback.* (no vibration motor), SystemChrome.* (single full-screen compositor surface, no system UI overlays / orientation).
Hovering froze the whole shell. Two causes from the platform-capabilities work: (1) flutter/mousecursor fires activateSystemCursor on every hover move, and the handler called SYS_CURSOR_SHAPE_SET each time → the kernel redraws the cursor (invalidate→composite) per event → a redraw storm that starves the cooperative core. Fix: only call the syscall when the cursor shape actually CHANGES. (2) Semantics was enabled, so the engine rebuilt the full accessibility tree on every hover-change — far too expensive on the single cooperative core. Disable the enablement (callback + channel stay wired; re-enable when throttled). Hover is responsive again; cursor shapes still work on real transitions.
SwitchListTile.activeColor is deprecated in favour of activeThumbColor; the required "Flutter shell analyze + test" check failed on it. Also set the shell background to pure black. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ipeline Brings the aarch64 port + ARM Flutter shell onto main (squashed from develop; full history on develop). The ARM shell renders the Material UI reliably under QEMU -M virt (-kernel + ramfb) and takes mouse hover/click via a virtio-input driver; release.yml now also publishes an aarch64 kernel artifact. Key ARM fixes (see develop history): epoll_event + vsnprintf va_list ABI, syscall arg3 (x3) restore from r10, cooperative-only scheduling, font/asset bundle, virtio-input pointer. Shared kernel/embedder paths refactored arch-neutral (enable_and_halt, rdtsc_ns, spin_pause, save_return_context_reexec, ptable_cpu_idx, lock_page_table) preserving x86 behavior. Verified: x86_64 + aarch64 build clean; aarch64 renders (present>0, crash=0).
Fetch the prebuilt Flutter shell runtime (engine .so + AOT shell snapshot + ICU + fonts/assets + embedder /init + libc + seed) from the oscortex-engine-1 release, stage it into initramfs/, and build the aarch64 kernel with it embedded — so a downloaded oscortex-aarch64-<tag>.kernel boots straight into the rendering shell (qemu -M virt -kernel + ramfb + virtio input), not just the platform layer. The runtime bundle (oscortex-arm64-shell-runtime.tar.gz) is a prebuilt snapshot on oscortex-engine-1; refresh it when the shell app or engine changes. sha256-verified on fetch.
Name the x86 ISO oscortex-x86_64-<tag>.iso (was oscortex-<tag>.iso — confusingly unlabelled next to oscortex-aarch64-<tag>.kernel). Compute checksums from inside dist/ so each .sha256 lists a bare filename and 'sha256sum -c' works after downloading just the artifact+checksum pair (previously the file carried a dist/ path prefix and failed verification). Clarify in the notes that the two artifacts differ in type because the arches boot differently (x86 Limine ISO vs aarch64 -kernel ELF).
Re-port of the Limine UEFI scaffolding (boot_limine.rs, aarch64-limine.ld, mmu::limine_setup_ttbr0, kernel_main_arch_limine, limine-boot feature, build-iso-aarch64.sh) onto origin/main — which has ALL the render fixes (epoll/ va_list ABI, x3 arg restore, cooperative scheduling, virtio-input). The earlier attempt was mistakenly branched off stale develop (pre-fixes).
…the shell Limine hands off at EL1t (SPSel=0), where `sp` aliases SP_EL0. Every kernel stack write then went through SP_EL0, and enter_user's kernel-stack reclaim (`mov sp, <syscall stack top>`) clobbered the user SP it had just delivered: pid1 entered EL0 with SP pointing at the kernel syscall stack and its first push faulted (EC=0x24, FAR=SP-0xa0). The QEMU -kernel path sets up EL1h itself, which is why the identical kernel worked there. One instruction: msr spsel, #1 before the boot-stack switch. Validated under edk2: present_callback=281 on a full run.
…% resume crash eret_to_el0/eret_to_el0_fp reset SP_EL1 then read the img/fp resume arrays that still live on the abandoned cooperative-yield frame. A wait loop that yields with IRQs unmasked leaves them unmasked here; a timer IRQ pushes a TrapFrame over img/fp → corrupted resume image (intermittent data abort in eret_to_el0_fp at the SPSR load with x18=0). Fix: msr daifset, #0xf before the SP reset; the eret restores SPSR_EL0T so EL0 runs with interrupts enabled. 12/12 -kernel boots clean (was ~2/8).
…UEFI ISO in CI Kernel: finish_cond_timedout_return advanced the resume PC by a hardcoded 2 bytes — correct for x86's 2-byte `syscall`, but on aarch64 `svc #0` is 4 bytes, so the parked cond_timedwait waiter resumed at svc+2 (a misaligned PC) → EC=0x22 PC-alignment fault, deterministically ~280 frames into a run (every cond_timedwait timeout was a live grenade). Use the cfg'd SYSCALL_INSN_LEN (4 on aarch64) and set aarch64_ret_in_x0 so ETIMEDOUT actually reaches x0. CI: release.yml now builds the aarch64 UEFI/Limine ISO via build-iso-aarch64.sh (LIMINE_DIR=$HOME/limine) and publishes oscortex-aarch64-<tag>.iso + .sha256 alongside the raw -kernel ELF — a real UTM/VM/bare-metal-bootable ARM image. build-iso-aarch64.sh: LIMINE_DIR is now overridable for CI.
…e (HVF/bare-metal) SP-alignment fault The x86 SysV convention seeds entry SP at stack_top-8 (RSP%16==8, return addr on the stack). AArch64 is the opposite: SP must be 16-byte aligned at ALL times (SCTLR_EL1.SA, hardware-enforced) and the return address lives in x30/LR (already seeded via p.user_lr=thread_return_trampoline_va). TCG doesn't enforce SP alignment so it silently worked; real silicon (HVF / UTM / Raspberry Pi) faults EC=0x26 the instant a spawned engine thread touches its stack — which is why it rendered headless but never on real hardware. cfg-split spawn_thread and spawn_with_bootstrap to align down to 16 on aarch64; x86 keeps the -8. Verified under HVF (cpu=host, real Apple Silicon): engine now spawns all worker threads with no SP fault and reaches the render event loop.
…PI27) — renders on real hardware The kernel ticked the EL1 PHYSICAL timer (CNTP_CTL_EL0, PPI 30). On QEMU -M virt TCG that interrupt is delivered, so it worked headless. But OSCortex runs at EL1 and under a hypervisor — Apple's HVF (what UTM uses), KVM, Xen — EL2 owns the physical timer: EL1 CNTP accesses are trapped and PPI 30 is NOT delivered to the guest. So under HVF the tick never fired → no vsync baton → the engine reached its event loop, scheduled frames, and stalled with present=0 (nothing ever rendered on real silicon, only in TCG emulation). Switch to the architected EL1-guest VIRTUAL timer (CNTV_CTL_EL0 / CNTV_TVAL_EL0 / CNTVCT_EL0, PPI 27). It is delivered under HVF, on bare metal (CNTVOFF=0 → virtual time == physical time), AND on TCG virt — strictly more portable. vsync_due already measured cadence against CNTVCT, so this is now consistent. Verified under HVF (cpu=host, real Apple Silicon): present_callback reaches 2063 frames, crash=0 — the full Flutter shell renders on real hardware.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Brings
developup to date withmainat v0.0.6.developandmainhad unrelated commit histories (no common ancestor —developwas started as a fresh lineage on Jun 9,mainroots at the May 19 initial commit), so git refused a normal merge. This PR carries a--allow-unrelated-historiesmerge oforigin/mainintodevelop, resolving the 21 overlapping files in favor ofmain.Result
After this merge,
develop's tree is byte-identical tomain(git diff origin/main HEADis empty). It picks up all the v0.0.5 → v0.0.6 release work that had only landed onmain, notably the aarch64 hardware-render bring-up:kernel/src/arch/aarch64/boot_limine.rs,mmu.rs, virtual-timer (CNTV/PPI27) scheduler/vsync tickkernel/linker/aarch64-limine.ld,scripts/build-iso-aarch64.sh, aarch64iso_rootboot assets.github/workflows/release.ymlupdatesNet:
21 files changed, 728 insertions(+), 43 deletions(-)plus the new aarch64 boot/linker/iso files.Why a PR
Direct push to
developis blocked by branch protection (403), so the merge commit is delivered via this branch for review/merge.https://claude.ai/code/session_01MSk4MAsfTvKoXfEzrWiZ3Q
Generated by Claude Code