# Program Disk example

Storage stage 3: FAT32 file reading on top of MMC and partitions

This example is the current read-only disk program for Alquist. The disk program initializes the Raspberry Pi MMC/eMMC storage interface, reads LBA 0, verifies the MBR signature, parses the MBR partition table, mounts the first FAT32 partition and exposes UART console commands for listing directories and printing a small file.

No writes are performed. Linux prepares the FAT32 directory and test file; Alquist only reads sectors, walks directories and follows FAT32 cluster chains.

The scheduler timer uses a 1 ms tick for responsive UART input. The visible ACT LED heartbeat is owned by `heartbeat_task()`, which toggles the LED every 500 ticks instead of tying the LED to every scheduler interrupt.
Use `led on`, `led off`, and `led toggle` from the shell to test the ACT LED path directly.
The normal `help` output lists user-facing commands. Low-level register and SD commands are still available through `debug`.

## Build

```sh
make
```

Outputs:

- `build/kernel8.elf` - bare-metal image for QEMU/debugging
- `boot/kernel8.img` - Raspberry Pi boot image

## Parser Tests

The MBR parser can be tested on the host without touching an SD card:

```sh
make test-mbr
```

The tests cover:

- invalid signature
- empty MBR
- one FAT32 LBA partition
- two partitions
- GPT/protective MBR
- non-empty entry with zero sectors

## File-backed disk image

QEMU can emulate a disk for this stage by using a regular file as the backing store. The example creates that file with Linux disk tools, then attaches the same file to the Raspberry Pi machine model.

The image flow is intentionally Linux-first:

1. `truncate` creates a 64 MiB raw disk file.
2. `sfdisk` writes a DOS/MBR partition table.
3. `mkfs.fat` formats the partition as FAT32 at LBA 2048.
4. QEMU attaches the file as the primary SD card.

Create the image and inspect the partition table:

```sh
make test-disk-image
```

This target requires `sfdisk`, `fdisk`, and `mkfs.fat` from Linux. On Debian/Ubuntu systems, `mkfs.fat` is provided by `dosfstools`.

Then boot the bare-metal example in QEMU:

```sh
make qemu
```

The current `qemu` target is a boot smoke test for the kernel image. The disk image remains useful for parser and future backend work. When the QEMU disk backend lands it will use QEMU's built-in SD slot for the `raspi4b` machine:

```sh
-drive file=tests/disk.img,format=raw,if=sd,index=0
```

This is safe for parser and driver work because it uses a disposable local image, not the Raspberry Pi system SD card. The bare-metal code still treats the medium as read-only.

## Driver boundary

The disk image and QEMU attachment are not the failing part when the boot log reaches the disk program. If QEMU starts with:

```sh
-drive file=tests/disk.img,format=raw,if=sd,index=0
```

and the kernel prints:

```text
disk: probing Raspberry Pi 4 eMMC2 host (read-only)
disk: no card-present bit from eMMC2 host
```

then the image is attached, but this stage is still using the Raspberry Pi eMMC2 backend. QEMU needs its own read-only disk backend below the same interface:

```c
disk_hw_init()
disk_hw_read_lba()
disk_hw_get_info()
```

The disk program, shell commands, MBR parser, and later FAT32 code should not know whether the lower layer is QEMU or Raspberry Pi hardware. The backend split is intentional: QEMU should talk to the emulated SD controller, while Raspberry Pi hardware should talk to the BCM2711/eMMC2 path.

## QEMU test

```sh
make clean
make test-mbr
make test-disk-image
make qemu
```

The disk-image test must print one MBR partition beginning at LBA 2048 with type `W95 FAT32 (LBA)`. The QEMU test must boot the kernel and keep the UART console alive. Until the QEMU backend is implemented, hardware storage acceptance is performed on the Raspberry Pi.

After the QEMU backend is implemented, `disk scan` must read LBA 0 from `tests/disk.img`, accept the `0x55AA` MBR signature, and list the FAT32 partition created by Linux.

## Hardware test

Build the Raspberry Pi image:

