Compare commits

..
Author SHA1 Message Date
t3hn3rd dd3bb1e016 Automate boot unit inclusion with architecture-aware Moderne analysis
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
- Replace boot.mgr's static unit list with generated dangling_units.inc
- Generate Pascal LSTs and run the dangling-unit recipe before compilation
- Exclude Wasuro, inactive architectures, and NOT_DANGLING units
- Add ASURO_ARCH selection with an x86 default
- Mark unfinished legacy units as NOT_DANGLING
- Inherit pinned Moderne tooling in the builder image
- Ensure Drone pulls the latest builder image
- Document the updated build pipeline in MkDocs

Validation:
- Full x86 ISO build and VirtualBox boot succeeded
- 1,133 kernel tests passed with no failures
- x86_64 build failed clearly due to unavailable sources
2026-11-07 21:49:06 +00:00
74 changed files with 4990 additions and 12286 deletions

No files matched your search

+1
View File
@@ -17,6 +17,7 @@ steps:
- name: compile - name: compile
image: t3hn3rd/asuro-build:latest image: t3hn3rd/asuro-build:latest
pull: always
depends_on: depends_on:
- build-image - build-image
commands: commands:
+4 -1
View File
@@ -15,9 +15,12 @@ localenv.json
dockerout.txt dockerout.txt
AGENTS.md AGENTS.md
lessons_learnt.md lessons_learnt.md
/plan.md
*.log *.log
wasuro wasuro
src/core/core.version.pas src/core/core.version.pas
site/* site/*
!site/.gitkeep !site/.gitkeep
/src/boot/dangling_units.inc
/lib/dangling/
.moderne
CLAUDE.md
+4 -4
View File
@@ -1,4 +1,5 @@
FROM ubuntu:latest ARG RECIPES_IMAGE=t3hn3rd/moderne-asuro-recipes:0.1.1@sha256:eaf54188053511dad9c620a8fb98b0f50c41770cd49a0b3d71a0538386522011
FROM ${RECIPES_IMAGE}
VOLUME ["/code"] VOLUME ["/code"]
@@ -7,7 +8,7 @@ RUN dpkg --add-architecture i386
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
curl dos2unix wget git make nasm binutils xorriso grub-pc-bin gcc gcc-multilib \ curl dos2unix wget git make nasm binutils xorriso grub-pc-bin gcc gcc-multilib \
python3 python3-pip python3-pil && \ python3 python3-pip python3-pil && \
apt-get clean my room apt-get clean && rm -rf /var/lib/apt/lists/*
RUN pip3 install --no-cache-dir --break-system-packages "mkdocs>=1.6,<2" mkdocs-material RUN pip3 install --no-cache-dir --break-system-packages "mkdocs>=1.6,<2" mkdocs-material
@@ -20,6 +21,5 @@ RUN curl -sL https://sourceforge.net/projects/freepascal/files/Linux/$FPC_VERSIO
ADD https://raw.githubusercontent.com/fsaintjacques/semver-tool/master/src/semver /usr/bin/semver ADD https://raw.githubusercontent.com/fsaintjacques/semver-tool/master/src/semver /usr/bin/semver
RUN chmod +x /usr/bin/semver RUN chmod +x /usr/bin/semver
WORKDIR /code WORKDIR /code
RUN find . -type f -print0 | xargs -0 dos2unix
ENTRYPOINT ["/bin/bash", "-c"] ENTRYPOINT ["/bin/bash", "-c"]
CMD ["find toolchain -name '*.sh' -exec dos2unix {} + 2>/dev/null; find toolchain -name '*.sh' -exec chmod +x {} +; bash toolchain/compile.sh"] CMD ["find toolchain -name '*.sh' -exec dos2unix {} + 2>/dev/null; find toolchain -name '*.sh' -exec chmod +x {} +; bash toolchain/compile.sh"]
+8
View File
@@ -0,0 +1,8 @@
**/.git
**/.gradle
**/build
**/.moderne
wasuro
site
lib
release
@@ -1,85 +1,49 @@
<!-- # driver.storage.ctl.usb
Copyright 2026 Aaron Hance
Author: Aaron Hance <[email protected]>
Licensed under the Apache License, Version 2.0 (the "License"); USB Mass Storage Class driver stub.
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 ## Overview
Unless required by applicable law or agreed to in writing, software This unit registers a USB class driver with the driver manager (`driver.mgr`) to claim USB Mass Storage Class interfaces (class `$08`, subclass `$06` SCSI, protocol `$50` Bulk-Only). When the USB stack enumerates a matching interface, the `load` callback is invoked. Currently the callback only logs a detection message; no I/O infrastructure is established.
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# USB mass storage Full Bulk-Only Transport (BOT) support — including Max LUN query, endpoint setup, CBW/CSW command exchange, and `driver.storage.mgr` device registration — is planned but not yet implemented.
`driver.storage.ctl.usb` implements SCSI transparent commands over USB Mass ## Boot Registration
Storage Bulk-Only Transport (interface class/subclass/protocol `08/06/50`).
It probes INQUIRY, TEST UNIT READY and READ CAPACITY(10), registers LUN 0 with Registered with `boot.mgr` as `driver.storage.ctl.usb` at the `device` barrier. The barrier order places `device` after `bus`, ensuring `driver.bus.usb.core.init` has run before the storage class driver registers with the driver manager.
the storage manager, and exposes READ(10), WRITE(10), REQUEST SENSE and
SYNCHRONIZE CACHE(10). Existing partition discovery and filesystem mounting
apply to the resulting USB disk. Writes complete only after cache synchronization.
## Execution and ownership ## Dependencies
An attachment task probes and registers each disk after scheduling starts; boot - `boot.mgr`
initialization never waits on a worker that cannot run yet.
Storage dispatch only publishes a request. One persistent `usb.storage` worker - `driver.mgr`
performs I/O in task context, with one command active per device and a reusable, - `memory.heap`
page-aligned 4 KiB bounce buffer. Caller buffers are never submitted for DMA. - `driver.storage.types`
Commands are split at the bounce-buffer boundary; all LBA/count and buffer-end - `io.syslog`
arithmetic is checked before submission. CBW/CSW wire sizes, tags, status, - `debug.tracer`
residue, and actual transfer lengths are validated. - `driver.bus.usb.core`
- `driver.bus.usb.types`
- `core.util`, `arch.x86.util`
OHCI batches bulk data into spans of at most 4 KiB per transfer descriptor, ## Constants
using the endpoint's hardware toggle carry across packets. A 4 KiB full-speed
read uses one data descriptor instead of 64. Short packets still halt the chain
and report the actual byte count; interrupt endpoint packet sizing is unchanged.
The worker performs reset recovery (class reset, clear bulk-IN halt, clear | Constant | Value | Description |
bulk-OUT halt) after malformed status or phase errors. Failed SCSI commands |---|---|---|
request sense and return storage errors, including write protection. | `USB_CLASS_MASS_STORAGE` | `$08` | USB interface class for mass storage |
| `USB_SC_SCSI` | `$06` | SCSI transparent command set subclass |
| `USB_PROTO_BBB` | `$50` | Bulk-Only (BOT) transport protocol |
On disconnect the disk is marked removed immediately. Accepted requests are ## Functions and Procedures
completed with errors, and idle volumes are invalidated/unmounted by the worker;
busy volumes are retried. Storage-manager records remain stable tombstones.
Class-driver buffers and context are freed after pending hardware work drains.
Host controllers currently have no cancellation API. A timed-out transfer is ### init
therefore quarantined with its private device snapshot and DMA memory. No further
commands are issued to that device. If hardware never completes, those allocations
remain retained until reboot; freeing them would permit DMA into reused memory.
The timeout uses the PIT's actual frequency and a bounded polling fallback.
## Supported scope ```pascal
procedure init;
```
- LUN 0 on a single-interface device with one bulk-IN and one bulk-OUT endpoint. Constructs a `TDeviceIdentifier` matching any USB device with `bInterfaceClass = $08`, `bInterfaceSubClass = $06`, `bInterfaceProtocol = $50`, and registers the `load` callback with `driver.mgr`.
- Direct-access disks with power-of-two sectors from 512 through 4096 bytes.
- Capacity representable by the existing 32-bit storage-sector API; the
READ CAPACITY(16) sentinel is rejected.
- OHCI, EHCI and xHCI bulk transfers. OHCI/EHCI report actual data lengths and
terminate bulk reads on short packets.
UAS, additional LUNs, composite/alternate-setting interfaces, UHCI storage, ## Notes
optical devices, and media replacement without USB reconnect are not supported.
The current xHCI driver lacks endpoint-ring reset support: a halted bulk endpoint
fails the device closed rather than attempting an unsafe ring reuse.
## Validation - The `load` callback currently returns true unconditionally after logging a detection message.
- Once full BOT support is implemented, `load` should query the Max LUN, allocate endpoint structures, and call `driver.storage.mgr.register_device` for each logical unit.
`UnitTest` is called by the boot test suite and is available as `USBMSCTEST`.
Tests use a memory-only mock host controller: wire layout, byte order, capacity
limits, reads, writes, sense errors, short transfers, status stalls, reset ordering,
and request bounds. They perform no disk I/O.
For hardware/VM checks, boot with a disposable USB disk, confirm `USBMSC` discovery
and filesystem mounting, test file read/write/read-back, then unplug while idle
and during I/O. Do not run destructive disk tests against a disk containing data.
Protocol: [USB-IF Bulk-Only Transport 1.0](https://www.usb.org/sites/default/files/usbmassbulk_10.pdf).
+10 -52
View File
@@ -1,20 +1,3 @@
<!--
Copyright 2026 Aaron Hance
Author: Aaron Hance <[email protected]>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# driver.storage.mgr # driver.storage.mgr
Physical storage device registry and I/O dispatch manager. Physical storage device registry and I/O dispatch manager.
@@ -31,7 +14,7 @@ The I/O model operates as follows:
- `storage_read` / `storage_write` are convenience wrappers that build a stack-local `TIORequest` and call `submit_io_wait`. - `storage_read` / `storage_write` are convenience wrappers that build a stack-local `TIORequest` and call `submit_io_wait`.
- `storage_read_async` / `storage_write_async` build and submit a request with a caller-supplied callback, returning immediately. - `storage_read_async` / `storage_write_async` build and submit a request with a caller-supplied callback, returning immediately.
Blocking callers use `storage_read` / `storage_write` and check the returned `TError`. Legacy `storage_read_legacy` / `storage_write_legacy` procedure wrappers are retained for callers not yet migrated to the function-returning API.
## Dependencies ## Dependencies
@@ -222,42 +205,17 @@ function storage_write_async(device : PStorage_Device; addr : uint32; sectors :
Non-blocking write. Analogous to `storage_read_async`. Non-blocking write. Analogous to `storage_read_async`.
### storage_read_legacy / storage_write_legacy
```pascal
procedure storage_read_legacy(device : PStorage_Device; addr : uint32; sectors : uint32; buffer : puint32);
procedure storage_write_legacy(device : PStorage_Device; addr : uint32; sectors : uint32; buffer : puint32);
```
Drop-in procedure replacements for callers using the old void-returning API. Forward to `storage_read` / `storage_write` and discard the error code. Marked for removal once all callers are migrated.
## Notes ## Notes
- `dispatch_next` is an internal procedure called with interrupts disabled. It dequeues the head of the CFIFO, allocates a heap copy via `ioreq_copy` (for ISR safety), and invokes the appropriate `dispatchRead` or `dispatchWrite` on the device. If neither dispatch pointer is set, the request is failed with `eDeviceNotFound`. - `dispatch_next` is an internal procedure called with interrupts disabled. It dequeues the head of the CFIFO, allocates a heap copy via `ioreq_copy` (for ISR safety), and invokes the appropriate `dispatchRead` or `dispatchWrite` on the device. If neither dispatch pointer is set, the request is failed with `eDeviceNotFound`.
- The blocking path relies on pointer-dereferenced spin (`puint32(@wait.Done)^`) to prevent FPC from caching the `Done` flag in a register across the `hlt` instruction. - The blocking path relies on pointer-dereferenced spin (`puint32(@wait.Done)^`) to prevent FPC from caching the `Done` flag in a register across the `hlt` instruction.
- Devices without `dispatchRead`/`dispatchWrite` set (i.e., drivers not yet migrated to Phase 2) will have `submit_io` return `eDeviceNotFound`. Legacy drivers must be migrated before they can use the new I/O path. - Devices without `dispatchRead`/`dispatchWrite` set (i.e., drivers not yet migrated to Phase 2) will have `submit_io` return `eDeviceNotFound`. Legacy drivers must be migrated before they can use the new I/O path.
## Bounded performance path
The manager merges up to 32 consecutive queued requests when operation, device,
LBA range, and virtual buffer range are contiguous. It never searches past the
queue head or delays a lone request to create a merge. Drivers opt in through
`mergeMaxSectors`; SATA AHCI advertises 65,536 sectors, while other drivers default
to zero (disabled). A merged command delivers one completion to each original
request, in FIFO order. A command error fails every member conservatively.
`storage_get_stats` takes an interrupt-protected snapshot of the device counters.
`DISK info <index>` displays accepted/completed/failed logical requests, hardware
commands, merged requests, queue-full rejections, queue and active high-water
marks, successful read/write sectors, request-size buckets, queue/service ticks,
and FAT metadata cache hits/misses. Queue ticks are accumulated per logical
request; service ticks are accumulated per hardware command. Tick values use
`Counters.c32`, not milliseconds. Counters are unsigned 32-bit values, start at
device registration, and wrap; use snapshot differences over bounded benchmark
intervals. Size buckets are <=8, 9..256, and >256 sectors.
FAT32 blocking and async reads share a pooled transfer engine with batches of up
to four outstanding reads, capped by device queue depth. Destination slices are
disjoint. On any error, the engine drains every accepted request before releasing
its context and reports only the contiguous successful byte prefix. Unaligned
edges and odd DMA addresses use the context's private scratch sector. Writes
remain sequential. Run maps store cumulative file-cluster offsets; an open-file
cursor handles sequential locality and binary search handles random seeks.
`STORPERF` runs memory-backed regression checks (also included in boot unit tests)
for reverse completion order, failures, unaligned reads, run lookup, queue
rejection, merging limits, and callback accounting. It does not access real disks
or measure hardware throughput. Compare counters alongside `STORBENCH` and file
benchmarks on contiguous and fragmented files for actual performance validation.
@@ -58,111 +58,3 @@ Registers the `STORTEST` and `DISKTEST` shell commands with `io.stdio`. Must be
- `run_tests` exercises `makeAbsolutePathFrom`, `resolvePathFrom`, `changeDirectoryFrom`, `GetDirectoryListingFrom`, `newVirtualDirectory`, and `PathValid` including `.` and `..` traversal. - `run_tests` exercises `makeAbsolutePathFrom`, `resolvePathFrom`, `changeDirectoryFrom`, `GetDirectoryListingFrom`, `newVirtualDirectory`, and `PathValid` including `.` and `..` traversal.
- `run_disk_tests` is destructive and will irreversibly wipe the partition table of disk 0. It is never called automatically. - `run_disk_tests` is destructive and will irreversibly wipe the partition table of disk 0. It is never called automatically.
- The `Assert` helper used in both procedures increments `passed` or `failed` counters and logs the test name on failure; passing tests produce no output to avoid noise. - The `Assert` helper used in both procedures increments `passed` or `failed` counters and logs the test name on failure; passing tests produce no output to avoid noise.
## Raw read benchmark
`STORBENCH` is implemented and registered by `driver.storage.bench`. It performs
read-only raw-device I/O; it does not invoke the destructive `DISKTEST` path.
```text
STORBENCH [disk] [MiB]
STORBENCH list
STORBENCH run [disk] [MiB] [KiB/request] [depth] [seq|random] [start_lba]
STORBENCH compare [disk] [MiB]
STORBENCH help
```
Manual (`run`) defaults are disk 0, 128 MiB, 1024 KiB/request, sequential reads, and a depth of
`min(4, hardware depth)` (at least one). Examples:
```text
STORBENCH 0 128 64 4
STORBENCH 0 128 4 16 random
STORBENCH 0 256 1024 1 seq 2048
STORBENCH compare 0 128
```
The comparison runs sequential reads at 4, 64, and 1024 KiB/request, each at depths
1, 4, 16, and 32. The MiB argument applies to each of the twelve cases. Queue depth
may exceed hardware depth to expose software queuing and request merging.
Limits are 1..16384 MiB, 1..1024 KiB/request, and depth 1..32. Request size must be
an exact number of device sectors. Buffers are allocated once per case, with a
separate slice for every outstanding request and a maximum total size of 32 MiB.
The transfer range is clamped to the device. Without an explicit LBA, reads start
at sector 2048 when the full requested range fits there; otherwise they start at
zero. Random mode uses a fixed xorshift seed and samples that range with replacement,
so total bytes read are bounded but unique coverage is not guaranteed.
Reports include successful bytes, elapsed microseconds, MiB/s, IOPS, accepted and
completed request counts, peak outstanding requests, average/p95/max completion
latency, and per-run storage-counter deltas. Latencies are measured from submission
to callback. The p95 is an upper bound from logarithmic microsecond buckets, not an exact
microsecond percentile. Runs shorter than 10 ms are excluded from suite speed comparisons.
No console output occurs inside the measured loop. Any failure stops new work;
already accepted DMA is drained before buffers are freed, and failed requests
contribute no bytes or successful IOPS.
Timing prefers the Hyper-V-compatible VM reference counter (100 ns ticks), detected
through its Hv#1 interface and reference-counter privilege. It needs no calibration.
Other platforms use an invariant TSC calibrated against two complete RTC seconds,
with up to three attempts to tolerate transient RTC updates or scheduling delays.
Calibration requires agreement within 1%; failures include the specific reason.
Unlike delivered timer interrupts, this clock advances while interrupts are masked.
Queue submission restores interrupts between requests. Filesystem caches are
bypassed, but drive and virtual-machine/host caches may still affect results.
Virtual or sparse disks can report throughput above physical-media speed.
Device counter deltas may include concurrent I/O from other processes;
benchmark-owned byte, request, and latency counters do not.
`driver.storage.bench.UnitTest` runs memory-only parser, arithmetic, range,
queue-depth, completion-order, and error-accounting tests at boot. It never reads
or writes a real disk. The clock tests also verify that an advertised VM reference
counter advances with interrupts masked; they never calibrate the RTC at boot.
### Automatic suite and table output
`STORBENCH` without arguments, or `STORBENCH auto [disk] [MiB]`, runs six
read-only profiles. Auto defaults to disk 0 and **32 MiB per profile** (up to
192 MiB total before device-size clamping). One or two numeric arguments select the auto suite: `STORBENCH 2 512` tests
disk 2 with 512 MiB per profile. `STORBENCH list` lists disk indices, capacities,
controllers, sector sizes, and hardware queue depths. Three or more numeric
arguments retain the legacy manual syntax; `run` selects manual mode explicitly.
| Profile | Request size | Queue depth |
| --- | --- | --- |
| Seq large | 1024 KiB | 1 |
| Seq queued | 1024 KiB | queued |
| Seq medium | 64 KiB | queued |
| Random small | 4 KiB | 1 |
| Random queued | 4 KiB | queued |
| Random medium | 64 KiB | queued |
Queued depth is the device hardware depth clamped to 4..16, allowing software
queue testing even on serial devices. A request smaller than a device sector is
increased to one sector. Each profile samples the same byte-span size, with the
existing fixed random seed and range clamping rules.
Auto and compare modes print an aligned ASCII table with request KiB, queue
depth, MiB/s, IOPS, average latency, p95 latency upper bound, and status. Rows are
printed outside the measured loop. Successful runs shorter than 10 ms show `-` metrics and
`SHORT`, and are excluded from best-result and queue-gain summaries. Errors show
`FAILED`, stop the suite after accepted DMA has drained, and leave remaining
profiles unrun. The summary includes successful profiles, bytes actually read,
measured time, best sequential throughput, peak random IOPS, and (in auto mode)
queued/QD1 throughput ratios. These ratios describe the measured cases, including
any controller-cache effects; they are not a hardware rating.
```text
STORBENCH
STORBENCH list
STORBENCH 2 512
STORBENCH auto 0 64
STORBENCH compare 0 32
```
Profiles reuse the same raw device range. Host/drive caches are not flushed, so
later profiles may read warm data. These results are observed raw read throughput,
not a claim of uncached physical-media performance.
+45
View File
@@ -0,0 +1,45 @@
# Automatic boot-unit inclusion
Run the usual `docker compose build builder` and `docker compose run builder`
from Asuro. Only this repository is required. Compose and Drone use the same
Dockerfile and repository build context, producing `t3hn3rd/asuro-build:latest`.
The Dockerfile inherits the published `t3hn3rd/moderne-asuro-recipes:0.1.1` image,
pinned by digest. It already includes Java, Moderne CLI, the Pascal LST generator,
and the registered recipe. The Asuro layer installs FPC and the kernel build tools.
To upgrade tooling, publish the upstream images first and update `RECIPES_IMAGE`
in the Dockerfile to the new tag and digest. Local tooling image testing can use
`docker compose build --build-arg RECIPES_IMAGE=<local-tag> builder`.
`ASURO_ARCH` selects the source architecture, defaulting to `x86`. For example:
```sh
export ASURO_ARCH=x86
docker compose run --rm builder
```
Compose forwards this variable to the builder; Drone inherits it from its compile
environment. Only `src/arch/$ASURO_ARCH` is added to FPC's unit search paths.
Other architecture folders are excluded from LST generation and from the recipe's
dependency graph and results. Common units outside `src/arch` remain eligible.
The selected architecture folder must exist. This source selection does not
change the existing i386 compiler target, assembly stub, or linker settings;
an x86_64 port still needs those platform-specific changes.
`compile_dangling.sh` runs after Wasuro synchronization and before FPC. It copies
only `src/` and `compat/` into an isolated temporary analysis repository, generates
LSTs with rewrite-pascal, runs the standalone Moderne recipe, exports its
datatable, and writes `src/boot/dangling_units.inc`. Wasuro is not parsed.
Datatable conversion is provided by the standalone project's Java exporter.
Parse errors, recipe failures, and malformed datatables stop the build. The
generated include is ignored by Git and removed before each generation attempt.
Use `{$DEFINE NOT_DANGLING}` near a unit declaration to exclude an experimental
or legacy unit from automatic inclusion. It can still be referenced normally.
This is a file-level marker; conditional definitions are not evaluated. The
analysis follows both interface and implementation uses, without expanding
includes or evaluating conditional compilation.
Moderne runs through its CLI normally; any license or tenant configuration
required by your CLI installation must be provided to the container separately.
No host credentials are copied into the image.
+4 -1
View File
@@ -1,5 +1,8 @@
services: services:
builder: builder:
image: t3hn3rd/asuro-build:latest
build: . build: .
environment:
ASURO_ARCH: ${ASURO_ARCH:-x86}
volumes: volumes:
- .:/code - .:/code
+1
View File
@@ -283,6 +283,7 @@ nav:
- Version Generation: toolchain/compile_vergen.md - Version Generation: toolchain/compile_vergen.md
- LVGL Compilation: toolchain/compile_lvgl.md - LVGL Compilation: toolchain/compile_lvgl.md
- Wasuro Pull: toolchain/compile_wasuro.md - Wasuro Pull: toolchain/compile_wasuro.md
- Automatic Boot Units: toolchain/dangling_units.md
- Source List: toolchain/compile_sourcelist.md - Source List: toolchain/compile_sourcelist.md
- Source Compilation: toolchain/compile_sources.md - Source Compilation: toolchain/compile_sources.md
- Checksum: toolchain/compile_checksum.md - Checksum: toolchain/compile_checksum.md
+2 -1
View File
@@ -28,7 +28,8 @@ We welcome everyone to give building/breaking/fixing/shooting Asuro a go, feel f
4. Install Git for Windows. 4. Install Git for Windows.
5. Install VSCode & the listed plugins. 5. Install VSCode & the listed plugins.
6. Install VirtualBox (v7+). 6. Install VirtualBox (v7+).
7. Clone this repository. 7. Clone this repository. The builder inherits the published Moderne/Pascal
tooling image; see [boot-unit generation](doc/toolchain/dangling_units.md).
8. Run the following command in the root of the repo to build the docker image: 8. Run the following command in the root of the repo to build the docker image:
```powershell ```powershell
docker compose build builder docker compose build builder
-11
View File
@@ -150,7 +150,6 @@ var
device : PStorage_Device; device : PStorage_Device;
volCount : uint32; volCount : uint32;
freeSec : uint32; freeSec : uint32;
stats : TStorageStats;
begin begin
if paramCount(params) < 2 then begin if paramCount(params) < 2 then begin
println('Usage: DISK info <disk>'); println('Usage: DISK info <disk>');
@@ -192,16 +191,6 @@ begin
else else
volCount := 0; volCount := 0;
print('Partitions: '); printint(volCount); println(''); print('Partitions: '); printint(volCount); println('');
storage_get_stats(device,stats);
print('I/O submitted/completed/failed: '); printint(stats.Submitted); print(' / '); printint(stats.Completed); print(' / '); printint(stats.Failed); println('');
print('Commands / merged requests: '); printint(stats.Commands); print(' / '); printint(stats.Merged); println('');
print('Queue peak / active peak / queue full: '); printint(stats.QueuePeak); print(' / '); printint(stats.ActivePeak); print(' / '); printint(stats.QueueFull); println('');
print('Read / written sectors: '); printint(stats.ReadSectors); print(' / '); printint(stats.WriteSectors); println('');
print('Requests <=8 / <=256 / >256 sectors: '); printint(stats.SmallRequests); print(' / '); printint(stats.MediumRequests); print(' / '); printint(stats.LargeRequests); println('');
print('Queue ticks total / max: '); printint(stats.QueueTicks); print(' / '); printint(stats.MaxQueueTicks); println('');
print('Service ticks total / max: '); printint(stats.ServiceTicks); print(' / '); printint(stats.MaxServiceTicks); println('');
print('FAT cache hits / misses: '); printint(stats.FatCacheHits); print(' / '); printint(stats.FatCacheMisses); println('');
end; end;
{ ---------- DISK WIPE ---------- } { ---------- DISK WIPE ---------- }
+172 -649
View File
File diff suppressed because it is too large. Load diff
+9 -69
View File
@@ -195,7 +195,6 @@ procedure do_refresh_load(state: PFileBrowserState); forward;
procedure do_refresh_build(state: PFileBrowserState); forward; procedure do_refresh_build(state: PFileBrowserState); forward;
procedure schedule_refresh(state: PFileBrowserState); forward; procedure schedule_refresh(state: PFileBrowserState); forward;
procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl; forward; procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl; forward;
function fb_error_name(err : TError) : pchar; forward;
procedure fb_collect_cb(key: pchar; data: void; ud: void); forward; procedure fb_collect_cb(key: pchar; data: void; ud: void); forward;
procedure do_refresh(state: PFileBrowserState); forward; procedure do_refresh(state: PFileBrowserState); forward;
procedure navigate_to(state: PFileBrowserState; newDir: pchar); forward; procedure navigate_to(state: PFileBrowserState; newDir: pchar); forward;
@@ -583,51 +582,6 @@ begin
puint32(@state^.refresh_pending)^ := 1; puint32(@state^.refresh_pending)^ := 1;
end; end;
function fb_error_name(err : TError) : pchar;
begin
case err of
eNone: fb_error_name := 'eNone';
eUnknown: fb_error_name := 'eUnknown';
eNotSupported: fb_error_name := 'eNotSupported';
eOutOfMemory: fb_error_name := 'eOutOfMemory';
eInvalidArgument: fb_error_name := 'eInvalidArgument';
eFileInUse: fb_error_name := 'eFileInUse';
eFileDoesNotExist: fb_error_name := 'eFileDoesNotExist';
eInvalidFileName: fb_error_name := 'eInvalidFileName';
eInvalidFileExtension: fb_error_name := 'eInvalidFileExtension';
eFilenameTooLong: fb_error_name := 'eFilenameTooLong';
eDirectoryDoesNotExist: fb_error_name := 'eDirectoryDoesNotExist';
eDirectoryAlreadyExists: fb_error_name := 'eDirectoryAlreadyExists';
eDirectoryNotEmpty: fb_error_name := 'eDirectoryNotEmpty';
eDirectoryFull: fb_error_name := 'eDirectoryFull';
eNotADirectory: fb_error_name := 'eNotADirectory';
eWriteOnly: fb_error_name := 'eWriteOnly';
eReadOnly: fb_error_name := 'eReadOnly';
ePermissionDenied: fb_error_name := 'ePermissionDenied';
eInvalidPath: fb_error_name := 'eInvalidPath';
eTooManyOpenFiles: fb_error_name := 'eTooManyOpenFiles';
eInvalidHandle: fb_error_name := 'eInvalidHandle';
eFileNotLoaded: fb_error_name := 'eFileNotLoaded';
eAlreadyExists: fb_error_name := 'eAlreadyExists';
eDiskFull: fb_error_name := 'eDiskFull';
eIOError: fb_error_name := 'eIOError';
eIOTimeout: fb_error_name := 'eIOTimeout';
eIOCancelled: fb_error_name := 'eIOCancelled';
eDeviceNotReady: fb_error_name := 'eDeviceNotReady';
eDeviceRemoved: fb_error_name := 'eDeviceRemoved';
eDeviceNotFound: fb_error_name := 'eDeviceNotFound';
eQueueFull: fb_error_name := 'eQueueFull';
eNoFreeSlot: fb_error_name := 'eNoFreeSlot';
eCorruptFilesystem: fb_error_name := 'eCorruptFilesystem';
eBadSector: fb_error_name := 'eBadSector';
eAlreadyMounted: fb_error_name := 'eAlreadyMounted';
eNotMounted: fb_error_name := 'eNotMounted';
eUnsupportedFilesystem: fb_error_name := 'eUnsupportedFilesystem';
eInvalidPartitionTable: fb_error_name := 'eInvalidPartitionTable';
eVolumeNotFound: fb_error_name := 'eVolumeNotFound';
end;
end;
{ fb_refresh_poll_cb — 50ms LVGL poll timer; when rd_ready=1, build UI } { fb_refresh_poll_cb — 50ms LVGL poll timer; when rd_ready=1, build UI }
procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl; procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl;
var var
@@ -650,12 +604,9 @@ begin
case state^.wop_kind of case state^.wop_kind of
1: lv_msgbox_add_text(mbox, 'Could not create folder.'); 1: lv_msgbox_add_text(mbox, 'Could not create folder.');
2: lv_msgbox_add_text(mbox, 'Could not rename item.'); 2: lv_msgbox_add_text(mbox, 'Could not rename item.');
3: lv_msgbox_add_text(mbox, 'Could not create file.');
4: lv_msgbox_add_text(mbox, 'Could not delete item.');
else else
lv_msgbox_add_text(mbox, 'Operation failed.'); lv_msgbox_add_text(mbox, 'Operation failed.');
end; end;
lv_msgbox_add_text(mbox, fb_error_name(state^.wop_result));
ok_b := lv_msgbox_add_footer_button(mbox, 'OK'); ok_b := lv_msgbox_add_footer_button(mbox, 'OK');
lv_obj_add_event_cb(ok_b, @fb_mbox_close_cb, LV_EVENT_CLICKED, mbox); lv_obj_add_event_cb(ok_b, @fb_mbox_close_cb, LV_EVENT_CLICKED, mbox);
end; end;
@@ -668,8 +619,8 @@ begin
kfree(void(state^.wop_path2)); kfree(void(state^.wop_path2));
state^.wop_path2 := nil; state^.wop_path2 := nil;
end; end;
{ For rename/delete, clear selection } { For rename, clear selection }
if (state^.wop_kind = 2) or (state^.wop_kind = 4) then if state^.wop_kind = 2 then
clear_selection(state); clear_selection(state);
schedule_refresh(state); schedule_refresh(state);
end; end;
@@ -1640,16 +1591,15 @@ begin
@fb_delete_done, state); @fb_delete_done, state);
end; end;
{ Async completion callback — runs outside the LVGL thread, so only set flags. } { Async completion callback — runs in ISR/worker context, defer to LVGL }
procedure fb_delete_done(error: TError; userdata: pointer); procedure fb_delete_done(error: TError; userdata: pointer);
var var
state: PFileBrowserState; state: PFileBrowserState;
begin begin
state := PFileBrowserState(userdata); state := PFileBrowserState(userdata);
if state = nil then exit; if state = nil then exit;
state^.wop_kind := 4; state^.op_err := error;
state^.wop_result := error; lv_timer_create(@fb_delete_done_timer, 1, state);
puint32(@state^.wop_done)^ := 1;
end; end;
{ Deferred UI update after delete } { Deferred UI update after delete }
@@ -1910,20 +1860,15 @@ begin
if (error = eNone) and (nfctx^.handle <> 0) then begin if (error = eNone) and (nfctx^.handle <> 0) then begin
{ WriteFileAsync needs a non-nil buffer; Length=0 so it is never read } { WriteFileAsync needs a non-nil buffer; Length=0 so it is never read }
driver.storage.vfs.WriteFileAsync(nfctx^.handle, 0, driver.storage.vfs.WriteFileAsync(nfctx^.handle, 0,
puint8(@nfctx^.err), 0, nil, @fb_newfile_written, nfctx); puint8(@nfctx^.err), 0, @fb_newfile_written, nfctx);
end else begin end else begin
if nfctx^.handle <> 0 then if nfctx^.handle <> 0 then
driver.storage.vfs.CloseFile(nfctx^.handle); driver.storage.vfs.CloseFile(nfctx^.handle);
if nfctx^.state <> nil then begin lv_timer_create(@fb_newfile_done_timer, 1, nfctx);
nfctx^.state^.wop_kind := 3;
nfctx^.state^.wop_result := nfctx^.err;
puint32(@nfctx^.state^.wop_done)^ := 1;
end;
kfree(void(nfctx));
end; end;
end; end;
{ Async step 2: write complete — close handle and defer UI work via poll flags } { Async step 2: write complete — close handle and schedule UI refresh }
procedure fb_newfile_written(error: TError; userdata: pointer); procedure fb_newfile_written(error: TError; userdata: pointer);
var var
nfctx : PNewFileCtx; nfctx : PNewFileCtx;
@@ -1933,12 +1878,7 @@ begin
if error <> eNone then nfctx^.err := error; if error <> eNone then nfctx^.err := error;
if nfctx^.handle <> 0 then if nfctx^.handle <> 0 then
driver.storage.vfs.CloseFile(nfctx^.handle); driver.storage.vfs.CloseFile(nfctx^.handle);
if nfctx^.state <> nil then begin lv_timer_create(@fb_newfile_done_timer, 1, nfctx);
nfctx^.state^.wop_kind := 3;
nfctx^.state^.wop_result := nfctx^.err;
puint32(@nfctx^.state^.wop_done)^ := 1;
end;
kfree(void(nfctx));
end; end;
procedure fb_newfile_done_timer(tmr: Plv_timer); cdecl; procedure fb_newfile_done_timer(tmr: Plv_timer); cdecl;
File diff suppressed because it is too large. Load diff
+1 -2
View File
@@ -26,7 +26,7 @@ uses
boot.mgr, boot.mgr,
debug.tracer, io.stdio, proc.mgr, debug.tracer, io.stdio, proc.mgr,
//progs //progs
app.base64, app.md5sum, app.dhclient, app.vbeinfo, app.testcmd, app.ping, app.meminfo, app.setres, app.iotest, app.base64, app.md5sum, app.dhclient, app.vbeinfo, app.testcmd, app.ping, app.meminfo, app.setres,
//drivers //drivers
driver.storage.ctl.ram, driver.mgr, driver.storage.ctl.ram, driver.mgr,
//network //network
@@ -92,7 +92,6 @@ begin
app.bsod.init(); app.bsod.init();
app.vterminal.init; app.vterminal.init;
app.filebrowser.init(); app.filebrowser.init();
app.iotest.init();
end; end;
Initialization Initialization
+2 -4
View File
@@ -355,10 +355,8 @@ begin
proc.mgr.kill(state^.pid); proc.mgr.kill(state^.pid);
state^.pid := 0; state^.pid := 0;
end; end;
if state^.ta <> nil then lv_group_remove_obj(state^.ta);
state^.ta := nil;
driver.video.windows.destroyWindow(wid);
freeState(state); freeState(state);
driver.video.windows.destroyWindow(wid);
end; end;
{ ============================================================ { ============================================================
@@ -442,7 +440,7 @@ begin
written := driver.storage.vfs.WriteFile(fHandle, 0, puint8(buf), len); written := driver.storage.vfs.WriteFile(fHandle, 0, puint8(buf), len);
driver.storage.vfs.CloseFile(fHandle); driver.storage.vfs.CloseFile(fHandle);
kfree(void(buf)); kfree(void(buf));
if written <> len then begin if written = 0 then begin
io.syslog.logln('NOTEPAD', 'saveFile: WriteFile returned 0'); io.syslog.logln('NOTEPAD', 'saveFile: WriteFile returned 0');
showErrorMsgbox('Save Failed', 'File write failed. Check filesystem.'); showErrorMsgbox('Save Failed', 'File write failed. Check filesystem.');
updateStatusBar(state); updateStatusBar(state);
+8 -17
View File
@@ -288,9 +288,7 @@ begin
if bootrecord <> nil then begin if bootrecord <> nil then begin
if bootrecord^.boot_sector <> $AA55 then begin if bootrecord^.boot_sector <> $AA55 then begin
println('No valid mbr found, initialising disk...'); println('No valid mbr found, initialising disk...');
if driver.storage.vol.mgr.init_disk(device) <> eNone then begin driver.storage.vol.mgr.init_disk(device);
println('Error: disk initialization failed.'); exit;
end;
end; end;
kfree(puint32(bootrecord)); kfree(puint32(bootrecord));
end; end;
@@ -337,9 +335,7 @@ begin
driver.storage.vol.mbr.setup_partition(@part, lba, sectors); driver.storage.vol.mbr.setup_partition(@part, lba, sectors);
part.system_id := 0; part.system_id := 0;
if driver.storage.vol.mgr.add_partition(device, slot, part) <> eNone then begin driver.storage.vol.mgr.add_partition(device, slot, part);
println('Error: partition creation failed.'); exit;
end;
print('Partition added in slot '); print('Partition added in slot ');
printint(slot); printint(slot);
@@ -411,9 +407,7 @@ begin
exit; exit;
end; end;
if driver.storage.vol.mgr.remove_partition(device, slot) <> eNone then begin driver.storage.vol.mgr.remove_partition(device, slot);
println('Error: partition removal failed.'); exit;
end;
print('Partition slot '); print('Partition slot ');
printint(slot); printint(slot);
@@ -529,14 +523,11 @@ begin
exit; exit;
end; end;
i := 0; vol^.filesystem := fs;
while i < driver.storage.vol.mgr.get_volume_count() do begin fs^.createCallback(vol, vol^.sectorCount, vol^.sectorStart, nil);
if driver.storage.vol.mgr.get_volume(i) = vol then break;
Inc(i); { Re-probe filesystem so identify picks up the new signature }
end; driver.storage.fs.mgr.probe_volume(vol);
if not driver.storage.vol.mgr.format_volume(device, i, fsName, nil) then begin
println('Error: format failed; the volume was not reported ready.'); exit;
end;
print('Partition '); print('Partition ');
printint(slot); printint(slot);
+1 -3
View File
@@ -179,9 +179,7 @@ begin
print('Size: '); printsize(vol^.sectorCount * vol^.sectorSize); println(''); print('Size: '); printsize(vol^.sectorCount * vol^.sectorSize); println('');
print('Free: '); print('Free: '); printsize(vol^.freeSectors * vol^.sectorSize); println('');
if (vol^.lifecycleFlags and STORAGE_VOLUME_FLAG_FREE_UNKNOWN) <> 0 then println('Unknown')
else begin printsize(vol^.freeSectors * vol^.sectorSize); println(''); end;
print('Boot Drive: '); print('Boot Drive: ');
if vol^.isBootDrive then println('yes') else println('no'); if vol^.isBootDrive then println('yes') else println('no');
-14
View File
@@ -44,7 +44,6 @@ function inl(port : uint16) : uint32;
procedure io_wait; procedure io_wait;
procedure __SSE_128_memcpy(source : uint32; dest : uint32); procedure __SSE_128_memcpy(source : uint32; dest : uint32);
procedure __REP_MOVSB_memcpy(source : uint32; dest : uint32; size : uint32);
procedure halt_and_catch_fire(); procedure halt_and_catch_fire();
procedure halt_and_dont_catch_fire(); procedure halt_and_dont_catch_fire();
@@ -114,19 +113,6 @@ asm
MOVAPS [EAX], XMM1 MOVAPS [EAX], XMM1
end; end;
procedure __REP_MOVSB_memcpy(source : uint32; dest : uint32; size : uint32); assembler;
asm
push esi
push edi
cld
mov esi, source
mov edi, dest
mov ecx, size
rep movsb
pop edi
pop esi
end;
function getESP : uint32; function getESP : uint32;
var var
tmp: uint32; tmp: uint32;
Loaded 20 of 74 files, more files were not shown because too many files have changed in this diff. Show more