kernel/ahci: support CD/DVD drives (ATAPI) #694

Merged
Leency merged 3 commits from kernel-ahci-atapi into main 2026-09-29 15:44:07 +00:00
Contributor

Summary: an optical drive on an AHCI port was detected and then ignored,
so CD/DVD only worked with the controller switched to IDE mode. It now
shows up as /srN and ISO9660 discs are readable, including disc changes.

Details:

  • ahci_atapi_cmd sends a SCSI command through the ATA PACKET command
    (A bit in the command header, CDB in acmd, DMA when IDENTIFY PACKET
    word 49 allows it, DMADIR when word 62 requires it). Built on top of it:
    TEST UNIT READY, REQUEST SENSE, READ CAPACITY(10), READ(10), READ TOC
    (start of the last session for multisession discs) and START STOP UNIT
    for DISKFUNC.LoadTray.
  • Drives are registered as sr0, sr1, ... with DISK_NO_INSERT_NOTIFICATION,
    2048-byte sectors and read-only media; iso9660 mounts them as /srN/1.
    They cannot be named cd*: fs_lfn.inc sends every path starting with "cd"
    to the old ATAPI-over-IDE code.
  • Disc change: the kernel lets go of a mounted medium only when querymedia
    fails, so after a UNIT ATTENTION querymedia fails once and the kernel
    immediately mounts the new disc. A READ(10) that hits UNIT ATTENTION
    fails instead of silently reading the new disc.
  • One ATAPI transfer is capped at (PRDT_MAX_ENTRIES - 1) * 4096 bytes, so
    the PRDT always covers the whole request. The PRDT builder moved out of
    ahci_rw_sectors into ahci_build_prdt and is shared by both paths.
  • A command the drive refuses (an empty tray does that on every TEST UNIT
    READY) restarts the port quietly instead of running the full recovery.
  • Optical ports never busy-poll a long command and never switch the whole
    controller to polling: missing interrupts are counted per port and only
    that port's PxIE is turned off.
  • Diagnostic output (command statistics, interrupt wakeups, register dumps
    during init) is now behind AHCI_DBGLVL, off by default. Errors and one
    line per detected device are still printed.

Tested: QEMU, VirtualBox and VMware (detection, listings and reads checked
against host checksums; eject/insert in QEMU and VirtualBox, a direct disc
swap in VirtualBox, an empty drive in VMware), SATA disk read/write on the
same controller in QEMU, and a GA-D525TUD (NM10) with an ASUS DRW-24B3ST:
detection, reading, disc change. The debug level change was build-tested
only.

Assisted-by: Claude Opus 5

Summary: an optical drive on an AHCI port was detected and then ignored, so CD/DVD only worked with the controller switched to IDE mode. It now shows up as /srN and ISO9660 discs are readable, including disc changes. Details: - ahci_atapi_cmd sends a SCSI command through the ATA PACKET command (A bit in the command header, CDB in acmd, DMA when IDENTIFY PACKET word 49 allows it, DMADIR when word 62 requires it). Built on top of it: TEST UNIT READY, REQUEST SENSE, READ CAPACITY(10), READ(10), READ TOC (start of the last session for multisession discs) and START STOP UNIT for DISKFUNC.LoadTray. - Drives are registered as sr0, sr1, ... with DISK_NO_INSERT_NOTIFICATION, 2048-byte sectors and read-only media; iso9660 mounts them as /srN/1. They cannot be named cd*: fs_lfn.inc sends every path starting with "cd" to the old ATAPI-over-IDE code. - Disc change: the kernel lets go of a mounted medium only when querymedia fails, so after a UNIT ATTENTION querymedia fails once and the kernel immediately mounts the new disc. A READ(10) that hits UNIT ATTENTION fails instead of silently reading the new disc. - One ATAPI transfer is capped at (PRDT_MAX_ENTRIES - 1) * 4096 bytes, so the PRDT always covers the whole request. The PRDT builder moved out of ahci_rw_sectors into ahci_build_prdt and is shared by both paths. - A command the drive refuses (an empty tray does that on every TEST UNIT READY) restarts the port quietly instead of running the full recovery. - Optical ports never busy-poll a long command and never switch the whole controller to polling: missing interrupts are counted per port and only that port's PxIE is turned off. - Diagnostic output (command statistics, interrupt wakeups, register dumps during init) is now behind AHCI_DBGLVL, off by default. Errors and one line per detected device are still printed. Tested: QEMU, VirtualBox and VMware (detection, listings and reads checked against host checksums; eject/insert in QEMU and VirtualBox, a direct disc swap in VirtualBox, an empty drive in VMware), SATA disk read/write on the same controller in QEMU, and a GA-D525TUD (NM10) with an ASUS DRW-24B3ST: detection, reading, disc change. The debug level change was build-tested only. Assisted-by: Claude Opus 5
CODEOWNERS rules requested review from system 2026-09-14 21:46:13 +00:00
Leency force-pushed kernel-ahci-atapi from 9a651cffcf to 50770e2459 2026-09-15 01:17:57 +00:00 Compare
Burer added a new dependency 2026-09-18 10:49:06 +00:00
Burer approved these changes 2026-09-18 10:52:23 +00:00
Burer left a comment
Owner

Three small things, none of them blocking.

1. tmp_buf leaks when disk_add fails. ahci_atapi_parse_identify allocates the 4 KiB scratch page into PORT_DATA.tmp_buf during identify, but the .disk_add_fail path in ahci_init returns without freeing it. One page, once, at boot, and only under memory pressure - but it is the only allocation in the new code without a counterpart.

