Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
720ba46630 | ||
|
|
8e0ab3da8b | ||
|
|
3d4f846a8f | ||
|
|
d8d51ba5df | ||
|
|
d1ecd3fc41 | ||
|
|
7e99bc2db1 | ||
|
|
096db148d0 | ||
|
|
663bfbb0e4 | ||
|
|
aadc59f831 | ||
|
|
5d7aacc28f | ||
|
|
4810617922 | ||
|
|
666a011a9f | ||
|
|
a6e81bf5d3 |
No files matched your search
@@ -219,3 +219,37 @@ Drop-in procedure replacements for callers using the old void-returning API. For
|
||||
- `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.
|
||||
- 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,3 +58,111 @@ 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_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.
|
||||
|
||||
|
||||
## 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.
|
||||
@@ -150,6 +150,7 @@ var
|
||||
device : PStorage_Device;
|
||||
volCount : uint32;
|
||||
freeSec : uint32;
|
||||
stats : TStorageStats;
|
||||
begin
|
||||
if paramCount(params) < 2 then begin
|
||||
println('Usage: DISK info <disk>');
|
||||
@@ -191,6 +192,16 @@ begin
|
||||
else
|
||||
volCount := 0;
|
||||
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;
|
||||
|
||||
{ ---------- DISK WIPE ---------- }
|
||||
|
||||
+644
-169
File diff suppressed because it is too large.
Load diff
@@ -195,6 +195,7 @@ procedure do_refresh_load(state: PFileBrowserState); forward;
|
||||
procedure do_refresh_build(state: PFileBrowserState); forward;
|
||||
procedure schedule_refresh(state: PFileBrowserState); 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 do_refresh(state: PFileBrowserState); forward;
|
||||
procedure navigate_to(state: PFileBrowserState; newDir: pchar); forward;
|
||||
@@ -582,6 +583,51 @@ begin
|
||||
puint32(@state^.refresh_pending)^ := 1;
|
||||
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 }
|
||||
procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl;
|
||||
var
|
||||
@@ -604,9 +650,12 @@ begin
|
||||
case state^.wop_kind of
|
||||
1: lv_msgbox_add_text(mbox, 'Could not create folder.');
|
||||
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
|
||||
lv_msgbox_add_text(mbox, 'Operation failed.');
|
||||
end;
|
||||
lv_msgbox_add_text(mbox, fb_error_name(state^.wop_result));
|
||||
ok_b := lv_msgbox_add_footer_button(mbox, 'OK');
|
||||
lv_obj_add_event_cb(ok_b, @fb_mbox_close_cb, LV_EVENT_CLICKED, mbox);
|
||||
end;
|
||||
@@ -619,8 +668,8 @@ begin
|
||||
kfree(void(state^.wop_path2));
|
||||
state^.wop_path2 := nil;
|
||||
end;
|
||||
{ For rename, clear selection }
|
||||
if state^.wop_kind = 2 then
|
||||
{ For rename/delete, clear selection }
|
||||
if (state^.wop_kind = 2) or (state^.wop_kind = 4) then
|
||||
clear_selection(state);
|
||||
schedule_refresh(state);
|
||||
end;
|
||||
@@ -1591,15 +1640,16 @@ begin
|
||||
@fb_delete_done, state);
|
||||
end;
|
||||
|
||||
{ Async completion callback — runs in ISR/worker context, defer to LVGL }
|
||||
{ Async completion callback — runs outside the LVGL thread, so only set flags. }
|
||||
procedure fb_delete_done(error: TError; userdata: pointer);
|
||||
var
|
||||
state: PFileBrowserState;
|
||||
begin
|
||||
state := PFileBrowserState(userdata);
|
||||
if state = nil then exit;
|
||||
state^.op_err := error;
|
||||
lv_timer_create(@fb_delete_done_timer, 1, state);
|
||||
state^.wop_kind := 4;
|
||||
state^.wop_result := error;
|
||||
puint32(@state^.wop_done)^ := 1;
|
||||
end;
|
||||
|
||||
{ Deferred UI update after delete }
|
||||
@@ -1860,15 +1910,20 @@ begin
|
||||
if (error = eNone) and (nfctx^.handle <> 0) then begin
|
||||
{ WriteFileAsync needs a non-nil buffer; Length=0 so it is never read }
|
||||
driver.storage.vfs.WriteFileAsync(nfctx^.handle, 0,
|
||||
puint8(@nfctx^.err), 0, @fb_newfile_written, nfctx);
|
||||
puint8(@nfctx^.err), 0, nil, @fb_newfile_written, nfctx);
|
||||
end else begin
|
||||
if nfctx^.handle <> 0 then
|
||||
driver.storage.vfs.CloseFile(nfctx^.handle);
|
||||
lv_timer_create(@fb_newfile_done_timer, 1, nfctx);
|
||||
if nfctx^.state <> nil then begin
|
||||
nfctx^.state^.wop_kind := 3;
|
||||
nfctx^.state^.wop_result := nfctx^.err;
|
||||
puint32(@nfctx^.state^.wop_done)^ := 1;
|
||||
end;
|
||||
kfree(void(nfctx));
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Async step 2: write complete — close handle and schedule UI refresh }
|
||||
{ Async step 2: write complete — close handle and defer UI work via poll flags }
|
||||
procedure fb_newfile_written(error: TError; userdata: pointer);
|
||||
var
|
||||
nfctx : PNewFileCtx;
|
||||
@@ -1878,7 +1933,12 @@ begin
|
||||
if error <> eNone then nfctx^.err := error;
|
||||
if nfctx^.handle <> 0 then
|
||||
driver.storage.vfs.CloseFile(nfctx^.handle);
|
||||
lv_timer_create(@fb_newfile_done_timer, 1, nfctx);
|
||||
if nfctx^.state <> nil then begin
|
||||
nfctx^.state^.wop_kind := 3;
|
||||
nfctx^.state^.wop_result := nfctx^.err;
|
||||
puint32(@nfctx^.state^.wop_done)^ := 1;
|
||||
end;
|
||||
kfree(void(nfctx));
|
||||
end;
|
||||
|
||||
procedure fb_newfile_done_timer(tmr: Plv_timer); cdecl;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+2
-1
@@ -26,7 +26,7 @@ uses
|
||||
boot.mgr,
|
||||
debug.tracer, io.stdio, proc.mgr,
|
||||
//progs
|
||||
app.base64, app.md5sum, app.dhclient, app.vbeinfo, app.testcmd, app.ping, app.meminfo, app.setres,
|
||||
app.base64, app.md5sum, app.dhclient, app.vbeinfo, app.testcmd, app.ping, app.meminfo, app.setres, app.iotest,
|
||||
//drivers
|
||||
driver.storage.ctl.ram, driver.mgr,
|
||||
//network
|
||||
@@ -92,6 +92,7 @@ begin
|
||||
app.bsod.init();
|
||||
app.vterminal.init;
|
||||
app.filebrowser.init();
|
||||
app.iotest.init();
|
||||
end;
|
||||
|
||||
Initialization
|
||||
|
||||
+17
-8
@@ -288,7 +288,9 @@ begin
|
||||
if bootrecord <> nil then begin
|
||||
if bootrecord^.boot_sector <> $AA55 then begin
|
||||
println('No valid mbr found, initialising disk...');
|
||||
driver.storage.vol.mgr.init_disk(device);
|
||||
if driver.storage.vol.mgr.init_disk(device) <> eNone then begin
|
||||
println('Error: disk initialization failed.'); exit;
|
||||
end;
|
||||
end;
|
||||
kfree(puint32(bootrecord));
|
||||
end;
|
||||
@@ -335,7 +337,9 @@ begin
|
||||
|
||||
driver.storage.vol.mbr.setup_partition(@part, lba, sectors);
|
||||
part.system_id := 0;
|
||||
driver.storage.vol.mgr.add_partition(device, slot, part);
|
||||
if driver.storage.vol.mgr.add_partition(device, slot, part) <> eNone then begin
|
||||
println('Error: partition creation failed.'); exit;
|
||||
end;
|
||||
|
||||
print('Partition added in slot ');
|
||||
printint(slot);
|
||||
@@ -407,7 +411,9 @@ begin
|
||||
exit;
|
||||
end;
|
||||
|
||||
driver.storage.vol.mgr.remove_partition(device, slot);
|
||||
if driver.storage.vol.mgr.remove_partition(device, slot) <> eNone then begin
|
||||
println('Error: partition removal failed.'); exit;
|
||||
end;
|
||||
|
||||
print('Partition slot ');
|
||||
printint(slot);
|
||||
@@ -523,11 +529,14 @@ begin
|
||||
exit;
|
||||
end;
|
||||
|
||||
vol^.filesystem := fs;
|
||||
fs^.createCallback(vol, vol^.sectorCount, vol^.sectorStart, nil);
|
||||
|
||||
{ Re-probe filesystem so identify picks up the new signature }
|
||||
driver.storage.fs.mgr.probe_volume(vol);
|
||||
i := 0;
|
||||
while i < driver.storage.vol.mgr.get_volume_count() do begin
|
||||
if driver.storage.vol.mgr.get_volume(i) = vol then break;
|
||||
Inc(i);
|
||||
end;
|
||||
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 ');
|
||||
printint(slot);
|
||||
|
||||
@@ -44,6 +44,7 @@ function inl(port : uint16) : uint32;
|
||||
procedure io_wait;
|
||||
|
||||
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_dont_catch_fire();
|
||||
@@ -113,6 +114,19 @@ asm
|
||||
MOVAPS [EAX], XMM1
|
||||
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;
|
||||
var
|
||||
tmp: uint32;
|
||||
|
||||
@@ -27,6 +27,10 @@ uses
|
||||
arch.x86.isr.mgr,
|
||||
arch.x86.idt;
|
||||
|
||||
const
|
||||
PIT_INPUT_HZ = 1193182;
|
||||
PIT_DIVISOR = 149;
|
||||
|
||||
procedure register();
|
||||
procedure hook(hook_method : uint32);
|
||||
procedure unhook(hook_method : uint32);
|
||||
@@ -54,7 +58,7 @@ procedure register();
|
||||
begin
|
||||
if not registered then begin
|
||||
asm
|
||||
mov ax, 149
|
||||
mov ax, PIT_DIVISOR
|
||||
out $40, al
|
||||
mov al, ah
|
||||
out $40, al
|
||||
|
||||
@@ -271,6 +271,7 @@ procedure fpc_shortstr_assign(len: longint; sstr, dstr: Pointer); compilerproc;
|
||||
any int64/uint64 arithmetic occurs (e.g. sint32 * uint32 promotion).
|
||||
Must live in the system unit so the compiler can resolve it. }
|
||||
function fpc_mul_int64(f1, f2: int64): int64; compilerproc;
|
||||
function fpc_mul_dword_to_qword(f1, f2: uint32): qword; compilerproc;
|
||||
function fpc_div_int64(d, n: int64): int64; compilerproc;
|
||||
function fpc_mod_int64(d, n: int64): int64; compilerproc;
|
||||
function fpc_mod_qword(d, n: qword): qword; compilerproc;
|
||||
@@ -523,6 +524,18 @@ begin
|
||||
fpc_mul_int64 := res;
|
||||
end;
|
||||
|
||||
function fpc_mul_dword_to_qword(f1, f2: uint32): qword; [public, alias: 'FPC_MUL_DWORD_TO_QWORD']; compilerproc;
|
||||
var res: qword;
|
||||
begin
|
||||
asm
|
||||
MOV EAX, f1
|
||||
MUL f2
|
||||
MOV DWORD [res], EAX
|
||||
MOV DWORD [res+4], EDX
|
||||
end;
|
||||
fpc_mul_dword_to_qword := res;
|
||||
end;
|
||||
|
||||
{ ---------- 64-bit division / modulo ---------- }
|
||||
|
||||
{ Helper: unsigned 64÷64→64 using shift-subtract (binary long division).
|
||||
|
||||
+28
-9
@@ -42,6 +42,26 @@ function HexCharToDecimal(hex : char) : uint8;
|
||||
function abs(x : sint32) : uint32;
|
||||
|
||||
implementation
|
||||
{$ifdef CPU386}
|
||||
uses
|
||||
arch.x86.util;
|
||||
|
||||
const
|
||||
MEMCPY_X86_SMALL_COPY_THRESHOLD = 16;
|
||||
{$endif}
|
||||
|
||||
procedure scalar_memcpy(source : uint32; dest : uint32; size : uint32);
|
||||
var
|
||||
src, dst : puint8;
|
||||
i : uint32;
|
||||
begin
|
||||
if size = 0 then exit;
|
||||
for i := 0 to size - 1 do begin
|
||||
src := puint8(source + i);
|
||||
dst := puint8(dest + i);
|
||||
dst^ := src^;
|
||||
end;
|
||||
end;
|
||||
|
||||
function abs(x : sint32) : uint32;
|
||||
var
|
||||
@@ -123,17 +143,16 @@ begin
|
||||
end;
|
||||
|
||||
procedure memcpy(source : uint32; dest : uint32; size : uint32);
|
||||
var
|
||||
src, dst : puint8;
|
||||
i : uint32;
|
||||
|
||||
begin
|
||||
if size = 0 then exit;
|
||||
for i:=0 to size-1 do begin
|
||||
src:= puint8(source + i);
|
||||
dst:= puint8(dest + i);
|
||||
dst^:= src^;
|
||||
end;
|
||||
{$ifdef CPU386}
|
||||
if size < MEMCPY_X86_SMALL_COPY_THRESHOLD then
|
||||
scalar_memcpy(source, dest, size)
|
||||
else
|
||||
arch.x86.util.__REP_MOVSB_memcpy(source, dest, size);
|
||||
{$else}
|
||||
scalar_memcpy(source, dest, size);
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
function getWord(i : uint32; hi : boolean) : uint16;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -21,6 +21,7 @@ unit driver.storage.ctl.ahci.types;
|
||||
interface
|
||||
uses
|
||||
io.syslog,
|
||||
driver.storage.types,
|
||||
driver.mgr,
|
||||
driver.types,
|
||||
driver.storage.ctl.ide.types,
|
||||
@@ -251,11 +252,13 @@ type
|
||||
{ Callback signature fired from IRQ context when an async I/O command completes.
|
||||
Must be very short — set a flag or post to a queue.
|
||||
Never call kalloc/kfree/VFS from within this callback. }
|
||||
TIOCompletion = procedure(success : boolean; userdata : puint32);
|
||||
TIOCompletion = procedure(error : TError; userdata : puint32);
|
||||
|
||||
{ Per-slot pending operation — one entry per HBA command slot }
|
||||
TPendingOp = record
|
||||
inUse : boolean;
|
||||
issued : boolean;
|
||||
started : uint32;
|
||||
completion : TIOCompletion;
|
||||
userdata : puint32;
|
||||
end;
|
||||
@@ -267,8 +270,16 @@ type
|
||||
}
|
||||
TAHCI_Device = record
|
||||
port : PHBA_Port;
|
||||
owner : pointer; { stable PAHCI_Controller }
|
||||
offline : boolean;
|
||||
recovering : boolean;
|
||||
recoveryTick : uint32;
|
||||
recoveryError: TError;
|
||||
device_type : TDeviceType;
|
||||
ata_info : TIdentResponse;
|
||||
supportsNCQ : boolean;
|
||||
queueDepth : uint32; { safe active depth for this device }
|
||||
slotCount : uint32; { controller slot count from CAP.NCS + 1 }
|
||||
command_list : PHBA_CMD_HEADER;
|
||||
fis : PHBA_FIS;
|
||||
command_table: PHBA_CMD_TABLE;
|
||||
@@ -285,6 +296,7 @@ type
|
||||
}
|
||||
TAHCI_Controller = record
|
||||
pci_device : PPCI_Device;
|
||||
offline : boolean;
|
||||
mio: PHBA_Memory;
|
||||
ata_info : TIdentResponse;
|
||||
devices : array[0..31] of TAHCI_Device;
|
||||
@@ -335,4 +347,3 @@ end;
|
||||
// end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -82,38 +82,12 @@ end;
|
||||
|
||||
{ ---- Filesystem hook callbacks ---- }
|
||||
|
||||
{ PPReadHook — load entire file into a new kalloc'd buffer.
|
||||
Sets buffer^ to the data pointer, bytecount^ to the size.
|
||||
Returns 0 on success, 1 on failure. }
|
||||
function rd_Read(volume : PStorage_Volume; directory : pchar;
|
||||
fileName : pchar; buffer : puint32;
|
||||
bytecount : puint32) : uint32;
|
||||
var
|
||||
clean : pchar;
|
||||
idx : sint32;
|
||||
dest : puint8;
|
||||
begin
|
||||
rd_Read := 1;
|
||||
clean := stripLeadingSlash(fileName);
|
||||
if clean = nil then exit;
|
||||
idx := findFileByName(clean);
|
||||
if idx < 0 then exit;
|
||||
|
||||
{ VFS expects a fresh buffer it can own }
|
||||
dest := puint8(kalloc(Files[idx].Size));
|
||||
if dest = nil then exit;
|
||||
memcpy(uint32(Files[idx].Data), uint32(dest), Files[idx].Size);
|
||||
|
||||
buffer^ := uint32(dest);
|
||||
bytecount^ := Files[idx].Size;
|
||||
rd_Read := 0;
|
||||
end;
|
||||
|
||||
{ PPReadOffsetHook — read byteCount bytes at offset into caller buffer.
|
||||
Returns actual bytes copied. }
|
||||
function rd_ReadOffset(volume : PStorage_Volume; directory : pchar;
|
||||
fileName : pchar; offset : uint32;
|
||||
buffer : puint32; byteCount : uint32) : uint32;
|
||||
buffer : puint32; byteCount : uint32;
|
||||
ctx : pointer) : uint32;
|
||||
var
|
||||
clean : pchar;
|
||||
idx : sint32;
|
||||
@@ -133,6 +107,20 @@ begin
|
||||
rd_ReadOffset := copyLen;
|
||||
end;
|
||||
|
||||
function rd_FileSize(volume : PStorage_Volume; directory : pchar;
|
||||
fileName : pchar) : uint32;
|
||||
var
|
||||
clean : pchar;
|
||||
idx : sint32;
|
||||
begin
|
||||
rd_FileSize := 0;
|
||||
clean := stripLeadingSlash(fileName);
|
||||
if clean = nil then exit;
|
||||
idx := findFileByName(clean);
|
||||
if idx < 0 then exit;
|
||||
rd_FileSize := Files[idx].Size;
|
||||
end;
|
||||
|
||||
{ PPReadDirHook — return a linked list of TDirectory_Entry for all files.
|
||||
VFS owns the returned list and frees each entry^.fileName. }
|
||||
function rd_ReadDir(volume : PStorage_Volume; directory : pchar;
|
||||
@@ -269,9 +257,9 @@ begin
|
||||
{ Set up a minimal filesystem descriptor with our callbacks }
|
||||
memset(uint32(@RAMFS), 0, sizeof(TFilesystem));
|
||||
RAMFS.sName := 'ramfs';
|
||||
RAMFS.readCallback := @rd_Read;
|
||||
RAMFS.readDirCallback := @rd_ReadDir;
|
||||
RAMFS.readOffsetCallback := @rd_ReadOffset;
|
||||
RAMFS.fileSizeCallback := @rd_FileSize;
|
||||
RAMFS.identifyCallback := @rd_Identify;
|
||||
|
||||
{ Set up a virtual volume backed by our filesystem }
|
||||
|
||||
@@ -45,10 +45,12 @@ const
|
||||
ATA_CMD_READ_PIO_EXT = $24;
|
||||
ATA_CMD_READ_DMA = $C8;
|
||||
ATA_CMD_READ_DMA_EXT = $25;
|
||||
ATA_CMD_READ_FPDMA_QUEUED = $60;
|
||||
ATA_CMD_WRITE_PIO = $30;
|
||||
ATA_CMD_WRITE_PIO_EXT = $34;
|
||||
ATA_CMD_WRITE_DMA = $CA;
|
||||
ATA_CMD_WRITE_DMA_EXT = $35;
|
||||
ATA_CMD_WRITE_FPDMA_QUEUED = $61;
|
||||
ATA_CMD_CACHE_FLUSH = $E7;
|
||||
ATA_CMD_CACHE_FLUSH_EXT = $EA;
|
||||
ATA_CMD_PACKET = $A0;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -37,17 +37,17 @@ type
|
||||
PFileDescriptor = ^TFileDescriptor;
|
||||
TFileDescriptor = record
|
||||
InUse : boolean;
|
||||
AsyncRefs : uint32; { in-flight async ops using this descriptor }
|
||||
Volume : PStorage_Volume;
|
||||
Directory : pchar; { directory path within the volume }
|
||||
FileName : pchar; { filename within that directory }
|
||||
VFSDir : pchar; { full VFS parent directory path, e.g. '/sys' }
|
||||
OpenMode : uint8; { TOpenMode ordinal — avoids VFS type dependency }
|
||||
DataBuffer : puint32; { pre-loaded data for small files, nil otherwise }
|
||||
DataSize : uint32; { size of pre-loaded data in bytes }
|
||||
Loaded : boolean; { true if pre-loaded into DataBuffer }
|
||||
StreamOff : uint32; { current byte offset for streaming / on-demand reads }
|
||||
DataSize : uint32; { cached file size in bytes (from fileSizeCallback) }
|
||||
DeviceOps : pointer; { PVFSDeviceOps — non-nil for device FDs }
|
||||
DeviceData : pointer; { opaque data passed to device callbacks }
|
||||
FSPrivate : pointer; { opaque FS-level per-file metadata (from openFileCallback) }
|
||||
FSCloseHook : pointer; { procedure(pointer) — frees FSPrivate on close }
|
||||
end;
|
||||
|
||||
PFDTable = ^TFDTable;
|
||||
@@ -71,6 +71,9 @@ function fd_alloc(table : PFDTable) : uint32;
|
||||
{ Get a pointer to the descriptor for a 1-based handle.
|
||||
Returns nil if handle is out of range or the slot is not in use. }
|
||||
function fd_get(table : PFDTable; handle : uint32) : PFileDescriptor;
|
||||
function fd_begin_async(entry : PFileDescriptor) : boolean;
|
||||
procedure fd_end_async(entry : PFileDescriptor);
|
||||
function fd_has_async(entry : PFileDescriptor) : boolean;
|
||||
|
||||
{ Close a single file descriptor: free its buffers and core.strings,
|
||||
mark the slot as not-in-use.
|
||||
@@ -81,6 +84,10 @@ function fd_close(table : PFDTable; handle : uint32) : boolean;
|
||||
Used during process cleanup. }
|
||||
procedure fd_close_all(table : PFDTable);
|
||||
|
||||
{ Close all file descriptors in the table that reference a specific volume.
|
||||
Called when a volume is removed to prevent dangling pointers. }
|
||||
procedure fd_close_for_volume(table : PFDTable; vol : PStorage_Volume);
|
||||
|
||||
implementation
|
||||
|
||||
function fd_table_new : PFDTable;
|
||||
@@ -93,15 +100,20 @@ begin
|
||||
fd_table_new := tbl;
|
||||
end;
|
||||
|
||||
type
|
||||
TFSPrivateClose = procedure(data : pointer);
|
||||
|
||||
procedure fd_free_entry(entry : PFileDescriptor);
|
||||
begin
|
||||
if entry = nil then exit;
|
||||
if not entry^.InUse then exit;
|
||||
|
||||
if entry^.DataBuffer <> nil then begin
|
||||
kfree(entry^.DataBuffer);
|
||||
entry^.DataBuffer := nil;
|
||||
end;
|
||||
{ Free FS-level per-file metadata first }
|
||||
if (entry^.FSPrivate <> nil) and (entry^.FSCloseHook <> nil) then
|
||||
TFSPrivateClose(entry^.FSCloseHook)(entry^.FSPrivate);
|
||||
entry^.FSPrivate := nil;
|
||||
entry^.FSCloseHook := nil;
|
||||
|
||||
if entry^.Directory <> nil then begin
|
||||
kfree(void(entry^.Directory));
|
||||
entry^.Directory := nil;
|
||||
@@ -116,9 +128,8 @@ begin
|
||||
end;
|
||||
|
||||
entry^.InUse := false;
|
||||
entry^.Loaded := false;
|
||||
entry^.AsyncRefs := 0;
|
||||
entry^.DataSize := 0;
|
||||
entry^.StreamOff := 0;
|
||||
entry^.DeviceOps := nil;
|
||||
entry^.DeviceData := nil;
|
||||
end;
|
||||
@@ -153,6 +164,34 @@ begin
|
||||
fd_get := @table^.Entries[handle - 1];
|
||||
end;
|
||||
|
||||
function fd_begin_async(entry : PFileDescriptor) : boolean;
|
||||
begin
|
||||
fd_begin_async := false;
|
||||
if entry = nil then exit;
|
||||
asm pushf; cli end;
|
||||
if entry^.InUse then begin
|
||||
entry^.AsyncRefs := entry^.AsyncRefs + 1;
|
||||
fd_begin_async := true;
|
||||
end;
|
||||
asm popf end;
|
||||
end;
|
||||
|
||||
procedure fd_end_async(entry : PFileDescriptor);
|
||||
begin
|
||||
if entry = nil then exit;
|
||||
asm pushf; cli end;
|
||||
if entry^.AsyncRefs > 0 then
|
||||
entry^.AsyncRefs := entry^.AsyncRefs - 1;
|
||||
asm popf end;
|
||||
end;
|
||||
|
||||
function fd_has_async(entry : PFileDescriptor) : boolean;
|
||||
begin
|
||||
fd_has_async := false;
|
||||
if entry = nil then exit;
|
||||
fd_has_async := entry^.AsyncRefs > 0;
|
||||
end;
|
||||
|
||||
function fd_close(table : PFDTable; handle : uint32) : boolean;
|
||||
begin
|
||||
fd_close := false;
|
||||
@@ -174,4 +213,16 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure fd_close_for_volume(table : PFDTable; vol : PStorage_Volume);
|
||||
var
|
||||
i : uint32;
|
||||
begin
|
||||
if table = nil then exit;
|
||||
if vol = nil then exit;
|
||||
for i := 0 to MAX_FDS - 1 do begin
|
||||
if table^.Entries[i].InUse and (table^.Entries[i].Volume = vol) then
|
||||
fd_free_entry(@table^.Entries[i]);
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
Loaded 20 of 43 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user