aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/fbos/dt.h20
-rw-r--r--include/fbos/init.h7
-rw-r--r--kernel/main.c11
-rw-r--r--kernel/trap.c11
-rw-r--r--lib/dt.c95
-rw-r--r--test/test_dt.c12
6 files changed, 108 insertions, 48 deletions
diff --git a/include/fbos/dt.h b/include/fbos/dt.h
index fd77c35..bbeb7cd 100644
--- a/include/fbos/dt.h
+++ b/include/fbos/dt.h
@@ -3,13 +3,21 @@
#include <fbos/compiler.h>
-// Pair of addresses where the initrd is located in memory.
-struct initrd_addr {
- uintptr_t start;
- uintptr_t end;
+// Default value for the 'dt_info.cpu_freq' property if none could be retrieved.
+#define DEFAULT_CPU_FREQ 10000000
+
+// Holds all the information that we gather from the initial DeviceTree blob.
+struct dt_info {
+ // Ticks per second. If `get_dt_info` fails at setting this value, then
+ // `DEFAULT_CPU_FREQ` is used.
+ uint64_t cpu_freq;
+
+ // Start and end addresses of the initramfs blob as stored in memory.
+ uintptr_t initrd_start;
+ uintptr_t initrd_end;
};
-// Returns the `initrd` addresses as parsed from the given DTB blob.
-struct initrd_addr find_dt_initrd_addr(uint32_t *dtb);
+// Set 'info' by parsing the given 'dtb' blob.
+void get_dt_info(uint32_t *dtb, struct dt_info *info);
#endif // __FBOS_DT_H_
diff --git a/include/fbos/init.h b/include/fbos/init.h
index f0f1dce..9e0049e 100644
--- a/include/fbos/init.h
+++ b/include/fbos/init.h
@@ -2,6 +2,7 @@
#define __FBOS_INIT_H
#include <fbos/compiler.h>
+#include <fbos/dt.h>
#include <fbos/sched.h>
// Tracks the amount of seconds that have elapsed since activating timer
@@ -10,6 +11,12 @@
// Instantiated in kernel/trap.c, initialized in main.c.
extern uint64_t seconds_elapsed;
+// General information from the DTB blob. This will be properly initialized at
+// the very beginning of kernel initialization.
+//
+// Instantiated and initialized in kernel/main.c.
+extern struct dt_info info;
+
// Extract the executables from the initrd that is located at `base_addr` and
// has the given `size`.
void extract_initrd(const unsigned char *const base_addr, uint64_t size,
diff --git a/kernel/main.c b/kernel/main.c
index fdcef1f..a39ed29 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -16,6 +16,13 @@ struct task_struct tasks[4] = {
[TASK_FIZZBUZZ] = { .stack = stack[3], .entry_addr = nullptr, },
};
+// Defined in fbos/init.h.
+struct dt_info info = {
+ .cpu_freq = 0,
+ .initrd_start = 0,
+ .initrd_end = 0,
+};
+
/*
* This is the main entry point of the kernel after head.S is done. This
* function can (and will) assume that everything has been reset and that we can
@@ -26,8 +33,8 @@ __noreturn __kernel void start_kernel(void *dtb)
printk("Welcome to FizzBuzz OS!\n");
// Extract information from the DTB blob.
- struct initrd_addr addr = find_dt_initrd_addr(dtb);
- extract_initrd((unsigned char *)addr.start, addr.end - addr.start, tasks);
+ get_dt_info(dtb, &info);
+ extract_initrd((unsigned char *)info.initrd_start, info.initrd_end - info.initrd_start, tasks);
// At this point everything has already been handled: setup the interrupt
// vector and enable the timer to start ticking and scheduling the three
diff --git a/kernel/trap.c b/kernel/trap.c
index bf8a89e..f98fcc7 100644
--- a/kernel/trap.c
+++ b/kernel/trap.c
@@ -1,13 +1,6 @@
#include <fbos/init.h>
#include <fbos/sbi.h>
#include <fbos/printk.h>
-#include <fbos/sched.h>
-
-// TODO: this is QEMU-specific. To obtain this:
-// - Parse the DTB and look for the 'cpus.timebase-frequency' property.
-// - If the system is on ACPI (e.g. VisionFive2), then the frequency has to be
-// picked up from somewhere else (constant on known boards?).
-#define TICKS_PER_SECOND 10000000
// Mask for 'scause' to check whether it came from an interrupt or an exception.
#define INTERRUPT_MASK 0x8000000000000000
@@ -34,10 +27,10 @@ __kernel void time_out_in_one_second(void)
register uint64_t one_second asm("a0");
asm volatile("rdtime t0\n\t"
- "li t1, %1\n\t"
+ "mv t1, %1\n\t"
"add %0, t0, t1"
: "=r"(one_second)
- : "i"(TICKS_PER_SECOND)
+ : "r"(info.cpu_freq)
: "t0", "t1");
ret = sbi_ecall1(TIME_EXT, TIME_SET_TIMER, one_second);
diff --git a/lib/dt.c b/lib/dt.c
index 3aab330..ddf01f3 100644
--- a/lib/dt.c
+++ b/lib/dt.c
@@ -29,13 +29,12 @@ struct fdt_header {
/*
* Find the device tree property by "name" starting at the given index "idx".
- * This function assumes that the property is exactly 8 bytes long (hey, it's
- * not so general purpose after all :D).
+ * The size of the property is to be provided by `prop_size`.
*
* Returns -1 if the given property could not be found.
*/
__kernel int64_t find_dt_property_from(uint32_t *dtb, struct fdt_header *header, uint32_t idx,
- const char *const name)
+ const char *const name, size_t prop_size)
{
char *base_dt_string = ((char *)dtb) + header->off_dt_string;
uint32_t len, nameoff;
@@ -45,7 +44,10 @@ __kernel int64_t find_dt_property_from(uint32_t *dtb, struct fdt_header *header,
len = __bswap_constant_32(dtb[idx + 1]);
nameoff = __bswap_constant_32(dtb[idx + 2]);
- if (len == 8 && strcmp(&base_dt_string[nameoff], name) == 0) {
+ if (len == prop_size && strcmp(&base_dt_string[nameoff], name) == 0) {
+ if (len == sizeof(uint32_t)) {
+ return (int64_t)__bswap_constant_32(dtb[idx + 3]);
+ }
ret = (int64_t)__bswap_constant_32(dtb[idx + 3]) << 32;
ret += (int64_t)__bswap_constant_32(dtb[idx + 4]);
return ret;
@@ -57,27 +59,30 @@ __kernel int64_t find_dt_property_from(uint32_t *dtb, struct fdt_header *header,
return -1;
}
-// Find the "initrd" values from the given DTB blob. Returns an empty
-// `initrd_addr` if these values could not be found.
-__kernel struct initrd_addr __find_dt_initrd_addr(uint32_t *dtb, struct fdt_header *header)
+/*
+ * Returns the index of the node identified by 'name' into the 'dtb' blob. This
+ * index will already account for the padding.
+ *
+ * Returns -1 if the node could not be found.
+ */
+__kernel int32_t find_dt_node(uint32_t *dtb, struct fdt_header *header, const char *const name)
{
uint32_t idx;
- struct initrd_addr ret = {
- .start = 0,
- .end = 0,
- };
// Try to find out the 32-bit offset of the "chosen" property inside of the
// FDT structure block.
for (idx = header->off_dt_struct; idx < header->size_dt_struct; idx++) {
/*
- * We only care about beginning of nodes, and then that the block is
- * literally named "chosen". After that, our offset will be that + 3
- * (skipping FDT_BEGIN_NODE + 2 that takes "chosen" with padding for
- * alignment).
+ * We only care about beginning of nodes, and then that the block has
+ * the interesting 'name'. After that, our offset will be that + 3
+ * (skipping FDT_BEGIN_NODE + 2 that takes "chosen/cpus" with padding
+ * for alignment).
+ *
+ * NOTE: for future extension, the +2 stems from "chosen/cpus". If there
+ * is another node name to be found, we are cooked.
*/
if (dtb[idx] == FDT_BEGIN_NODE_LE) {
- if (strcmp((char *)&dtb[idx + 1], "chosen") == 0) {
+ if (strcmp((char *)&dtb[idx + 1], name) == 0) {
idx += 3;
break;
}
@@ -86,7 +91,32 @@ __kernel struct initrd_addr __find_dt_initrd_addr(uint32_t *dtb, struct fdt_head
// "chosen" property could not be found. Leave early with an empty result.
if (idx == header->size_dt_struct || dtb[idx] != FDT_PROP_LE) {
- return ret;
+ return -1;
+ }
+ return (int32_t)idx;
+}
+
+// Set the 'cpu_freq' field of 'info' if available on the 'dtb' blob.
+__kernel void set_cpu_freq(uint32_t *dtb, struct dt_info *info, struct fdt_header *header)
+{
+ int32_t idx = find_dt_node(dtb, header, "cpus");
+ if (idx < 0) {
+ return;
+ }
+
+ int64_t i =
+ find_dt_property_from(dtb, header, (uint32_t)idx, "timebase-frequency", sizeof(uint32_t));
+ if (i > 0) {
+ info->cpu_freq = (uint64_t)i;
+ }
+}
+
+// Set the "initrd" values from the given 'dtb' blob into 'info'.
+__kernel void set_initrd_addr(uint32_t *dtb, struct dt_info *info, struct fdt_header *header)
+{
+ int32_t idx = find_dt_node(dtb, header, "chosen");
+ if (idx < 0) {
+ return;
}
/*
@@ -94,23 +124,22 @@ __kernel struct initrd_addr __find_dt_initrd_addr(uint32_t *dtb, struct fdt_head
* values and return that.
*/
- int64_t i = find_dt_property_from(dtb, header, idx, "linux,initrd-start");
+ int64_t i =
+ find_dt_property_from(dtb, header, (uint32_t)idx, "linux,initrd-start", sizeof(uint64_t));
if (i < 0) {
- return ret;
+ return;
}
- ret.start = (uintptr_t)i;
+ info->initrd_start = (uintptr_t)i;
- i = find_dt_property_from(dtb, header, idx, "linux,initrd-end");
+ i = find_dt_property_from(dtb, header, (uint32_t)idx, "linux,initrd-end", sizeof(uint64_t));
if (i < 0) {
- ret.start = 0;
- return ret;
+ info->initrd_start = 0;
+ return;
}
- ret.end = (uintptr_t)i;
-
- return ret;
+ info->initrd_end = (uintptr_t)i;
}
-__kernel struct initrd_addr find_dt_initrd_addr(uint32_t *dtb)
+__kernel void get_dt_info(uint32_t *dtb, struct dt_info *info)
{
if (dtb[0] != FDT_MAGIC_LE) {
die("FDT structure does not have a valid magic identifier\n");
@@ -122,5 +151,15 @@ __kernel struct initrd_addr find_dt_initrd_addr(uint32_t *dtb)
.size_dt_struct = __bswap_constant_32(dtb[9]) / sizeof(uint32_t),
};
- return __find_dt_initrd_addr(dtb, &header);
+ set_initrd_addr(dtb, info, &header);
+ if (!info->initrd_start || !info->initrd_end) {
+ die("Could not fetch the addresses for the initramfs\n");
+ }
+
+ set_cpu_freq(dtb, info, &header);
+ if (!info->cpu_freq) {
+ printk("WARNING: could not figure out the CPU frequency. "
+ "Defaulting to 10000000 even if this might be bad\n");
+ info->cpu_freq = DEFAULT_CPU_FREQ;
+ }
}
diff --git a/test/test_dt.c b/test/test_dt.c
index 31f729f..1eb9751 100644
--- a/test/test_dt.c
+++ b/test/test_dt.c
@@ -19,11 +19,17 @@ int main(void)
contents[fsize] = 0;
- struct initrd_addr addr = find_dt_initrd_addr(contents);
+ struct dt_info info = {
+ .cpu_freq = 0,
+ .initrd_start = 0,
+ .initrd_end = 0,
+ };
+ get_dt_info(contents, &info);
free(contents);
- assert(addr.start == 0x84200000);
- assert(addr.end == 0x84200c00);
+ assert(info.initrd_start == 0x84200000);
+ assert(info.initrd_end == 0x84200c00);
+ assert(info.cpu_freq == 0x989680);
exit(0);
}