Skip to content
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
build/
*.bin
*.elf

.vscode/
85 changes: 59 additions & 26 deletions source/vdev/sdcard.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
#include "hw/sdmmc.h"
#include "virt/manager.h"

#define VIRTIO_BLK_F_RO BIT(5)
#define VIRTIO_BLK_F_SIZE_MAX BIT(1)
#define VIRTIO_BLK_F_SEG_MAX BIT(2)
#define VIRTIO_BLK_F_READONLY BIT(5)
#define VIRTIO_BLK_F_BLK_SIZE BIT(6)

#define VIRTIO_BLK_TYPE_IN 0
#define VIRTIO_BLK_TYPE_OUT 1

typedef struct {
u64 capacity;
Expand Down Expand Up @@ -37,8 +43,8 @@ typedef struct {
} PACKED blk_config;

typedef struct {
u32 resv;
u32 type;
u32 resv;
u64 sector_offset;
} PACKED vblk_t;

Expand All @@ -50,7 +56,9 @@ static void sdmc_hard_reset(vdev_s *vdev) {
data[i] = 0;
sdmmc_sdcard_init();
sdmc_blk_config.capacity = sdmmc_sdcard_size();
sdmc_blk_config.blk_size = 512;
sdmc_blk_config.blk_size = 512u;
sdmc_blk_config.size_max = 512u * ((1u << 16) - 1); // limited by hardware
sdmc_blk_config.seg_max = 16u; // picked by a fair dice roll
}

static u8 sdmc_cfg_read(vdev_s *vdev, uint offset) {
Expand All @@ -59,39 +67,64 @@ static u8 sdmc_cfg_read(vdev_s *vdev, uint offset) {
return 0xFF;
}

static void sdmc_txrx(vqueue_s *vq, const vblk_t *blk, vjob_s *vj) {
int err = 0;
u32 type = blk->type, offset = blk->sector_offset;

while(vqueue_fetch_job_next(vq, vj) >= 0) {
vdesc_s desc;
vqueue_get_job_desc(vq, vj, &desc);

u8 *data = desc.data;
u32 len = desc.length;

if ((len % 512) == 0) {
u32 nsect = len / 512;

if (type == VIRTIO_BLK_TYPE_IN) {
// SD -> memory
err |= sdmmc_sdcard_readsectors(offset, nsect, data) ? 1 : 0;
vjob_add_written(vj, len);
} else if (type == VIRTIO_BLK_TYPE_OUT) {
// memory -> SD
err |= sdmmc_sdcard_writesectors(offset, nsect, data) ? 1 : 0;
} else {
__builtin_trap();
__builtin_unreachable();
}

data += len;
offset += nsect;
} else if ((len % 512) == 1) {
// assume single status byte
*data = err;
} else {
__builtin_trap();
__builtin_unreachable();
}
}
}

static void sdmc_process_vqueue(vdev_s *vdev, vqueue_s *vq) {
vjob_s vjob;

while(vqueue_fetch_job_new(vq, &vjob) >= 0) {
do {
u32 *data;
vdesc_s desc;
vqueue_get_job_desc(vq, &vjob, &desc);

if (desc.dir == HOST_TO_VDEV) {
const vblk_t *blk = (const vblk_t*)desc.data;
OBJ_SETPRIV(vq, (u32)blk->sector_offset);
} else {
u8 *data = desc.data;
if (desc.length < 512) {
*data = 0;
} else {
u32 sectors = desc.length >> 9;
sdmmc_sdcard_readsectors(OBJ_GETPRIV(vq, u32), sectors, data);
OBJ_SETPRIV(vq, OBJ_GETPRIV(vq, u32) + sectors);
vjob_add_written(&vjob, desc.length);
}
}
} while(vqueue_fetch_job_next(vq, &vjob) >= 0);
vblk_t vblk;
vdesc_s desc;
vqueue_get_job_desc(vq, &vjob, &desc);

vblk = *(volatile vblk_t*)(desc.data);

sdmc_txrx(vq, &vblk, &vjob);

vqueue_push_job(vq, &vjob);
vman_notify_host(vdev, VIRQ_VQUEUE);
}

vman_notify_host(vdev, VIRQ_VQUEUE);
}

DECLARE_VIRTDEV(
vdev_sdcard, NULL,
VDEV_T_BLOCK, VIRTIO_BLK_F_RO, 1,
VDEV_T_BLOCK, VIRTIO_BLK_F_SIZE_MAX | VIRTIO_BLK_F_SEG_MAX | VIRTIO_BLK_F_BLK_SIZE, 1,
sdmc_hard_reset,
sdmc_cfg_read, vdev_cfg_write_stub,
sdmc_process_vqueue
Expand Down