summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/GENERIC1
-rw-r--r--sys/dev/ic/ahci.c71
-rw-r--r--sys/include/sys/disk.h81
-rw-r--r--sys/kern/init_main.c4
-rw-r--r--sys/kern/kern_disk.c413
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;
+}