diff --git a/sys/riscv/include/elf.h b/sys/riscv/include/elf.h index 671e2d2617c0..c0817d8fb47b 100644 --- a/sys/riscv/include/elf.h +++ b/sys/riscv/include/elf.h @@ -1,88 +1,88 @@ /*- * Copyright (c) 1996-1997 John D. Polstra. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #ifndef _MACHINE_ELF_H_ #define _MACHINE_ELF_H_ /* * ELF definitions for the RISC-V architecture. */ #include /* Definitions common to all 32 bit architectures. */ #include /* Definitions common to all 64 bit architectures. */ #define __ELF_WORD_SIZE 64 /* Used by */ #include /* * Auxiliary vector entries for passing information to the interpreter. */ typedef struct { /* Auxiliary vector entry on initial stack */ int a_type; /* Entry type. */ union { int a_val; /* Integer value. */ } a_un; } Elf32_Auxinfo; typedef struct { /* Auxiliary vector entry on initial stack */ long a_type; /* Entry type. */ union { long a_val; /* Integer value. */ void *a_ptr; /* Address. */ void (*a_fcn)(void); /* Function pointer (not used). */ } a_un; } Elf64_Auxinfo; __ElfType(Auxinfo); #define ELF_ARCH EM_RISCV #define ELF_MACHINE_OK(x) ((x) == (ELF_ARCH)) /* Define "machine" characteristics */ #define ELF_TARG_CLASS ELFCLASS64 #define ELF_TARG_DATA ELFDATA2LSB #define ELF_TARG_MACH EM_RISCV #define ELF_TARG_VER 1 /* TODO: set correct value */ #define ET_DYN_LOAD_ADDR 0x100000 /* Flags passed in AT_HWCAP */ -#define HWCAP_ISA_BIT(c) (1 << ((c) - 'A')) -#define HWCAP_ISA_I HWCAP_ISA_BIT('I') -#define HWCAP_ISA_M HWCAP_ISA_BIT('M') -#define HWCAP_ISA_A HWCAP_ISA_BIT('A') -#define HWCAP_ISA_F HWCAP_ISA_BIT('F') -#define HWCAP_ISA_D HWCAP_ISA_BIT('D') -#define HWCAP_ISA_C HWCAP_ISA_BIT('C') +#define HWCAP_ISA_BIT(c) (1 << ((c) - 'a')) +#define HWCAP_ISA_I HWCAP_ISA_BIT('i') +#define HWCAP_ISA_M HWCAP_ISA_BIT('m') +#define HWCAP_ISA_A HWCAP_ISA_BIT('a') +#define HWCAP_ISA_F HWCAP_ISA_BIT('f') +#define HWCAP_ISA_D HWCAP_ISA_BIT('d') +#define HWCAP_ISA_C HWCAP_ISA_BIT('c') #define HWCAP_ISA_G \ (HWCAP_ISA_I | HWCAP_ISA_M | HWCAP_ISA_A | HWCAP_ISA_F | HWCAP_ISA_D) #endif /* !_MACHINE_ELF_H_ */ diff --git a/sys/riscv/riscv/identcpu.c b/sys/riscv/riscv/identcpu.c index f3afa9b8c7ea..4c151eb47939 100644 --- a/sys/riscv/riscv/identcpu.c +++ b/sys/riscv/riscv/identcpu.c @@ -1,226 +1,368 @@ /*- * Copyright (c) 2015-2016 Ruslan Bukin * All rights reserved. * * Portions of this software were developed by SRI International and the * University of Cambridge Computer Laboratory under DARPA/AFRL contract * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. * * Portions of this software were developed by the University of Cambridge * Computer Laboratory as part of the CTSRD Project, with support from the * UK Higher Education Innovation Fund (HEIF). * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif char machine[] = "riscv"; SYSCTL_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD, machine, 0, "Machine class"); /* Hardware implementation info. These values may be empty. */ register_t mvendorid; /* The CPU's JEDEC vendor ID */ register_t marchid; /* The architecture ID */ register_t mimpid; /* The implementation ID */ struct cpu_desc { u_int cpu_impl; u_int cpu_part_num; const char *cpu_impl_name; const char *cpu_part_name; }; struct cpu_desc cpu_desc[MAXCPU]; struct cpu_parts { u_int part_id; const char *part_name; }; #define CPU_PART_NONE { -1, "Unknown Processor" } struct cpu_implementers { u_int impl_id; const char *impl_name; }; #define CPU_IMPLEMENTER_NONE { 0, "Unknown Implementer" } /* * CPU base */ static const struct cpu_parts cpu_parts_std[] = { { CPU_PART_RV32, "RV32" }, { CPU_PART_RV64, "RV64" }, { CPU_PART_RV128, "RV128" }, CPU_PART_NONE, }; /* * Implementers table. */ const struct cpu_implementers cpu_implementers[] = { { CPU_IMPL_UCB_ROCKET, "UC Berkeley Rocket" }, CPU_IMPLEMENTER_NONE, }; -#ifdef FDT /* - * The ISA string is made up of a small prefix (e.g. rv64) and up to 26 letters - * indicating the presence of the 26 possible standard extensions. Therefore 32 - * characters will be sufficient. + * The ISA string describes the complete set of instructions supported by a + * RISC-V CPU. The string begins with a small prefix (e.g. rv64) indicating the + * base ISA. It is followed first by single-letter ISA extensions, and then + * multi-letter ISA extensions. + * + * Underscores are used mainly to separate consecutive multi-letter extensions, + * but may optionally appear between any two extensions. An extension may be + * followed by a version number, in the form of 'Mpm', where M is the + * extension's major version number, and 'm' is the minor version number. + * + * The format is described in detail by the "ISA Extension Naming Conventions" + * chapter of the unprivileged spec. */ -#define ISA_NAME_MAXLEN 32 #define ISA_PREFIX ("rv" __XSTRING(__riscv_xlen)) #define ISA_PREFIX_LEN (sizeof(ISA_PREFIX) - 1) +static __inline int +parse_ext_s(char *isa, int idx, int len) +{ + /* + * Proceed to the next multi-letter extension or the end of the + * string. + * + * TODO: parse these once we gain support + */ + while (isa[idx] != '_' && idx < len) { + idx++; + } + + return (idx); +} + +static __inline int +parse_ext_x(char *isa, int idx, int len) +{ + /* + * Proceed to the next multi-letter extension or the end of the + * string. + */ + while (isa[idx] != '_' && idx < len) { + idx++; + } + + return (idx); +} + +static __inline int +parse_ext_z(char *isa, int idx, int len) +{ + /* + * Proceed to the next multi-letter extension or the end of the + * string. + * + * TODO: parse some of these. + */ + while (isa[idx] != '_' && idx < len) { + idx++; + } + + return (idx); +} + +static __inline int +parse_ext_version(char *isa, int idx, u_int *majorp __unused, + u_int *minorp __unused) +{ + /* Major version. */ + while (isdigit(isa[idx])) + idx++; + + if (isa[idx] != 'p') + return (idx); + else + idx++; + + /* Minor version. */ + while (isdigit(isa[idx])) + idx++; + + return (idx); +} + +/* + * Parse the ISA string, building up the set of HWCAP bits as they are found. + */ static void -fill_elf_hwcap(void *dummy __unused) +parse_riscv_isa(char *isa, int len, u_long *hwcapp) { - u_long caps[256] = {0}; - char isa[ISA_NAME_MAXLEN]; u_long hwcap; - phandle_t node; - ssize_t len; int i; - caps['i'] = caps['I'] = HWCAP_ISA_I; - caps['m'] = caps['M'] = HWCAP_ISA_M; - caps['a'] = caps['A'] = HWCAP_ISA_A; + hwcap = 0; + i = ISA_PREFIX_LEN; + while (i < len) { + switch(isa[i]) { + case 'a': + case 'c': #ifdef FPE - caps['f'] = caps['F'] = HWCAP_ISA_F; - caps['d'] = caps['D'] = HWCAP_ISA_D; + case 'd': + case 'f': #endif - caps['c'] = caps['C'] = HWCAP_ISA_C; + case 'i': + case 'm': + hwcap |= HWCAP_ISA_BIT(isa[i]); + i++; + break; + case 'g': + hwcap |= HWCAP_ISA_G; + i++; + break; + case 's': + /* + * XXX: older versions of this string erroneously + * indicated supervisor and user mode support as + * single-letter extensions. Detect and skip both 's' + * and 'u'. + */ + if (isa[i - 1] != '_' && isa[i + 1] == 'u') { + i += 2; + continue; + } + + /* + * Supervisor-level extension namespace. + */ + i = parse_ext_s(isa, i, len); + break; + case 'x': + /* + * Custom extension namespace. For now, we ignore + * these. + */ + i = parse_ext_x(isa, i, len); + break; + case 'z': + /* + * Multi-letter standard extension namespace. + */ + i = parse_ext_z(isa, i, len); + break; + case '_': + i++; + continue; + default: + /* Unrecognized/unsupported. */ + i++; + break; + } + + i = parse_ext_version(isa, i, NULL, NULL); + } + + if (hwcapp != NULL) + *hwcapp = hwcap; +} + +#ifdef FDT +static void +fill_elf_hwcap(void *dummy __unused) +{ + char isa[1024]; + u_long hwcap; + phandle_t node; + ssize_t len; node = OF_finddevice("/cpus"); if (node == -1) { if (bootverbose) printf("fill_elf_hwcap: Can't find cpus node\n"); return; } /* * Iterate through the CPUs and examine their ISA string. While we * could assign elf_hwcap to be whatever the boot CPU supports, to * handle the (unusual) case of running a system with hetergeneous * ISAs, keep only the extension bits that are common to all harts. */ for (node = OF_child(node); node > 0; node = OF_peer(node)) { /* Skip any non-CPU nodes, such as cpu-map. */ if (!ofw_bus_node_is_compatible(node, "riscv")) continue; len = OF_getprop(node, "riscv,isa", isa, sizeof(isa)); - KASSERT(len <= ISA_NAME_MAXLEN, ("ISA string truncated")); + KASSERT(len <= sizeof(isa), ("ISA string truncated")); if (len == -1) { if (bootverbose) printf("fill_elf_hwcap: " "Can't find riscv,isa property\n"); return; } else if (strncmp(isa, ISA_PREFIX, ISA_PREFIX_LEN) != 0) { if (bootverbose) printf("fill_elf_hwcap: " "Unsupported ISA string: %s\n", isa); return; } - hwcap = 0; - for (i = ISA_PREFIX_LEN; i < len; i++) - hwcap |= caps[(unsigned char)isa[i]]; + /* + * The string is specified to be lowercase, but let's be + * certain. + */ + for (int i = 0; i < len; i++) + isa[i] = tolower(isa[i]); + parse_riscv_isa(isa, len, &hwcap); if (elf_hwcap != 0) elf_hwcap &= hwcap; else elf_hwcap = hwcap; } } SYSINIT(identcpu, SI_SUB_CPU, SI_ORDER_ANY, fill_elf_hwcap, NULL); #endif void identify_cpu(void) { const struct cpu_parts *cpu_partsp; uint32_t part_id; uint32_t impl_id; uint64_t misa; u_int cpu; size_t i; cpu_partsp = NULL; /* TODO: can we get misa somewhere ? */ misa = 0; cpu = PCPU_GET(cpuid); impl_id = CPU_IMPL(mimpid); for (i = 0; i < nitems(cpu_implementers); i++) { if (impl_id == cpu_implementers[i].impl_id || cpu_implementers[i].impl_id == 0) { cpu_desc[cpu].cpu_impl = impl_id; cpu_desc[cpu].cpu_impl_name = cpu_implementers[i].impl_name; cpu_partsp = cpu_parts_std; break; } } part_id = CPU_PART(misa); for (i = 0; &cpu_partsp[i] != NULL; i++) { if (part_id == cpu_partsp[i].part_id || cpu_partsp[i].part_id == -1) { cpu_desc[cpu].cpu_part_num = part_id; cpu_desc[cpu].cpu_part_name = cpu_partsp[i].part_name; break; } } /* Print details for boot CPU or if we want verbose output */ if (cpu == 0 || bootverbose) { printf("CPU(%d): %s %s\n", cpu, cpu_desc[cpu].cpu_impl_name, cpu_desc[cpu].cpu_part_name); } }