diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dda8dd76..f70b6b6c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -510,6 +510,8 @@ set_property(SOURCE kernel_all.i PROPERTY LANGUAGE C) if(KernelArchARM) set(linker_source "src/arch/arm/common_arm.lds") +elseif(KernelArchRiscV) + set(linker_source "src/arch/riscv/common_riscv.lds") else() set(linker_source "src/plat/${KernelPlatform}/linker.lds") endif() diff --git a/config.cmake b/config.cmake index 27e343eb0..32cd098ed 100644 --- a/config.cmake +++ b/config.cmake @@ -60,6 +60,41 @@ config_choice( # Set defaults for common variables set(KernelHaveFPU OFF) +function(declare_default_headers) + cmake_parse_arguments( + PARSE_ARGV + 0 + CONFIGURE + "" + "TIMER_FREQUENCY;MAX_IRQ;PLIC_MAX_NUM_INT;INTERRUPT_CONTROLLER;TIMER;SMMU" + "" + ) + # calculate the irq cnode size based on MAX_IRQ + if("${KernelArch}" STREQUAL "riscv") + set(MAX_IRQ "${CONFIGURE_PLIC_MAX_NUM_INT} + 2") + else() + set(MAX_IRQ "${CONFIGURE_MAX_IRQ}") + endif() + set(BITS "0") + set(MAX "${MAX_IRQ}") + while(MAX GREATER "0") + math(EXPR BITS "${BITS} + 1") + math(EXPR MAX "${MAX} >> 1") + endwhile() + math(EXPR SLOTS "1 << ${BITS}") + if("${SLOTS}" LESS "${MAX_IRQ}") + math(EXPR BITS "${BITS} + 1") + endif() + set(CONFIGURE_IRQ_SLOT_BITS "${BITS}") + # variables parsed by the above will be prepended with CONFIGURE_, so pipe them + # straight to configure_file + configure_file( + src/arch/${KernelArch}/platform_gen.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen_headers/plat/platform_gen.h + @ONLY + ) + include_directories(include/plat/default) +endfunction() + include(src/arch/arm/config.cmake) include(src/arch/riscv/config.cmake) include(src/arch/x86/config.cmake) @@ -68,6 +103,79 @@ include(include/32/mode/config.cmake) include(include/64/mode/config.cmake) include(src/config.cmake) +if(DEFINED KernelDTSList) + set(KernelDTSIntermediate "${CMAKE_CURRENT_BINARY_DIR}/kernel.dts") + set( + KernelDTBPath "${CMAKE_CURRENT_BINARY_DIR}/kernel.dtb" + CACHE INTERNAL "Location of kernel DTB file" + ) + set(compatibility_outfile "${CMAKE_CURRENT_BINARY_DIR}/kernel_compat.txt") + set(device_dest "${CMAKE_CURRENT_BINARY_DIR}/gen_headers/plat/machine/devices_gen.h") + set( + platform_yaml "${CMAKE_CURRENT_BINARY_DIR}/gen_headers/plat/machine/platform_gen.yaml" + CACHE INTERNAL "Location of platform YAML description" + ) + set(config_file "${CMAKE_CURRENT_SOURCE_DIR}/tools/hardware.yml") + set(config_schema "${CMAKE_CURRENT_SOURCE_DIR}/tools/hardware_schema.yml") + + find_program(DTC_TOOL dtc) + if("${DTC_TOOL}" STREQUAL "DTC_TOOL-NOTFOUND") + message(FATAL_ERROR "Cannot find 'dtc' program.") + endif() + + # Generate final DTS based on Linux DTS + seL4 overlay[s] + foreach(entry ${KernelDTSList}) + get_absolute_source_or_binary(dts_tmp ${entry}) + list(APPEND dts_list "${dts_tmp}") + endforeach() + + check_outfile_stale(regen ${KernelDTBPath} dts_list ${CMAKE_CURRENT_BINARY_DIR}/dts.cmd) + if(regen) + file(REMOVE "${KernelDTSIntermediate}") + foreach(entry ${dts_list}) + file(READ ${entry} CONTENTS) + file(APPEND "${KernelDTSIntermediate}" "${CONTENTS}") + endforeach() + # Compile DTS to DTB + execute_process( + COMMAND + ${DTC_TOOL} -q -I dts -O dtb -o ${KernelDTBPath} ${KernelDTSIntermediate} + ) + endif() + + set(deps ${KernelDTBPath} ${config_file} ${config_schema} ${HARDWARE_GEN_PATH}) + check_outfile_stale(regen ${device_dest} deps ${CMAKE_CURRENT_BINARY_DIR}/gen_header.cmd) + if(regen) + # Generate devices_gen header based on DTB + message(STATUS "${device_dest} is out of date. Regenerating...") + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/gen_headers/plat/machine/") + execute_process( + COMMAND + ${PYTHON} "${HARDWARE_GEN_PATH}" --dtb "${KernelDTBPath}" --compatibility-strings + "${compatibility_outfile}" --output "${device_dest}" --config "${config_file}" + --schema "${config_schema}" --yaml "${platform_yaml}" --arch "${KernelArch}" + INPUT_FILE /dev/stdin + OUTPUT_FILE /dev/stdout + ERROR_FILE /dev/stderr + RESULT_VARIABLE error + ) + if(error) + message(FATAL_ERROR "Failed to generate: ${device_dest}") + endif() + endif() + file(READ "${compatibility_outfile}" compatibility_strings) + + # Mark all file dependencies as CMake rerun dependencies. + set(cmake_deps ${deps} ${KernelDTSIntermediate} ${KernelDTSList} ${compatibility_outfile}) + set_property( + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${cmake_deps} + ) + + include(src/drivers/config.cmake) +endif() + # Enshrine common variables in the config config_set(KernelHaveFPU HAVE_FPU "${KernelHaveFPU}") diff --git a/include/arch/riscv/arch/32/mode/hardware.h b/include/arch/riscv/arch/32/mode/hardware.h index 4b1198cf7..5e20b33be 100644 --- a/include/arch/riscv/arch/32/mode/hardware.h +++ b/include/arch/riscv/arch/32/mode/hardware.h @@ -21,4 +21,18 @@ #define LOAD lw #define STORE sw +/* Contain the typical location of memory */ +#define PADDR_BASE 0x80000000lu +/* This represents the physical address that the kernel image will be linked to. This needs to + * be on a 1gb boundary as we currently require being able to creating a mapping to this address + * as the largest frame size */ +#define PADDR_LOAD 0x84000000 +/* This is the base of the kernel window, which is directly mapped to PADDR_BASE */ +#define PPTR_BASE seL4_UserTop +/* This is the mapping of the kernel (mapped above the kernel window currently) */ +#define KERNEL_BASE 0xFF800000 +#define KERNEL_ELF_BASE KERNEL_BASE +/* Start of kernel device mapping region in highest 4MiB of memory. */ +#define KDEV_PPTR 0xFFC00000 + #endif /* __ARCH_MODE_HARDWARE_H */ diff --git a/include/arch/riscv/arch/64/mode/hardware.h b/include/arch/riscv/arch/64/mode/hardware.h index 6dd2fb205..c67906a82 100644 --- a/include/arch/riscv/arch/64/mode/hardware.h +++ b/include/arch/riscv/arch/64/mode/hardware.h @@ -21,4 +21,86 @@ #define LOAD ld #define STORE sd +#if CONFIG_PT_LEVELS == 3 + +/* + * The top half of the address space is reserved for the kernel. This means that 256 top level + * entries are for the user, and 256 are for the kernel. This will be further split into the + * 'regular' kernel window, which contains mappings to physical memory, a small (1GiB) higher + * kernel image window that we use for running the actual kernel from and a top 1GiB window for + * kernel device mappings. This means that between PPTR_BASE and + * KERNEL_BASE there are 254 entries remaining, which represents how much physical memory + * can be used. + * + * Almost all of the top 256 kernel entries will contain 1GiB page mappings. The only 2 entries + * that contain a 2nd level PageTable consisting of 2MiB page entries is the entry + * for the 1GiB Kernel ELF region and the 1GiB region corresponding to the physical memory + * of the kernel ELF in the kernel window. The same 2nd level PageTable is used and so both + * entries refer to the same 1GiB of physical memory. + * This means that the 1GiB kernel ELF mapping will correspond to physical memory with a 1GiB + * alignment. + * + * +-----------------------------+ 2^64 + * | Kernel Devices | + * -> +-------------------KDEV_PPTR-+ 2^64 - 1GiB + * | | Kernel ELF | + * ----| +-------------KERNEL_ELF_BASE-+ --+ 2^64 - 2GiB + (PADDR_LOAD % 1GiB) + * | | | | + * | -> +-----------------KERNEL_BASE-+ --+ 2^64 - 2GiB + * Shared 1GiB| | | | + * table entry| | PSpace | | + * | | (direct kernel mappings) | +----+ + * ------>| | | | + * | | | | + * +-------------------PPTR_BASE-+ --+ 2^64 - 2^c + * | | | +-------------------------+ + * | | | | | + * | | | | | + * | Invalid | | | | + * | | | | not | + * | | | | kernel | + * | | | | addressable | + * +-----------------------------+ 2^c | | | + * | | | | | + * | | | | | + * | | | +- --------------------------+ PADDR_TOP = + * | | | | | | KERNEL_BASE - PPTR_BASE + * | | | | | | + * | | | | | | + * | User | | | | | + * | | | | | | + * | | +------+ +-------------------------+ PADDR_HIGH_TOP = + * | | kernel | | Kernel ELF | (KDEV_PPTR - KERNEL_ELF_BASE + PADDR_LOAD) + * | | addressable | +-------------------------+ PADDR_LOAD + * | | | | | + * | | | | | + * +-----------------------------+ 0 +- +-------------------------+ 0 PADDR_BASE + * + * virtual address space physical address space + * + * + * c = one less than number of bits the page tables can translate + * = sign extension bit for canonical addresses + * (= 47 on x64, 38 on RISCV64 sv39, 47 on RISCV64 sv48) + * + */ + +/* The main kernel window will start at the 0 physical address so that it can contain + * any potential memory that may exist */ +#define PADDR_BASE 0x0lu +/* This represents the physical address that the kernel image will be linked to. This needs to + * be on a 1gb boundary as we currently require being able to creating a mapping to this address + * as the largest frame size */ +#define PADDR_LOAD 0x84000000 +/* This is the base of the kernel window, which is directly mapped to PADDR_BASE */ +#define PPTR_BASE 0xFFFFFFC000000000lu +/* This is the mapping of the kernel (mapped above the kernel window currently) */ +#define KERNEL_BASE 0xFFFFFFFF80000000lu +#define KERNEL_ELF_BASE 0xFFFFFFFF84000000 +/* Start of kernel device mapping region in highest 1GiB of memory. */ +#define KDEV_PPTR 0xFFFFFFFFC0000000lu +#else +#error Only PT_LEVELS == 3 is supported +#endif + #endif /* __ARCH_MODE_HARDWARE_H */ diff --git a/include/arch/riscv/arch/machine.h b/include/arch/riscv/arch/machine.h index a3746fc86..fc8af570e 100644 --- a/include/arch/riscv/arch/machine.h +++ b/include/arch/riscv/arch/machine.h @@ -131,6 +131,47 @@ static inline void Arch_finaliseInterrupt(void) { } -#endif // __ASSEMBLER__ +int get_num_avail_p_regs(void); +p_region_t *get_avail_p_regs(void); +int get_num_dev_p_regs(void); +p_region_t get_dev_p_reg(word_t i); +void map_kernel_devices(void); + +typedef uint32_t irq_t; +void ackInterrupt(irq_t irq); +bool_t isIRQPending(void); +void maskInterrupt(bool_t enable, irq_t irq); +irq_t getActiveIRQ(void); +static inline void setInterruptMode(irq_t irq, bool_t levelTrigger, bool_t polarityLow) { } +/** MODIFIES: [*] */ +void initTimer(void); +/* L2 cache control */ +void initL2Cache(void); + +void initIRQController(void); +void setIRQTrigger(irq_t irq, bool_t trigger); + +void handleSpuriousIRQ(void); + +void plat_cleanL2Range(paddr_t start, paddr_t end); + +void plat_invalidateL2Range(paddr_t start, paddr_t end); + +void plat_cleanInvalidateL2Range(paddr_t start, paddr_t end); + +static inline void *CONST paddr_to_kpptr(paddr_t paddr) +{ + assert(paddr < PADDR_HIGH_TOP); + assert(paddr >= PADDR_LOAD); + return (void *)(paddr + KERNEL_BASE_OFFSET); +} + +static inline paddr_t CONST kpptr_to_paddr(void *pptr) +{ + assert((word_t)pptr >= KERNEL_BASE); + return (paddr_t)pptr - KERNEL_BASE_OFFSET; +} + +#endif // !__ASSEMBLER__ #endif diff --git a/include/arch/riscv/arch/machine/hardware.h b/include/arch/riscv/arch/machine/hardware.h index 0ccb961c9..ff2572452 100644 --- a/include/arch/riscv/arch/machine/hardware.h +++ b/include/arch/riscv/arch/machine/hardware.h @@ -31,6 +31,23 @@ #include #include +/* The highest valid physical address that can be indexed in the kernel window */ +#define PADDR_TOP (KERNEL_BASE - PPTR_BASE + PADDR_BASE) +/* A contiguous region of physical address space at PADDR_LOAD is mapped + * to KERNEL_ELF_BASE, and the size of this region is KDEV_PPTR-KERNEL_ELF_BASE. + * PADDR_HIGH_TOP is the end of this physical address region. */ +#define PADDR_HIGH_TOP (KDEV_PPTR - KERNEL_ELF_BASE + PADDR_LOAD) + +/* Translates from a physical address and a value in the kernel image */ +#define KERNEL_BASE_OFFSET (KERNEL_ELF_BASE - PADDR_LOAD) + +/* Convert our values into general values expected by the common code */ +#define kernelBase KERNEL_BASE +/* This is the top of the kernel window, not including the kernel image */ +#define PPTR_TOP KERNEL_BASE +#define PPTR_USER_TOP seL4_UserTop +#define BASE_OFFSET (PPTR_BASE - PADDR_BASE) + #define PAGE_BITS seL4_PageBits /* MMU RISC-V related definitions. See RISC-V manual priv-1.10 */ diff --git a/include/plat/spike/plat/machine.h b/include/arch/riscv/arch/machine/plic.h similarity index 56% rename from include/plat/spike/plat/machine.h rename to include/arch/riscv/arch/machine/plic.h index 61eec3bbe..89ba8281a 100644 --- a/include/plat/spike/plat/machine.h +++ b/include/arch/riscv/arch/machine/plic.h @@ -1,5 +1,5 @@ /* - * Copyright 2018, Data61 + * Copyright 2019, Data61 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) * ABN 41 687 119 230. * @@ -10,18 +10,6 @@ * @TAG(DATA61_GPL) */ -/* - * - * Copyright 2016, 2017 Hesham Almatary, Data61/CSIRO - * Copyright 2015, 2016 Hesham Almatary - */ - -#ifndef __PLAT_MACHINE_H -#define __PLAT_MACHINE_H - -#ifndef __ASSEMBLER__ - - /* * RISC-V defines a Platform-level interrupt controller (PLIC) (priv-1.10). * It is responsible for managing global interrupts in a RISC-V system. @@ -53,44 +41,25 @@ * void plic_init_controller(void): Perform PLIC initialisation during boot. */ typedef uint32_t interrupt_t; -typedef uint32_t irq_t; -enum irqNumbers { - irqInvalid = 0 -}; +static inline interrupt_t plic_get_claim(void) +{ + return irqInvalid; +} -#if defined(CONFIG_BUILD_SPIKE_QEMU) -#include -#elif defined(CONFIG_BUILD_ROCKET_CHIP_ZEDBOARD) -#include -#elif defined(CONFIG_BUILD_HI_FIVE_UNLEASHED) -#include -#else -#error "Unsupported spike platform chosen" +static inline void plic_complete_claim(interrupt_t irq) +{ +} + +static inline void plic_mask_irq(bool_t disable, interrupt_t irq) +{ +} + +#ifdef HAVE_SET_TRIGGER +static inline void plic_irq_set_trigger(interrupt_t irq, bool_t edge_triggered); #endif -/* - * seL4 assigns all IRQs global interrupt numbers that are used in interrupt - * invocations. On RISC-V we have 3 different types of interrupts: core timer, - * core software generated, and global external IRQs delivered through the PLIC. - * Only global external interrupts are available from user level and so it is - * nice to be able to match PLIC IRQ numbers to seL4 IRQ numbers. The PLIC uses - * IRQ 0 to refer to no IRQ pending and so we can also use 0 for irqInvalid in - * the global IRQ number space and not have any aliasing issues. We then place - * the kernel timer interrupts after the last PLIC interrupt and intend on - * placing software generated interrupts after this in the future. As the kernel - * timer and SGI interrupts are never seen outside of the kernel, it doesn't - * matter what number they get assigned to as we can refer to them by their enum - * field name. - */ -enum IRQConstants { - PLIC_IRQ_OFFSET = 0, - PLIC_MAX_IRQ = PLIC_IRQ_OFFSET + PLIC_MAX_NUM_INT, - INTERRUPT_CORE_TIMER, - maxIRQ = INTERRUPT_CORE_TIMER, -} platform_interrupt_t; +static inline void plic_init_controller(void) +{ +} -#define KERNEL_TIMER_IRQ INTERRUPT_CORE_TIMER - -#endif /* !__ASSEMBLER__ */ -#endif diff --git a/include/arch/riscv/arch/machine/timer.h b/include/arch/riscv/arch/machine/timer.h index b8e8d50a8..2b67acf84 100644 --- a/include/arch/riscv/arch/machine/timer.h +++ b/include/arch/riscv/arch/machine/timer.h @@ -12,6 +12,4 @@ #ifndef __ARCH_MACHINE_TIMER_H #define __ARCH_MACHINE_TIMER_H -#include - #endif /* __ARCH_MACHINE_TIMER_H */ diff --git a/include/arch/riscv/arch/model/statedata.h b/include/arch/riscv/arch/model/statedata.h index 39652b159..cf18a6769 100644 --- a/include/arch/riscv/arch/model/statedata.h +++ b/include/arch/riscv/arch/model/statedata.h @@ -26,7 +26,6 @@ #include #include #include -#include NODE_STATE_BEGIN(archNodeState) diff --git a/include/arch/riscv/arch/types.h b/include/arch/riscv/arch/types.h index e2b3ffd6e..ed6d00ebe 100644 --- a/include/arch/riscv/arch/types.h +++ b/include/arch/riscv/arch/types.h @@ -46,4 +46,9 @@ typedef dom_t seL4_Domain; #define wordBits BIT(wordRadix) +typedef struct kernel_frame { + paddr_t paddr; + pptr_t pptr; +} kernel_frame_t; + #endif diff --git a/include/plat/spike/plat/32/plat_mode/machine/hardware.h b/include/plat/spike/plat/32/plat_mode/machine/hardware.h deleted file mode 100644 index b7811a30b..000000000 --- a/include/plat/spike/plat/32/plat_mode/machine/hardware.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2018, Data61 - * Commonwealth Scientific and Industrial Research Organisation (CSIRO) - * ABN 41 687 119 230. - * - * This software may be distributed and modified according to the terms of - * the GNU General Public License version 2. Note that NO WARRANTY is provided. - * See "LICENSE_GPLv2.txt" for details. - * - * @TAG(DATA61_GPL) - */ -#ifndef __PLAT_MODE_MACHINE_HARDWARE_H -#define __PLAT_MODE_MACHINE_HARDWARE_H - -#include -#include - -/* This is the base of the kernel window, which is directly mapped to PADDR_BASE */ -#define PPTR_BASE seL4_UserTop -/* This is the mapping of the kernel (mapped above the kernel window currently) */ -#define KERNEL_BASE 0xFF800000lu -#define KERNEL_ELF_BASE KERNEL_BASE -/* Start of kernel device mapping region in highest 4MiB of memory. */ -#define PPTR_KDEV 0xFFC00000 - -#endif diff --git a/include/plat/spike/plat/64/plat_mode/machine/hardware.h b/include/plat/spike/plat/64/plat_mode/machine/hardware.h deleted file mode 100644 index 35c9f4b16..000000000 --- a/include/plat/spike/plat/64/plat_mode/machine/hardware.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2018, Data61 - * Commonwealth Scientific and Industrial Research Organisation (CSIRO) - * ABN 41 687 119 230. - * - * This software may be distributed and modified according to the terms of - * the GNU General Public License version 2. Note that NO WARRANTY is provided. - * See "LICENSE_GPLv2.txt" for details. - * - * @TAG(DATA61_GPL) - */ -#ifndef __PLAT_MODE_MACHINE_HARDWARE_H -#define __PLAT_MODE_MACHINE_HARDWARE_H - -#include - -#if CONFIG_PT_LEVELS == 3 - -/* - * The top half of the address space is reserved for the kernel. This means that 256 top level - * entries are for the user, and 256 are for the kernel. This will be further split into the - * 'regular' kernel window, which contains mappings to physical memory, a small (1GiB) higher - * kernel image window that we use for running the actual kernel from and a top 1GiB window for - * kernel device mappings. This means that between PPTR_BASE and - * KERNEL_BASE there are 254 entries remaining, which represents how much physical memory - * can be used. - * - * Almost all of the top 256 kernel entries will contain 1GiB page mappings. The only 2 entries - * that contain a 2nd level PageTable consisting of 2MiB page entries is the entry - * for the 1GiB Kernel ELF region and the 1GiB region corresponding to the physical memory - * of the kernel ELF in the kernel window. The same 2nd level PageTable is used and so both - * entries refer to the same 1GiB of physical memory. - * This means that the 1GiB kernel ELF mapping will correspond to physical memory with a 1GiB - * alignment. - * - * +-----------------------------+ 2^64 - * | Kernel Devices | - * -> +-------------------PPTR_KDEV-+ 2^64 - 1GiB - * | | Kernel ELF | - * ----| +-------------KERNEL_ELF_BASE-+ --+ 2^64 - 2GiB + (PADDR_LOAD % 1GiB) - * | | | | - * | -> +-----------------KERNEL_BASE-+ --+ 2^64 - 2GiB - * Shared 1GiB| | | | - * table entry| | PSpace | | - * | | (direct kernel mappings) | +----+ - * ------>| | | | - * | | | | - * +-------------------PPTR_BASE-+ --+ 2^64 - 2^c - * | | | +-------------------------+ - * | | | | | - * | | | | | - * | Invalid | | | | - * | | | | not | - * | | | | kernel | - * | | | | addressable | - * +-----------------------------+ 2^c | | | - * | | | | | - * | | | | | - * | | | +- --------------------------+ PADDR_TOP = - * | | | | | | KERNEL_BASE - PPTR_BASE - * | | | | | | - * | | | | | | - * | User | | | | | - * | | | | | | - * | | +------+ +-------------------------+ PADDR_HIGH_TOP = - * | | kernel | | Kernel ELF | (PPTR_KDEV - KERNEL_ELF_BASE + PADDR_LOAD) - * | | addressable | +-------------------------+ PADDR_LOAD - * | | | | | - * | | | | | - * +-----------------------------+ 0 +- +-------------------------+ 0 PADDR_BASE - * - * virtual address space physical address space - * - * - * c = one less than number of bits the page tables can translate - * = sign extension bit for canonical addresses - * (= 47 on x64, 38 on RISCV64 sv39, 47 on RISCV64 sv48) - * - */ - -/* This is the base of the kernel window, which is directly mapped to PADDR_BASE */ -#define PPTR_BASE 0xFFFFFFC000000000lu -/* This is the mapping of the kernel (mapped above the kernel window currently) */ -#define KERNEL_BASE 0xFFFFFFFF80000000lu -#define KERNEL_ELF_BASE 0xFFFFFFFF84000000lu -/* Start of kernel device mapping region in highest 1GiB of memory. */ -#define PPTR_KDEV 0xFFFFFFFFC0000000lu -#else -#error Only PT_LEVELS == 3 is supported -#endif - -#endif diff --git a/include/plat/spike/plat/api/constants.h b/include/plat/spike/plat/api/constants.h deleted file mode 100644 index 855d86a82..000000000 --- a/include/plat/spike/plat/api/constants.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2017, Data61 - * Commonwealth Scientific and Industrial Research Organisation (CSIRO) - * ABN 41 687 119 230. - * - * This software may be distributed and modified according to the terms of - * the BSD 2-Clause license. Note that NO WARRANTY is provided. - * See "LICENSE_BSD2.txt" for details. - * - * @TAG(DATA61_BSD) - */ - -#ifndef __LIBSEL4_SEL4_PLAT_API_CONSTANTS_H_ -#define __LIBSEL4_SEL4_PLAT_API_CONSTANTS_H_ - -#ifdef HAVE_AUTOCONF -#include -#endif - -#endif /* __LIBSEL4_SEL4_PLAT_API_CONSTANTS_H_ */ diff --git a/include/plat/spike/plat/instance/qemu/hardware.h b/include/plat/spike/plat/instance/qemu/hardware.h deleted file mode 100644 index 496e54574..000000000 --- a/include/plat/spike/plat/instance/qemu/hardware.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2019, Data61 - * Commonwealth Scientific and Industrial Research Organisation (CSIRO) - * ABN 41 687 119 230. - * - * This software may be distributed and modified according to the terms of - * the GNU General Public License version 2. Note that NO WARRANTY is provided. - * See "LICENSE_GPLv2.txt" for details. - * - * @TAG(DATA61_GPL) - */ - -#ifndef __PLAT_INSTANCE_HARDWARE_H -#define __PLAT_INSTANCE_HARDWARE_H - -#define PLIC_MAX_NUM_INT 0 -#define IRQ_CNODE_SLOT_BITS 1 - -static inline interrupt_t plic_get_claim(void) -{ - return irqInvalid; -} - -static inline void plic_complete_claim(interrupt_t irq) -{ -} - -static inline void plic_mask_irq(bool_t disable, interrupt_t irq) -{ -} - -static inline void plic_init_controller(void) -{ -} - -/* Available physical memory regions on platform (RAM minus kernel image). */ -/* NOTE: Regions are not allowed to be adjacent! */ -static p_region_t BOOT_DATA avail_p_regs[] = { - /* The first 2MB are reserved for the SBI in the BBL */ -#if defined(CONFIG_ARCH_RISCV64) - { /*.start = */ 0x80200000, /* .end = */ 0x17FF00000} -#elif defined(CONFIG_ARCH_RISCV32) - { /*.start = */ 0x80200000, /* .end = */ 0xFD000000} -#endif -}; - -static const paddr_t BOOT_RODATA *kernel_devices = NULL; - -static const p_region_t BOOT_RODATA *dev_p_regs = NULL; - -#endif diff --git a/include/plat/spike/plat/machine/devices.h b/include/plat/spike/plat/machine/devices.h deleted file mode 100644 index 520380a49..000000000 --- a/include/plat/spike/plat/machine/devices.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2018, Data61 - * Commonwealth Scientific and Industrial Research Organisation (CSIRO) - * ABN 41 687 119 230. - * - * This software may be distributed and modified according to the terms of - * the GNU General Public License version 2. Note that NO WARRANTY is provided. - * See "LICENSE_GPLv2.txt" for details. - * - * @TAG(DATA61_GPL) - */ - -#ifndef __PLAT_MACHINE_DEVICES_H -#define __PLAT_MACHINE_DEVICES_H - -#endif diff --git a/include/plat/spike/plat/machine/hardware.h b/include/plat/spike/plat/machine/hardware.h deleted file mode 100644 index 481fa9293..000000000 --- a/include/plat/spike/plat/machine/hardware.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2018, Data61 - * Commonwealth Scientific and Industrial Research Organisation (CSIRO) - * ABN 41 687 119 230. - * - * This software may be distributed and modified according to the terms of - * the GNU General Public License version 2. Note that NO WARRANTY is provided. - * See "LICENSE_GPLv2.txt" for details. - * - * @TAG(DATA61_GPL) - */ - -/* - * - * Copyright 2016, 2017 Hesham Almatary, Data61/CSIRO - * Copyright 2015, 2016 Hesham Almatary - */ - -#ifndef __PLAT_MACHINE_HARDWARE_H -#define __PLAT_MACHINE_HARDWARE_H - -#include -#include - -#if __riscv_xlen == 32 -/* Contain the typical location of memory */ -#define PADDR_BASE 0x80000000lu -#else -/* The main kernel window will start at the 0 physical address so that it can contain - * any potential memory that may exist */ -#define PADDR_BASE 0x0lu -#endif - -#ifdef CONFIG_BUILD_ROCKET_CHIP_ZEDBOARD -/* The Rocket-Chip for zedboard only has 256MiB of Memory. */ -#define PADDR_LOAD 0x88000000lu -#else -/* This represents the physical address that the kernel image will be linked to. This needs to - * be on a 1gb boundary as we currently require being able to creating a mapping to this address - * as the largest frame size */ -#define PADDR_LOAD 0x84000000lu -#endif - -/* The highest valid physical address that can be indexed in the kernel window */ -#define PADDR_TOP (KERNEL_BASE - PPTR_BASE + PADDR_BASE) -/* A contiguous region of physical address space at PADDR_LOAD is mapped - * to KERNEL_ELF_BASE, and the size of this region is PPTR_KDEV-KERNEL_ELF_BASE. - * PADDR_HIGH_TOP is the end of this physical address region. */ -#define PADDR_HIGH_TOP (PPTR_KDEV - KERNEL_ELF_BASE + PADDR_LOAD) - -/* Translates from a physical address and a value in the kernel image */ -#define KERNEL_BASE_OFFSET (KERNEL_ELF_BASE - PADDR_LOAD) - -/* Convert our values into general values expected by the common code */ -#define kernelBase KERNEL_BASE -/* This is the top of the kernel window, not including the kernel image */ -#define PPTR_TOP KERNEL_BASE -#define PPTR_USER_TOP seL4_UserTop -#define BASE_OFFSET (PPTR_BASE - PADDR_BASE) - -#ifndef __ASSEMBLER__ - -int get_num_avail_p_regs(void); -p_region_t *get_avail_p_regs(void); -int get_num_dev_p_regs(void); -p_region_t get_dev_p_reg(word_t i); -void map_kernel_devices(void); - -void ackInterrupt(irq_t irq); -bool_t isIRQPending(void); -void maskInterrupt(bool_t enable, irq_t irq); -irq_t getActiveIRQ(void); -static inline void setInterruptMode(irq_t irq, bool_t levelTrigger, bool_t polarityLow) { } -/** MODIFIES: [*] */ -void initTimer(void); -/* L2 cache control */ -void initL2Cache(void); - -void initIRQController(void); -void setIRQTrigger(irq_t irq, bool_t trigger); - -void handleSpuriousIRQ(void); - -void plat_cleanL2Range(paddr_t start, paddr_t end); - -void plat_invalidateL2Range(paddr_t start, paddr_t end); - -void plat_cleanInvalidateL2Range(paddr_t start, paddr_t end); - -static inline void *CONST paddr_to_kpptr(paddr_t paddr) -{ - assert(paddr < PADDR_HIGH_TOP); - assert(paddr >= PADDR_LOAD); - return (void *)(paddr + KERNEL_BASE_OFFSET); -} - -static inline paddr_t CONST kpptr_to_paddr(void *pptr) -{ - assert((word_t)pptr >= KERNEL_BASE); - return (paddr_t)pptr - KERNEL_BASE_OFFSET; -} - -#endif /* !__ASSEMBLER__ */ - -#endif diff --git a/include/plat/spike/plat/machine/io.h b/include/plat/spike/plat/machine/io.h deleted file mode 100644 index 9308797ad..000000000 --- a/include/plat/spike/plat/machine/io.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2018, Data61 - * Commonwealth Scientific and Industrial Research Organisation (CSIRO) - * ABN 41 687 119 230. - * - * This software may be distributed and modified according to the terms of - * the GNU General Public License version 2. Note that NO WARRANTY is provided. - * See "LICENSE_GPLv2.txt" for details. - * - * @TAG(DATA61_GPL) - */ - -/* - * - * Copyright 2016, 2017 Hesham Almatary, Data61/CSIRO - * Copyright 2015, 2016 Hesham Almatary - */ - -#ifndef __PLAT_IO_H -#define __PLAT_IO_H - -#include -#include - -#endif diff --git a/include/plat/spike/plat/machine/timer.h b/include/plat/spike/plat/machine/timer.h deleted file mode 100644 index c06e59ef9..000000000 --- a/include/plat/spike/plat/machine/timer.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2018, Data61 - * Commonwealth Scientific and Industrial Research Organisation (CSIRO) - * ABN 41 687 119 230. - * - * This software may be distributed and modified according to the terms of - * the GNU General Public License version 2. Note that NO WARRANTY is provided. - * See "LICENSE_GPLv2.txt" for details. - * - * @TAG(DATA61_GPL) - */ - -#ifndef __PLAT_MACHINE_TIMER_H -#define __PLAT_MACHINE_TIMER_H - -#endif /* !__PLAT_MACHINE_TIMER_H */ diff --git a/src/arch/arm/config.cmake b/src/arch/arm/config.cmake index b744b9027..d1e30ff5b 100644 --- a/src/arch/arm/config.cmake +++ b/src/arch/arm/config.cmake @@ -68,36 +68,6 @@ elseif(KernelSel4ArchAarch64) set_kernel_64() endif() -function(declare_default_headers) - cmake_parse_arguments( - PARSE_ARGV - 0 - CONFIGURE - "" - "TIMER_FREQUENCY;MAX_IRQ;INTERRUPT_CONTROLLER;TIMER;SMMU" - "" - ) - # calculate the irq cnode size based on MAX_IRQ - set(BITS "0") - set(MAX "${CONFIGURE_MAX_IRQ}") - while(MAX GREATER "0") - math(EXPR BITS "${BITS} + 1") - math(EXPR MAX "${MAX} >> 1") - endwhile() - math(EXPR SLOTS "1 << ${BITS}") - if("${SLOTS}" LESS "${CONFIGURE_MAX_IRQ}") - math(EXPR BITS "${BITS} + 1") - endif() - set(CONFIGURE_IRQ_SLOT_BITS "${BITS}") - # variables parsed by the above will be prepended with CONFIGURE_, so pipe them - # straight to configure_file - configure_file( - src/arch/arm/platform_gen.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen_headers/plat/platform_gen.h - @ONLY - ) - include_directories(include/plat/default) -endfunction() - # Include all the platforms. For all of the common variables we set a default value here # and let the platforms override them. set(KernelArmMachFeatureModifiers "" CACHE INTERNAL "") @@ -133,79 +103,6 @@ include(src/plat/zynq7000/config.cmake) include(src/plat/zynqmp/config.cmake) include(src/plat/odroidc2/config.cmake) -if(DEFINED KernelDTSList) - set(KernelDTSIntermediate "${CMAKE_CURRENT_BINARY_DIR}/kernel.dts") - set( - KernelDTBPath "${CMAKE_CURRENT_BINARY_DIR}/kernel.dtb" - CACHE INTERNAL "Location of kernel DTB file" - ) - set(compatibility_outfile "${CMAKE_CURRENT_BINARY_DIR}/kernel_compat.txt") - set(device_dest "${CMAKE_CURRENT_BINARY_DIR}/gen_headers/plat/machine/devices_gen.h") - set( - platform_yaml "${CMAKE_CURRENT_BINARY_DIR}/gen_headers/plat/machine/platform_gen.yaml" - CACHE INTERNAL "Location of platform YAML description" - ) - set(config_file "${CMAKE_CURRENT_SOURCE_DIR}/tools/hardware.yml") - set(config_schema "${CMAKE_CURRENT_SOURCE_DIR}/tools/hardware_schema.yml") - - find_program(DTC_TOOL dtc) - if("${DTC_TOOL}" STREQUAL "DTC_TOOL-NOTFOUND") - message(FATAL_ERROR "Cannot find 'dtc' program.") - endif() - - # Generate final DTS based on Linux DTS + seL4 overlay[s] - foreach(entry ${KernelDTSList}) - get_absolute_source_or_binary(dts_tmp ${entry}) - list(APPEND dts_list "${dts_tmp}") - endforeach() - - check_outfile_stale(regen ${KernelDTBPath} dts_list ${CMAKE_CURRENT_BINARY_DIR}/dts.cmd) - if(regen) - file(REMOVE "${KernelDTSIntermediate}") - foreach(entry ${dts_list}) - file(READ ${entry} CONTENTS) - file(APPEND "${KernelDTSIntermediate}" "${CONTENTS}") - endforeach() - # Compile DTS to DTB - execute_process( - COMMAND - ${DTC_TOOL} -q -I dts -O dtb -o ${KernelDTBPath} ${KernelDTSIntermediate} - ) - endif() - - set(deps ${KernelDTBPath} ${config_file} ${config_schema} ${HARDWARE_GEN_PATH}) - check_outfile_stale(regen ${device_dest} deps ${CMAKE_CURRENT_BINARY_DIR}/gen_header.cmd) - if(regen) - # Generate devices_gen header based on DTB - message(STATUS "${device_dest} is out of date. Regenerating...") - file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/gen_headers/plat/machine/") - execute_process( - COMMAND - ${PYTHON} "${HARDWARE_GEN_PATH}" --dtb "${KernelDTBPath}" --compatibility-strings - "${compatibility_outfile}" --output "${device_dest}" --config "${config_file}" - --schema "${config_schema}" --yaml "${platform_yaml}" - INPUT_FILE /dev/stdin - OUTPUT_FILE /dev/stdout - ERROR_FILE /dev/stderr - RESULT_VARIABLE error - ) - if(error) - message(FATAL_ERROR "Failed to generate: ${device_dest}") - endif() - endif() - file(READ "${compatibility_outfile}" compatibility_strings) - - # Mark all file dependencies as CMake rerun dependencies. - set(cmake_deps ${deps} ${KernelDTSIntermediate} ${KernelDTSList} ${compatibility_outfile}) - set_property( - DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - APPEND - PROPERTY CMAKE_CONFIGURE_DEPENDS ${cmake_deps} - ) - - include(src/drivers/config.cmake) -endif() - # Now enshrine all the common variables in the config config_set(KernelArmCortexA7 ARM_CORTEX_A7 "${KernelArmCortexA7}") config_set(KernelArmCortexA8 ARM_CORTEX_A8 "${KernelArmCortexA8}") diff --git a/src/plat/spike/linker.lds b/src/arch/riscv/common_riscv.lds similarity index 81% rename from src/plat/spike/linker.lds rename to src/arch/riscv/common_riscv.lds index 5ab3a0491..3158a148e 100644 --- a/src/plat/spike/linker.lds +++ b/src/arch/riscv/common_riscv.lds @@ -20,21 +20,11 @@ OUTPUT_ARCH(riscv) ENTRY(_start) #include +#define __ASSEMBLER__ +#include +#include +#include -#if CONFIG_PT_LEVELS == 2 -KERNEL_ELF_BASE = 0xFF800000; -#elif CONFIG_PT_LEVELS == 3 -KERNEL_ELF_BASE = 0xFFFFFFFF84000000; -#elif CONFIG_PT_LEVELS == 4 -#error PT_LEVELS == 4 is not supported yet -#endif - -/* WARNING: constants also defined in plat/machine/hardware.h */ -#ifdef CONFIG_BUILD_ROCKET_CHIP_ZEDBOARD -PADDR_LOAD = 0x0000000088000000; -#else -PADDR_LOAD = 0x0000000084000000; -#endif KERNEL_OFFSET = KERNEL_ELF_BASE - PADDR_LOAD; SECTIONS diff --git a/src/arch/riscv/kernel/vspace.c b/src/arch/riscv/kernel/vspace.c index 5f98ae3e8..69d4c20fd 100644 --- a/src/arch/riscv/kernel/vspace.c +++ b/src/arch/riscv/kernel/vspace.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/src/arch/riscv/machine/hardware.c b/src/arch/riscv/machine/hardware.c index c86f1bc47..5ce0b21a6 100644 --- a/src/arch/riscv/machine/hardware.c +++ b/src/arch/riscv/machine/hardware.c @@ -17,8 +17,20 @@ */ #include #include +#include #include +#define STIMER_IP 5 +#define STIMER_IE 5 +#define STIMER_CAUSE 5 +#define SEXTERNAL_IP 9 +#define SEXTERNAL_IE 9 +#define SEXTERNAL_CAUSE 9 + +#define RESET_CYCLES ((CONFIG_SPIKE_CLOCK_FREQ / MS_IN_S) * CONFIG_TIMER_TICK_MS) + +#define IS_IRQ_VALID(X) (((X)) <= maxIRQ && (X)!= irqInvalid) + word_t PURE getRestartPC(tcb_t *thread) { return getRegister(thread, FaultIP); @@ -28,3 +40,275 @@ void setNextPC(tcb_t *thread, word_t v) { setRegister(thread, NextIP, v); } + +BOOT_CODE int get_num_avail_p_regs(void) +{ + return sizeof(avail_p_regs) / sizeof(p_region_t); +} + +BOOT_CODE p_region_t *get_avail_p_regs(void) +{ + return (p_region_t *) avail_p_regs; +} + +BOOT_CODE int get_num_dev_p_regs(void) +{ + if (dev_p_regs != NULL) { + return (sizeof(dev_p_regs) / sizeof(p_region_t)); + } else { + return 0; + } +} + +BOOT_CODE p_region_t get_dev_p_reg(word_t i) +{ + /* We need this if guard as some RISC-V configurations don't declare any + * device regions and some compilers complain about indexing an empty array + * due to not being able to infer that get_dev_p_reg is only called if + * dev_p_regs contains entries. + */ + if (get_num_dev_p_regs() == 0) { + printf("%s: No devices present.\n", __func__); + halt(); + } + return dev_p_regs[i]; +} + +BOOT_CODE void map_kernel_devices(void) +{ + if (kernel_devices == NULL) { + return; + } + + for (int i = 0; i < (sizeof(kernel_devices) / sizeof(paddr_t)); i++) { + map_kernel_frame(kernel_devices[i].paddr, KDEV_PPTR, + VMKernelOnly); + } +} + +/* + * The following assumes familiarity with RISC-V interrupt delivery and the PLIC. + * See the RISC-V privileged specifivation v1.10 and the comment in + * include/plat/spike/plat/machine.h for more information. + * RISC-V IRQ handling on seL4 works as follows: + * + * On other architectures the kernel masks interrupts between delivering them to + * userlevel and receiving the acknowledgement invocation. This strategy doesn't + * work on RISC-V as an IRQ is implicitly masked when it is claimed, until the + * claim is acknowledged. If we mask and unmask the interrupt at the PLIC while + * a claim is in progress we sometimes experience IRQ sources not being masked + * and unmasked as expected. Because of this, we don't mask and unmask IRQs that + * are for user level, and also call plic_complete_claim for seL4_IRQHandler_Ack. + */ + +/** + * Gets the new active irq from the PLIC or STIP. + * + * getNewActiveIRQ is only called by getActiveIRQ and checks for a pending IRQ. + * We read sip and if the SEIP bit is set we claim an + * IRQ from the PLIC. If STIP is set then it is a kernel timer interrupt. + * Otherwise we return IRQ invalid. It is possible to reveive irqInvalid from + * the PLIC if another HART context has claimed the IRQ before us. This function + * is not idempotent as plic_get_claim is called which accepts an IRQ message + * from the PLIC and will claim different IRQs if called subsequent times. + * + * @return The new active irq. + */ +static irq_t getNewActiveIRQ(void) +{ + + uint64_t sip = read_sip(); + + if (sip & BIT(STIMER_IP)) { + // Supervisor timer interrupt + return INTERRUPT_CORE_TIMER; + } else if (BIT(SEXTERNAL_IP)) { + /* External IRQ */ + return plic_get_claim(); + } else { + return irqInvalid; + } +} + +static uint32_t active_irq = irqInvalid; + + +/** + * Gets the active irq. Returns the same irq if called again before ackInterrupt. + * + * getActiveIRQ is used to return a currently pending IRQ. This function can be + * called multiple times and needs to return the same IRQ until ackInterrupt is + * called. getActiveIRQ returns irqInvalid if no interrupt is pending. It is + * assumed that if isIRQPending is true, then getActiveIRQ will not return + * irqInvalid. getActiveIRQ will call getNewActiveIRQ and cache its result until + * ackInterrupt is called. + * + * @return The active irq. + */ +irq_t getActiveIRQ(void) +{ + + uint32_t irq; + if (!IS_IRQ_VALID(active_irq)) { + active_irq = getNewActiveIRQ(); + } + + if (IS_IRQ_VALID(active_irq)) { + irq = active_irq; + } else { + irq = irqInvalid; + } + + return irq; +} + +#ifdef HAVE_SET_TRIGGER +/** + * Sets the irq trigger. + * + * setIRQTrigger can change the trigger between edge and level at the PLIC for + * external interrupts. It is implementation specific as whether the PLIC has + * support for this operation. + * + * @param[in] irq The irq + * @param[in] edge_triggered edge triggered otherwise level triggered + */ +void setIRQTrigger(irq_t irq, bool_t edge_triggered) +{ + plic_irq_set_trigger(irq, edge_triggered); +} +#endif + +/* isIRQPending is used to determine whether to preempt long running + * operations at various preemption points throughout the kernel. If this + * returns true, it means that if the Kernel were to return to user mode, it + * would then immediately take an interrupt. We check the SIP register for if + * either a timer interrupt (STIP) or an external interrupt (SEIP) is pending. + * We don't check software generated interrupts. These are used to perform cross + * core signalling which isn't currently supported. + * TODO: Add SSIP check when SMP support is added. + */ +bool_t isIRQPending(void) +{ + word_t sip = read_sip(); + return (sip & (BIT(STIMER_IP) | BIT(SEXTERNAL_IP))); +} + +/** + * Disable or enable IRQs. + * + * maskInterrupt disables and enables IRQs. When an IRQ is disabled, it should + * not raise an interrupt on the Kernel's HART context. This either masks the + * core timer on the sie register or masks an external IRQ at the plic. + * + * @param[in] disable The disable + * @param[in] irq The irq + */ +void maskInterrupt(bool_t disable, interrupt_t irq) +{ + assert(IS_IRQ_VALID(irq)); + if (irq == INTERRUPT_CORE_TIMER) { + if (disable) { + clear_sie_mask(BIT(STIMER_IE)); + } else { + set_sie_mask(BIT(STIMER_IE)); + } + } else { + plic_mask_irq(disable, irq); + } +} + +/** + * Kernel has dealt with the pending interrupt getActiveIRQ can return next IRQ. + * + * ackInterrupt is used by the kernel to indicate it has processed the interrupt + * delivery and getActiveIRQ is now able to return a different IRQ number. Note + * that this is called after a notification has been signalled to user level, + * but before user level has handled the cause. + * + * @param[in] irq The irq + */ +void ackInterrupt(irq_t irq) +{ + assert(IS_IRQ_VALID(irq)); + active_irq = irqInvalid; + + if (irq == INTERRUPT_CORE_TIMER) { + /* Reprogramming the timer has cleared the interrupt. */ + return; + } +} + +static inline uint64_t get_cycles(void) +#if __riscv_xlen == 32 +{ + uint32_t nH, nL; + asm volatile( + "rdtimeh %0\n" + "rdtime %1\n" + : "=r"(nH), "=r"(nL)); + return ((uint64_t)((uint64_t) nH << 32)) | (nL); +} +#else +{ + uint64_t n; + asm volatile( + "rdtime %0" + : "=r"(n)); + return n; +} +#endif + +static inline int read_current_timer(unsigned long *timer_val) +{ + *timer_val = get_cycles(); + return 0; +} + +void resetTimer(void) +{ + uint64_t target; + // repeatedly try and set the timer in a loop as otherwise there is a race and we + // may set a timeout in the past, resulting in it never getting triggered + do { + target = get_cycles() + RESET_CYCLES; + sbi_set_timer(target); + } while (get_cycles() > target); +} + +/** + DONT_TRANSLATE + */ +BOOT_CODE void initTimer(void) +{ + sbi_set_timer(get_cycles() + RESET_CYCLES); +} + +void plat_cleanL2Range(paddr_t start, paddr_t end) +{ +} +void plat_invalidateL2Range(paddr_t start, paddr_t end) +{ +} + +void plat_cleanInvalidateL2Range(paddr_t start, paddr_t end) +{ +} + +BOOT_CODE void initL2Cache(void) +{ +} + +BOOT_CODE void initIRQController(void) +{ + printf("Initialing PLIC...\n"); + + plic_init_controller(); + set_sie_mask(BIT(9)); +} + +void handleSpuriousIRQ(void) +{ + /* Do nothing */ + printf("Superior IRQ!! \n"); +} diff --git a/src/arch/riscv/platform_gen.h.in b/src/arch/riscv/platform_gen.h.in new file mode 100644 index 000000000..f35ffb933 --- /dev/null +++ b/src/arch/riscv/platform_gen.h.in @@ -0,0 +1,48 @@ +/* + * Copyright 2019, Data61 + * Commonwealth Scientific and Industrial Research Organisation (CSIRO) + * ABN 41 687 119 230. + * + * This software may be distributed and modified according to the terms of + * the GNU General Public License version 2. Note that NO WARRANTY is provided. + * See "LICENSE_GPLv2.txt" for details. + * + * @TAG(DATA61_GPL) + */ + +#ifndef __RISCV_PLAT_H +#define __RISCV_PLAT_H + +#define TIMER_CLOCK_HZ @CONFIGURE_TIMER_FREQUENCY@ + +/* + * seL4 assigns all IRQs global interrupt numbers that are used in interrupt + * invocations. On RISC-V we have 3 different types of interrupts: core timer, + * core software generated, and global external IRQs delivered through the PLIC. + * Only global external interrupts are available from user level and so it is + * nice to be able to match PLIC IRQ numbers to seL4 IRQ numbers. The PLIC uses + * IRQ 0 to refer to no IRQ pending and so we can also use 0 for irqInvalid in + * the global IRQ number space and not have any aliasing issues. We then place + * the kernel timer interrupts after the last PLIC interrupt and intend on + * placing software generated interrupts after this in the future. As the kernel + * timer and SGI interrupts are never seen outside of the kernel, it doesn't + * matter what number they get assigned to as we can refer to them by their enum + * field name. + */ +enum IRQConstants { + PLIC_IRQ_OFFSET = 0, + PLIC_MAX_IRQ = PLIC_IRQ_OFFSET + (@CONFIGURE_PLIC_MAX_NUM_INT@), + INTERRUPT_CORE_TIMER, + maxIRQ = INTERRUPT_CORE_TIMER, +} platform_interrupt_t; + +enum irqNumbers { + irqInvalid = 0 +}; + +#define KERNEL_TIMER_IRQ INTERRUPT_CORE_TIMER +#define IRQ_CNODE_SLOT_BITS (@CONFIGURE_IRQ_SLOT_BITS@) + +#include <@CONFIGURE_INTERRUPT_CONTROLLER@> + +#endif /* !__RISCV_PLAT_H */ diff --git a/src/plat/spike/config.cmake b/src/plat/spike/config.cmake index 3068c36a9..26094223b 100644 --- a/src/plat/spike/config.cmake +++ b/src/plat/spike/config.cmake @@ -16,6 +16,9 @@ cmake_minimum_required(VERSION 3.7.2) if(KernelPlatformSpike) config_set(KernelPlatform PLAT "spike") + list(APPEND KernelDTSList "tools/dts/spike.dts") + declare_default_headers(TIMER_FREQUENCY 10000000llu PLIC_MAX_NUM_INT 2 + INTERRUPT_CONTROLLER arch/machine/plic.h) endif() config_choice( @@ -35,7 +38,6 @@ config_string( set(DefaultFirstHartID 0) # Include all of the different instances of the Spike platform -include(src/plat/spike/instance/qemu/config.cmake) include(src/plat/spike/instance/rocket-chip/config.cmake) include(src/plat/spike/instance/freedom/config.cmake) @@ -45,4 +47,3 @@ config_string( UNQUOTE ) -add_sources(DEP "KernelPlatformSpike" CFILES src/plat/spike/machine/hardware.c) diff --git a/src/plat/spike/machine/hardware.c b/src/plat/spike/machine/hardware.c deleted file mode 100644 index 9debe2ca2..000000000 --- a/src/plat/spike/machine/hardware.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright 2018, Data61 - * Commonwealth Scientific and Industrial Research Organisation (CSIRO) - * ABN 41 687 119 230. - * - * This software may be distributed and modified according to the terms of - * the GNU General Public License version 2. Note that NO WARRANTY is provided. - * See "LICENSE_GPLv2.txt" for details. - * - * @TAG(DATA61_GPL) - */ - -/* - * - * Copyright 2016, 2017 Hesham Almatary, Data61/CSIRO - * Copyright 2015, 2016 Hesham Almatary - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MAX_AVAIL_P_REGS 2 - -#define STIMER_IP 5 -#define STIMER_IE 5 -#define STIMER_CAUSE 5 -#define SEXTERNAL_IP 9 -#define SEXTERNAL_IE 9 -#define SEXTERNAL_CAUSE 9 - -#define RESET_CYCLES ((CONFIG_SPIKE_CLOCK_FREQ / MS_IN_S) * CONFIG_TIMER_TICK_MS) - -#define IS_IRQ_VALID(X) (((X)) <= maxIRQ && (X)!= irqInvalid) - -BOOT_CODE int get_num_avail_p_regs(void) -{ - return sizeof(avail_p_regs) / sizeof(p_region_t); -} - -BOOT_CODE p_region_t *get_avail_p_regs(void) -{ - return (p_region_t *) avail_p_regs; -} - -BOOT_CODE int get_num_dev_p_regs(void) -{ - if (dev_p_regs != NULL) { - return (sizeof(dev_p_regs) / sizeof(p_region_t)); - } else { - return 0; - } -} - -BOOT_CODE p_region_t get_dev_p_reg(word_t i) -{ - /* We need this if guard as some RISC-V configurations don't declare any - * device regions and some compilers complain about indexing an empty array - * due to not being able to infer that get_dev_p_reg is only called if - * dev_p_regs contains entries. - */ - if (get_num_dev_p_regs() == 0) { - printf("%s: No devices present.\n", __func__); - halt(); - } - return dev_p_regs[i]; -} - -BOOT_CODE void map_kernel_devices(void) -{ - if (kernel_devices == NULL) { - return; - } - - for (int i = 0; i < (sizeof(kernel_devices) / sizeof(paddr_t)); i++) { - map_kernel_frame(kernel_devices[i], PPTR_KDEV, - VMKernelOnly); - } -} - -/* - * The following assumes familiarity with RISC-V interrupt delivery and the PLIC. - * See the RISC-V privileged specifivation v1.10 and the comment in - * include/plat/spike/plat/machine.h for more information. - * RISC-V IRQ handling on seL4 works as follows: - * - * On other architectures the kernel masks interrupts between delivering them to - * userlevel and receiving the acknowledgement invocation. This strategy doesn't - * work on RISC-V as an IRQ is implicitly masked when it is claimed, until the - * claim is acknowledged. If we mask and unmask the interrupt at the PLIC while - * a claim is in progress we sometimes experience IRQ sources not being masked - * and unmasked as expected. Because of this, we don't mask and unmask IRQs that - * are for user level, and also call plic_complete_claim for seL4_IRQHandler_Ack. - */ - -/** - * Gets the new active irq from the PLIC or STIP. - * - * getNewActiveIRQ is only called by getActiveIRQ and checks for a pending IRQ. - * We read sip and if the SEIP bit is set we claim an - * IRQ from the PLIC. If STIP is set then it is a kernel timer interrupt. - * Otherwise we return IRQ invalid. It is possible to reveive irqInvalid from - * the PLIC if another HART context has claimed the IRQ before us. This function - * is not idempotent as plic_get_claim is called which accepts an IRQ message - * from the PLIC and will claim different IRQs if called subsequent times. - * - * @return The new active irq. - */ -static irq_t getNewActiveIRQ(void) -{ - - uint64_t sip = read_sip(); - - if (sip & BIT(STIMER_IP)) { - // Supervisor timer interrupt - return INTERRUPT_CORE_TIMER; - } else if (BIT(SEXTERNAL_IP)) { - /* External IRQ */ - return plic_get_claim(); - } else { - return irqInvalid; - } -} - -static uint32_t active_irq = irqInvalid; - - -/** - * Gets the active irq. Returns the same irq if called again before ackInterrupt. - * - * getActiveIRQ is used to return a currently pending IRQ. This function can be - * called multiple times and needs to return the same IRQ until ackInterrupt is - * called. getActiveIRQ returns irqInvalid if no interrupt is pending. It is - * assumed that if isIRQPending is true, then getActiveIRQ will not return - * irqInvalid. getActiveIRQ will call getNewActiveIRQ and cache its result until - * ackInterrupt is called. - * - * @return The active irq. - */ -irq_t getActiveIRQ(void) -{ - - uint32_t irq; - if (!IS_IRQ_VALID(active_irq)) { - active_irq = getNewActiveIRQ(); - } - - if (IS_IRQ_VALID(active_irq)) { - irq = active_irq; - } else { - irq = irqInvalid; - } - - return irq; -} - -#ifdef HAVE_SET_TRIGGER -/** - * Sets the irq trigger. - * - * setIRQTrigger can change the trigger between edge and level at the PLIC for - * external interrupts. It is implementation specific as whether the PLIC has - * support for this operation. - * - * @param[in] irq The irq - * @param[in] edge_triggered edge triggered otherwise level triggered - */ -void setIRQTrigger(irq_t irq, bool_t edge_triggered) -{ - plic_irq_set_trigger(irq, edge_triggered); -} -#endif - -/* isIRQPending is used to determine whether to preempt long running - * operations at various preemption points throughout the kernel. If this - * returns true, it means that if the Kernel were to return to user mode, it - * would then immediately take an interrupt. We check the SIP register for if - * either a timer interrupt (STIP) or an external interrupt (SEIP) is pending. - * We don't check software generated interrupts. These are used to perform cross - * core signalling which isn't currently supported. - * TODO: Add SSIP check when SMP support is added. - */ -bool_t isIRQPending(void) -{ - word_t sip = read_sip(); - return (sip & (BIT(STIMER_IP) | BIT(SEXTERNAL_IP))); -} - -/** - * Disable or enable IRQs. - * - * maskInterrupt disables and enables IRQs. When an IRQ is disabled, it should - * not raise an interrupt on the Kernel's HART context. This either masks the - * core timer on the sie register or masks an external IRQ at the plic. - * - * @param[in] disable The disable - * @param[in] irq The irq - */ -void maskInterrupt(bool_t disable, interrupt_t irq) -{ - assert(IS_IRQ_VALID(irq)); - if (irq == INTERRUPT_CORE_TIMER) { - if (disable) { - clear_sie_mask(BIT(STIMER_IE)); - } else { - set_sie_mask(BIT(STIMER_IE)); - } - } else { - plic_mask_irq(disable, irq); - } -} - -/** - * Kernel has dealt with the pending interrupt getActiveIRQ can return next IRQ. - * - * ackInterrupt is used by the kernel to indicate it has processed the interrupt - * delivery and getActiveIRQ is now able to return a different IRQ number. Note - * that this is called after a notification has been signalled to user level, - * but before user level has handled the cause. - * - * @param[in] irq The irq - */ -void ackInterrupt(irq_t irq) -{ - assert(IS_IRQ_VALID(irq)); - active_irq = irqInvalid; - - if (irq == INTERRUPT_CORE_TIMER) { - /* Reprogramming the timer has cleared the interrupt. */ - return; - } -} - -static inline uint64_t get_cycles(void) -#if __riscv_xlen == 32 -{ - uint32_t nH, nL; - asm volatile( - "rdtimeh %0\n" - "rdtime %1\n" - : "=r"(nH), "=r"(nL)); - return ((uint64_t)((uint64_t) nH << 32)) | (nL); -} -#else -{ - uint64_t n; - asm volatile( - "rdtime %0" - : "=r"(n)); - return n; -} -#endif - -static inline int read_current_timer(unsigned long *timer_val) -{ - *timer_val = get_cycles(); - return 0; -} - -void resetTimer(void) -{ - uint64_t target; - // repeatedly try and set the timer in a loop as otherwise there is a race and we - // may set a timeout in the past, resulting in it never getting triggered - do { - target = get_cycles() + RESET_CYCLES; - sbi_set_timer(target); - } while (get_cycles() > target); -} - -/** - DONT_TRANSLATE - */ -BOOT_CODE void initTimer(void) -{ - sbi_set_timer(get_cycles() + RESET_CYCLES); -} - -void plat_cleanL2Range(paddr_t start, paddr_t end) -{ -} -void plat_invalidateL2Range(paddr_t start, paddr_t end) -{ -} - -void plat_cleanInvalidateL2Range(paddr_t start, paddr_t end) -{ -} - -BOOT_CODE void initL2Cache(void) -{ -} - -BOOT_CODE void initIRQController(void) -{ - printf("Initialing PLIC...\n"); - - plic_init_controller(); - set_sie_mask(BIT(9)); -} - -void handleSpuriousIRQ(void) -{ - /* Do nothing */ - printf("Superior IRQ!! \n"); -} diff --git a/tools/dts/spike.dts b/tools/dts/spike.dts new file mode 100644 index 000000000..e6cc9d547 --- /dev/null +++ b/tools/dts/spike.dts @@ -0,0 +1,56 @@ +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + * @TAG(OTHER_GPL) + */ + +/dts-v1/; + +/ { + #address-cells = <0x00000002>; + #size-cells = <0x00000002>; + compatible = "ucbbar,spike-bare-dev"; + model = "ucbbar,spike-bare,qemu"; + chosen { + bootargs = ""; + }; + cpus { + #address-cells = <0x00000001>; + #size-cells = <0x00000000>; + timebase-frequency = <0x00989680>; + cpu@0 { + device_type = "cpu"; + reg = <0x00000000>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdcsu"; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3b9aca00>; + interrupt-controller { + #interrupt-cells = <0x00000001>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + linux,phandle = <0x00000001>; + phandle = <0x00000001>; + }; + }; + }; + memory@80000000 { + device_type = "memory"; + reg = <0x00000000 0x80000000 0x00000000 0xfff00000>; + }; + soc { + #address-cells = <0x00000002>; + #size-cells = <0x00000002>; + compatible = "simple-bus"; + ranges; + }; + htif { + compatible = "ucb,htif0"; + }; +}; diff --git a/tools/hardware.yml b/tools/hardware.yml index f2001ae85..bbeb04b5a 100644 --- a/tools/hardware.yml +++ b/tools/hardware.yml @@ -235,7 +235,11 @@ devices: macro: CONFIG_PRINTING user: true - - - + # RISC-V PLIC + - compatible: + - riscv,plic0 + regions: + - executeNever: true + index: 0 + kernel: PLIC_PPTR diff --git a/tools/hardware_gen.py b/tools/hardware_gen.py index 57be1ea84..a81ab7490 100644 --- a/tools/hardware_gen.py +++ b/tools/hardware_gen.py @@ -833,6 +833,7 @@ HEADER_TEMPLATE = """ #ifndef __ASSEMBLER__ +{% if kernel -%} static const kernel_frame_t BOOT_RODATA kernel_devices[] = { {% for reg in kernel %}{{ reg.get_macro_string() }} { /* {{ ' '.join(sorted(reg.names)) }} */ @@ -843,13 +844,18 @@ static const kernel_frame_t BOOT_RODATA kernel_devices[] = { /* region contains {{ ', '.join(reg.var_names) }} */ {{ "KDEV_PPTR + 0x%x" % reg.kaddr }}, {%- endif %} + {% if args.arch == 'arm' -%} {{ "true" if reg.executeNever else "false" }} /* armExecuteNever */ + {%- endif %} }, {% if reg.get_macro_end() -%} {{ reg.get_macro_end()}} {% endif -%} {% endfor %} }; +{% else -%} +static const kernel_frame_t BOOT_RODATA *kernel_devices = NULL; +{% endif -%} static const p_region_t BOOT_RODATA avail_p_regs[] = { {%- for reg in memory %} @@ -857,6 +863,7 @@ static const p_region_t BOOT_RODATA avail_p_regs[] = { {%- endfor %} }; +{% if devices -%} static const p_region_t BOOT_RODATA dev_p_regs[] = { {%- for reg in devices %} {%- if reg.user_macro %} @@ -868,6 +875,9 @@ static const p_region_t BOOT_RODATA dev_p_regs[] = { {%- endif %} {%- endfor %} }; +{% else -%} +static const p_region_t BOOT_RODATA *dev_p_regs = NULL; +{% endif -%} #endif /* !__ASSEMBLER__ */ @@ -891,10 +901,12 @@ static const p_region_t BOOT_RODATA dev_p_regs[] = { def add_build_rules(devices, output): - devices[-1] = devices[-1] + ";" - # print result to cmake variable - print(';'.join(devices), file=output) + if devices: + devices[-1] = devices[-1] + ";" + # print result to cmake variable + print(';'.join(devices), file=output) +MEGA_PAGE_SIZE = 0x200000 def output_regions(args, devices, memory, kernel, irqs, fp): """ generate the device list for the C header file """ @@ -929,14 +941,22 @@ def output_regions(args, devices, memory, kernel, irqs, fp): kernel += extra_kernel kernel.sort(key=lambda a: a.start) - # make sure physBase is at least 16MB (supersection) aligned for the ELF loader's sake. - # TODO: this may need to be larger for aarch64. It seems to work OK on currently supported platforms though. - # - # XXX: This also assumes that the kernel image is the first memory region - # described, and propgates that assumption forward. What enforces that? - paddr = align_up(memory[0].start, 1 << args.phys_align) - memory[0].size -= paddr - memory[0].start - memory[0].start = paddr + if args.arch == 'arm': + # make sure physBase is at least 16MB (supersection) aligned for the ELF loader's sake. + # TODO: this may need to be larger for aarch64. It seems to work OK on currently supported platforms though. + # + # XXX: This also assumes that the kernel image is the first memory region + # described, and propagates that assumption forward. What enforces that? + paddr = align_up(memory[0].start, 1 << args.phys_align) + memory[0].size -= paddr - memory[0].start + memory[0].start = paddr + elif args.arch == 'riscv': + # BBL is loaded to the physical memory starting from "memory[0].start". + # Moving up the start to skip the BBL memory, making it unavailable + # to the kernel. + paddr = memory[0].start + memory[0].start += MEGA_PAGE_SIZE + memory[0].size -= MEGA_PAGE_SIZE # Write out what we need to consume in YAML format. yaml_out = { @@ -1036,6 +1056,7 @@ if __name__ == '__main__': parser.add_argument('--page-bits', help='number of bits per page', default=12, type=int) parser.add_argument( '--phys-align', help='alignment in bits of the base address of the kernel', default=24, type=int) + parser.add_argument('--arch', help='arch of the targeting platform', default="arm") args = parser.parse_args() main(args) else: