diff --git a/drivers/virtio/virtio-blk/Makefile b/drivers/virtio/virtio-blk/Makefile new file mode 100644 index 00000000..2d725b76 --- /dev/null +++ b/drivers/virtio/virtio-blk/Makefile @@ -0,0 +1,13 @@ + +SRCDIRS := . +INCLUDES := $(ROOTDIR)/include +DEFINES := KERNEL=1 MODULE=1 +LIBS := + +CFLAGS := -include $(ROOTDIR)/config.h +ASFLAGS := -include $(ROOTDIR)/config.h + + +include $(ROOTDIR)/drivers/platform/$(PLATFORM)/config.mk +include $(ROOTDIR)/build/cross.mk +include $(ROOTDIR)/build/build-ko.mk \ No newline at end of file diff --git a/drivers/virtio/virtio-blk/main.c b/drivers/virtio/virtio-blk/main.c new file mode 100644 index 00000000..cc9d2fb3 --- /dev/null +++ b/drivers/virtio/virtio-blk/main.c @@ -0,0 +1,285 @@ +/* + * Author: + * Antonino Natale + * + * Copyright (c) 2013-2019 Antonino Natale + * + * + * This file is part of aplus. + * + * aplus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * aplus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with aplus. If not, see . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +MODULE_NAME("virtio/virtio-blk"); +MODULE_DEPS("dev/interface,dev/pci,dev/block,virtio/virtio-pci,virtio/virtio-queue"); +MODULE_AUTHOR("Antonino Natale"); +MODULE_LICENSE("GPL"); + + +static void virtblk_init(device_t* device); +static void virtblk_dnit(device_t* device); +static void virtblk_reset(device_t* device); + + +static void virtblk_init(device_t* device) { + + DEBUG_ASSERT(device); + DEBUG_ASSERT(device->userdata); + + virtblk_reset(device); +} + + +static void virtblk_dnit(device_t* device) { + + DEBUG_ASSERT(device); + DEBUG_ASSERT(device->userdata); +} + + +static void virtblk_reset(device_t* device) { + + DEBUG_ASSERT(device); + DEBUG_ASSERT(device->userdata); + + virtio_driver_t* driver = (virtio_driver_t*)device->userdata; + virtio_blk_config_t* config = (virtio_blk_config_t*)&driver->internals.device_config; + + device->blk.blksize = MAX(config->blk_size, MAX(config->geometry.blk_size, 512)); + device->blk.blkcount = config->capacity; + device->blk.blkmax = 1; // config->seg_max; + + kprintf("virtio-blk: %s %s %d %d %d\n", device->name, device->description, device->blk.blksize, device->blk.blkcount, device->blk.blkmax); +} + + +static ssize_t virtblk_read(device_t* device, void* buf, off_t offset, size_t count) { + + DEBUG_ASSERT(device); + DEBUG_ASSERT(device->userdata); + + virtio_driver_t* driver = (virtio_driver_t*)device->userdata; + + + for (size_t sector = 0; sector < count; sector++) { + + virtio_blk_request_t request; + + request.hdr.type = VIRTIO_BLK_T_IN; + request.hdr.sector = offset + sector; + + if (virtq_sendrecv(driver, 0, &request, sizeof(request.hdr), &request.data, device->blk.blksize + sizeof(uint8_t)) < 0) { + return errno = EIO, -1; + } + + + uint8_t status = request.data[device->blk.blksize]; + + switch (status) { + case VIRTIO_BLK_S_OK: + break; + case VIRTIO_BLK_S_IOERR: + return errno = EIO, -1; + case VIRTIO_BLK_S_UNSUPP: + return errno = ENOSYS, -1; + default: + kpanicf("virtio-blk: unknown status %d\n", status); + } + + + memcpy(buf, &request.data, device->blk.blksize); + + buf += device->blk.blksize; + } + + return count; +} + +static ssize_t virtblk_write(device_t* device, const void* buf, off_t offset, size_t count) { + + DEBUG_ASSERT(device); + DEBUG_ASSERT(device->userdata); + + virtio_driver_t* driver = (virtio_driver_t*)device->userdata; + + + for (size_t sector = 0; sector < count; sector++) { + + virtio_blk_request_t request; + + request.hdr.type = VIRTIO_BLK_T_OUT; + request.hdr.sector = offset + sector; + + memcpy(&request.data, buf, device->blk.blksize); + + + uint8_t status = VIRTIO_BLK_S_UNSUPP; + + if (virtq_sendrecv(driver, 0, &request, sizeof(request.hdr) + device->blk.blksize, &status, sizeof(uint8_t)) < 0) { + return errno = EIO, -1; + } + + switch (status) { + case VIRTIO_BLK_S_OK: + break; + case VIRTIO_BLK_S_IOERR: + return errno = EIO, -1; + case VIRTIO_BLK_S_UNSUPP: + return errno = ENOSYS, -1; + default: + kpanicf("virtio-blk: unknown status %d\n", status); + } + + buf += device->blk.blksize; + } + + return count; +} + + + +static int virtblk_dev_init(struct virtio_driver* driver, size_t index) { + + DEBUG_ASSERT(driver); + DEBUG_ASSERT(index < 26); + + + device_t* device = kcalloc(sizeof(device_t), 1, GFP_KERNEL); + + device->type = DEVICE_TYPE_BLOCK; + + strncpy(device->name, "vda", DEVICE_MAXNAMELEN); + strncpy(device->description, "VIRTIO Block Device", DEVICE_MAXDESCLEN); + + device->name[2] = 'a' + index; + device->name[3] = '\0'; + + device->major = 3; + device->minor = 0; + device->status = DEVICE_STATUS_UNKNOWN; + device->init = &virtblk_init; + device->dnit = &virtblk_dnit; + device->reset = &virtblk_reset; + + device->blk.write = &virtblk_write; + device->blk.read = &virtblk_read; + + device->userdata = (void*)driver; + + device_mkdev(device, 0660); + + return 0; +} + +static int negotiate_features(struct virtio_driver* driver, uint32_t* features, size_t index) { + + if (index == 0) { + + *features |= VIRTIO_BLK_F_SIZE_MAX; + *features |= VIRTIO_BLK_F_BLK_SIZE; + *features |= VIRTIO_BLK_F_SEG_MAX; + + *features &= ~VIRTIO_BLK_F_MQ; + *features &= ~VIRTIO_BLK_F_FLUSH; + *features &= ~VIRTIO_BLK_F_ZONED; + } + + return 0; +} + +static int setup_config(struct virtio_driver* driver, uintptr_t device_config) { + return 0; +} + +static int interrupt_handler(pcidev_t device, irq_t vector, struct virtio_driver* driver) { + return 0; +} + + +static void pci_find(pcidev_t device, uint16_t vid, uint16_t did, void* arg) { + + if (vid != VIRTIO_PCI_VENDOR) + return; + + if (did != VIRTIO_PCI_DEVICE(VIRTIO_DEVICE_TYPE_BLOCK)) + return; + + + struct virtio_driver* driver = kcalloc(sizeof(struct virtio_driver), 1, GFP_KERNEL); + + driver->type = VIRTIO_DEVICE_TYPE_BLOCK; + driver->device = device; + driver->send_window_size = 4096; + driver->recv_window_size = 4096; + + driver->negotiate = &negotiate_features; + driver->setup = &setup_config; + driver->interrupt = &interrupt_handler; + + + if (virtio_pci_init(driver) < 0) { + +#if DEBUG_LEVEL_ERROR + kprintf("virtio-blk: device %d (%X:%X) initialization failed\n", device, vid, did); +#endif + + return; + } + + + static size_t virtblk_dev_index = 0; + + if (virtblk_dev_init(driver, virtblk_dev_index++) < 0) { + +#if DEBUG_LEVEL_ERROR + kprintf("virtio-blk: device %d (%X:%X) initialization failed\n", device, vid, did); +#endif + + return; + } + + kprintf("virtio-blk: device #%zd %d (%X:%X) initialized\n", virtblk_dev_index, device, vid, did); +} + + +void init(const char* args) { + + if (strstr(core->boot.cmdline, "virtio=off")) + return; + + pci_scan(&pci_find, PCI_TYPE_ALL, NULL); +} + +void dnit(void) { +} diff --git a/drivers/virtio/virtio-queue/main.c b/drivers/virtio/virtio-queue/main.c index 68e91305..515043c9 100644 --- a/drivers/virtio/virtio-queue/main.c +++ b/drivers/virtio/virtio-queue/main.c @@ -165,6 +165,26 @@ int virtq_get_free_descriptor(struct virtio_driver* driver, uint16_t queue) { } +int virtq_free_descriptor(struct virtio_driver* driver, uint16_t queue, uint16_t descriptor) { + + DEBUG_ASSERT(driver); + DEBUG_ASSERT(driver->device); + + DEBUG_ASSERT(queue < driver->internals.num_queues); + DEBUG_ASSERT(descriptor < driver->internals.queues[queue].size); + + + driver->internals.queues[queue].descriptors[descriptor].q_address = 0; + driver->internals.queues[queue].descriptors[descriptor].q_length = 0; + driver->internals.queues[queue].descriptors[descriptor].q_flags = 0; + driver->internals.queues[queue].descriptors[descriptor].q_next = 0; + + atomic_thread_fence(memory_order_acq_rel); + + return 0; +} + + ssize_t virtq_sendrecv(struct virtio_driver* driver, uint16_t queue, void* message, size_t size, void* output, size_t outsize) { @@ -263,6 +283,9 @@ ssize_t virtq_sendrecv(struct virtio_driver* driver, uint16_t queue, void* messa } while (seen != 0xFFFF); + virtq_free_descriptor(driver, queue, inp); + virtq_free_descriptor(driver, queue, out); + #if DEBUG_LEVEL_TRACE kprintf("virtio-queue: device %d has sent and received %ld bytes of data on queue %d\n", driver->device, outsize, queue); #endif diff --git a/include/dev/virtio/virtio-blk.h b/include/dev/virtio/virtio-blk.h new file mode 100644 index 00000000..0cd8c8a8 --- /dev/null +++ b/include/dev/virtio/virtio-blk.h @@ -0,0 +1,148 @@ +/* + * Author: + * Antonino Natale + * + * Copyright (c) 2013-2019 Antonino Natale + * + * + * This file is part of aplus. + * + * aplus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * aplus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with aplus. If not, see . + */ + +#ifndef _DEV_VIRTIO_VIRTIO_BLK_H +#define _DEV_VIRTIO_VIRTIO_BLK_H + +// Feature Bits +#define VIRTIO_BLK_F_SIZE_MAX (1 << 1) +#define VIRTIO_BLK_F_SEG_MAX (1 << 2) +#define VIRTIO_BLK_F_GEOMETRY (1 << 4) +#define VIRTIO_BLK_F_RO (1 << 5) +#define VIRTIO_BLK_F_BLK_SIZE (1 << 6) +#define VIRTIO_BLK_F_FLUSH (1 << 9) +#define VIRTIO_BLK_F_TOPOLOGY (1 << 10) +#define VIRTIO_BLK_F_CONFIG_WCE (1 << 11) +#define VIRTIO_BLK_F_MQ (1 << 12) +#define VIRTIO_BLK_F_DISCARD (1 << 13) +#define VIRTIO_BLK_F_WRITE_ZEROES (1 << 14) +#define VIRTIO_BLK_F_LIFETIME (1 << 15) +#define VIRTIO_BLK_F_SECURE_ERASE (1 << 16) +#define VIRTIO_BLK_F_ZONED (1 << 17) + +// Request Types +#define VIRTIO_BLK_T_IN 0 +#define VIRTIO_BLK_T_OUT 1 +#define VIRTIO_BLK_T_FLUSH 4 +#define VIRTIO_BLK_T_GET_ID 8 +#define VIRTIO_BLK_T_GET_LIFETIME 10 +#define VIRTIO_BLK_T_DISCARD 11 +#define VIRTIO_BLK_T_WRITE_ZEROES 13 +#define VIRTIO_BLK_T_SECURE_ERASE 14 + +// Status +#define VIRTIO_BLK_S_OK 0 +#define VIRTIO_BLK_S_IOERR 1 +#define VIRTIO_BLK_S_UNSUPP 2 + +// Maximum Block Size +#define VIRTIO_BLK_MAX_BLK_SIZE 4096 + + +#ifndef __ASSEMBLY__ + + #include + #include + #include + #include + + +typedef struct virtio_blk_config { + + uint64_t capacity; + uint32_t size_max; + uint32_t seg_max; + + struct { + + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; + uint16_t blk_size; + + } geometry; + + uint32_t blk_size; + + struct { + + uint8_t physical_block_exp; + uint8_t alignment_offset; + uint16_t min_io_size; + uint32_t opt_io_size; + + } topology; + + uint8_t writeback; + uint8_t unused0; + + uint16_t num_queues; + uint32_t max_discard_sectors; + uint32_t max_discard_seg; + uint32_t discard_sector_alignment; + uint32_t max_write_zeroes_sectors; + uint32_t max_write_zeroes_seg; + uint8_t write_zeroes_may_unmap; + uint8_t unused1[3]; + uint32_t max_secure_erase_sectors; + uint32_t max_secure_erase_seg; + uint32_t secure_erase_sector_alignment; + + struct { + + uint8_t num_zones; + uint8_t zone_size_shift; + uint16_t max_persistent_active_zones; + uint8_t zone_write_granularity_shift; + uint8_t zone_write_granularity_alignment; + uint8_t max_zone_append_size; + uint8_t max_append_sectors; + uint8_t unused[2]; + + } zoned; + +} __packed virtio_blk_config_t; + + +typedef struct virtio_blk_request { + + struct { + uint32_t type; + uint32_t reserved; + uint64_t sector; + } __packed hdr; + + uint8_t data[VIRTIO_BLK_MAX_BLK_SIZE]; + // uint8_t status; + +} __packed virtio_blk_request_t; + + +__BEGIN_DECLS + + +__END_DECLS + +#endif + +#endif diff --git a/include/dev/virtio/virtio.h b/include/dev/virtio/virtio.h index be2255f8..6c452245 100644 --- a/include/dev/virtio/virtio.h +++ b/include/dev/virtio/virtio.h @@ -26,11 +26,10 @@ // Device PCI -#define VIRTIO_PCI_VENDOR 0x1AF4 -#define VIRTIO_PCI_DEVICE_MIN 0x1040 -#define VIRTIO_PCI_DEVICE_MAX 0x107F -#define VIRTIO_PCI_DEVICE(d) (VIRTIO_PCI_DEVICE_MIN + d) -#define VIRTIO_PCI_DEVICE_TRANSITIONAL(d) (VIRTIO_PCI_DEVICE_MIN - 0x40 + d) +#define VIRTIO_PCI_VENDOR 0x1AF4 +#define VIRTIO_PCI_DEVICE_MIN 0x1040 +#define VIRTIO_PCI_DEVICE_MAX 0x107F +#define VIRTIO_PCI_DEVICE(d) (VIRTIO_PCI_DEVICE_MIN + d) // Device PCI Capabilities @@ -111,7 +110,7 @@ __BEGIN_DECLS -struct virtio_driver { +typedef struct virtio_driver { uint16_t type; pcidev_t device; @@ -155,7 +154,7 @@ struct virtio_driver { } queues[VIRTQ_MAX_QUEUES]; } internals; -}; +} virtio_driver_t; struct virtio_pci_cap { @@ -247,7 +246,6 @@ int virtio_pci_init(struct virtio_driver*); int virtq_init(struct virtio_driver*, struct virtio_pci_common_cfg volatile*, uint16_t); ssize_t virtq_send(struct virtio_driver*, uint16_t, void*, size_t); ssize_t virtq_sendrecv(struct virtio_driver*, uint16_t, void*, size_t, void*, size_t); -ssize_t virtq_recv(struct virtio_driver*, uint16_t, void*, size_t); void virtq_flush(struct virtio_driver*, uint16_t); __END_DECLS diff --git a/scripts/gen-grubcfg b/scripts/gen-grubcfg index 2a112042..b71850f0 100755 --- a/scripts/gen-grubcfg +++ b/scripts/gen-grubcfg @@ -88,8 +88,8 @@ if [ $LIVE_MODE -eq 1 ]; then entry "aplus-nographics" "root=/dev/ram0 rootfs=iso9660 $DEFAULT_KARGS graphics=off $EXTRA_KARGS" "$EXTRA_MARGS" >> $SYSROOT/boot/grub/grub.cfg entry "aplus-builtin-graphics" "root=/dev/ram0 rootfs=iso9660 $DEFAULT_KARGS graphics=builtin $EXTRA_KARGS" "$EXTRA_MARGS" >> $SYSROOT/boot/grub/grub.cfg else - entry "aplus" "root=/dev/sda2 rootfs=ext2 $DEFAULT_KARGS $EXTRA_KARGS" "$EXTRA_MARGS" >> $SYSROOT/boot/grub/grub.cfg - entry "aplus-nographics" "root=/dev/sda2 rootfs=ext2 $DEFAULT_KARGS graphics=off $EXTRA_KARGS" "$EXTRA_MARGS" >> $SYSROOT/boot/grub/grub.cfg - entry "aplus-builtin-graphics" "root=/dev/sda2 rootfs=ext2 $DEFAULT_KARGS graphics=builtin $EXTRA_KARGS" "$EXTRA_MARGS" >> $SYSROOT/boot/grub/grub.cfg + entry "aplus" "root=/dev/vda2 rootfs=ext2 $DEFAULT_KARGS $EXTRA_KARGS" "$EXTRA_MARGS" >> $SYSROOT/boot/grub/grub.cfg + entry "aplus-nographics" "root=/dev/vda2 rootfs=ext2 $DEFAULT_KARGS graphics=off $EXTRA_KARGS" "$EXTRA_MARGS" >> $SYSROOT/boot/grub/grub.cfg + entry "aplus-builtin-graphics" "root=/dev/vda2 rootfs=ext2 $DEFAULT_KARGS graphics=builtin $EXTRA_KARGS" "$EXTRA_MARGS" >> $SYSROOT/boot/grub/grub.cfg fi diff --git a/scripts/run-qemu b/scripts/run-qemu index 39201e22..a8a1ae92 100755 --- a/scripts/run-qemu +++ b/scripts/run-qemu @@ -142,6 +142,9 @@ x86_64) # Boot flags="$flags -boot order=c" + # Quick Exit + flags="$flags -device isa-debug-exit,iobase=0xf4,iosize=0x04" + ;; *)