Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 98 additions & 3 deletions drivers/block/ata/sata/satapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <drivers/block/ata/sata/ahci.h>
#include <drivers/block/ata/sata/satapi.h>
#include <kernel/debug/printk.h>
#include <kernel/timer/timer.h>
#include <libs/std/errno.h>
#include <libs/std/string.h>
#include <libs/util/byteorder.h>
Expand All @@ -26,6 +27,25 @@
ahci_satapi_device_t ahci_satapi_devices[AHCI_MAX_DEVICES];
int ahci_satapi_device_count = 0;

/* Restart the port engine to release a failed command's PxCI bit. */
static void satapi_port_recover(ahci_port_state_t *port)
{
volatile uint8_t *p = port->port_mmio;
int tout;

ahci_write32(p, PORT_CMD, ahci_read32(p, PORT_CMD) & ~PORT_CMD_ST);

tout = 500000;
while (ahci_read32(p, PORT_CMD) & PORT_CMD_CR)
if (--tout <= 0) return;

ahci_write32(p, PORT_CMD, ahci_read32(p, PORT_CMD) | PORT_CMD_ST);

tout = 500000;
while (!(ahci_read32(p, PORT_CMD) & PORT_CMD_CR))
if (--tout <= 0) return;
}

/* Issue an ATAPI packet command via AHCI */
static int satapi_issue_packet(ahci_port_state_t *port, int slot, const uint8_t *cdb, uint16_t cdb_len, uint8_t direction, int is_dma, uint64_t buf_phys, uint32_t byte_count)
{
Expand Down Expand Up @@ -73,7 +93,7 @@ static int satapi_issue_packet(ahci_port_state_t *port, int slot, const uint8_t
hdr->ctbau = (uint32_t)(port->ct_phys >> 32);

tout = 1000000;
while (ahci_read32(p, PORT_TFDATA) & 0x88)
while (ahci_read32(p, PORT_TFDATA) & 0x80)
if (--tout <= 0) return -ETIMEDOUT;

ahci_write32(p, PORT_IRQ_STAT, 0xFFFFFFFF);
Expand All @@ -84,12 +104,19 @@ static int satapi_issue_packet(ahci_port_state_t *port, int slot, const uint8_t
if (!(ahci_read32(p, PORT_CI) & (uint32_t)(1 << slot))) break;
if (ahci_read32(p, PORT_IRQ_STAT) & PORT_IRQ_TF_ERR) {
ahci_write32(p, PORT_IRQ_STAT, PORT_IRQ_TF_ERR);
satapi_port_recover(port);
return -EIO;
}
if (--tout <= 0) return -ETIMEDOUT;
if (--tout <= 0) {
satapi_port_recover(port);
return -ETIMEDOUT;
}
}

if (ahci_read32(p, PORT_TFDATA) & 0x01) return -EIO;
if (ahci_read32(p, PORT_TFDATA) & 0x01) {
satapi_port_recover(port);
return -EIO;
}
if (byte_count && hdr->prdbc != byte_count) return -EIO;
return 0;
}
Expand Down Expand Up @@ -241,6 +268,74 @@ uint8_t ahci_satapi_read_capacity(uint8_t drive, uint32_t *lba_size, uint32_t *b
return 0;
}

/* Fetch sense data after a failed packet command. */
static int satapi_request_sense(uint8_t drive, uint8_t *sense_key, uint8_t *asc)
{
uint8_t cdb[SATAPI_CDB_LEN] = {GPCMD_REQUEST_SENSE, 0, 0, 0, SCSI_SENSE_BUFFER_SIZE, 0};
uint8_t sense[SCSI_SENSE_BUFFER_SIZE];
size_t len = sizeof(sense);

memset(sense, 0, sizeof(sense));
if (ahci_satapi_send_packet(drive, cdb, SCSI_SENSE_BUFFER_SIZE, SATAPI_PROT_PIO, sense, &len)) return -EIO;

*sense_key = sense[2] & 0x0F;
*asc = sense[12];
return 0;
}

/* Refresh capacity after a possible disc change. */
int ahci_satapi_revalidate(uint8_t drive, uint32_t *lba_size, uint32_t *blk_size)
{
uint32_t lba = 0;
uint32_t blk = 0;

if (drive >= AHCI_MAX_DEVICES || !ahci_satapi_devices[drive].reserved) return -ENODEV;
if (!lba_size || !blk_size) return -EINVAL;

for (int attempt = 0; attempt < 20; attempt++) {
uint8_t key = 0;
uint8_t asc = 0;
uint8_t retryable;

if (ahci_satapi_read_capacity(drive, &lba, &blk) == 0) {
if (lba == 0 || blk == 0 || blk > 4096) {
ahci_satapi_devices[drive].lba_size = 0;
ahci_devices[drive].size = 0;
return -ENOMEDIUM;
}
ahci_satapi_devices[drive].lba_size = lba;
ahci_satapi_devices[drive].blk_size = blk;
ahci_devices[drive].size = lba;
ahci_devices[drive].sector_size = blk;
*lba_size = lba;
*blk_size = blk;
return EOK;
}
lba = 0;
blk = 0;

/*
* A new disc reports unit attention or becoming ready; 0x3A also means empty,
* so it gets only two quick retries; REQUEST SENSE clears the unit attention.
*/
retryable = 0;
if (satapi_request_sense(drive, &key, &asc) == 0) {
if ((key == 6 && asc == 0x28) || (key == 2 && (asc == 0x04 || asc == 0x11))) {
retryable = 2;
} else if (key == 2 && asc == 0x3A && attempt < 2) {
retryable = 1;
}
}
if (!retryable) break;
if (retryable == 2) msleep(50);
}

ahci_satapi_devices[drive].lba_size = 0;
ahci_devices[drive].size = 0;

return -ENOMEDIUM;
}

/* Send INQUIRY, writing the vendor and product identification into model */
uint8_t ahci_satapi_inquiry(uint8_t drive, char *model, size_t model_size)
{
Expand Down
72 changes: 71 additions & 1 deletion drivers/block/core/blockdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,20 @@ static void blk_empty_reference(const struct blockdev_device *dev)
(void)dev;
}

/* Block I/O empty revalidate. */
static int blk_empty_revalidate(struct blockdev_device *dev)
{
(void)dev;
return EOK;
}

static struct blockdev_ops blk_empty_ops = {
.read_sectors = blk_empty_read,
.write_sectors = blk_empty_write,
.flush = blk_empty_flush,
.retain = blk_empty_reference,
.release = blk_empty_reference,
.revalidate = blk_empty_revalidate,
};

/* NVMe backend ops (forwarders to nvme.c) */
Expand Down Expand Up @@ -157,10 +165,30 @@ static int blk_ide_flush(const blockdev_device_t *dev)
return ide_flush_cache(dev->drive) == 0 ? EOK : -EIO;
}