2. ahci_atapi_closemedia does not take the port mutex. The other three callbacks (read, querymedia, loadtray) all do. It is safe as things stand, since the kernel only closes a medium when nobody holds it, but the odd one out invites the question every time someone reads the file. Two lines to make it uniform.

3. Stray blank line - there are two before jmp .after_add_disk at the end of .add_optical_drive.

Three small things, none of them blocking. **1. `tmp_buf` leaks when `disk_add` fails.** `ahci_atapi_parse_identify` allocates the 4 KiB scratch page into `PORT_DATA.tmp_buf` during identify, but the `.disk_add_fail` path in `ahci_init` returns without freeing it. One page, once, at boot, and only under memory pressure - but it is the only allocation in the new code without a counterpart. **2. `ahci_atapi_closemedia` does not take the port mutex.** The other three callbacks (`read`, `querymedia`, `loadtray`) all do. It is safe as things stand, since the kernel only closes a medium when nobody holds it, but the odd one out invites the question every time someone reads the file. Two lines to make it uniform. **3. Stray blank line** - there are two before `jmp .after_add_disk` at the end of `.add_optical_drive`.
Leency force-pushed kernel-ahci-atapi from ec71bd204d to b56f9a967f 2026-09-28 11:25:09 +00:00 Compare
dunkaist approved these changes 2026-09-29 15:28:37 +00:00
Burer added 3 commits 2026-09-29 15:35:25 +00:00
Summary: an optical drive on an AHCI port was detected and then ignored,
so CD/DVD only worked with the controller switched to IDE mode. It now
shows up as /srN and ISO9660 discs are readable, including disc changes.

Подробно:
- ahci_atapi_cmd sends a SCSI command through the ATA PACKET command
  (A bit in the command header, CDB in acmd, DMA when IDENTIFY PACKET
  word 49 allows it, DMADIR when word 62 requires it). Built on top of it:
  TEST UNIT READY, REQUEST SENSE, READ CAPACITY(10), READ(10), READ TOC
  (start of the last session for multisession discs) and START STOP UNIT
  for DISKFUNC.LoadTray.
- Drives are registered as sr0, sr1, ... with DISK_NO_INSERT_NOTIFICATION,
  2048-byte sectors and read-only media; iso9660 mounts them as /srN/1.
  They cannot be named cd*: fs_lfn.inc sends every path starting with "cd"
  to the old ATAPI-over-IDE code.
- Disc change: the kernel lets go of a mounted medium only when querymedia
  fails, so after a UNIT ATTENTION querymedia fails once and the kernel
  immediately mounts the new disc. A READ(10) that hits UNIT ATTENTION
  fails instead of silently reading the new disc.
- One ATAPI transfer is capped at (PRDT_MAX_ENTRIES - 1) * 4096 bytes, so
  the PRDT always covers the whole request. The PRDT builder moved out of
  ahci_rw_sectors into ahci_build_prdt and is shared by both paths.
- A command the drive refuses (an empty tray does that on every TEST UNIT
  READY) restarts the port quietly instead of running the full recovery.
- Optical ports never busy-poll a long command and never switch the whole
  controller to polling: missing interrupts are counted per port and only
  that port's PxIE is turned off.
- Diagnostic output (command statistics, interrupt wakeups, register dumps
  during init) is now behind AHCI_DBGLVL, off by default. Errors and one
  line per detected device are still printed.

Tested: QEMU, VirtualBox and VMware (detection, listings and reads checked
against host checksums; eject/insert in QEMU and VirtualBox, a direct disc
swap in VirtualBox, an empty drive in VMware), SATA disk read/write on the
same controller in QEMU, and a GA-D525TUD (NM10) with an ASUS DRW-24B3ST:
detection, reading, disc change. The debug level change was build-tested
only.

Not touched here: disk_scan_partitions gets its buffer from kernel_alloc
for sector sizes other than 512 but releases it with free.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
- Return the ATAPI scratch page (PORT_DATA.tmp_buf) when disk_add fails
  for an optical drive; it was the only allocation in the new code
  without a counterpart.
- ahci_atapi_closemedia takes the port mutex like the read, querymedia
  and loadtray callbacks. The kernel calls it from disk_media_dereference
  after querymedia has returned, so the mutex is never held by the caller.
- Drop a stray blank line in .add_optical_drive.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
kernel/ahci: report a missing controller only when there is none
Check kernel codestyle / Check kernel codestyle (pull_request) Successful in 24s
Test PR / Build (en_US) (pull_request) Successful in 2m27s
Test PR / Build (ru_RU) (pull_request) Successful in 2m32s
Test PR / Build (es_ES) (pull_request) Successful in 2m41s
df611c3d9b
The controller scan does not stop at the first AHCI controller: it walks
the whole PCI device list to pick up to eight of them, and it always ends
at .ahci_ctr_not_found. So "K: AHCI controller not found" was printed on
every boot, right after the ports of the controller that had just been
set up. Print it only when the scan found no controller at all.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
Burer force-pushed kernel-ahci-atapi from b56f9a967f to df611c3d9b 2026-09-29 15:35:25 +00:00 Compare
CODEOWNERS rules requested review from system 2026-09-29 15:35:25 +00:00
Leency merged commit 95bcf78537 into main 2026-09-29 15:44:07 +00:00
Leency deleted branch kernel-ahci-atapi 2026-09-29 15:44:07 +00:00
Sign in to join this conversation.
No Reviewers
KolibriOS/system
No labels
3 Participants
Notifications
Due Date
No due date set.
Reference: KolibriOS/kolibrios#694