summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/acpi/uacpi.c32
-rw-r--r--sys/dev/cons/cons.c356
-rw-r--r--sys/dev/cons/cons_ansi.c181
-rw-r--r--sys/dev/dcdr/cache.c14
-rw-r--r--sys/dev/dmi/dmi.c251
-rw-r--r--sys/dev/ic/ahci.c284
-rw-r--r--sys/dev/ic/nvme.c2
-rw-r--r--sys/dev/pci/pci.c101
-rw-r--r--sys/dev/phy/e1000.c358
-rw-r--r--sys/dev/phy/rtl.c (renamed from sys/dev/phy/rt8139.c)178
-rw-r--r--sys/dev/usb/xhci.c37
-rw-r--r--sys/dev/video/fbdev.c85
12 files changed, 1671 insertions, 208 deletions
diff --git a/sys/dev/acpi/uacpi.c b/sys/dev/acpi/uacpi.c
index 7dbcb35..b133288 100644
--- a/sys/dev/acpi/uacpi.c
+++ b/sys/dev/acpi/uacpi.c
@@ -41,10 +41,10 @@
#include <machine/cdefs.h>
#include <machine/pio.h>
#include <machine/cpu.h>
+#include <machine/intr.h>
#if defined(__x86_64__)
#include <machine/idt.h>
#include <machine/ioapic.h>
-#include <machine/intr.h>
#endif /* __x86_64__ */
#include <dev/acpi/uacpi.h>
#include <dev/acpi/acpi.h>
@@ -259,17 +259,16 @@ uacpi_status
uacpi_kernel_install_interrupt_handler(uacpi_u32 irq, uacpi_interrupt_handler fn,
uacpi_handle ctx, uacpi_handle *out_irq_handle)
{
- int vec;
+ struct intr_hand ih;
+
+ ih.func = (void *)fn;
+ ih.priority = IPL_HIGH;
+ ih.irq = irq;
+ if (intr_register("acpi", &ih) == NULL) {
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
-#if defined(__x86_64__)
- vec = intr_alloc_vector("acpi", IPL_HIGH);
- idt_set_desc(vec, IDT_INT_GATE, ISR(fn), IST_HW_IRQ);
- ioapic_set_vec(irq, vec);
- ioapic_irq_unmask(irq);
return UACPI_STATUS_OK;
-#else
- return UACPI_STATUS_UNIMPLEMENTED;
-#endif /* __x86_64__ */
}
uacpi_status
@@ -514,9 +513,18 @@ uacpi_u64
uacpi_kernel_get_nanoseconds_since_boot(void)
{
static uacpi_u64 time = 0;
+ static struct timer tmr = {0};
+ tmrr_status_t tmr_error;
+
+ if (time == 0) {
+ tmr_error = req_timer(TIMER_GP, &tmr);
+ if (tmr_error != TMRR_SUCCESS) {
+ time += 1000000;
+ return time;
+ }
+ }
- /* TODO */
- time += 1000000;
+ time = tmr.get_time_nsec();
return time;
}
diff --git a/sys/dev/cons/cons.c b/sys/dev/cons/cons.c
index b89727f..9921ff8 100644
--- a/sys/dev/cons/cons.c
+++ b/sys/dev/cons/cons.c
@@ -37,6 +37,7 @@
#include <dev/cons/font.h>
#include <dev/cons/cons.h>
#include <fs/devfs.h>
+#include <fs/ctlfs.h>
#include <vm/dynalloc.h>
#include <string.h>
@@ -44,7 +45,7 @@
cons_draw_cursor((SCR), (SCR)->bg)
#define SHOW_CURSOR(SCR) \
- cons_draw_cursor((SCR), (SCR)->fg)
+ cons_draw_cursor((SCR), rgb_invert((SCR)->bg))
/* Console background from kconf */
#if defined(__CONSOLE_BG)
@@ -62,10 +63,27 @@
struct cons_screen g_root_scr = {0};
static struct cdevsw cons_cdevsw;
+static struct ctlops cons_feat_ctl;
static void cons_draw_cursor(struct cons_screen *scr, uint32_t color);
static int cons_handle_special(struct cons_screen *scr, char c);
-static void cons_clear_scr(struct cons_screen *scr, uint32_t bg);
+
+static uint32_t
+rgb_invert(uint32_t rgb)
+{
+ uint8_t r, g, b;
+ uint32_t ret;
+
+ r = (rgb >> 16) & 0xFF;
+ g = (rgb >> 8) & 0xFF;
+ b = rgb & 0xFF;
+
+ ret = (255 - r) << 16;
+ ret |= (255 - g) << 8;
+ ret |= 255 - b;
+ return ret;
+}
+
/*
* Render a character onto the screen.
@@ -91,9 +109,9 @@ cons_draw_char(struct cons_screen *scr, struct cons_char ch)
y = ch.y;
for (uint32_t cy = 0; cy < FONT_HEIGHT; ++cy) {
+ idx = fbdev_get_index(&scr->fbdev, x + (FONT_WIDTH - 1), y + cy);
for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) {
- idx = fbdev_get_index(&scr->fbdev, x + (FONT_WIDTH - 1) - cx, y + cy);
- scr->fb_mem[idx] = ISSET(glyph[cy], BIT(cx)) ? ch.fg : ch.bg;
+ scr->fb_mem[idx--] = ISSET(glyph[cy], BIT(cx)) ? ch.fg : ch.bg;
}
}
}
@@ -158,6 +176,17 @@ cons_handle_special(struct cons_screen *scr, char c)
}
switch (c) {
+ case ASCII_HT:
+ HIDE_CURSOR(scr);
+ scr->curs_col += 4;
+ scr->ch_col += 4;
+ if (scr->ch_col >= scr->ncols - 1) {
+ cons_handle_special(scr, '\n');
+ }
+ SHOW_CURSOR(scr);
+ return 0;
+ case ASCII_NUL:
+ return 0;
case ASCII_BS:
bp = scr->ob[scr->ch_row];
if (bp->head > bp->tail) {
@@ -165,27 +194,21 @@ cons_handle_special(struct cons_screen *scr, char c)
}
HIDE_CURSOR(scr);
- --scr->ch_col;
- --scr->curs_col;
+ if (scr->ch_col > 0 && scr->curs_col > 0) {
+ --scr->ch_col;
+ --scr->curs_col;
+ }
SHOW_CURSOR(scr);
return 0;
case ASCII_LF:
- HIDE_CURSOR(scr);
-
/* Are we past screen width? */
if (scr->ch_row >= scr->nrows - 1) {
cons_clear_scr(scr, scr->bg);
- cons_flush(scr);
- scr->ch_col = 0;
- scr->ch_row = 0;
-
- /* Update cursor */
- scr->curs_row = 0;
- scr->curs_col = 0;
- SHOW_CURSOR(scr);
return 0;
}
+ HIDE_CURSOR(scr);
+
/* Make a newline */
cons_flush(scr);
++scr->ch_row;
@@ -212,8 +235,14 @@ cons_handle_special(struct cons_screen *scr, char c)
static void
cons_draw_cursor(struct cons_screen *scr, uint32_t color)
{
+ struct console_feat *featp;
size_t idx;
+ featp = &scr->feat;
+ if (!featp->show_curs) {
+ color = scr->bg;
+ }
+
/* Past screen width? */
if (scr->curs_col >= scr->ncols) {
scr->curs_col = 0;
@@ -221,9 +250,9 @@ cons_draw_cursor(struct cons_screen *scr, uint32_t color)
}
for (uint32_t cy = 0; cy < FONT_HEIGHT; ++cy) {
+ idx = fbdev_get_index(&scr->fbdev, scr->curs_col * FONT_WIDTH, (scr->curs_row * FONT_HEIGHT) + cy);
for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) {
- idx = fbdev_get_index(&scr->fbdev, (scr->curs_col * FONT_WIDTH) + cx, (scr->curs_row * FONT_HEIGHT) + cy);
- scr->fb_mem[idx] = color;
+ scr->fb_mem[idx++] = color;
}
}
}
@@ -234,34 +263,88 @@ cons_draw_cursor(struct cons_screen *scr, uint32_t color)
* @scr: Screen to clear.
* @bg: Color to clear it to.
*/
-static void
+void
cons_clear_scr(struct cons_screen *scr, uint32_t bg)
{
struct fbdev fbdev = scr->fbdev;
- struct cons_buf *bp;
+
+ cons_flush(scr);
+ HIDE_CURSOR(scr);
+
+ scr->ch_col = 0;
+ scr->ch_row = 0;
+ scr->curs_col = 0;
+ scr->curs_row = 0;
for (size_t i = 0; i < fbdev.height * fbdev.pitch; ++i) {
scr->fb_mem[i] = bg;
}
- bp = scr->ob[scr->nrows - 1];
- bp->flags |= CONS_BUF_CLEAN;
+ SHOW_CURSOR(scr);
+
}
/*
- * Character device function.
+ * Quickly put a character on the screen.
+ * XXX: Does not acquire the screen's lock or show/hide the cursor.
+ *
+ * @scr: Screen.
+ * @c: Character to draw.
*/
-static int
-dev_write(dev_t dev, struct sio_txn *sio, int flags)
+static void
+cons_fast_putch(struct cons_screen *scr, char c)
{
- char *p;
+ struct cons_char cc;
+ struct cons_buf *bp;
+ int ansi;
+
+ ansi = ansi_feed(&scr->ansi_s, c);
+ if (ansi > 0) {
+ c = ASCII_NUL;
+ } else if (ansi < 0) {
+ c = ASCII_NUL;
+ }
+
+ /* Handle specials */
+ if (cons_handle_special(scr, c) == 0) {
+ return;
+ }
+
+ /* Create a new character */
+ cc.c = c;
+ cc.fg = scr->fg;
+ cc.bg = scr->bg;
+ cc.x = scr->ch_col * FONT_WIDTH;
+ cc.y = scr->ch_row * FONT_HEIGHT;
+
+ /* Push our new character */
+ bp = scr->ob[scr->ch_row];
+ bp->flags &= ~CONS_BUF_CLEAN;
+ cons_obuf_push(bp, cc);
+ ++scr->ch_col;
- p = sio->buf;
+ /* Check screen bounds */
+ if (cc.x >= (scr->ncols * FONT_WIDTH) - 1) {
+ scr->ch_col = 0;
+ ++scr->ch_row;
+ }
- for (size_t i = 0; i < sio->len; ++i) {
- cons_putch(&g_root_scr, p[i]);
+ ++scr->curs_col;
+ if (scr->curs_col > scr->ncols - 1) {
+ scr->curs_col = 0;
+ if (scr->curs_row < scr->nrows)
+ ++scr->curs_row;
}
+}
+/*
+ * Character device function.
+ */
+static int
+dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ cons_attach();
+ cons_putstr(&g_root_scr, sio->buf, sio->len);
cons_flush(&g_root_scr);
return sio->len;
}
@@ -290,6 +373,7 @@ dev_read(dev_t dev, struct sio_txn *sio, int flags)
return -EAGAIN;
}
+ cons_attach();
spinlock_acquire(&g_root_scr.lock);
for (;;) {
/* Buffer too small */
@@ -338,6 +422,144 @@ cons_init_bufs(struct cons_screen *scr)
return 0;
}
+static int
+ctl_feat_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_feat *featp;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ featp = &scr->feat;
+ if (sio->len > sizeof(*featp)) {
+ sio->len = sizeof(*featp);
+ }
+
+ memcpy(sio->buf, featp, sio->len);
+ return sio->len;
+}
+
+static int
+ctl_feat_write(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_feat *featp, oldfeat;
+
+ featp = &scr->feat;
+ oldfeat = *featp;
+ if (sio->len > sizeof(*featp)) {
+ sio->len = sizeof(*featp);
+ }
+
+ memcpy(featp, sio->buf, sio->len);
+
+ /*
+ * If we are suddenly trying to reset the cursor
+ * status, redraw it.
+ */
+ if (featp->show_curs != oldfeat.show_curs) {
+ if (featp->show_curs == 0) {
+ HIDE_CURSOR(scr);
+ } else {
+ SHOW_CURSOR(scr);
+ }
+ }
+ return sio->len;
+}
+
+/*
+ * Detach the currently running process from the
+ * console.
+ */
+int
+cons_detach(void)
+{
+ struct cons_screen *scr;
+
+ scr = &g_root_scr;
+ if (scr->atproc == NULL) {
+ return 0;
+ }
+ if (scr->atproc_lock == NULL) {
+ return 0;
+ }
+
+ scr = &g_root_scr;
+ scr->atproc = NULL;
+ mutex_release(scr->atproc_lock);
+ return 0;
+}
+
+/*
+ * Attach the current process to the
+ * console.
+ */
+int
+cons_attach(void)
+{
+ struct cons_screen *scr;
+ struct proc *td, *atproc;
+
+ td = this_td();
+ if (td == NULL) {
+ return -1;
+ }
+
+ scr = &g_root_scr;
+ if (scr->atproc_lock == NULL) {
+ return 0;
+ }
+
+ scr = &g_root_scr;
+ atproc = scr->atproc;
+
+ if (atproc != NULL) {
+ if (atproc->pid == td->pid) {
+ return 0;
+ }
+
+ /*
+ * Do not release this here as we want
+ * any other process that tries to attach
+ * to wait.
+ */
+ mutex_acquire(scr->atproc_lock, 0);
+ }
+
+ scr->atproc = td;
+ return 0;
+}
+
+/*
+ * Reset console color.
+ */
+void
+cons_reset_color(struct cons_screen *scr)
+{
+ g_root_scr.fg = CONSOLE_FG;
+ g_root_scr.bg = CONSOLE_BG;
+}
+
+void
+cons_update_color(struct cons_screen *scr, uint32_t fg, uint32_t bg)
+{
+ scr->fg = fg;
+ scr->bg = bg;
+}
+
+void
+cons_reset_cursor(struct cons_screen *scr)
+{
+ HIDE_CURSOR(scr);
+ scr->ch_col = 0;
+ scr->ch_row = 0;
+ scr->curs_col = 0;
+ scr->curs_row = 0;
+ SHOW_CURSOR(scr);
+}
+
/*
* Put a character on the screen.
*
@@ -347,47 +569,37 @@ cons_init_bufs(struct cons_screen *scr)
int
cons_putch(struct cons_screen *scr, char c)
{
- struct cons_buf *bp;
- struct cons_char cc;
- size_t max_width;
-
spinlock_acquire(&scr->lock);
+ HIDE_CURSOR(scr);
- /* Handle specials */
- if (cons_handle_special(scr, c) == 0) {
- goto done;
- }
+ cons_fast_putch(scr, c);
- HIDE_CURSOR(scr);
+ SHOW_CURSOR(scr);
+ spinlock_release(&scr->lock);
+ return 0;
+}
- /* Create a new character */
- cc.c = c;
- cc.fg = scr->fg;
- cc.bg = scr->bg;
- cc.x = scr->ch_col * FONT_WIDTH;
- cc.y = scr->ch_row * FONT_HEIGHT;
+/*
+ * Put a string on the screen.
+ *
+ * @scr: Screen.
+ * @s: String to draw.
+ * @l: Length of s.
+ */
+int
+cons_putstr(struct cons_screen *scr, const char *s, size_t len)
+{
+ const char *p = s;
- /* Push our new character */
- bp = scr->ob[scr->ch_row];
- bp->flags &= ~CONS_BUF_CLEAN;
- cons_obuf_push(bp, cc);
- ++scr->ch_col;
+ spinlock_acquire(&scr->lock);
+ HIDE_CURSOR(scr);
- /* Check screen bounds */
- max_width = scr->ncols * FONT_WIDTH;
- if (cc.x >= max_width - 1) {
- scr->ch_col = 0;
- ++scr->ch_row;
+ while (len--) {
+ cons_fast_putch(scr, *p);
+ ++p;
}
- ++scr->curs_col;
- if (scr->curs_col > scr->ncols - 1) {
- scr->curs_col = 0;
- if (scr->curs_row < scr->nrows)
- ++scr->curs_row;
- }
SHOW_CURSOR(scr);
-done:
spinlock_release(&scr->lock);
return 0;
}
@@ -396,7 +608,11 @@ void
cons_init(void)
{
struct fbdev fbdev = fbdev_get();
+ struct console_feat *featp;
+ featp = &g_root_scr.feat;
+ featp->ansi_esc = 1;
+ featp->show_curs = 1;
g_root_scr.ch_col = 0;
g_root_scr.ch_row = 0;
g_root_scr.fg = CONSOLE_FG;
@@ -405,6 +621,8 @@ cons_init(void)
g_root_scr.nrows = fbdev.height / FONT_HEIGHT;
g_root_scr.ncols = fbdev.width / FONT_WIDTH;
g_root_scr.fbdev = fbdev;
+ g_root_scr.atproc = NULL;
+ g_root_scr.atproc_lock = NULL;
memset(&g_root_scr.lock, 0, sizeof(g_root_scr.lock));
cons_init_bufs(&g_root_scr);
SHOW_CURSOR(&g_root_scr);
@@ -417,6 +635,7 @@ void
cons_expose(void)
{
static int once = 0;
+ struct ctlfs_dev ctl;
char devname[] = "console";
devmajor_t major;
dev_t dev;
@@ -426,11 +645,21 @@ cons_expose(void)
return;
}
+ /* Init the attached proc mutex lock */
+ g_root_scr.atproc_lock = mutex_new("console0");
+
/* Register the device here */
major = dev_alloc_major();
dev = dev_alloc(major);
dev_register(major, dev, &cons_cdevsw);
devfs_create_entry(devname, major, dev, 0444);
+
+ /* Register the control file */
+ ctl.mode = 0666;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &cons_feat_ctl;
+ ctlfs_create_entry("feat", &ctl);
once ^= 1;
}
@@ -438,3 +667,8 @@ static struct cdevsw cons_cdevsw = {
.read = dev_read,
.write = dev_write
};
+
+static struct ctlops cons_feat_ctl = {
+ .read = ctl_feat_read,
+ .write = ctl_feat_write
+};
diff --git a/sys/dev/cons/cons_ansi.c b/sys/dev/cons/cons_ansi.c
new file mode 100644
index 0000000..e00bdc9
--- /dev/null
+++ b/sys/dev/cons/cons_ansi.c
@@ -0,0 +1,181 @@
+/*
+ * 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/cdefs.h>
+#include <sys/console.h>
+#include <dev/cons/cons.h>
+#include <dev/cons/ansi.h>
+#include <string.h>
+
+__always_inline static inline bool
+is_valid_color(int c)
+{
+ return c >= '0' && c <= '7';
+}
+
+static inline void
+ansi_reset(struct ansi_state *statep)
+{
+ memset(statep, 0, sizeof(*statep));
+}
+
+/*
+ * Feed a byte into the ANSI escape sequence
+ * state machine.
+ *
+ * @statep: State machine pointer.
+ * @c: Byte to feed.
+ *
+ * On success, `c' is returned. On failure,
+ * 0 is returned. Values less than 0 indicate
+ * success with console attributes updated
+ * (ANSI_UPDATE_*).
+ */
+int
+ansi_feed(struct ansi_state *statep, char c)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_feat *featp;
+
+
+ /* Standard colors */
+ static uint32_t colortab[] = {
+ ANSI_BLACK, ANSI_RED,
+ ANSI_GREEN, ANSI_YELLOW,
+ ANSI_BLUE, ANSI_MAGENTA,
+ ANSI_CYAN, ANSI_WHITE
+ };
+
+ featp = &scr->feat;
+ if (!featp->ansi_esc) {
+ return 0;
+ }
+
+ /*
+ * Handle the control sequence introducer
+ * bytes.
+ */
+ switch (statep->csi) {
+ case 0: /* '\033' */
+ if (c != '\033') {
+ return 0;
+ }
+ statep->csi = 1;
+ statep->prev = c;
+ return c;
+ case 1: /* '[' */
+ if (c != '[') {
+ ansi_reset(statep);
+ return 0;
+ }
+ statep->csi = 2;
+ statep->prev = c;
+ return c;
+ case 2:
+ if (c == 'H') {
+ cons_clear_scr(scr, g_root_scr.bg);
+ return ANSI_UPDATE_CURSOR;
+ }
+ break;
+ }
+
+ if (!statep->set_fg && !statep->set_bg) {
+ /* Reset attributes? */
+ if (statep->reset_color) {
+ ansi_reset(statep);
+ cons_reset_color(scr);
+ return ANSI_UPDATE_COLOR;
+ }
+
+ /* Mark attributes to be reset? */
+ if (c == '0') {
+ statep->reset_color = 1;
+ statep->prev = c;
+ return c;
+ }
+
+ /* Expect foreground */
+ if (c != '3') {
+ ansi_reset(statep);
+ return 0;
+ }
+ statep->set_fg = 1;
+ statep->prev = c;
+ return c;
+ }
+
+ if (statep->set_fg && c != ';') {
+ /* Make sure this is valid */
+ if (!is_valid_color(c)) {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ /* Set the foreground */
+ statep->fg = colortab[c - '0'];
+ statep->set_bg = 1;
+ statep->set_fg = 0;
+ statep->prev = c;
+ return c;
+ }
+
+ if (statep->set_bg) {
+ if (c == ';') {
+ statep->prev = c;
+ return c;
+ }
+
+ /* Expect '4' after ';' */
+ if (statep->prev == ';' && c != '4') {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ if (c == 'm') {
+ cons_update_color(scr, statep->fg, statep->bg);
+ ansi_reset(statep);
+ return ANSI_UPDATE_COLOR;
+ }
+
+ /* Make sure this is valid */
+ if (!is_valid_color(c)) {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ /* Set the background */
+ statep->bg = colortab[c - '0'];
+ statep->prev = c;
+ return c;
+ }
+
+ ansi_reset(statep);
+ return 0;
+}
diff --git a/sys/dev/dcdr/cache.c b/sys/dev/dcdr/cache.c
index c44c8ea..33f977e 100644
--- a/sys/dev/dcdr/cache.c
+++ b/sys/dev/dcdr/cache.c
@@ -126,6 +126,20 @@ struct dcd *
dcdr_cachein(struct dcdr *dcdr, void *block, off_t lba)
{
struct dcd *dcd, *tmp;
+ struct dcdr_lookup check;
+ int status;
+
+ /*
+ * If there is already a block within this
+ * DCDR, then we simply need to copy the
+ * new data into the old DCD.
+ */
+ status = dcdr_lookup(dcdr, lba, &check);
+ if (status == 0) {
+ dcd = check.dcd_res;
+ memcpy(dcd->block, block, dcdr->bsize);
+ return dcd;
+ }
dcd = dynalloc(sizeof(*dcd));
if (dcd == NULL) {
diff --git a/sys/dev/dmi/dmi.c b/sys/dev/dmi/dmi.c
new file mode 100644
index 0000000..59946ad
--- /dev/null
+++ b/sys/dev/dmi/dmi.c
@@ -0,0 +1,251 @@
+/*
+ * 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/limine.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/driver.h>
+#include <sys/cdefs.h>
+#include <sys/syslog.h>
+#include <dev/dmi/dmi.h>
+#include <dev/acpi/tables.h>
+#include <string.h>
+
+#define DMI_BIOS_INFO 0
+#define DMI_SYSTEM_INFO 1
+#define DMI_PROCESSOR_INFO 4
+#define DMI_END_OF_TABLE 127
+
+/* String offsets */
+#define BIOSINFO_VENDOR 0x01
+#define SYSINFO_PRODUCT 0x02
+#define SYSINFO_FAMILY 0x03
+#define PROCINFO_MANUFACT 0x02
+#define PROCINFO_PARTNO 0x06
+
+static struct limine_smbios_request smbios_req = {
+ .id = LIMINE_SMBIOS_REQUEST,
+ .revision = 0
+};
+
+/* DMI/SMBIOS structure header */
+struct __packed dmi_shdr {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+} *hdrs[DMI_END_OF_TABLE + 1];
+
+/*
+ * Grab a structure header from a type
+ *
+ * @type: A DMI structure type to find
+ *
+ * Returns NULL if not found.
+ */
+static inline struct dmi_shdr *
+dmi_shdr(uint8_t type)
+{
+ struct dmi_shdr *hdr;
+
+ hdr = hdrs[type];
+ if (hdr == NULL) {
+ return NULL;
+ }
+
+ return hdr;
+}
+
+/*
+ * Grab a string from the DMI/SMBIOS formatted
+ * section.
+ *
+ * @hdr: DMI header to lookup string index
+ * @index: 1-based string index
+ *
+ * See section 6.1.3 of the DTMF SMBIOS Reference
+ * Specification
+ */
+static const char *
+dmi_str_index(struct dmi_shdr *hdr, uint8_t index)
+{
+ const char *strdata = PTR_OFFSET(hdr, hdr->length);
+
+ for (uint8_t i = 1; *strdata != '\0'; ++i) {
+ if (i == index) {
+ return strdata;
+ }
+
+ strdata += strlen(strdata) + 1;
+ }
+
+ return NULL;
+}
+
+/*
+ * Get the DMI/SMBIOS structure size from a
+ * header.
+ */
+static size_t
+dmi_struct_size(struct dmi_shdr *hdr)
+{
+ const char *strdata;
+ size_t i = 1;
+
+ strdata = PTR_OFFSET(hdr, hdr->length);
+ while (strdata[i - 1] != '\0' || strdata[i] != '\0') {
+ ++i;
+ }
+
+ return hdr->length + i + 1;
+}
+
+/*
+ * Get the vendor string from the DMI/SMBIOS BIOS
+ * info structure
+ *
+ * Returns NULL if not found.
+ */
+const char *
+dmi_vendor(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_BIOS_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, BIOSINFO_VENDOR);
+}
+
+/*
+ * Return the product string from the DMI/SMBIOS System
+ * Info structure
+ *
+ * Returns NULL if not found.
+ */
+const char *
+dmi_product(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_SYSTEM_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, SYSINFO_PRODUCT);
+}
+
+/*
+ * Return the product version from the DMI/SMBIOS
+ * System Info structure
+ *
+ * Returns NULL if not found
+ */
+const char *
+dmi_prodver(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_SYSTEM_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, SYSINFO_FAMILY);
+}
+
+/*
+ * Return the CPU manufacturer string from the
+ * DMI/SMBIOS Processor Info structure
+ *
+ * Returns NULL if not found
+ */
+const char *
+dmi_cpu_manufact(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_PROCESSOR_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, PROCINFO_MANUFACT);
+}
+
+static int
+dmi_init(void)
+{
+ struct dmi_entry32 *entry32 = NULL;
+ struct limine_smbios_response *resp = smbios_req.response;
+ struct dmi_entry64 *entry64 = NULL;
+ struct dmi_shdr *hdr = NULL;
+ size_t scount = 0, smax_len = 0;
+ size_t nbytes = 0, cur_nbytes = 0;
+
+ if (resp == NULL) {
+ return -ENODEV;
+ }
+ if (resp->entry_32 == 0 && resp->entry_64 == 0) {
+ return -ENODEV;
+ }
+
+ if (resp->entry_64 != 0) {
+ entry64 = (void *)resp->entry_64;
+ hdr = (void *)entry64->addr;
+ smax_len = entry64->max_size;
+ } else if (resp->entry_32 != 0) {
+ entry32 = (void *)(uint64_t)resp->entry_32;
+ hdr = (void *)(uint64_t)entry32->addr;
+ scount = entry32->nstruct;
+ } else {
+ return -ENODEV;
+ }
+
+ memset(hdrs, 0, sizeof(hdrs));
+ for (size_t i = 0; i < scount; ++i) {
+ if (hdr->type == DMI_END_OF_TABLE) {
+ break;
+ }
+
+ if (hdr->type < NELEM(hdrs)) {
+ hdrs[hdr->type] = hdr;
+ }
+ cur_nbytes = dmi_struct_size(hdr);
+ if (smax_len > 0 && (nbytes + cur_nbytes) >= smax_len) {
+ break;
+ }
+
+ nbytes += cur_nbytes;
+ hdr = PTR_OFFSET(hdr, cur_nbytes);
+ }
+
+ return 0;
+}
+
+DRIVER_EXPORT(dmi_init, "dmi");
diff --git a/sys/dev/ic/ahci.c b/sys/dev/ic/ahci.c
index 8b72cde..d17c6a3 100644
--- a/sys/dev/ic/ahci.c
+++ b/sys/dev/ic/ahci.c
@@ -41,10 +41,12 @@
#include <dev/timer.h>
#include <dev/ic/ahcivar.h>
#include <dev/ic/ahciregs.h>
+#include <dev/dcdr/cache.h>
#include <fs/devfs.h>
#include <fs/ctlfs.h>
#include <vm/dynalloc.h>
#include <vm/physmem.h>
+#include <machine/cdefs.h>
#include <string.h>
#define pr_trace(fmt, ...) kprintf("ahci: " fmt, ##__VA_ARGS__)
@@ -55,6 +57,8 @@ static struct bdevsw ahci_bdevsw;
static struct hba_device *devs;
static struct pci_device *ahci_dev;
static struct timer tmr;
+static struct ahci_hba g_hba;
+static struct driver_var __driver_var;
/*
* Poll register to have 'bits' set/unset.
@@ -94,6 +98,18 @@ ahci_poll_reg(volatile uint32_t *reg, uint32_t bits, bool pollset)
return 0;
}
+static struct hba_device *
+ahci_get_dev(dev_t dev)
+{
+ for (int i = 0; i < devs_max; ++i) {
+ if (devs[i].dev == dev) {
+ return &devs[i];
+ }
+ }
+
+ return NULL;
+}
+
/*
* Allocate a command slot for a port on
* the HBA.
@@ -103,7 +119,6 @@ ahci_alloc_cmdslot(struct ahci_hba *hba, struct hba_port *port)
{
uint32_t slotlist;
- slotlist = port->ci | port->sact;
slotlist = mmio_read32(&port->ci);
slotlist |= mmio_read32(&port->sact);
@@ -315,22 +330,24 @@ hba_port_chkerr(struct hba_port *port)
static int
hba_port_reset(struct ahci_hba *hba, struct hba_port *port)
{
- uint32_t sctl, ssts;
- uint8_t det, ipm;
- int error;
+ uint32_t sctl, ssts, cmd;
+ uint8_t det, ipm, spd;
+ uint32_t elapsed = 0;
+
+ sctl = mmio_read32(&port->sctl);
/*
- * The port must not be in an idle state when a
- * COMRESET is sent over the interface as some
- * chipsets do not know how to handle this...
- *
- * After bringing up the port, send a COMRESET
- * over the interface for roughly ~2ms.
+ * Transmit a COMRESET to the device. If the HBA
+ * supports staggered spin-up, we'll need to set
+ * the PxCMD.SUD bit as well.
*/
- hba_port_start(port);
- sctl = mmio_read32(&port->sctl);
sctl = (sctl & ~0x0F) | AHCI_DET_COMRESET;
mmio_write32(&port->sctl, sctl);
+ if (hba->sss) {
+ cmd = mmio_read32(&port->cmd);
+ cmd |= AHCI_PXCMD_SUD;
+ mmio_write32(&port->cmd, cmd);
+ }
/*
* Wait for the link to become reestablished
@@ -340,34 +357,50 @@ hba_port_reset(struct ahci_hba *hba, struct hba_port *port)
sctl &= ~AHCI_DET_COMRESET;
mmio_write32(&port->sctl, sctl);
- /*
- * Now we'll need to grab some power management
- * and detection flags as the port must have
- * a device present along with an active
- * interface.
- */
- ssts = mmio_read32(&port->ssts);
- det = AHCI_PXSCTL_DET(ssts);
- ipm = AHCI_PXSSTS_IPM(ssts);
+ for (;;) {
+ if (elapsed >= AHCI_TIMEOUT) {
+ break;
+ }
+ ssts = mmio_read32(&port->ssts);
+ det = AHCI_PXSSTS_DET(ssts);
+ if (det == AHCI_DET_COMM) {
+ break;
+ }
- /* If there is no device, fake success */
- if (det == AHCI_DET_NULL) {
- return 0;
+ tmr.msleep(10);
+ elapsed += 10;
}
+ ipm = AHCI_PXSSTS_IPM(ssts);
+ spd = AHCI_PXSSTS_SPD(ssts);
+
+ if (det == AHCI_DET_PRESENT) {
+ pr_error("SATA link timeout\n");
+ return -EAGAIN;
+ }
if (det != AHCI_DET_COMM) {
- pr_trace("failed to establish link\n");
return -EAGAIN;
}
+ /*
+ * Ensure the interface is in an active
+ * state.
+ */
if (ipm != AHCI_IPM_ACTIVE) {
- pr_trace("device interface not active\n");
+ pr_error("device interface not active\n");
return -EAGAIN;
}
- if ((error = hba_port_stop(port)) < 0) {
- pr_trace("failed to stop port\n");
- return error;
+ switch (spd) {
+ case AHCI_SPD_GEN1:
+ pr_trace("SATA link rate @ ~1.5 Gb/s\n");
+ break;
+ case AHCI_SPD_GEN2:
+ pr_trace("SATA link rate @ ~3 Gb/s\n");
+ break;
+ case AHCI_SPD_GEN3:
+ pr_trace("SATA link rate @ ~6 Gb/s\n");
+ break;
}
return 0;
@@ -416,12 +449,14 @@ ahci_submit_cmd(struct ahci_hba *hba, struct hba_port *port, uint8_t slot)
* SATA device.
*/
static int
-ahci_identify(struct ahci_hba *hba, struct hba_port *port)
+ahci_identify(struct ahci_hba *hba, struct hba_device *dp)
{
paddr_t base, buf;
+ struct hba_port *port;
struct ahci_cmd_hdr *cmdhdr;
struct ahci_cmdtab *cmdtbl;
struct ahci_fis_h2d *fis;
+ uint16_t *p;
int cmdslot, status;
buf = vm_alloc_frame(1);
@@ -430,6 +465,7 @@ ahci_identify(struct ahci_hba *hba, struct hba_port *port)
return -ENOMEM;
}
+ port = dp->io;
cmdslot = ahci_alloc_cmdslot(hba, port);
if (cmdslot < 0) {
pr_trace("failed to alloc cmdslot\n");
@@ -461,6 +497,9 @@ ahci_identify(struct ahci_hba *hba, struct hba_port *port)
}
ahci_dump_identity(PHYS_TO_VIRT(buf));
+ p = (uint16_t *)PHYS_TO_VIRT(buf);
+ dp->nlba = (p[61] << 16) | p[60];
+ pr_trace("max block size: %d\n", dp->nlba);
done:
vm_free_frame(buf, 1);
return status;
@@ -470,7 +509,7 @@ done:
* Send a read/write command to a SATA drive
*
* @hba: Host bus adapter of target port
- * @port: Port to send over
+ * @dev: Device to send over
* @sio: System I/O descriptor
* @write: If true, data pointed to by `sio` will be written
*
@@ -480,14 +519,21 @@ done:
* - The `offset` field in `sio` is the LBA address.
*/
static int
-ahci_sata_rw(struct ahci_hba *hba, struct hba_port *port, struct sio_txn *sio,
+ahci_sata_rw(struct ahci_hba *hba, struct hba_device *dev, struct sio_txn *sio,
bool write)
{
paddr_t base, buf;
+ char *p, *dest;
+ bool dcdr_hit = false;
+ struct hba_port *port;
+ struct dcdr_lookup dcd_lookup;
+ struct dcd *dcd;
struct ahci_cmd_hdr *cmdhdr;
struct ahci_cmdtab *cmdtbl;
struct ahci_fis_h2d *fis;
int cmdslot, status;
+ size_t nblocks, cur_lba;
+ size_t len;
if (sio == NULL) {
return -EINVAL;
@@ -496,6 +542,60 @@ ahci_sata_rw(struct ahci_hba *hba, struct hba_port *port, struct sio_txn *sio,
return -EINVAL;
}
+ port = dev->io;
+
+ /*
+ * Compute how many blocks can be cached.
+ *
+ * XXX: We do not want to fill the entire DCDR
+ * with a single drive read to reduce the
+ * frequency of DCDR evictions.
+ *
+ * TODO: We should also take advantage of logical
+ * block coalescing.
+ */
+ nblocks = sio->len;
+ if (nblocks >= AHCI_DCDR_CAP) {
+ nblocks = AHCI_DCDR_CAP / 2;
+ }
+
+ /*
+ * If we are reading the drive, see if we have
+ * anything in the cache.
+ *
+ * XXX: If there is a break in the cache and we
+ * have a miss inbetween, other DCDs are
+ * ignored. Wonder how we can mitigate
+ * fragmentation.
+ */
+ cur_lba = sio->offset;
+ len = sio->len;
+ for (size_t i = 0; i < nblocks && !write; ++i) {
+ status = dcdr_lookup(dev->dcdr, cur_lba, &dcd_lookup);
+ if (status != 0) {
+ break;
+ }
+ if (len == 0) {
+ break;
+ }
+
+ dcdr_hit = true;
+ dcd = dcd_lookup.dcd_res;
+
+ /* Hit, copy the cached data */
+ dest = &((char *)sio->buf)[i * 512];
+ p = dcd->block;
+ memcpy(dest, p, 512);
+
+ ++cur_lba;
+ --len;
+ }
+
+ /* Did we get everything already? */
+ if (len == 0) {
+ return 0;
+ }
+
buf = VIRT_TO_PHYS(sio->buf);
cmdslot = ahci_alloc_cmdslot(hba, port);
if (cmdslot < 0) {
@@ -524,22 +624,32 @@ ahci_sata_rw(struct ahci_hba *hba, struct hba_port *port, struct sio_txn *sio,
fis->device = (1 << 6); /* LBA */
/* Setup LBA */
- fis->lba0 = sio->offset & 0xFF;
- fis->lba1 = (sio->offset >> 8) & 0xFF;
- fis->lba2 = (sio->offset >> 16) & 0xFF;
- fis->lba3 = (sio->offset >> 24) & 0xFF;
- fis->lba4 = (sio->offset >> 32) & 0xFF;
- fis->lba5 = (sio->offset >> 40) & 0xFF;
+ fis->lba0 = cur_lba & 0xFF;
+ fis->lba1 = (cur_lba >> 8) & 0xFF;
+ fis->lba2 = (cur_lba >> 16) & 0xFF;
+ fis->lba3 = (cur_lba >> 24) & 0xFF;
+ fis->lba4 = (cur_lba >> 32) & 0xFF;
+ fis->lba5 = (cur_lba >> 40) & 0xFF;
/* Setup count */
- fis->countl = sio->len & 0xFF;
- fis->counth = (sio->len >> 8) & 0xFF;
+ fis->countl = len & 0xFF;
+ fis->counth = (len >> 8) & 0xFF;
- pr_trace("SUBMIT: RIGHT!\n");
if ((status = ahci_submit_cmd(hba, port, cmdslot)) != 0) {
return status;
}
+ /* Don't cache again on hit */
+ if (!write && dcdr_hit) {
+ return 0;
+ }
+
+ /* Cache our read */
+ for (size_t i = 0; i < nblocks; ++i) {
+ cur_lba = sio->offset + i;
+ p = sio->buf;
+ dcdr_cachein(dev->dcdr, &p[i * 512], cur_lba);
+ }
return 0;
}
@@ -549,7 +659,6 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
const size_t BSIZE = 512;
struct sio_txn wr_sio;
struct hba_device *devp;
- struct ahci_hba *hba;
size_t block_count, len;
off_t block_off, read_off;
char *buf;
@@ -561,11 +670,11 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
if (sio->len == 0 || sio->buf == NULL) {
return -EINVAL;
}
- if (dev >= devs_max) {
+ if (dev > devs_max) {
return -ENODEV;
}
- devp = &devs[dev];
+ devp = ahci_get_dev(dev);
if (__unlikely(devp == NULL)) {
return -ENODEV;
}
@@ -595,14 +704,14 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
wr_sio.buf = buf;
wr_sio.len = block_count;
wr_sio.offset = block_off;
- status = ahci_sata_rw(hba, devp->io, &wr_sio, write);
+ status = ahci_sata_rw(&g_hba, devp, &wr_sio, write);
if (status == 0 && !write) {
read_off = sio->offset & (BSIZE - 1);
memcpy(sio->buf, buf + read_off, sio->len);
}
dynfree(buf);
- return status;
+ return sio->len;
}
/*
@@ -611,10 +720,46 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
static int
ahci_dev_read(dev_t dev, struct sio_txn *sio, int flags)
{
+ while (DRIVER_DEFERRED()) {
+ md_pause();
+ }
+
return sata_dev_rw(dev, sio, false);
}
/*
+ * Device interface write
+ */
+static int
+ahci_dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ while (DRIVER_DEFERRED()) {
+ md_pause();
+ }
+
+ return sata_dev_rw(dev, sio, true);
+}
+
+/*
+ * Device interface number of blocks
+ */
+static int
+ahci_dev_bsize(dev_t dev)
+{
+ struct hba_device *dp;
+
+ while (DRIVER_DEFERRED()) {
+ md_pause();
+ }
+
+ if ((dp = ahci_get_dev(dev)) == NULL) {
+ return -ENODEV;
+ }
+
+ return dp->nlba;
+}
+
+/*
* Initialize a drive on an HBA port
*
* @hba: HBA descriptor
@@ -629,27 +774,19 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno)
struct hba_device *dp;
struct ctlfs_dev dev;
size_t clen, pagesz;
- uint32_t lo, hi, ssts;
- uint8_t det;
+ uint32_t lo, hi, sig;
paddr_t fra, cmdlist, tmp;
int error;
pagesz = DEFAULT_PAGESIZE;
port = &abar->ports[portno];
- /* Is anything on the port? */
- ssts = mmio_read32(&port->ssts);
- det = AHCI_PXSCTL_DET(ssts);
- switch (det) {
- case AHCI_DET_NULL:
- /* No device attached */
- return 0;
- case AHCI_DET_PRESENT:
- if ((error = hba_port_reset(hba, port)) < 0) {
- pr_trace("failed to reset port %d\n", portno);
- return error;
- }
- break;
+ if ((error = hba_port_reset(hba, port)) < 0) {
+ return error;
+ }
+ sig = mmio_read32(&port->sig);
+ if (sig == ATAPI_SIG) {
+ return -ENOTSUP;
}
pr_trace("found device @ port %d\n", portno);
@@ -658,6 +795,12 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno)
dp->hba = hba;
dp->dev = portno;
+ dp->dcdr = dcdr_alloc(512, AHCI_DCDR_CAP);
+ if (dp->dcdr == NULL) {
+ pr_error("failed to alloc dcdr\n");
+ return -ENOMEM;
+ }
+
/* Allocate a command list */
clen = ALIGN_UP(hba->nslots * AHCI_CMDENTRY_SIZE, pagesz);
clen /= pagesz;
@@ -677,8 +820,6 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno)
}
dp->fra = PHYS_TO_VIRT(fra);
- memset(dp->cmdlist, 0, clen * pagesz);
- memset(dp->fra, 0, pagesz);
/* Write the command list */
lo = cmdlist & 0xFFFFFFFF;
@@ -697,7 +838,6 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno)
tmp = vm_alloc_frame(1);
dp->cmdlist[i].prdtl = 1;
dp->cmdlist[i].ctba = tmp;
- memset(PHYS_TO_VIRT(tmp), 0, pagesz);
}
mmio_write32(&port->serr, 0xFFFFFFFF);
@@ -712,7 +852,7 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno)
return error;
}
- ahci_identify(hba, port);
+ ahci_identify(hba, dp);
if (hba->major == 0) {
hba->major = dev_alloc_major();
@@ -733,7 +873,7 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno)
dev.devname = devname;
dev.ops = &g_sata_bsize_ops;
ctlfs_create_entry("bsize", &dev);
- return devfs_create_entry(devname, hba->major, dp->dev, 0444);
+ return devfs_create_entry(devname, hba->major, dp->dev, 060444);
}
/*
@@ -841,10 +981,9 @@ ahci_init(void)
{
struct pci_lookup lookup;
int status;
- struct ahci_hba hba;
void *abar_vap = NULL;
- hba.major = 0;
+ g_hba.major = 0;
lookup.pci_class = 0x01;
lookup.pci_subclass = 0x06;
@@ -890,14 +1029,15 @@ ahci_init(void)
}
ahci_init_pci();
- hba.io = (struct hba_memspace*)abar_vap;
- ahci_hba_init(&hba);
+ g_hba.io = (struct hba_memspace*)abar_vap;
+ ahci_hba_init(&g_hba);
return 0;
}
static struct bdevsw ahci_bdevsw = {
.read = ahci_dev_read,
- .write = nowrite
+ .write = ahci_dev_write,
+ .bsize = ahci_dev_bsize
};
-DRIVER_EXPORT(ahci_init);
+DRIVER_DEFER(ahci_init, "ahci");
diff --git a/sys/dev/ic/nvme.c b/sys/dev/ic/nvme.c
index 704fdec..147ab4f 100644
--- a/sys/dev/ic/nvme.c
+++ b/sys/dev/ic/nvme.c
@@ -662,4 +662,4 @@ static struct bdevsw nvme_bdevsw = {
.write = nowrite
};
-DRIVER_EXPORT(nvme_init);
+DRIVER_DEFER(nvme_init, "nvme");
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index d59f68f..9dfb90e 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -32,15 +32,32 @@
#include <sys/syslog.h>
#include <sys/errno.h>
#include <sys/spinlock.h>
+#include <sys/mmio.h>
#include <dev/pci/pci.h>
#include <dev/pci/pciregs.h>
+#include <dev/acpi/acpi.h>
+#include <dev/acpi/tables.h>
+#include <machine/pci/pci.h>
#include <vm/dynalloc.h>
+#include <vm/vm.h>
#include <lib/assert.h>
#define pr_trace(fmt, ...) kprintf("pci: " fmt, ##__VA_ARGS__)
static TAILQ_HEAD(, pci_device) device_list;
static struct spinlock devlist_lock = {0};
+static struct acpi_mcfg *mcfg;
+
+struct cam_hook {
+ /* PCI CAM */
+ pcireg_t(*cam_readl)(struct pci_device *dev, uint32_t off);
+ void(*cam_writel)(struct pci_device *dev, uint32_t off, pcireg_t val);
+
+ /* PCIe ECAM */
+ pcireg_t(*ecam_readl)(struct pci_device *dev, uint32_t off);
+ void(*ecam_writel)(struct pci_device *dev, uint32_t off, pcireg_t val);
+ void *ecam_base[1];
+} cam_hook = { NULL };
static bool
pci_dev_exists(uint8_t bus, uint8_t slot, uint8_t func)
@@ -123,6 +140,9 @@ pci_set_device_info(struct pci_device *dev)
dev->prog_if = PCIREG_PROGIF(classrev);
dev->hdr_type = (uint8_t)pci_readl(dev, PCIREG_HDRTYPE);
+ /* This is a PCIe device if it has CAP ID of 0x10 */
+ dev->pci_express = pci_get_cap(dev, 0x10) != 0;
+
/* Set type-specific data */
switch (dev->hdr_type & ~BIT(7)) {
case PCI_HDRTYPE_NORMAL:
@@ -151,6 +171,53 @@ pci_set_device_info(struct pci_device *dev)
static void
pci_scan_bus(uint8_t bus);
+static inline vaddr_t
+pcie_ecam_addr(struct pci_device *dev)
+{
+ vaddr_t base = (vaddr_t)cam_hook.ecam_base[0];
+
+ base += dev->bus << 20 |
+ dev->slot << 15 |
+ dev->func << 12;
+ return base;
+}
+
+static pcireg_t
+pcie_ecam_readl(struct pci_device *dev, uint32_t offset)
+{
+ vaddr_t address;
+
+ address = pcie_ecam_addr(dev);
+ address += (offset & ~3);
+ return mmio_read32((void *)address);
+}
+
+static void
+pcie_ecam_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
+{
+ vaddr_t address;
+
+ address = pcie_ecam_addr(dev);
+ address += (offset & ~3);
+ mmio_write32((void *)address, val);
+}
+
+static int
+pcie_init(struct acpi_mcfg_base *base)
+{
+ void *iobase;
+
+ pr_trace("[group %02d] @ bus [%02d - %02d]\n", base->seg_grpno,
+ base->bus_start, base->bus_end);
+ pr_trace("ecam @ %p\n", base->base_pa);
+
+ iobase = PHYS_TO_VIRT(base->base_pa);
+ cam_hook.ecam_base[0] = iobase;
+ cam_hook.ecam_writel = pcie_ecam_writel;
+ cam_hook.ecam_readl = pcie_ecam_readl;
+ return 0;
+}
+
/*
* Attempt to register a device.
*
@@ -273,12 +340,46 @@ pci_add_device(struct pci_device *dev)
spinlock_release(&devlist_lock);
}
+
+pcireg_t
+pci_readl(struct pci_device *dev, uint32_t offset)
+{
+ bool have_ecam = cam_hook.ecam_readl != NULL;
+
+ if (dev->pci_express && have_ecam) {
+ return cam_hook.ecam_readl(dev, offset);
+ }
+
+ return cam_hook.cam_readl(dev, offset);
+}
+
+void
+pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
+{
+ bool have_ecam = cam_hook.ecam_writel != NULL;
+
+ if (dev->pci_express && have_ecam) {
+ cam_hook.ecam_writel(dev, offset, val);
+ return;
+ }
+
+ cam_hook.cam_writel(dev, offset, val);
+}
+
int
pci_init(void)
{
size_t ndev;
TAILQ_INIT(&device_list);
+ mcfg = acpi_query("MCFG");
+ if (mcfg != NULL) {
+ pcie_init(&mcfg->base[0]);
+ }
+
+ cam_hook.cam_readl = md_pci_readl;
+ cam_hook.cam_writel = md_pci_writel;
+
/* Recursively scan bus 0 */
pci_scan_bus(0);
ndev = TAILQ_NELEM(&device_list);
diff --git a/sys/dev/phy/e1000.c b/sys/dev/phy/e1000.c
new file mode 100644
index 0000000..41a2a27
--- /dev/null
+++ b/sys/dev/phy/e1000.c
@@ -0,0 +1,358 @@
+/*
+ * 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/driver.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/mmio.h>
+#include <dev/phy/e1000regs.h>
+#include <dev/pci/pci.h>
+#include <dev/pci/pciregs.h>
+#include <dev/timer.h>
+#include <net/if_var.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("e1000: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+#define E1000_VENDOR 0x8086
+#define E1000_DEVICE 0x100E
+#define E1000_TIMEOUT 500 /* In msec */
+
+static struct timer tmr;
+static struct pci_device *e1000;
+static struct netif netif;
+
+struct e1000_nic {
+ void *vap;
+ uint8_t has_eeprom : 1;
+ uint16_t eeprom_size;
+ uint16_t io_port;
+};
+
+static int
+e1000_poll_reg(volatile uint32_t *reg, uint32_t bits, bool pollset)
+{
+ size_t usec_start, usec;
+ size_t elapsed_msec;
+ uint32_t val;
+ bool tmp;
+
+ usec_start = tmr.get_time_usec();
+
+ for (;;) {
+ val = mmio_read32(reg);
+ tmp = (pollset) ? ISSET(val, bits) : !ISSET(val, bits);
+
+ usec = tmr.get_time_usec();
+ elapsed_msec = (usec - usec_start) / 1000;
+
+ /* If tmp is set, the register updated in time */
+ if (tmp) {
+ break;
+ }
+
+ /* Exit with an error if we timeout */
+ if (elapsed_msec > E1000_TIMEOUT) {
+ return -ETIME;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Query information about any EEPROMs for diagnostic
+ * purposes.
+ *
+ * TODO: Some wacky older chips don't show their presence
+ * too easily, we could fallback to microwire / SPI
+ * bit banging to see if it responds to us manually
+ * clocking a dummy read operation in.
+ */
+static void
+eeprom_query(struct e1000_nic *np)
+{
+ uint16_t size_bits = 1024;
+ uint32_t eecd, *eecd_p;
+ const char *typestr = "microwire";
+
+ eecd_p = PTR_OFFSET(np->vap, E1000_EECD);
+
+ /*
+ * First we should check if there is an EEPROM
+ * on-board as if not, there is nothing we can do
+ * here.
+ */
+ eecd = mmio_read32(eecd_p);
+ if (!ISSET(eecd, E1000_EECD_PRES)) {
+ return;
+ }
+
+ np->has_eeprom = 1;
+ if (ISSET(eecd, E1000_EECD_TYPE)) {
+ typestr = "SPI";
+ }
+ if (ISSET(eecd, E1000_EECD_SIZE)) {
+ size_bits = 4096;
+ }
+
+ np->eeprom_size = size_bits;
+ pr_trace("%d-bit %s EEPROM detected\n", size_bits, typestr);
+}
+
+/*
+ * If there is no EEPROM, we can still read
+ * the MAC address through the Receive address
+ * registers
+ *
+ * XXX: This is typically only used as a fallback.
+ *
+ * Returns a less than zero value if an ethernet
+ * address is not found, which would be kind of
+ * not good.
+ *
+ * @np: NIC descriptor
+ * @addr: Pointer to MAC address data
+ */
+static int
+e1000_read_recvaddr(struct e1000_nic *np, struct netif_addr *addr)
+{
+ const uint32_t RECVADDR_OFF = 0x5400;
+ uint32_t tmp;
+ uint32_t *dword_p;
+
+ dword_p = PTR_OFFSET(np->vap, RECVADDR_OFF);
+
+ if (dword_p[0] == 0) {
+ pr_error("bad hwaddr in recvaddr\n");
+ return -ENOTSUP;
+ }
+
+ /* DWORD 0 */
+ tmp = mmio_read32(&dword_p[0]);
+ addr->data[0] = tmp & 0xFF;
+ addr->data[1] = (tmp >> 8) & 0xFF;
+ addr->data[2] = (tmp >> 16) & 0xFF;
+ addr->data[3] = (tmp >> 24) & 0xFF;
+
+ /* DWORD 1 */
+ tmp = mmio_read32(&dword_p[1]);
+ addr->data[4] = tmp & 0xFF;
+ addr->data[5] = (tmp >> 8) & 0xFF;
+ return 0;
+}
+
+/*
+ * Read 16-bytes from the NIC's on-board EEPROM.
+ *
+ * XXX: This should only be used if the caller is
+ * certain that the NIC has an EEPROM
+ *
+ * @addr: EEPROM address to read from
+ *
+ * A returned value of 0xFFFF should be seen as invalid.
+ */
+static uint16_t
+eeprom_readw(struct e1000_nic *np, uint8_t addr)
+{
+ uint32_t eerd, *eerd_p;
+ int error;
+
+ if (!np->has_eeprom) {
+ pr_error("e1000_read_eeprom: EEPROM not present\n");
+ return 0xFFFF;
+ }
+
+ eerd_p = PTR_OFFSET(np->vap, E1000_EERD);
+ eerd = (addr << 8) | E1000_EERD_START;
+ mmio_write32(eerd_p, eerd);
+
+ error = e1000_poll_reg(eerd_p, E1000_EERD_DONE, true);
+ if (error < 0) {
+ pr_error("e1000_read_eeprom: timeout\n");
+ return 0xFFFF;
+ }
+
+ eerd = mmio_read32(eerd_p);
+ return (eerd >> 16) & 0xFFFF;
+}
+
+/*
+ * Read the MAC address from the NICs EEPROM.
+ *
+ * XXX: This should usually work, however if the NIC does
+ * not have an on-board EEPROM, this will fail. In such
+ * cases, e1000_read_recvaddr() can be called instead.
+ *
+ * @np: NIC descriptor
+ * @addr: Pointer to MAC address data
+ */
+static int
+e1000_read_macaddr(struct e1000_nic *np, struct netif_addr *addr)
+{
+ uint16_t eeprom_word;
+
+ if (!np->has_eeprom) {
+ pr_trace("EEPROM not present, trying recvaddr\n");
+ return e1000_read_recvaddr(np, addr);
+ }
+
+ /* Word 0 */
+ eeprom_word = eeprom_readw(np, E1000_HWADDR0);
+ addr->data[0] = (eeprom_word & 0xFF);
+ addr->data[1] = (eeprom_word >> 8) & 0xFF;
+
+ /* Word 1 */
+ eeprom_word = eeprom_readw(np, E1000_HWADDR1);
+ addr->data[2] = (eeprom_word & 0xFF);
+ addr->data[3] = (eeprom_word >> 8) & 0xFF;
+
+ /* Word 2 */
+ eeprom_word = eeprom_readw(np, E1000_HWADDR2);
+ addr->data[4] = (eeprom_word & 0xFF);
+ addr->data[5] = (eeprom_word >> 8) & 0xFF;
+ return 0;
+}
+
+/*
+ * Reset the entire E1000
+ */
+static int
+e1000_reset(struct e1000_nic *np)
+{
+ uint32_t ctl, *ctl_p;
+ int error;
+
+ ctl_p = PTR_OFFSET(np->vap, E1000_CTL);
+ ctl = mmio_read32(&ctl_p);
+ ctl |= E1000_CTL_RST;
+ mmio_write32(&ctl_p, ctl);
+
+ error = e1000_poll_reg(ctl_p, E1000_CTL_RST, false);
+ if (error < 0) {
+ pr_error("reset timeout\n");
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize an E1000(e) chip
+ */
+static int
+e1000_chip_init(struct e1000_nic *np)
+{
+ struct netif_addr *addr = &netif.addr;
+ int error;
+
+ /*
+ * To ensure that BIOS/UEFI or whatever firmware got us
+ * here didn't fuck anything up in the process or at the
+ * very least, put the controller in a seemingly alright
+ * state that gives us a suprise screwing in the future,
+ * we'll reset everything to its default startup state.
+ *
+ * Better safe than sorry...
+ */
+ if ((error = e1000_reset(np)) < 0) {
+ return error;
+ }
+
+ eeprom_query(np);
+ if ((error = e1000_read_macaddr(np, addr)) < 0) {
+ return error;
+ }
+
+ pr_trace("MAC address: %x:%x:%x:%x:%x:%x\n",
+ (uint64_t)addr->data[0], (uint64_t)addr->data[1],
+ (uint64_t)addr->data[2], (uint64_t)addr->data[3],
+ (uint64_t)addr->data[4], (uint64_t)addr->data[5]);
+
+ return 0;
+}
+
+/*
+ * Enables PCI specific bits like bus mastering (for DMA)
+ * as well as MMIO.
+ */
+static void
+e1000_init_pci(void)
+{
+ uint32_t tmp;
+
+ tmp = pci_readl(e1000, PCIREG_CMDSTATUS);
+ tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE);
+ pci_writel(e1000, PCIREG_CMDSTATUS, tmp);
+}
+
+static int
+e1000_init(void)
+{
+ struct pci_lookup lookup;
+ struct e1000_nic nic;
+ int status;
+
+ lookup.vendor_id = E1000_VENDOR;
+ lookup.device_id = E1000_DEVICE;
+ e1000 = pci_get_device(lookup, PCI_DEVICE_ID | PCI_VENDOR_ID);
+ if (e1000 == NULL) {
+ return -ENODEV;
+ }
+
+ /* Get a GP timer */
+ if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) {
+ pr_error("failed to fetch general purpose timer\n");
+ return -ENODEV;
+ }
+
+ /* We need msleep() */
+ if (tmr.msleep == NULL) {
+ pr_error("general purpose timer has no msleep()\n");
+ return -ENODEV;
+ }
+
+ memset(&nic, 0, sizeof(nic));
+ pr_trace("e1000 at pci%d:%x.%x.%d\n",
+ e1000->bus, e1000->device_id, e1000->func,
+ e1000->slot);
+
+ if ((status = pci_map_bar(e1000, 0, &nic.vap)) != 0) {
+ pr_error("failed to map BAR0\n");
+ return status;
+ }
+
+ e1000_init_pci();
+ e1000_chip_init(&nic);
+ return 0;
+}
+
+DRIVER_EXPORT(e1000_init, "e1000");
diff --git a/sys/dev/phy/rt8139.c b/sys/dev/phy/rtl.c
index ab8a6c0..d096d1a 100644
--- a/sys/dev/phy/rt8139.c
+++ b/sys/dev/phy/rtl.c
@@ -30,31 +30,30 @@
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/syslog.h>
+#include <sys/spinlock.h>
#include <sys/driver.h>
#include <sys/device.h>
#include <dev/pci/pci.h>
-#include <dev/phy/rt8139.h>
+#include <dev/phy/rtl.h>
#include <dev/timer.h>
#include <dev/pci/pciregs.h>
-#include <net/if_ether.h>
+#include <net/netbuf.h>
+#include <net/if_var.h>
#include <vm/physmem.h>
+#include <vm/dynalloc.h>
#include <vm/vm.h>
#include <machine/pio.h>
+#include <machine/intr.h>
#include <string.h>
-/* TODO: Make this smoother */
-#if defined(__x86_64__)
-#include <machine/intr.h>
-#include <machine/ioapic.h>
-#include <machine/lapic.h>
-#include <machine/idt.h>
-#endif
+#define IFNAME "rt0"
-#define pr_trace(fmt, ...) kprintf("rt8139: " fmt, ##__VA_ARGS__)
+#define pr_trace(fmt, ...) kprintf("rt81xx: " fmt, ##__VA_ARGS__)
#define pr_error(...) pr_trace(__VA_ARGS__)
#define RX_BUF_SIZE 3 /* In pages */
#define RX_REAL_BUF_SIZE 8192 /* In bytes */
+#define TXQ_ENTRIES 4
#define RX_PTR_MASK (~3)
@@ -65,12 +64,26 @@
#define HAVE_PIO 0
#endif /* _MACHINE_HAVE_PIO */
+static struct spinlock netif_lock;
+static struct netbuf netif_buf[TXQ_ENTRIES];
static struct pci_device *dev;
+static struct netif netif;
static struct timer tmr;
-static struct etherdev wire;
+static uint32_t tx_ptr = 0;
+static uint32_t netif_enq_ptr = 0;
static uint16_t ioport;
static paddr_t rxbuf, txbuf;
+/* TXAD regs */
+static uint16_t tsads[TXQ_ENTRIES] = {
+ RT_TXAD_N(0), RT_TXAD_N(4),
+ RT_TXAD_N(8), RT_TXAD_N(12)
+};
+static uint16_t tsds[TXQ_ENTRIES] = {
+ RT_TXSTATUS_N(0), RT_TXSTATUS_N(4),
+ RT_TXSTATUS_N(8), RT_TXSTATUS_N(8)
+};
+
/*
* Write to an RTL8139 register
*
@@ -159,53 +172,112 @@ rt_poll(uint8_t reg, uint8_t size, uint32_t bits, bool pollset)
return val;
}
-#if defined(__x86_64__)
-__isr static void
-rt8139_pin_irq(void *sp)
+static int
+rt_tx(void *packet, size_t len)
+{
+ static uint32_t tx_ptr = 0;
+ void *tx_data;
+ paddr_t tx_pa;
+
+ tx_data = dynalloc(len);
+ if (tx_data == NULL) {
+ return -ENOMEM;
+ }
+
+ memcpy(tx_data, packet, len);
+ tx_pa = VIRT_TO_PHYS(tx_data);
+ rt_write(tsads[tx_ptr], 4, tx_pa);
+ rt_write(tsds[tx_ptr++], 4, len);
+ if (tx_ptr > TXQ_ENTRIES - 1) {
+ tx_ptr = 0;
+ }
+ return 0;
+}
+
+static void
+__rt81xx_tx_start(struct netif *nifp)
+{
+ struct netbuf *dest;
+ int error;
+
+ for (int i = 0; i < netif_enq_ptr; ++i) {
+ dest = &netif_buf[i];
+ error = rt_tx(dest->data, dest->len);
+ if (error < 0) {
+ pr_error("tx_start fail @queue %d (errno=%d)\n", i, error);
+ }
+ }
+}
+
+static void
+rt81xx_tx_start(struct netif *nifp)
+{
+ spinlock_acquire(&netif_lock);
+ __rt81xx_tx_start(nifp);
+ spinlock_release(&netif_lock);
+}
+
+static int
+rt81xx_tx_enq(struct netif *nifp, struct netbuf *nbp, void *data)
+{
+ struct netbuf *dest;
+
+ spinlock_acquire(&netif_lock);
+ dest = &netif_buf[netif_enq_ptr++];
+ memcpy(dest, nbp, sizeof(*dest));
+
+ if (netif_enq_ptr > TXQ_ENTRIES - 1) {
+ __rt81xx_tx_start(nifp);
+ netif_enq_ptr = 0;
+ }
+ spinlock_release(&netif_lock);
+ return 0;
+}
+
+static int
+rt81xx_intr(void *sp)
{
- static uint32_t packet_ptr = 0;
uint16_t len;
uint16_t *p;
uint16_t status;
status = rt_read(RT_INTRSTATUS, 2);
- p = (uint16_t *)(rxbuf + packet_ptr);
+ p = (uint16_t *)(rxbuf + tx_ptr);
len = *(p + 1); /* Length after header */
p += 2; /* Points to data now */
- if (status & RT_TOK) {
- return;
+ if (!ISSET(status, RT_TOK | RT_ROK)) {
+ return 0;
+ }
+
+ if (ISSET(status, RT_TOK)) {
+ pr_trace("sent packet\n");
+ return 1;
}
/* Update rxbuf offset in CAPR */
- packet_ptr = (packet_ptr + len + 4 + 3) & RX_PTR_MASK;
- if (packet_ptr > RX_REAL_BUF_SIZE) {
- packet_ptr -= RX_REAL_BUF_SIZE;
+ tx_ptr = (tx_ptr + len + 4 + 3) & RX_PTR_MASK;
+ if (tx_ptr > RX_REAL_BUF_SIZE) {
+ tx_ptr -= RX_REAL_BUF_SIZE;
}
- rt_write(RT_RXBUFTAIL, 2, packet_ptr - 0x10);
+ rt_write(RT_RXBUFTAIL, 2, tx_ptr - 0x10);
rt_write(RT_INTRSTATUS, 2, RT_ACKW);
- lapic_eoi();
+ return 1; /* handled */
}
static int
-rtl8139_irq_init(void)
+rt81xx_irq_init(void)
{
- int vec;
+ struct intr_hand ih;
- vec = intr_alloc_vector("rt8139", IPL_BIO);
- if (vec < 0) {
- return vec;
+ ih.func = rt81xx_intr;
+ ih.priority = IPL_BIO;
+ ih.irq = dev->irq_line;
+ if (intr_register("rt81xx", &ih) == NULL) {
+ return -EIO;
}
-
- /* Map interrupt vector to IRQ */
- idt_set_desc(vec, IDT_INT_GATE, ISR(rt8139_pin_irq), 0);
- ioapic_set_vec(dev->irq_line, vec);
- ioapic_irq_unmask(dev->irq_line);
return 0;
}
-#else
-#define rtl8139_irq_init(...) -ENOTSUP
-#endif
static void
rt_init_pci(void)
@@ -221,6 +293,7 @@ rt_init_pci(void)
static int
rt_init_mac(void)
{
+ struct netif_addr *addr = &netif.addr;
uint8_t conf;
uint32_t tmp;
int error;
@@ -256,20 +329,20 @@ rt_init_mac(void)
/* MAC address dword 0 */
tmp = rt_read(RT_IDR0, 4);
- wire.mac_addr[0] = tmp & 0xFF;
- wire.mac_addr[1] = (tmp >> 8) & 0xFF;
- wire.mac_addr[2] = (tmp >> 16) & 0xFF;
- wire.mac_addr[3] = (tmp >> 24) & 0xFF;
+ addr->data[0] = tmp & 0xFF;
+ addr->data[1] = (tmp >> 8) & 0xFF;
+ addr->data[2] = (tmp >> 16) & 0xFF;
+ addr->data[3] = (tmp >> 24) & 0xFF;
/* MAC address word 1 */
tmp = rt_read(RT_IDR2, 4);
- wire.mac_addr[4] = (tmp >> 16) & 0xFF;
- wire.mac_addr[5] = (tmp >> 24) & 0xFF;
+ addr->data[4] = (tmp >> 16) & 0xFF;
+ addr->data[5] = (tmp >> 24) & 0xFF;
pr_trace("MAC address: %x:%x:%x:%x:%x:%x\n",
- (uint64_t)wire.mac_addr[0], (uint64_t)wire.mac_addr[1],
- (uint64_t)wire.mac_addr[2], (uint64_t)wire.mac_addr[3],
- (uint64_t)wire.mac_addr[4], (uint64_t)wire.mac_addr[5]);
+ (uint64_t)addr->data[0], (uint64_t)addr->data[1],
+ (uint64_t)addr->data[2], (uint64_t)addr->data[3],
+ (uint64_t)addr->data[4], (uint64_t)addr->data[5]);
/*
* Alright, now we don't want those EEM bits
@@ -293,6 +366,11 @@ rt_init_mac(void)
return -ENOMEM;
}
+ memcpy(netif.name, IFNAME, strlen(IFNAME) + 1);
+ netif.tx_enq = rt81xx_tx_enq;
+ netif.tx_start = rt81xx_tx_start;
+ netif_add(&netif);
+
/*
* Configure the chip:
*
@@ -310,19 +388,17 @@ rt_init_mac(void)
* - Enable interrupts through ROK/TOK
* - Enable RX state machines
*
- * TODO: Support TX
- *
*/
- rtl8139_irq_init();
+ rt81xx_irq_init();
rt_write(RT_RXBUF, 4, rxbuf);
rt_write(RT_RXCONFIG, 4, RT_AB | RT_AM | RT_APM | RT_AAP);
rt_write(RT_INTRMASK, 2, RT_ROK | RT_TOK);
- rt_write(RT_CHIPCMD, 1, RT_RE);
+ rt_write(RT_CHIPCMD, 1, RT_RE | RT_TE);
return 0;
}
static int
-rt813l_init(void)
+rt81xx_init(void)
{
struct pci_lookup lookup;
@@ -364,4 +440,4 @@ rt813l_init(void)
return rt_init_mac();
}
-DRIVER_EXPORT(rt813l_init);
+DRIVER_DEFER(rt81xx_init, "rtl81xx");
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
index d68f98e..0ccb7a0 100644
--- a/sys/dev/usb/xhci.c
+++ b/sys/dev/usb/xhci.c
@@ -37,6 +37,7 @@
#include <dev/usb/xhciregs.h>
#include <dev/usb/xhcivar.h>
#include <dev/pci/pci.h>
+#include <dev/pci/pciregs.h>
#include <dev/acpi/acpi.h>
#include <vm/physmem.h>
#include <vm/dynalloc.h>
@@ -56,10 +57,11 @@
static struct pci_device *hci_dev;
static struct timer tmr;
-__attribute__((__interrupt__)) static void
-xhci_common_isr(void *sf)
+static int
+xhci_intr(void *sf)
{
pr_trace("received xHCI interrupt (via PCI MSI-X)\n");
+ return 1; /* handled */
}
/*
@@ -174,6 +176,7 @@ xhci_init_scratchpads(struct xhci_hc *hc)
struct xhci_caps *caps = XHCI_CAPS(hc->base);
uint16_t max_bufs_lo, max_bufs_hi;
uint16_t max_bufs;
+ size_t len;
uintptr_t *bufarr, tmp;
max_bufs_lo = XHCI_MAX_SP_LO(caps->hcsparams1);
@@ -187,8 +190,9 @@ xhci_init_scratchpads(struct xhci_hc *hc)
return 0;
}
- pr_trace("using %d pages for xHC scratchpads\n");
- bufarr = dynalloc_memalign(sizeof(uintptr_t)*max_bufs, 0x1000);
+ len = sizeof(uint64_t) * max_bufs;
+ pr_trace("using %d bytes for xHC scratchpads\n", len);
+ bufarr = dynalloc_memalign(len, 0x1000);
if (bufarr == NULL) {
pr_error("failed to allocate scratchpad buffer array\n");
return -1;
@@ -232,7 +236,7 @@ xhci_init_msix(struct xhci_hc *hc)
struct msi_intr intr;
intr.name = "xHCI MSI-X";
- intr.handler = xhci_common_isr;
+ intr.handler = xhci_intr;
return pci_enable_msix(hci_dev, &intr);
}
@@ -254,7 +258,7 @@ xhci_init_evring(struct xhci_hc *hc)
memset(segtab, 0, DEFAULT_PAGESIZE);
/* Set the size of the event ring segment table */
- erst_size = PTR_OFFSET(runtime, 0x28);
+ erst_size = PTR_OFFSET(runtime, XHCI_RT_ERSTSZ);
mmio_write32(erst_size, 1);
/* Allocate the event ring segment */
@@ -268,20 +272,20 @@ xhci_init_evring(struct xhci_hc *hc)
segtab->size = XHCI_EVRING_LEN;
/* Setup the event ring dequeue pointer */
- erdp = PTR_OFFSET(runtime, 0x38);
+ erdp = PTR_OFFSET(runtime, XHCI_RT_ERDP);
mmio_write64(erdp, segtab->base);
/* Point ERSTBA to our event ring segment */
- erstba = PTR_OFFSET(runtime, 0x30);
+ erstba = PTR_OFFSET(runtime, XHCI_RT_ERSTBA);
mmio_write64(erstba, VIRT_TO_PHYS(segtab));
hc->evring = PHYS_TO_VIRT(segtab->base);
/* Setup interrupt moderation */
- imod = PTR_OFFSET(runtime, 0x24);
+ imod = PTR_OFFSET(runtime, XHCI_RT_IMOD);
mmio_write32(imod, XHCI_IMOD_DEFAULT);
/* Enable interrupts */
- iman = PTR_OFFSET(runtime, 0x20);
+ iman = PTR_OFFSET(runtime, XHCI_RT_IMAN);
tmp = mmio_read32(iman);
mmio_write32(iman, tmp | XHCI_IMAN_IE);
}
@@ -495,6 +499,16 @@ xhci_init_hc(struct xhci_hc *hc)
return 0;
}
+static void
+xhci_init_pci(void)
+{
+ uint32_t tmp;
+
+ tmp = pci_readl(hci_dev, PCIREG_CMDSTATUS);
+ tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE);
+ pci_writel(hci_dev, PCIREG_CMDSTATUS, tmp);
+}
+
static int
xhci_init(void)
{
@@ -527,7 +541,8 @@ xhci_init(void)
return -ENODEV;
}
+ xhci_init_pci();
return xhci_init_hc(&xhc);
}
-DRIVER_EXPORT(xhci_init);
+DRIVER_EXPORT(xhci_init, "xhci");
diff --git a/sys/dev/video/fbdev.c b/sys/dev/video/fbdev.c
index f21c769..6b1c6c8 100644
--- a/sys/dev/video/fbdev.c
+++ b/sys/dev/video/fbdev.c
@@ -28,17 +28,65 @@
*/
#include <sys/types.h>
+#include <sys/errno.h>
#include <sys/limine.h>
+#include <sys/device.h>
+#include <sys/driver.h>
+#include <sys/fbdev.h>
#include <dev/video/fbdev.h>
+#include <fs/devfs.h>
+#include <fs/ctlfs.h>
+#include <vm/vm.h>
+#include <string.h>
#define FRAMEBUFFER \
framebuffer_req.response->framebuffers[0]
+static struct cdevsw fb_cdevsw;
+static const struct ctlops fb_size_ctl;
static volatile struct limine_framebuffer_request framebuffer_req = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0
};
+static paddr_t
+fbdev_mmap(dev_t dev, size_t size, off_t off, int flags)
+{
+ size_t max_bounds;
+
+ max_bounds = FRAMEBUFFER->pitch * FRAMEBUFFER->height;
+ if ((off + size) > max_bounds) {
+ return 0;
+ }
+
+ return VIRT_TO_PHYS(FRAMEBUFFER->address);
+}
+
+static int
+ctl_attr_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct fbattr attr;
+ size_t len = sizeof(attr);
+
+ if (sio == NULL) {
+ return -EINVAL;
+ }
+ if (sio->buf == NULL) {
+ return -EINVAL;
+ }
+
+ if (len > sio->len) {
+ len = sio->len;
+ }
+
+ attr.width = FRAMEBUFFER->width;
+ attr.height = FRAMEBUFFER->height;
+ attr.pitch = FRAMEBUFFER->pitch;
+ attr.bpp = FRAMEBUFFER->bpp;
+ memcpy(sio->buf, &attr, len);
+ return len;
+}
+
struct fbdev
fbdev_get(void)
{
@@ -51,3 +99,40 @@ fbdev_get(void)
ret.bpp = FRAMEBUFFER->bpp;
return ret;
}
+
+static int
+fbdev_init(void)
+{
+ struct ctlfs_dev ctl;
+ char devname[] = "fb0";
+ devmajor_t major;
+ dev_t dev;
+
+ /* Register the device here */
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+ dev_register(major, dev, &fb_cdevsw);
+ devfs_create_entry(devname, major, dev, 0444);
+
+
+ /* Register control files */
+ ctl.mode = 0444;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &fb_size_ctl;
+ ctlfs_create_entry("attr", &ctl);
+ return 0;
+}
+
+static struct cdevsw fb_cdevsw = {
+ .read = noread,
+ .write = nowrite,
+ .mmap = fbdev_mmap
+};
+
+static const struct ctlops fb_size_ctl = {
+ .read = ctl_attr_read,
+ .write = NULL,
+};
+
+DRIVER_EXPORT(fbdev_init, "fbdev");