/* Refresh PATA optical geometry after a media change. */
static int blk_ide_revalidate(blockdev_device_t *dev)
{
uint32_t lba = 0, blk = 0;
int status;

if (!dev) return -EINVAL;
if (!dev->optical) return EOK;

status = atapi_revalidate(dev->drive, &lba, &blk);
if (status == EOK) {
dev->sector_count = lba;
dev->sector_size = blk;
} else {
dev->sector_count = 0;
}
return status;
}

static struct blockdev_ops blk_ide_ops = {
.read_sectors = blk_ide_read_sectors,
.write_sectors = blk_ide_write_sectors,
.flush = blk_ide_flush,
.revalidate = blk_ide_revalidate,
};

static int blk_ide_type_id = -1;
Expand Down Expand Up @@ -234,8 +262,28 @@ static int blk_ahci_atapi_read_sectors(const blockdev_device_t *dev, uint64_t lb
return EOK;
}

/* Refresh SATA optical geometry after a media change. */
static int blk_ahci_atapi_revalidate(blockdev_device_t *dev)
{
uint32_t lba = 0, blk = 0;
int status;

if (!dev) return -EINVAL;

status = ahci_satapi_revalidate(dev->drive, &lba, &blk);
if (status == EOK) {
dev->sector_count = lba;
dev->sector_size = blk;
} else {
dev->sector_count = 0;
}

return status;
}