```sh
make clean
make
```

Copy `boot/kernel8.img` and the included boot config to the Raspberry Pi boot partition, then capture UART at 115200 baud. The minimum hardware smoke test is:

```text
Alquist boot
UART ready
Timer ready
Scheduler ready
Heartbeat task started
Disk program ready
console ready
>
```

Then run these UART commands:

```text
help
tasks
led toggle
disk
partitions
dir
cat
cat /ALQ/HELLO.TXT
debug
```

For this stage, `partitions` must ask the disk task to read LBA 0 from the real card, verify the MBR signature, parse the table and list the partitions that Linux prepared on that card. If a raw sector proof is needed, run `debug` and then use `readblk` or `disk dump0` deliberately.

To test file reading, create a small file in a FAT32 subdirectory from Linux before booting bare metal:

```sh
sudo mkdir -p /boot/firmware/ALQ
printf 'Hello from Alquist FAT32.\nThis file lives in /ALQ/HELLO.TXT.\n' | sudo tee /boot/firmware/ALQ/HELLO.TXT
sync
```

Then run `cat` or `cat /ALQ/HELLO.TXT` from the bare-metal shell. The command resolves the subdirectory path, finds the file entry and prints the file contents from its FAT32 cluster chain.

The partition listing prints each valid entry as a bounded range and then reports unused ranges on the medium:

```text
Partitions:
	#1 boot=no  type=0x0C FAT32 LBA first_lba=16384 last_lba=1064959 sectors=1048576 size=512 MiB ok (ok)
	#2 boot=no  type=0x83 Linux first_lba=1064960 last_lba=62333951 sectors=61268992 size=29.21 GiB ok (ok)
Unused ranges:
	unused first_lba=1 last_lba=16383 sectors=16383 size=7 MiB
	media size unknown; end after last partition not shown
```

## UART Commands

```text
help
tasks
led on|off|toggle
disk
disks
partitions
dir
cat [path]
disk partitions
disk dir
disk cat [path]
disk linux
reset
debug
```

`debug` prints low-level commands for register reads and writes, raw SD commands, direct block reads, cached MBR dumps and task kill/restart controls:

```text
rr <addr>
wr <addr> <value>
dump <addr> <n>
sd
prep
init
cmd0
cmd <cmdtm> <arg>
readblk <lba> [n]
blk <sz> <n>
data <n>
disk mbr
disk dump0
kill <task>
k <task>
run <task>
r <task>
```

`kill <task>` removes a non-idle task from scheduling; `run <task>` rebuilds its initial stack frame and schedules it again. `k` and `r` are short aliases for these task-control commands. `disk dump0` only prints the cached sector 0 after a successful partition scan.

## Read-Only Rule

This stage does not provide any write command or write-LBA function. The Raspberry Pi disk module is read-only by construction; write, format, partition creation, FAT modification and MBR/GPT modification are intentionally absent.

## Current Hardware Boundary

The system environment, console, scheduler, heartbeat, MBR parser, test disk image and parser tests are implemented. The Raspberry Pi eMMC2 module is isolated behind `disk_hw_init()`, `disk_hw_read_lba()` and `disk_hw_get_info()`. The read command sequence is intentionally kept small and read-only; if hardware initialization or LBA 0 reading fails, the console reports that failure without writing to the card.

## Expected Boot Shape

```text
Alquist boot
UART ready
Timer ready
Scheduler ready
Heartbeat task started
Disk program ready
...
console ready
>
```

## Storage stage 2

- keep disk operations in the disk task, requested by the shell instead of running on the console task,
- read FAT32 boot sector from the first valid FAT32 partition,
- decode FAT32 parameters and locate FAT/data regions,
- list root directory entries using long names when present and 8.3 names as fallback.

## Storage stage 3

- follow FAT32 cluster chains for regular files,
- add a bounded read-file command for small files,
- resolve one-level and nested subdirectory paths component by component,
- use FAT32 file loading in later kernel tutorial demos,
- keep the implementation read-only until the write path, journal discipline and diagnostics are designed.
