Skip to content

Commit

Permalink
[arch][riscv] read the riscv feature string out of device tree
Browse files Browse the repository at this point in the history
Also added initial implementation of a way to query run time features of
the cpu.
  • Loading branch information
travisg committed Jun 2, 2024
1 parent 479f7fb commit 566b25d
Show file tree
Hide file tree
Showing 8 changed files with 348 additions and 19 deletions.
5 changes: 5 additions & 0 deletions arch/riscv/arch.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <platform.h>
#include <arch.h>

#include "arch/riscv/feature.h"
#include "riscv_priv.h"

#define LOCAL_TRACE 0
Expand Down Expand Up @@ -71,6 +72,8 @@ void riscv_early_init_percpu(void) {
void arch_early_init(void) {
riscv_early_init_percpu();

riscv_feature_early_init();

#if RISCV_S_MODE
sbi_early_init();
#endif
Expand Down Expand Up @@ -110,6 +113,8 @@ void arch_init(void) {
riscv_get_mvendorid(), riscv_get_marchid(),
riscv_get_mimpid(), riscv_current_hart());

riscv_feature_init();

#if RISCV_M_MODE
dprintf(INFO, "RISCV: misa %#lx\n", riscv_csr_read(RISCV_CSR_MISA));
#elif RISCV_S_MODE
Expand Down
214 changes: 214 additions & 0 deletions arch/riscv/feature.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
* Copyright (c) 2024 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include "arch/riscv/feature.h"

#include <lk/trace.h>
#include <lk/debug.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#define LOCAL_TRACE 0

// Make feature bitmap
uint32_t riscv_feature_bitmap[ROUNDUP(RISCV_FEAT_COUNT, 32) / 32];

static void set_feature(enum riscv_feature feature) {
riscv_feature_bitmap[feature / 32] |= (1U << feature % 32);
}

// match a one word feature from a known list
static void match_feature(const char *str, size_t start, size_t end) {
const struct {
const char *str;
enum riscv_feature feat;
} oneword[] = {
{ "zba", RISCV_FEAT_ZBA },
{ "zbb", RISCV_FEAT_ZBB },
{ "zbc", RISCV_FEAT_ZBC },
{ "zbs", RISCV_FEAT_ZBS },
{ "sstc", RISCV_FEAT_SSTC },
{ "zicsr", RISCV_FEAT_ZICSR },
{ "zfencei", RISCV_FEAT_ZIFENCEI },
};

if (LOCAL_TRACE) {
char feat[128];
strlcpy(feat, &str[start], end - start + 1);
printf("feature '%s'\n", feat);
}

for (size_t i = 0; i < countof(oneword); i++) {
if (strlen(oneword[i].str) != end - start)
continue;

if (strncasecmp(oneword[i].str, &str[start], end - start) == 0) {
dprintf(INFO, "riscv: found feature '%s'\n", oneword[i].str);
set_feature(oneword[i].feat);
}
}
}

void riscv_set_isa_string(const char *str) {
LTRACEF("%s\n", str);

const size_t slen = strlen(str);

// Handle simple features first.
// Feature string must start with either 'rv64' or 'rv32' followed by
// one or more one character features until _ or null.
if (slen < 4 || str[0] != 'r' || str[1] != 'v') {
return;
}

// We're going to continue, so wipe out the existing default feature list
memset(riscv_feature_bitmap, 0, 4 * countof(riscv_feature_bitmap));

size_t pos = 4;
while (str[pos] != 0 && str[pos] != '_') {
bool found = true;
switch (tolower(str[pos])) {
// TODO: make sure this is the complete list
case 'i': set_feature(RISCV_FEAT_I); break;
case 'm': set_feature(RISCV_FEAT_M); break;
case 'a': set_feature(RISCV_FEAT_A); break;
case 'q': set_feature(RISCV_FEAT_Q);
// fallthrough
case 'd':
feat_d:
set_feature(RISCV_FEAT_D);
// fallthrough
case 'f': set_feature(RISCV_FEAT_F); break;
case 'c': set_feature(RISCV_FEAT_C); break;
case 'b': set_feature(RISCV_FEAT_B); break;
case 'p': set_feature(RISCV_FEAT_P); break;
case 'h': set_feature(RISCV_FEAT_H); break;
case 'v':
set_feature(RISCV_FEAT_V);
goto feat_d;
case 'g':
// 'g' is a special case that implies IMAFDZisr_Zifenci
set_feature(RISCV_FEAT_I);
set_feature(RISCV_FEAT_M);
set_feature(RISCV_FEAT_A);
set_feature(RISCV_FEAT_ZICSR);
set_feature(RISCV_FEAT_ZIFENCEI);
goto feat_d;
default:
found = false;
}
if (found) {
dprintf(INFO, "riscv: found feature '%c'\n", tolower(str[pos]));
}

pos++;
}

// walk the one-word features
bool in_word = false;
size_t start;
for (; pos <= slen; pos++) {
if (!in_word) {
if (str[pos] == '_') {
continue;
} else if (str[pos] == 0) {
break;
}
start = pos;
in_word = true;
} else {
// we're in a word
if (str[pos] == '_' || str[pos] == 0) {
// end of word
in_word = false;

// process the feature word, between str[start...pos]
match_feature(str, start, pos);
}
}
}
}

void riscv_feature_early_init(void) {
// set the default features based on the compiler switches
#if __riscv_i
set_feature(RISCV_FEAT_I);
#endif
#if __riscv_m
set_feature(RISCV_FEAT_M);
#endif
#if __riscv_a
set_feature(RISCV_FEAT_A);
#endif
#if __riscv_f
set_feature(RISCV_FEAT_F);
#endif
#if __riscv_d
set_feature(RISCV_FEAT_D);
#endif
#if __riscv_q
set_feature(RISCV_FEAT_Q);
#endif
#if __riscv_c
set_feature(RISCV_FEAT_C);
#endif
#if __riscv_zba
set_feature(RISCV_FEAT_ZBA);
#endif
#if __riscv_zbb
set_feature(RISCV_FEAT_ZBB);
#endif
#if __riscv_zba
set_feature(RISCV_FEAT_ZBA);
#endif
#if __riscv_zbc
set_feature(RISCV_FEAT_ZBC);
#endif
#if __riscv_zbs
set_feature(RISCV_FEAT_ZBS);
#endif
}

const char *riscv_feature_to_string(enum riscv_feature feature) {
switch (feature) {
case RISCV_FEAT_I: return "i";
case RISCV_FEAT_M: return "m";
case RISCV_FEAT_A: return "a";
case RISCV_FEAT_F: return "f";
case RISCV_FEAT_D: return "d";
case RISCV_FEAT_Q: return "q";
case RISCV_FEAT_C: return "c";
case RISCV_FEAT_B: return "b";
case RISCV_FEAT_P: return "p";
case RISCV_FEAT_V: return "v";
case RISCV_FEAT_H: return "h";
case RISCV_FEAT_ZBA: return "zba";
case RISCV_FEAT_ZBB: return "zbb";
case RISCV_FEAT_ZBC: return "zbc";
case RISCV_FEAT_ZBS: return "zbs";
case RISCV_FEAT_ZICSR: return "zicsr";
case RISCV_FEAT_ZIFENCEI: return "zifencei";
case RISCV_FEAT_SSTC: return "sstc";

// keep this in so the compiler warns if something is missing
case RISCV_FEAT_COUNT: return "";
}
return "";
}

void riscv_feature_init(void) {
dprintf(INFO, "RISCV: detected features");
for (size_t i = 0; i < countof(riscv_feature_bitmap); i++) {
for (size_t j = 0; j < sizeof(riscv_feature_bitmap[i]) * 8; j++) {
if (riscv_feature_bitmap[i] & (1U << j)) {
dprintf(INFO, " %s", riscv_feature_to_string(i * 32 + j));
}
}
}
dprintf(INFO, "\n");
}
58 changes: 58 additions & 0 deletions arch/riscv/include/arch/riscv/feature.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2024 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once

#include <lk/compiler.h>
#include <stdbool.h>
#include <stdlib.h>

__BEGIN_CDECLS

// Given a list of features in foo_bar_baz form, break out known features.
// Does not keep a pointer to the string.
void riscv_set_isa_string(const char *string);

enum riscv_feature {
// Basic one character features
RISCV_FEAT_I,
RISCV_FEAT_M,
RISCV_FEAT_A,
RISCV_FEAT_F,
RISCV_FEAT_D,
RISCV_FEAT_Q,
RISCV_FEAT_C,
RISCV_FEAT_B,
RISCV_FEAT_P,
RISCV_FEAT_V,
RISCV_FEAT_H,

// multichar features
RISCV_FEAT_ZBA,
RISCV_FEAT_ZBB,
RISCV_FEAT_ZBC,
RISCV_FEAT_ZBS,
RISCV_FEAT_ZICSR,
RISCV_FEAT_ZIFENCEI,
RISCV_FEAT_SSTC,

RISCV_FEAT_COUNT
};

extern uint32_t riscv_feature_bitmap[];

// Test if a particular feature is present
static inline bool riscv_feature_test(enum riscv_feature feature) {
return riscv_feature_bitmap[feature / 32] & (1U << feature % 32);
}

const char *riscv_feature_to_string(enum riscv_feature feature);

void riscv_feature_early_init(void);
void riscv_feature_init(void);

__END_CDECLS
4 changes: 1 addition & 3 deletions arch/riscv/mmu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@

#define LOCAL_TRACE 0

#include <kernel/vm.h>

#if __riscv_xlen == 32
#error "32 bit mmu not supported yet"
#endif
Expand Down Expand Up @@ -640,7 +638,7 @@ void riscv_early_mmu_init() {
// called a bit later once on the boot cpu
extern "C"
void riscv_mmu_init() {
printf("RISCV: MMU ASID mask %#lx\n", riscv_asid_mask);
dprintf(INFO, "RISCV: MMU ASID mask %#lx\n", riscv_asid_mask);
}

#endif
4 changes: 3 additions & 1 deletion arch/riscv/rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)

MODULE_SRCS += $(LOCAL_DIR)/start.S

MODULE_SRCS += $(LOCAL_DIR)/arch.c
MODULE_SRCS += $(LOCAL_DIR)/asm.S
MODULE_SRCS += $(LOCAL_DIR)/exceptions.c
MODULE_SRCS += $(LOCAL_DIR)/feature.c
MODULE_SRCS += $(LOCAL_DIR)/fpu_asm.S
MODULE_SRCS += $(LOCAL_DIR)/thread.c
MODULE_SRCS += $(LOCAL_DIR)/mmu.cpp
MODULE_SRCS += $(LOCAL_DIR)/mp.c
MODULE_SRCS += $(LOCAL_DIR)/sbi.c
MODULE_SRCS += $(LOCAL_DIR)/spinlock.c
MODULE_SRCS += $(LOCAL_DIR)/thread.c
MODULE_SRCS += $(LOCAL_DIR)/time.c

MODULE_DEPS += lib/libcpp
Expand Down
33 changes: 31 additions & 2 deletions lib/fdtwalk/fdtwalk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,35 @@ status_t read_base_len_pair(const uint8_t *prop_ptr, size_t prop_len,
// returns true or false if a particular property is a particular value
bool check_prop_is_val_string(const void *fdt, int offset, const char *prop, const char *val) {
int lenp;
const uint8_t *prop_ptr = (const uint8_t *)fdt_getprop(fdt, offset, prop, &lenp);
const uint8_t *prop_ptr = static_cast<const uint8_t *>(fdt_getprop(fdt, offset, prop, &lenp));
if (!prop_ptr || lenp <= 0) {
return false;
}

if (strncmp(val, (const char *)prop_ptr, strlen(val)) == 0) {
if (strncmp(val, reinterpret_cast<const char *>(prop_ptr), strlen(val)) == 0) {
return true;
}

return false;
}

const char *get_prop_string(const void *fdt, int offset, const char *prop) {
int lenp;
const uint8_t *prop_ptr = static_cast<const uint8_t *>(fdt_getprop(fdt, offset, prop, &lenp));
if (!prop_ptr || lenp <= 0) {
return nullptr;
}

// check to see that it appears to be null terminated
auto str = reinterpret_cast<const char *>(prop_ptr);
if (str[lenp-1] != '\0') {
return nullptr;
}

// seems safe
return str;
}

struct fdt_walk_state {
const void *fdt;
int offset;
Expand Down Expand Up @@ -210,6 +227,18 @@ status_t fdt_walk_find_cpus(const void *fdt, struct fdt_walk_cpu_info *cpu, size
cpu[*cpu_count].id = id;
(*cpu_count)++;
}
#if ARCH_RISCV
// look for riscv,isa and riscv,isa-extensions
auto isa_string = get_prop_string(state.fdt, state.offset, "riscv,isa");
if (isa_string) {
cpu->isa_string = isa_string;
}

auto isa_extensions_string = get_prop_string(state.fdt, state.offset, "riscv,isa-extensions");
if (isa_extensions_string) {
cpu->isa_extensions_string = isa_extensions_string;
}
#endif
}
}
};
Expand Down
Loading

0 comments on commit 566b25d

Please sign in to comment.