From d0c0c78f65fa06a5bec571bbdc415ad3fed151c4 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Tue, 14 Oct 2025 16:32:47 -0400 Subject: kern: ahci: Implement r/w of SATA drives Introduces reading and writing of SATA drives via a bufargs structure to describe the buffer as well as its attributes Signed-off-by: Ian Moffett --- src/sys/io/ic/ahci.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) (limited to 'src/sys/io') diff --git a/src/sys/io/ic/ahci.c b/src/sys/io/ic/ahci.c index 4dc0273..682c983 100644 --- a/src/sys/io/ic/ahci.c +++ b/src/sys/io/ic/ahci.c @@ -56,6 +56,13 @@ #define dtrace(...) __nothing #endif /* AHCI_DEBUG */ +struct bufargs { + void *buf; + size_t nblocks; + off_t lba; + uint8_t write : 1; +}; + /* * Represents the PCI advocation descriptor for this * device so we can advertise ourselves as its driver. @@ -263,6 +270,88 @@ ahci_identify(struct ahci_hba *hba, struct ahci_port *port) vm_free_frame(buf, 1); return 0; } +/* + * Read or write a SATA drive + */ +static int +ahci_rw(struct ahci_hba *hba, struct ahci_port *port, struct bufargs *bufd) +{ + volatile struct hba_port *io; + struct ahci_cmd_hdr *cmdhdr; + struct ahci_cmdtab *cmdtbl; + struct ahci_fis_h2d *fis; + paddr_t buf, cmdbase; + int cmdslot, status; + size_t npgs; + + if (hba == NULL || port == NULL) + return -EINVAL; + if (bufd == NULL) + return -EINVAL; + + if (bufd->buf == NULL) + return -EINVAL; + if (bufd->nblocks == 0) + return -EINVAL; + + io = port->io; + npgs = ALIGN_UP((bufd->nblocks * 512), DEFAULT_PAGESIZE); + npgs /= DEFAULT_PAGESIZE; + + buf = vm_alloc_frame(npgs); + if (buf == 0) { + pr_trace("identify: failed to allocate frame\n"); + return -ENOMEM; + } + + /* Get the command list entry for this slot */ + cmdslot = ahci_alloc_cmdslot(hba, port); + cmdbase = port->cmdlist; + cmdbase += cmdslot * sizeof(*cmdhdr); + + cmdhdr = PHYS_TO_VIRT(cmdbase); + cmdhdr->w = bufd->write; + cmdhdr->cfl = sizeof(struct ahci_fis_h2d) / 4; + cmdhdr->prdtl = 1; + + cmdtbl = PHYS_TO_VIRT(cmdhdr->ctba); + cmdtbl->prdt[0].dba = buf; + cmdtbl->prdt[0].dbc = (bufd->nblocks << 9) - 1; + cmdtbl->prdt[0].i = 0; + + fis = (void *)&cmdtbl->cfis; + fis->command = bufd->write ? ATA_CMD_WRITE_DMA : ATA_CMD_READ_DMA; + fis->c = 1; + fis->type = FIS_TYPE_H2D; + fis->device = (1 << 6); /* LBA */ + + /* Encode the LBA into the FIS */ + fis->lba1 = (bufd->lba >> 8) & 0xFF; + fis->lba2 = (bufd->lba >> 16) & 0xFF; + fis->lba3 = (bufd->lba >> 24) & 0xFF; + fis->lba4 = (bufd->lba >> 32) & 0xFF; + fis->lba5 = (bufd->lba >> 40) & 0xFF; + + /* Encode the block count into the FIS */ + fis->countl = bufd->nblocks & 0xFF; + fis->counth = (bufd->nblocks >> 8) & 0xFF; + + status = ahci_submit_cmd(hba, port, cmdslot); + if (status < 0) { + vm_free_frame(buf, npgs); + return status; + } + + /* Now copy it to the real buffer */ + memcpy( + bufd->buf, + PHYS_TO_VIRT(buf), + bufd->nblocks * 512 + ); + + vm_free_frame(buf, npgs); + return 0; +} /* * Stop a running port, turn off the command list engine, -- cgit v1.2.3