diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/conf/GENERIC | 1 | ||||
-rw-r--r-- | sys/dev/ic/ahci.c | 71 | ||||
-rw-r--r-- | sys/include/sys/disk.h | 81 | ||||
-rw-r--r-- | sys/kern/init_main.c | 4 | ||||
-rw-r--r-- | sys/kern/kern_disk.c | 413 |
5 files changed, 545 insertions, 25 deletions
diff --git a/sys/conf/GENERIC b/sys/conf/GENERIC index 5734c43..a8e4620 100644 --- a/sys/conf/GENERIC +++ b/sys/conf/GENERIC @@ -3,6 +3,7 @@ option PANIC_SCR no // Clear screen on panic // Kernel constants setval SCHED_NQUEUE 4 // Number of scheduler queues (for MLFQ) +setval DISK_MAX 16 // Maximum disks to be registered // Console attributes setval CONSOLE_BG 0x000000 diff --git a/sys/dev/ic/ahci.c b/sys/dev/ic/ahci.c index 7e4a001..a793bc0 100644 --- a/sys/dev/ic/ahci.c +++ b/sys/dev/ic/ahci.c @@ -760,6 +760,53 @@ ahci_dev_bsize(dev_t dev) } /* + * Register a block device connected to an HBA port + * to the rest of the system. + * + * @dp: Device pointer + * @hba: HBA this device belongs to + * + * Returns zero on success, otherwise a less than + * zero value is returned. + */ +static int +ahci_register(struct hba_device *dp, struct ahci_hba *hba) +{ + struct ctlfs_dev dev; + char devname[128]; + int error; + + if (hba->major == 0) { + hba->major = dev_alloc_major(); + } + + dp->dev = dev_alloc(hba->major); + snprintf(devname, sizeof(devname), "sd%d", dp->dev); + + /* Register the device */ + dev_register(hba->major, dp->dev, &ahci_bdevsw); + pr_trace("drive @ /dev/%s\n", devname); + + /* Register a control node */ + dev.mode = 0444; + ctlfs_create_node(devname, &dev); + pr_trace("drive control @ /ctl/%s/\n", devname); + + /* Register control files */ + dev.devname = devname; + dev.ops = &g_sata_bsize_ops; + ctlfs_create_entry("bsize", &dev); + + error = devfs_create_entry(devname, hba->major, dp->dev, 060444); + if (error < 0) { + pr_error("failed to create devfs entry\n"); + return error; + } + + return 0; +} + +/* * Initialize a drive on an HBA port * * @hba: HBA descriptor @@ -768,11 +815,9 @@ ahci_dev_bsize(dev_t dev) static int ahci_init_port(struct ahci_hba *hba, uint32_t portno) { - char devname[128]; struct hba_memspace *abar = hba->io; struct hba_port *port; struct hba_device *dp; - struct ctlfs_dev dev; size_t clen, pagesz; uint32_t lo, hi, sig; paddr_t fra, cmdlist, tmp; @@ -853,27 +898,7 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) } ahci_identify(hba, dp); - - if (hba->major == 0) { - hba->major = dev_alloc_major(); - } - dp->dev = dev_alloc(hba->major); - snprintf(devname, sizeof(devname), "sd%d", dp->dev); - - /* Register the device */ - dev_register(hba->major, dp->dev, &ahci_bdevsw); - pr_trace("drive @ /dev/%s\n", devname); - - /* Register a control node */ - dev.mode = 0444; - ctlfs_create_node(devname, &dev); - pr_trace("drive control @ /ctl/%s/\n", devname); - - /* Register control files */ - dev.devname = devname; - dev.ops = &g_sata_bsize_ops; - ctlfs_create_entry("bsize", &dev); - return devfs_create_entry(devname, hba->major, dp->dev, 060444); + return ahci_register(dp, hba); } /* diff --git a/sys/include/sys/disk.h b/sys/include/sys/disk.h new file mode 100644 index 0000000..09319f5 --- /dev/null +++ b/sys/include/sys/disk.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_DISK_H_ +#define _SYS_DISK_H_ + +#include <sys/queue.h> +#include <sys/device.h> +#include <sys/types.h> +#include <sys/limits.h> + +#define DISK_PRIMARY 0 /* ID of primary disk */ + +/* + * A disk identifier is a zero-based index into + * the disk registry. + */ +typedef uint16_t diskid_t; + +/* + * Block offset / LBA + */ +typedef off_t blkoff_t; + +/* + * Represents a block storage device + * + * @name: Name of disk + * @cookie: Used internally to ensure validity + * @bsize: Block size (defaults to 512 bytes) + * @dev: Device minor + * @id: Disk ID (zero-based index) + * @bdev: Block device operations + * @link: TAILQ link + */ +struct disk { + char name[NAME_MAX]; + uint32_t cookie; + uint16_t bsize; + dev_t dev; + diskid_t id; + const struct bdevsw *bdev; + TAILQ_ENTRY(disk) link; +}; + +void *disk_buf_alloc(diskid_t id, size_t len); +void disk_buf_free(void *p); + +ssize_t disk_read(diskid_t id, blkoff_t blk, void *buf, size_t len); +ssize_t disk_write(diskid_t id, blkoff_t blk, const void *buf, size_t len); + +int disk_add(const char *name, dev_t dev, const struct bdevsw *bdev, int flags); +int disk_get_id(diskid_t id, struct disk **res); + +#endif /* !_SYS_DISK_H_ */ diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 577b7ec..4a0f7a8 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -111,8 +111,6 @@ main(void) /* Expose the console to devfs */ cons_expose(); - uacpi_init(); - /* Start scheduler and bootstrap APs */ md_intoff(); sched_init(); @@ -124,6 +122,8 @@ main(void) spawn(&g_proc0, start_init, NULL, 0, &g_init); md_inton(); + uacpi_init(); + /* Load all early drivers */ DRIVERS_INIT(); diff --git a/sys/kern/kern_disk.c b/sys/kern/kern_disk.c new file mode 100644 index 0000000..1b16e4d --- /dev/null +++ b/sys/kern/kern_disk.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#include <sys/sio.h> +#include <sys/param.h> +#include <sys/panic.h> +#include <sys/spinlock.h> +#include <sys/device.h> +#include <sys/disk.h> +#include <vm/dynalloc.h> +#include <assert.h> +#include <string.h> + +#define pr_trace(fmt, ...) kprintf("disk: " fmt, ##__VA_ARGS__) +#define pr_error(...) pr_trace(__VA_ARGS__) + +#define DEFAULT_BSIZE 512 /* Default block size in bytes */ +#define DISKQ_COOKIE 0xD9EA /* Verification cookie */ + +/* + * The maximum disks supported by the kernel + * is defined by the `DISK_MAX' kconf(9) option. + * + * We define a default of 16 if that option is not + * specified. + */ +#if defined(__DISK_MAX) +#define DISK_MAX __DISK_MAX +#else +#define DISK_MAX 16 /* Maximum disks */ +#endif + +/* + * We set a hard limit at 64 disks to prevent misconfiguration as + * it is unlikely that one would ever have that many on a single + * instance. Though of course, anything is possible, so one may + * patch the hard limit defined below to a higher value if needed. + */ +__static_assert(DISK_MAX < 64, "DISK_MAX exceeds hard limit"); + +/* + * The disk queue stores descriptors of disks that + * are registered with the system. This allows for + * easy and simplified access of the storage medium. + * + * XXX: An array would be more efficent, however disks + * could be detached or swapped during runtime thus + * making the usage of queues a more sane design. + * + * This also provides the added benefit of lazy-allocation + * so memory isn't wasted and only allocated when we actually + * have a disk descriptor that it would be used to store. + */ +static struct spinlock diskq_lock; +static TAILQ_HEAD(, disk) diskq; +static uint16_t disk_count = 0; +static uint16_t diskq_cookie = 0; + +/* + * Verify that a disk descriptor has been properly + * initialized by comparing against the cookie field. + * + * Returns a value of zero if valid, otherwise a less + * than zero value is returned. + */ +__always_inline static inline int +check_disk_cookie(struct disk *dp) +{ + __assert(dp != NULL); + return (dp->cookie == DISKQ_COOKIE) ? 0 : -1; +} + +/* + * Verify if the disk queue is initialized and + * ready for descriptors to be added. + * + * Returns a value of zero if it has already been + * initialized, otherwise a value less than zero + * is returned after check_diskq() initializes + * the disk queue. + */ +static inline int +check_diskq(void) +{ + if (diskq_cookie != DISKQ_COOKIE) { + TAILQ_INIT(&diskq); + diskq_cookie = DISKQ_COOKIE; + return -1; + } + + return 0; +} + +/* + * Acquire a disk descriptor through a zero-based + * disk index. Returns a pointer to the disk descriptor + * on success, otherwise a less than zero value is returned. + * + * @id: Disk index + * + * XXX: This is the lockless internal implementation, + * please use disk_get_id() instead. + */ +static struct disk * +__disk_get_id(diskid_t id) +{ + struct disk *dp; + + if (id >= disk_count) { + return NULL; + } + + dp = TAILQ_FIRST(&diskq); + if (dp == NULL) { + return NULL; + } + + /* + * Now, we start at the first disk entry and + * traverse the list. If the ID of a disk matches + * the ID we are looking for, return it. + */ + while (dp != NULL) { + if (dp->id == id) { + return dp; + } + + dp = TAILQ_NEXT(dp, link); + } + + /* Nothing found :( */ + return NULL; +} + +/* + * Attempt to perform a read/write operation on + * a disk. + * + * @id: ID of disk to operate on + * @blk: Block offset to read at + * @buf: Buffer to read data into + * @len: Number of bytes to read + * @write: If true, do a write + */ +static ssize_t +disk_rw(diskid_t id, blkoff_t blk, void *buf, size_t len, bool write) +{ + const struct bdevsw *bdev; + struct sio_txn sio; + struct disk *dp; + int error; + + /* Attempt to grab the disk object */ + error = disk_get_id(id, &dp); + if (error < 0) { + return error; + } + + /* Sanity check, should not happen */ + bdev = dp->bdev; + if (__unlikely(bdev == NULL)) { + return -EIO; + } + + /* Prepare the buffer */ + sio.buf = buf; + sio.offset = blk * dp->bsize; + sio.len = len; + + /* Handle writes */ + if (write) { + if (bdev->write == NULL) { + return -ENOTSUP; + } + + return bdev->write(dp->dev, &sio, 0); + } + + /* Do we support this operation? */ + if (bdev->read == NULL) { + return -ENOTSUP; + } + + return bdev->read(dp->dev, &sio, 0); +} + +/* + * Register a disk with the system so that it may + * be accessible independently of its device major + * and minor numbers + * + * @name: Name of the disk + * @dev: Device minor + * @bdev: Block device operations associated with device + * + * Returns zero on success, otherwise a less than zero + * value is returned. + */ +int +disk_add(const char *name, dev_t dev, const struct bdevsw *bdev, int flags) +{ + struct disk *dp; + size_t name_len; + + if (name == NULL || bdev == NULL) { + return -EINVAL; + } + + /* Disk queue must be initialized */ + check_diskq(); + + /* Is the disk name of correct length? */ + name_len = strlen(name); + if (name_len >= sizeof(dp->name) - 1) { + pr_error("disk_add: name too big (len=%d)\n", name_len); + return -E2BIG; + } + + dp = dynalloc(sizeof(*dp)); + if (dp == NULL) { + pr_error("failed to allocate disk\n"); + return -ENOMEM; + } + + /* Initialize the descriptor */ + memset(dp, 0, sizeof(*dp)); + memcpy(dp->name, name, name_len); + dp->cookie = DISKQ_COOKIE; + dp->bdev = bdev; + dp->dev = dev; + dp->id = disk_count++; + dp->bsize = DEFAULT_BSIZE; + + /* Now we can add it to the queue */ + spinlock_acquire(&diskq_lock); + TAILQ_INSERT_TAIL(&diskq, dp, link); + spinlock_release(&diskq_lock); + return 0; +} + +/* + * Acquire a disk descriptor by using a zero-based + * index. + * + * @id: Disk index (0: primary) + * @res: Resulting disk descriptor + * + * Returns zero on success, otherwise a less than + * zero value is returned. + */ +int +disk_get_id(diskid_t id, struct disk **res) +{ + int error; + struct disk *dp; + + if (res == NULL) { + return -EINVAL; + } + + if (id >= disk_count) { + return -ENODEV; + } + + /* Grab the disk */ + spinlock_acquire(&diskq_lock); + dp = __disk_get_id(id); + spinlock_release(&diskq_lock); + + /* Did it even exist? */ + if (dp == NULL) { + return -ENODEV; + } + + /* Should not fail but make sure */ + error = check_disk_cookie(dp); + if (__unlikely(error < 0)) { + panic("disk_get_id: got bad disk object\n"); + } + + *res = dp; + return 0; +} + +/* + * Allocate a memory buffer that may be used for + * disk I/O. + * + * @id: ID of disk buffer will be used for + * @len: Length to allocate + */ +void * +disk_buf_alloc(diskid_t id, size_t len) +{ + struct disk *dp; + void *buf; + + if (len == 0) { + return NULL; + } + + /* Attempt to acquire the disk */ + if (disk_get_id(id, &dp) < 0) { + return NULL; + } + + /* + * Here we will align the buffer size by the + * disk's block size to ensure it is big enough. + */ + len = ALIGN_UP(len, dp->bsize); + buf = dynalloc(len); + return buf; +} + +/* + * Free a memory buffer that was allocated by + * disk_buf_alloc() + */ +void +disk_buf_free(void *p) +{ + if (p != NULL) { + dynfree(p); + } +} + +/* + * Attempt to perform a read operation on + * a disk. + * + * @id: ID of disk to operate on + * @blk: Block offset to read at + * @buf: Buffer to read data into + * @len: Number of bytes to read + */ +ssize_t +disk_read(diskid_t id, blkoff_t blk, void *buf, size_t len) +{ + ssize_t retval; + char *tmp; + + tmp = disk_buf_alloc(id, len); + if (tmp == NULL) { + return -ENOMEM; + } + + retval = disk_rw(id, blk, tmp, len, false); + if (retval < 0) { + disk_buf_free(tmp); + return retval; + } + + memcpy(buf, tmp, len); + disk_buf_free(tmp); + return retval; +} + +/* + * Attempt to perform a write operation on + * a disk. + * + * @id: ID of disk to operate on + * @blk: Block offset to read at + * @buf: Buffer containing data to write + * @len: Number of bytes to read + */ +ssize_t +disk_write(diskid_t id, blkoff_t blk, const void *buf, size_t len) +{ + ssize_t retval; + char *tmp; + + tmp = disk_buf_alloc(id, len); + if (tmp == NULL) { + return -ENOMEM; + } + + memcpy(tmp, buf, len); + retval = disk_rw(id, blk, tmp, len, true); + disk_buf_free(tmp); + return retval; +} |