static struct blockdev_ops blk_ahci_atapi_ops = {
.read_sectors = blk_ahci_atapi_read_sectors,
.revalidate = blk_ahci_atapi_revalidate,
};

static int blk_ahci_atapi_type_id = -1;
Expand Down Expand Up @@ -333,10 +381,16 @@ int blockdev_open_nvme(void *ns, blockdev_device_t *device)
int blockdev_open_atapi(uint8_t drive, blockdev_device_t *device)
{
#if CONFIG_ATA
uint32_t lba = 0;
uint32_t blk = 0;

if (!device) return -EINVAL;
if (drive > 3 || !atapi_devices[drive].reserved) return -ENODEV;
if (atapi_devices[drive].type != IDE_ATAPI) return -EINVAL;

/* Pick up a disc inserted after the boot probe. */
(void)atapi_revalidate(drive, &lba, &blk);

device->ops_id = (uint8_t)blk_ide_type();
device->backend_data = NULL;
device->drive = drive;
Expand Down Expand Up @@ -381,10 +435,16 @@ int blockdev_open_ahci(uint8_t drive, blockdev_device_t *device)
int blockdev_open_ahci_atapi(uint8_t drive, blockdev_device_t *device)
{
#if CONFIG_ATA
uint32_t lba = 0;
uint32_t blk = 0;

if (!device) return -EINVAL;
if (drive >= AHCI_MAX_DEVICES || !ahci_devices[drive].reserved) return -ENODEV;
if (ahci_devices[drive].type != AHCI_DEV_SATAPI) return -EINVAL;

/* Pick up a disc inserted after the boot probe. */
(void)ahci_satapi_revalidate(drive, &lba, &blk);

device->ops_id = (uint8_t)blk_ahci_atapi_type();
device->backend_data = NULL;
device->drive = drive;
Expand Down Expand Up @@ -706,6 +766,7 @@ int blockdev_read_sectors(const blockdev_device_t *device, uint64_t lba, uint32_
{
if (!device) return -EINVAL;
if (!count) return EOK;
if (device->optical && device->sector_count == 0) return -ENOMEDIUM;
if (!buffer) return -EINVAL;
if (lba >= device->sector_count || count > device->sector_count - lba) return -EINVAL;

Expand All @@ -718,6 +779,7 @@ int blockdev_write_sectors(const blockdev_device_t *device, uint64_t lba, uint32
if (!device) return -EINVAL;
if (!count) return EOK;
if (device->read_only) return -EROFS;
if (device->optical && device->sector_count == 0) return -ENOMEDIUM;
if (!buffer) return -EINVAL;
if (lba >= device->sector_count || count > device->sector_count - lba) return -EINVAL;

Expand All @@ -731,6 +793,13 @@ int blockdev_flush(const blockdev_device_t *device)
return blk_ops(device, flush)(device);
}

/* Refresh removable-media geometry through the backend. */
int blockdev_revalidate(blockdev_device_t *device)
{
if (!device) return -EINVAL;
return blk_ops(device, revalidate)(device);
}

/* True when the descriptor's sector geometry can address storage. */
bool blockdev_geometry_valid(const blockdev_device_t *device)
{
Expand Down Expand Up @@ -761,6 +830,7 @@ int blockdev_read_bytes(const blockdev_device_t *device, uint64_t offset, void *
if (!device) return -EINVAL;
if (!size) return EOK;
if (!buffer || !blockdev_geometry_valid(device)) return -EINVAL;
if (device->optical && device->sector_count == 0) return -ENOMEDIUM;
if (size > (size_t)128 * 1024 * 1024) return -EINVAL;
device_bytes = device->sector_count * device->sector_size;

Expand All @@ -786,7 +856,7 @@ int blockdev_read_bytes(const blockdev_device_t *device, uint64_t offset, void *
static DEFINE_RATELIMIT_STATE(ratelimit, PRINTK_RATELIMIT_TICKS, PRINTK_RATELIMIT_BURST);
if (ratelimit_allow(&ratelimit)) plogk("blockdev: Read failed at LBA %llu count %u (offset %llu size %zu): %d\n", start_sector, sector_count, offset, size, status);
free(scratch);
return -EIO;
return status == -ENOMEDIUM ? -ENOMEDIUM : -EIO;
}

memcpy(buffer, scratch + sector_offset, size);
Expand Down
34 changes: 34 additions & 0 deletions fs/devtmpfs/devtmpfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,33 @@ static size_t devtmpfs_block_write(void *context, const void *buffer, size_t off
return blockdev_write_bytes(device, offset, buffer, size) == EOK ? size : 0;
}

/* Re-probe removable media and refresh the node geometry. */
static int devtmpfs_block_refresh(vfs_node_t node)
{
tmpfs_file_t *handle;
blockdev_device_t *device;
int status = EOK;

if (!node || !(handle = node->handle) || !handle->device.ctx) return -ENODEV;
device = handle->device.ctx;
if (device->optical) status = blockdev_revalidate(device);

node->blksz = device->sector_size;
node->size = device->sector_count * device->sector_size;

return status;
}

/* tmpfs device callback: refresh removable media on open. */
static int devtmpfs_block_open(vfs_node_t node, uint64_t flags, void **private_data)
{
(void)flags;
if (private_data) *private_data = NULL;

(void)devtmpfs_block_refresh(node);
return EOK;
}

/* Geometry queries for whole disks and partition nodes. */
static int devtmpfs_block_ioctl(void *context, size_t request, void *argument)
{
Expand Down Expand Up @@ -258,6 +285,7 @@ static int devtmpfs_block_ops_init(tmpfs_device_ops_t *ops, const blockdev_devic
ops->read = devtmpfs_block_read;
ops->write = devtmpfs_block_write;
ops->ioctl = devtmpfs_block_ioctl;
ops->open = devtmpfs_block_open;
ops->destroy = devtmpfs_block_destroy;
ops->ctx = context;
return EOK;
Expand Down Expand Up @@ -291,6 +319,12 @@ int devtmpfs_open_block_device(const char *path, blockdev_device_t *device)
return -ENODEV;
}

int status = devtmpfs_block_refresh(node);
if (status != EOK) {
vfs_close(node);
return status;
}

*device = *(blockdev_device_t *)handle->device.ctx;
blockdev_retain(device);
vfs_close(node);
Expand Down
3 changes: 3 additions & 0 deletions include/drivers/block/ata/sata/satapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ uint8_t ahci_satapi_test_unit_ready(uint8_t drive);
/* Send READ CAPACITY, returning the LBA count and block size */
uint8_t ahci_satapi_read_capacity(uint8_t drive, uint32_t *lba_size, uint32_t *blk_size);

/* Refresh capacity after a possible disc change */
int ahci_satapi_revalidate(uint8_t drive, uint32_t *lba_size, uint32_t *blk_size);

/* Send INQUIRY, returning the vendor and product identification in model */
uint8_t ahci_satapi_inquiry(uint8_t drive, char *model, size_t model_size);

Expand Down
4 changes: 4 additions & 0 deletions include/drivers/block/core/blockdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ typedef struct blockdev_ops {
int (*flush)(const struct blockdev_device *dev);
void (*retain)(const struct blockdev_device *dev);
void (*release)(const struct blockdev_device *dev);
int (*revalidate)(struct blockdev_device *dev);
} *blockdev_ops_t;

typedef struct blockdev_device {
Expand Down Expand Up @@ -107,6 +108,9 @@ int blockdev_write_sectors(const blockdev_device_t *device, uint64_t lba, uint32
/* Commit volatile device write caches, if the backend provides one. */
int blockdev_flush(const blockdev_device_t *device);

/* Refresh removable-media geometry; fixed disks pass through unchanged. */
int blockdev_revalidate(blockdev_device_t *device);

/* True when the descriptor's sector geometry can address storage. */
bool blockdev_geometry_valid(const blockdev_device_t *device);

Expand Down
Loading