diff options
| author | Miquel Sabaté Solà <mikisabate@gmail.com> | 2024-11-26 13:59:54 +0100 |
|---|---|---|
| committer | Miquel Sabaté Solà <mikisabate@gmail.com> | 2024-11-26 13:59:54 +0100 |
| commit | 7870d56b155c2be73cdacfba94eeb893cd55628e (patch) | |
| tree | f95699d91e19635c7aa0e99dbf114da2a06e80a2 | |
| parent | 30d6ab8c6b40b2897fa3f49cce0302cc6c3a66e6 (diff) | |
| download | fbos-7870d56b155c2be73cdacfba94eeb893cd55628e.tar.gz fbos-7870d56b155c2be73cdacfba94eeb893cd55628e.zip | |
dt: Fetch the CPU frequency as well
If the 'timebase-frequency' property is available under the 'cpus' node
from the DTB blob, it makes sense to try to fetch this value from there
instead of hardcoding it. For other use-cases, where this information is
not available through DT (e.g. ACPI on the VisionFive2 board), we will
have to hardcode it with a default value even if it's not the proper
one.
Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
| -rw-r--r-- | include/fbos/dt.h | 20 | ||||
| -rw-r--r-- | include/fbos/init.h | 7 | ||||
| -rw-r--r-- | kernel/main.c | 11 | ||||
| -rw-r--r-- | kernel/trap.c | 11 | ||||
| -rw-r--r-- | lib/dt.c | 95 | ||||
| -rw-r--r-- | test/test_dt.c | 12 |
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); @@ -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); } |
