aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2023-06-04 15:13:43 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2023-06-04 15:13:43 +0300
commit3dbd8c34176312b0bd3aba2309d284ac83181411 (patch)
tree9bf6216f2c120a388308457444738d0c55002bd2
parent52e5c94314fa8e10efded82db85d181ed32f00bd (diff)
downloadkmi-3dbd8c34176312b0bd3aba2309d284ac83181411.tar.gz
kmi-3dbd8c34176312b0bd3aba2309d284ac83181411.zip
visionfive2 boots
+ Make 8250 serial driver more generic + Still TODO: write a tutorial on how to boot on the visionfive2
-rw-r--r--arch/riscv64/config.h3
-rw-r--r--arch/riscv64/kernel/vmem.c16
-rw-r--r--common/debug.c133
-rw-r--r--common/initrd.c1
-rw-r--r--common/pmem.c67
-rw-r--r--include/arch/vmem.h6
6 files changed, 138 insertions, 88 deletions
diff --git a/arch/riscv64/config.h b/arch/riscv64/config.h
index 3eb2e72..a2694d5 100644
--- a/arch/riscv64/config.h
+++ b/arch/riscv64/config.h
@@ -21,8 +21,7 @@
#include <kmi/sizes.h>
/* --- START ARCH USER CONFIG VALUES --- */
-/** Physical address to where the OS image will be loaded. */
-//#define RAM_BASE 0x80000000
+/* nada for now */
/* --- END ARCH USER CONFIG VALUES --- */
/* don't touch >:( */
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c
index f29c325..89d895f 100644
--- a/arch/riscv64/kernel/vmem.c
+++ b/arch/riscv64/kernel/vmem.c
@@ -369,7 +369,7 @@ void flush_tlb(uintptr_t addr)
void flush_tlb_full()
{
- __asm__ volatile ("sfence.vma %0\n" :: "r" (0) : "memory");
+ __asm__ volatile ("sfence.vma %0\n" : : "r" (0) : "memory");
}
void flush_tlb_all()
@@ -451,9 +451,17 @@ stat_t populate_kvmem(struct vmem *b)
vm_t setup_kernel_io(struct vmem *b, vm_t paddr)
{
pm_t top_page = paddr / TOP_PAGE_SIZE;
- b->leaf[IO_PAGE] = (struct vmem *)to_pte(top_page * TOP_PAGE_SIZE,
- VM_V | VM_R | VM_W | VM_A | VM_D);
- return -TOP_PAGE_SIZE + paddr - (top_page * TOP_PAGE_SIZE);
+ pm_t addr = top_page * TOP_PAGE_SIZE;
+ b->leaf[IO_PAGE] = (struct vmem *)to_pte(addr,
+ VM_V | VM_R | VM_W | VM_A |
+ VM_D);
+ /* flush might be necessary when we're in the actual vmem we're
+ * modifying, or during the startup stage where we don't have a tcb yet.
+ * I don't think checking the rpc context is necessary? */
+ if (!cur_tcb() || cur_tcb()->proc.vmem == b)
+ flush_tlb((uintptr_t)pte_addr(b->leaf[IO_PAGE]));
+
+ return -TOP_PAGE_SIZE + paddr - addr;
}
#endif
diff --git a/common/debug.c b/common/debug.c
index 346934a..f3cacdf 100644
--- a/common/debug.c
+++ b/common/debug.c
@@ -20,18 +20,21 @@
/** Debug context structure. */
struct dbg_info {
- /** Address of serial device in memory. */
- pm_t dbg_ptr;
-
+ /** Address of serial device in virtual memory. Always access registers
+ * through this address rather than \p addr. */
+ pm_t base;
+ /** Shift count between registers of serial device. */
+ size_t shift;
/** Type of serial device. */
enum serial_dev dev;
+ /** Physical address of serial device. */
+ pm_t addr;
};
/** Static debugging information. */
static struct dbg_info dbg_info = (struct dbg_info){ 0 };
/* forward declarations. */
-static void __setup_dbg(pm_t pt, enum serial_dev dev);
static struct dbg_info __dbg_from_fdt(const void *fdt);
void init_dbg(const void *fdt)
@@ -41,51 +44,41 @@ void init_dbg(const void *fdt)
void setup_dmap_dbg()
{
- __setup_dbg(dbg_info.dbg_ptr, dbg_info.dev);
+ /* noop with the new 8250 driver, though might still be useful in the
+ * future if I add in some other kind of simple uart */
}
void setup_io_dbg(struct vmem *b)
{
- vm_t io_ptr = map_io_dbg(b);
- __setup_dbg(io_ptr, dbg_info.dev);
+ dbg_info.base = map_io_dbg(b);
}
vm_t map_io_dbg(struct vmem *b)
{
- return setup_kernel_io(b, dbg_info.dbg_ptr);
+ return setup_kernel_io(b, dbg_info.addr);
}
+/** 8250 data register index. */
+#define UART_8250_DATA 0
+/** 8250 irq register idex. */
+#define UART_8250_IRQ 1
+/** 8250 ird_id register index. */
+#define UART_8250_IRQ_ID 2
+/** 8250 lcr register index. */
+#define UART_8250_LCR 3
+/** 8250 mcr register index. */
+#define UART_8250_MCR 4
+/** 8250 lsr register index. */
+#define UART_8250_LSR 5
+/** 8250 msr register index. */
+#define UART_8250_MSR 6
+/** 8250 scr register index. */
+#define UART_8250_SCR 7
+
/* if there arises a need for more supported serial drivers, I should probably
* try to implement some kind of basic driver subsystem, but this is good enough
* for now. */
-/** 8250 and compatible serial drivers. */
-struct __packed uart_8250 {
- /** Receiver buffer/transmitter holding register. */
- uint8_t data;
-
- /** Interrupt enable register. */
- uint8_t irq;
-
- /** Interrupt identity/FIFO control register. */
- uint8_t irq_id;
-
- /** Line control register. */
- uint8_t lcr;
-
- /** Modem control register. */
- uint8_t mcr;
-
- /** Line status register. */
- uint8_t lsr;
-
- /** Modem status register. */
- uint8_t msr;
-
- /** Scratch register. */
- uint8_t scr;
-};
-
/** Line status data ready. */
#define LSR_DR (1 << 0)
@@ -111,17 +104,11 @@ struct __packed uart_8250 {
#define LSR_ERR (1 << 7)
/**
- * Address of generic 8250 port. If other serial drivers are added, this should
- * maybe be made a void *. No support for quirks at the moment.
- */
-static struct uart_8250 *port = 0;
-
-/**
- * Serial transmitter empty.
+ * 8250 transmitter empty.
*
* @return \c 0 if not empty, non-zero otherwise.
*/
-static int __serial_tx_empty()
+static int __8250_tx_empty()
{
/**
* @todo visionfive2 loops on lsr indefinitely, why?
@@ -129,23 +116,43 @@ static int __serial_tx_empty()
* meaning the registers are spaced apart more in memory than my naive
* struct.
* */
- return port->lsr & LSR_THRE;
+ volatile uint8_t *lsr = (uint8_t *)dbg_info.base +
+ (UART_8250_LSR << dbg_info.shift);
+ return (*lsr) & LSR_THRE;
}
/**
- * Put character out onto serial lines.
+ * Put character out onto 8250 serial lines.
*
* @param c Character to output.
*/
-static void __putchar(char c)
+static void __8250_putchar(char c)
{
- if (!port)
+ if (!dbg_info.base)
return;
- while (__serial_tx_empty() == 0)
+ while (__8250_tx_empty() == 0)
;
- port->data = c;
+ volatile uint8_t *data = (uint8_t *)dbg_info.base +
+ (UART_8250_DATA << dbg_info.shift);
+ *data = c;
+}
+
+/**
+ * Put one character out on the serial lines.
+ * Automatically handles newlines.
+ *
+ * @param c Character to put.
+ */
+static void __putchar(char c)
+{
+ if (c == '\n')
+ __putchar('\r');
+
+ switch (dbg_info.dev) {
+ case UART_8250: __8250_putchar(c); return;
+ }
}
/**
@@ -200,28 +207,18 @@ static struct dbg_info __dbg_from_fdt(const void *fdt)
/* get serial device address */
struct cell_info ci = get_reginfo(fdt, stdout);
void *reg_ptr = (void *)fdt_getprop(fdt, stdout_offset, "reg", NULL);
-
pm_t dbg_ptr = (pm_t)fdt_load_int_ptr(ci.addr_cells, reg_ptr);
- return (struct dbg_info){ dbg_ptr, dev };
-}
-
-/**
- * Set static port.
- *
- * @param pt Address of memory mapped serial device.
- * @param dev Chosen device.
- */
-void __setup_dbg(vm_t pt, enum serial_dev dev)
-{
- switch (dev) {
- case UART_8250:
- port = (struct uart_8250 *)pt;
- break;
- }
+ /* get serial device offset if present */
+ size_t shift = 0;
+ void *shift_ptr = (void *)fdt_getprop(fdt, stdout_offset, "reg-shift",
+ NULL);
+ if (shift_ptr)
+ shift = (size_t)fdt_load_int32_ptr(shift_ptr);
- /* in the future possibly configure the serial connection, though the
- * defaults (set by U-boot) seem to work alright */
+ /* while in direct map, base == addr, and this changes only when we jump
+ * into virtually mapped io */
+ return (struct dbg_info){ dbg_ptr, shift, dev, dbg_ptr };
}
/** Printf formatting left align flag. */
diff --git a/common/initrd.c b/common/initrd.c
index 4bb25e9..caff8b3 100644
--- a/common/initrd.c
+++ b/common/initrd.c
@@ -11,6 +11,7 @@
#include <kmi/string.h>
#include <kmi/utils.h>
#include <kmi/attrs.h>
+#include <kmi/debug.h>
#include <libfdt.h>
/** GNU cpio, POSIX 'newc' format header. */
diff --git a/common/pmem.c b/common/pmem.c
index c817c5a..0fa647f 100644
--- a/common/pmem.c
+++ b/common/pmem.c
@@ -518,6 +518,11 @@ pm_t probe_pmap(size_t ram_size)
*/
static void __mark_area_used(pm_t base, pm_t top)
{
+ if (top < base) {
+ bug("top < base: %lx < %lx\n", top, base);
+ return;
+ }
+
size_t area_left = top - base;
pm_t runner = base;
while (area_left >= BASE_PAGE_SIZE) {
@@ -537,20 +542,29 @@ static void __mark_area_used(pm_t base, pm_t top)
*/
static void __mark_reserved_mem(void *fdt)
{
- int rmem_offset = fdt_path_offset(fdt, "/reserved-memory/mmode_resv0");
- struct cell_info ci = get_reginfo(fdt, "/reserved-memory/mmode_resv0");
- uint8_t *rmem_reg =
- (uint8_t *)fdt_getprop(fdt, rmem_offset, "reg", NULL);
+ int rmem_offset = fdt_path_offset(fdt, "/reserved-memory");
+ struct cell_info ci = get_reginfo(fdt, "/reserved-memory");
- pm_t base = (pm_t)fdt_load_int_ptr(ci.addr_cells, rmem_reg);
+ int node = 0;
+ fdt_for_each_subnode(node, fdt, rmem_offset) {
+ uint8_t *rmem_reg =
+ (uint8_t *)fdt_getprop(fdt, node, "reg", NULL);
- if (ci.addr_cells == 2)
- rmem_reg += sizeof(fdt64_t);
- else
- rmem_reg += sizeof(fdt32_t);
+ pm_t base = (pm_t)fdt_load_int_ptr(ci.addr_cells, rmem_reg);
+
+ if (ci.addr_cells == 2)
+ rmem_reg += sizeof(fdt64_t);
+ else
+ rmem_reg += sizeof(fdt32_t);
- pm_t top = (pm_t)fdt_load_int_ptr(ci.size_cells, rmem_reg) + base;
- __mark_area_used((pm_t)__va(base), (pm_t)__va(top));
+ /** @todo make sure the top of a reserved memory area doesn't go
+ * against our assumptions in FW_MAX_SIZE? */
+ pm_t top =
+ (pm_t)fdt_load_int_ptr(ci.size_cells, rmem_reg) + base;
+ __mark_area_used((pm_t)__va(base), (pm_t)__va(top));
+ info("marked [%lx - %lx] reserved\n", (pm_t)__va(
+ base), (pm_t)__va(top));
+ }
}
/**
@@ -603,6 +617,13 @@ static pm_t __get_fdtbase(void *fdt)
void init_pmem(void *fdt)
{
+ /** @todo should I keep the info outputs? I suppose it's nice to see
+ * if any assumption is being broken in the serial log, but in that case
+ * I should really try adding more of them to other parts of the
+ * codebase as well, the pmem subsystem isn't really especially complex.
+ */
+ info("initializing pmem\n");
+
size_t max_order = 0;
size_t base_bits = 0;
size_t bits[NUM_ORDERS] = { 0 };
@@ -612,17 +633,29 @@ void init_pmem(void *fdt)
pm_t ram_size = __get_ramtop(fdt) - get_ram_base();
pm_t ram_base = (pm_t)__va(get_ram_base());
+ info("using ram range [%lx - %lx]\n",
+ ram_base, ram_base + ram_size);
+
/** @todo could probably improve error messages on failing to get fdt
* values */
+ pm_t initrd_base = get_initrdbase(fdt);
pm_t initrd_top = get_initrdtop(fdt);
+ info("found initrd at [%lx - %lx]\n", initrd_base, initrd_top);
+
pm_t fdt_top = __get_fdttop(fdt);
+ pm_t fdt_base = __get_fdtbase(fdt);
+ info("found fdt at [%lx - %lx]\n", fdt_base, fdt_top);
/* find probably most suitable contiguous region of ram for our physical
* ram map */
pm_t pmap_base = align_up(MAX(initrd_top, fdt_top), sizeof(int));
+ info("choosing to place pmem map at %lx\n", pmap_base);
size_t probe_size = probe_pmap(ram_size);
+ info("pmem map probe size returned %lu\n", probe_size);
+
size_t actual_size = populate_pmap(ram_base, ram_size, pmap_base);
+ info("pmem map actual size %lu\n", actual_size);
if (probe_size != actual_size) {
bug("probe_size (%#lx) != actual_size (%#lx)\n", probe_size,
@@ -632,17 +665,25 @@ void init_pmem(void *fdt)
/* mark init stack, this should be unmapped once we get to executing
* processes */
__mark_area_used(VM_STACK_BASE, VM_STACK_TOP);
+ info("marked stack [%lx - %lx] used\n", VM_STACK_BASE, VM_STACK_TOP);
/* mark kernel */
/* this could be made more explicit, I suppose. */
__mark_area_used(VM_KERN, VM_KERN + PM_KERN_SIZE);
+ info("marked kernel [%lx - %lx] used\n", VM_KERN,
+ VM_KERN + PM_KERN_SIZE);
/* mark fdt and initrd */
- __mark_area_used(get_initrdbase(fdt), initrd_top);
- __mark_area_used(__get_fdtbase(fdt), fdt_top);
+ __mark_area_used(initrd_base, initrd_top);
+ info("marked initrd [%lx - %lx] used\n", initrd_base, initrd_top);
+
+ __mark_area_used(fdt_base, fdt_top);
+ info("marked fdt [%lx - %lx] used\n", fdt_base, fdt_top);
/* mark pmap */
__mark_area_used(pmap_base, pmap_base + actual_size);
+ info("marked pmap [%lx - %lx] used\n", pmap_base,
+ pmap_base + actual_size);
/* mark reserved mem */
__mark_reserved_mem(fdt);
diff --git a/include/arch/vmem.h b/include/arch/vmem.h
index d2f031d..118e6ff 100644
--- a/include/arch/vmem.h
+++ b/include/arch/vmem.h
@@ -98,7 +98,11 @@ stat_t mod_vpage(struct vmem *branch, vm_t vaddr, pm_t paddr, vmflags_t flags);
stat_t stat_vpage(struct vmem *branch, vm_t vaddr, pm_t *paddr,
enum mm_order *order, vmflags_t *flags);
-/** Flush tlb entry where associated with address \p addr. */
+/**
+ * Flush tlb entry where associated with address \p addr.
+ *
+ * @param addr Virtual address region to flush.
+ */
void flush_tlb(uintptr_t addr);
/** Flush full tlb of current address space. */