diff --git a/Makefile b/Makefile index b742d6dbc5e..730dbd910e5 100644 --- a/Makefile +++ b/Makefile @@ -254,6 +254,7 @@ deps-y=$(platform-objs-path-y:.o=.dep) deps-y+=$(libsbi-objs-path-y:.o=.dep) deps-y+=$(libsbiutils-objs-path-y:.o=.dep) deps-y+=$(firmware-objs-path-y:.o=.dep) +deps-y+=$(firmware-elfs-path-y:=.dep) # Setup platform ABI, ISA and Code Model ifndef PLATFORM_RISCV_ABI @@ -330,7 +331,12 @@ GENFLAGS += $(libsbiutils-genflags-y) GENFLAGS += $(platform-genflags-y) GENFLAGS += $(firmware-genflags-y) -CFLAGS = -g -Wall -Werror -ffreestanding -nostdlib -fno-stack-protector -fno-strict-aliasing -O2 +CFLAGS = -g -Wall -Werror -ffreestanding -nostdlib -fno-stack-protector -fno-strict-aliasing +ifneq ($(DEBUG),) +CFLAGS += -O0 +else +CFLAGS += -O2 +endif CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls -mstrict-align # enable -m(no-)save-restore option by CC_SUPPORT_SAVE_RESTORE ifeq ($(CC_SUPPORT_SAVE_RESTORE),y) @@ -395,10 +401,10 @@ merge_deps = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ cat $(2) > $(1) copy_file = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ echo " COPY $(subst $(build_dir)/,,$(1))"; \ - cp -f $(2) $(1) + cp -L -f $(2) $(1) inst_file = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ echo " INSTALL $(subst $(install_root_dir)/,,$(1))"; \ - cp -f $(2) $(1) + cp -L -f $(2) $(1) inst_file_list = $(CMD_PREFIX)if [ ! -z "$(4)" ]; then \ mkdir -p $(1)/$(3); \ for file in $(4) ; do \ @@ -407,12 +413,17 @@ inst_file_list = $(CMD_PREFIX)if [ ! -z "$(4)" ]; then \ dest_dir=`dirname $$dest_file`; \ echo " INSTALL "$(3)"/"`echo $$rel_file`; \ mkdir -p $$dest_dir; \ - cp -f $$file $$dest_file; \ + cp -L -f $$file $$dest_file; \ done \ fi inst_header_dir = $(CMD_PREFIX)mkdir -p $(1); \ echo " INSTALL $(subst $(install_root_dir)/,,$(1))"; \ - cp -rf $(2) $(1) + cp -L -rf $(2) $(1) +compile_cpp_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " CPP-DEP $(subst $(build_dir)/,,$(1))"; \ + printf %s `dirname $(1)`/ > $(1) && \ + $(CC) $(CPPFLAGS) -x c -MM $(3) \ + -MT `basename $(1:.dep=$(2))` >> $(1) || rm -f $(1) compile_cpp = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ echo " CPP $(subst $(build_dir)/,,$(1))"; \ $(CPP) $(CPPFLAGS) -x c $(2) | grep -v "\#" > $(1) @@ -543,6 +554,9 @@ $(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf $(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a $(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a) +$(platform_build_dir)/%.dep: $(src_dir)/%.ldS $(KCONFIG_CONFIG) + $(call compile_cpp_dep,$@,.ld,$<) + $(platform_build_dir)/%.ld: $(src_dir)/%.ldS $(call compile_cpp,$@,$<) diff --git a/README.md b/README.md index 895bbf27a37..73de8eaf84f 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ RISC-V Open Source Supervisor Binary Interface (OpenSBI) ======================================================== +![RISC-V OpenSBI](docs/riscv_opensbi_logo_final_color.png) + Copyright and License --------------------- -The OpenSBI project is copyright (c) 2019 Western Digital Corporation -or its affiliates and other contributors. +The OpenSBI project is: + +* Copyright (c) 2019 Western Digital Corporation or its affiliates +* Copyright (c) 2023 RISC-V International It is distributed under the terms of the BSD 2-clause license ("Simplified BSD License" or "FreeBSD License", SPDX: *BSD-2-Clause*). @@ -298,6 +302,19 @@ NOTE: Using `BUILD_INFO=y` without specifying SOURCE_DATE_EPOCH will violate purpose, and should NOT be used in a product which follows "reproducible builds". +Building with optimization off for debugging +-------------------------------------------- + +When debugging OpenSBI, we may want to turn off the compiler optimization and +make debugging produce the expected results for a better debugging experience. +To build with optimization off we can just simply add `DEBUG=1`, like: +``` +make DEBUG=1 +``` + +This definition is ONLY for development and debug purpose, and should NOT be +used in a product build. + Contributing to OpenSBI ----------------------- diff --git a/docs/domain_support.md b/docs/domain_support.md index 8963b57e378..b285d65aec0 100644 --- a/docs/domain_support.md +++ b/docs/domain_support.md @@ -52,6 +52,7 @@ has following details: * **next_mode** - Privilege mode of the next booting stage for this domain. This can be either S-mode or U-mode. * **system_reset_allowed** - Is domain allowed to reset the system? +* **system_suspend_allowed** - Is domain allowed to suspend the system? The memory regions represented by **regions** in **struct sbi_domain** have following additional constraints to align with RISC-V PMP requirements: @@ -91,6 +92,7 @@ following manner: * **next_mode** - Next booting stage mode in coldboot HART scratch space is the next mode for the ROOT domain * **system_reset_allowed** - The ROOT domain is allowed to reset the system +* **system_suspend_allowed** - The ROOT domain is allowed to suspend the system Domain Effects -------------- @@ -124,6 +126,9 @@ The DT properties of a domain configuration DT node are as follows: * **compatible** (Mandatory) - The compatible string of the domain configuration. This DT property should have value *"opensbi,domain,config"* +* **system-suspend-test** (Optional) - When present, enable a system + suspend test implementation which simply waits five seconds and issues a WFI. + ### Domain Memory Region Node The domain memory region DT node describes details of a memory region and @@ -160,8 +165,16 @@ The DT properties of a domain instance DT node are as follows: * **regions** (Optional) - The list of domain memory region DT node phandle and access permissions for the domain instance. Each list entry is a pair of DT node phandle and access permissions. The access permissions are - represented as a 32bit bitmask having bits: **readable** (BIT[0]), - **writeable** (BIT[1]), **executable** (BIT[2]), and **m-mode** (BIT[3]). + represented as a 32bit bitmask having bits: **M readable** (BIT[0]), + **M writeable** (BIT[1]), **M executable** (BIT[2]), **SU readable** + (BIT[3]), **SU writable** (BIT[4]), and **SU executable** (BIT[5]). + The enforce permission bit (BIT[6]), if set, will lock the permissions + in the PMP. This will enforce the permissions on M-mode as well which + otherwise will have unrestricted access. This bit must be used with + caution because no changes can be made to a PMP entry once its locked + until the hart is reset. + Any region of a domain defined in DT node cannot have only M-bits set + in access permissions i.e. it cannot be an m-mode only accessible region. * **boot-hart** (Optional) - The DT node phandle of the HART booting the domain instance. If coldboot HART is assigned to the domain instance then this DT property is ignored and the coldboot HART is assumed to be the @@ -180,13 +193,15 @@ The DT properties of a domain instance DT node are as follows: is used as default value. * **next-mode** (Optional) - The 32 bit next booting stage mode for the domain instance. The possible values of this DT property are: **0x1** - (s-mode), and **0x0** (u-mode). If this DT property is not available + (S-mode), and **0x0** (U-mode). If this DT property is not available and coldboot HART is not assigned to the domain instance then **0x1** is used as default value. If this DT property is not available and coldboot HART is assigned to the domain instance then **next booting stage mode of coldboot HART** is used as default value. * **system-reset-allowed** (Optional) - A boolean flag representing whether the domain instance is allowed to do system reset. +* **system-suspend-allowed** (Optional) - A boolean flag representing + whether the domain instance is allowed to do system suspend. ### Assigning HART To Domain Instance @@ -195,9 +210,9 @@ platform support can provide the HART to domain instance assignment using platform specific callback. The HART to domain instance assignment can be parsed from the device tree -using optional DT property **opensbi,domain** in each CPU DT node. The -value of DT property **opensbi,domain** is the DT phandle of the domain -instance DT node. If **opensbi,domain** DT property is not specified then +using optional DT property **opensbi-domain** in each CPU DT node. The +value of DT property **opensbi-domain** is the DT phandle of the domain +instance DT node. If **opensbi-domain** DT property is not specified then corresponding HART is assigned to **the ROOT domain**. ### Domain Configuration Only Accessible to OpenSBI @@ -222,6 +237,7 @@ be done: chosen { opensbi-domains { compatible = "opensbi,domain,config"; + system-suspend-test; tmem: tmem { compatible = "opensbi,domain,memregion"; @@ -246,18 +262,19 @@ be done: tdomain: trusted-domain { compatible = "opensbi,domain,instance"; possible-harts = <&cpu0>; - regions = <&tmem 0x7>, <&tuart 0x7>; + regions = <&tmem 0x3f>, <&tuart 0x3f>; boot-hart = <&cpu0>; next-arg1 = <0x0 0x0>; next-addr = <0x0 0x80100000>; next-mode = <0x0>; system-reset-allowed; + system-suspend-allowed; }; udomain: untrusted-domain { compatible = "opensbi,domain,instance"; possible-harts = <&cpu1 &cpu2 &cpu3 &cpu4>; - regions = <&tmem 0x0>, <&tuart 0x0>, <&allmem 0x7>; + regions = <&tmem 0x0>, <&tuart 0x0>, <&allmem 0x3f>; }; }; }; diff --git a/docs/firmware/fw.md b/docs/firmware/fw.md index 18faaa9e27e..38351c84a54 100644 --- a/docs/firmware/fw.md +++ b/docs/firmware/fw.md @@ -61,7 +61,7 @@ Firmware Configuration and Compilation All firmware types support the following common compile time configuration parameters: -* **FW_TEXT_ADDR** - Defines the execution address of the OpenSBI firmware. +* **FW_TEXT_START** - Defines the execution address of the OpenSBI firmware. This configuration parameter is mandatory. * **FW_FDT_PATH** - Path to an external flattened device tree binary file to be embedded in the *.rodata* section of the final firmware. If this option diff --git a/docs/firmware/fw_jump.md b/docs/firmware/fw_jump.md index 956897e7e7c..3e883fa814f 100644 --- a/docs/firmware/fw_jump.md +++ b/docs/firmware/fw_jump.md @@ -43,18 +43,18 @@ follows: When using the default *FW_JUMP_FDT_ADDR* with *PLATFORM=generic*, you must ensure *FW_JUMP_FDT_ADDR* is set high enough to avoid overwriting the kernel. - You can use the following method. + You can use the following method (e.g., using bash or zsh): ``` - ${CROSS_COMPILE}objdump -h $KERNEL_ELF | sort -k 5,5 | awk -n '/^ +[0-9]+ /\ - {addr="0x"$3; size="0x"$5; printf "0x""%x\n",addr+size}' \ - | (( `tail -1` > 0x2200000 )) && echo fdt overlaps kernel,\ - increase FW_JUMP_FDT_ADDR - - ${LLVM}objdump -h --show-lma $KERNEL_ELF | sort -k 5,5 | \ - awk -n '/^ +[0-9]+ / {addr="0x"$3; size="0x"$5; printf "0x""%x\n",addr+size}'\ - | (( `tail -1` > 0x2200000 )) && echo fdt overlaps kernel,\ - increase FW_JUMP_FDT_ADDR + ${CROSS_COMPILE}objdump -h $KERNEL_ELF | sort -k 5,5 | awk -n ' + /^ +[0-9]+ / {addr="0x"$3; size="0x"$5; printf "0x""%x\n",addr+size}' | + (( `tail -1` > (FW_JUMP_FDT_ADDR - FW_JUMP_ADDR) )) && + echo fdt overlaps kernel, increase FW_JUMP_FDT_ADDR + + ${LLVM}objdump -h --show-lma $KERNEL_ELF | sort -k 5,5 | awk -n ' + /^ +[0-9]+ / {addr="0x"$3; size="0x"$5; printf "0x""%x\n",addr+size}' | + (( `tail -1` > (FW_JUMP_FDT_ADDR - FW_JUMP_ADDR) )) && + echo fdt overlaps kernel, increase FW_JUMP_FDT_ADDR ``` *FW_JUMP* Example diff --git a/docs/platform/generic.md b/docs/platform/generic.md index 8e4cf2d090c..c29eb047607 100644 --- a/docs/platform/generic.md +++ b/docs/platform/generic.md @@ -53,7 +53,7 @@ RISC-V Platforms Using Generic Platform * **Spike** (*[spike.md]*) * **T-HEAD C9xx series Processors** (*[thead-c9xx.md]*) -[andes-ae350.md]: andse-ae350.md +[andes-ae350.md]: andes-ae350.md [qemu_virt.md]: qemu_virt.md [renesas-rzfive.md]: renesas-rzfive.md [shakti_cclass.md]: shakti_cclass.md diff --git a/docs/riscv_opensbi_logo_final_color.png b/docs/riscv_opensbi_logo_final_color.png new file mode 100644 index 00000000000..111478119b5 Binary files /dev/null and b/docs/riscv_opensbi_logo_final_color.png differ diff --git a/docs/riscv_opensbi_logo_final_grey.png b/docs/riscv_opensbi_logo_final_grey.png new file mode 100644 index 00000000000..a370f4119d6 Binary files /dev/null and b/docs/riscv_opensbi_logo_final_grey.png differ diff --git a/firmware/fw_base.S b/firmware/fw_base.S index 3f622b3b39a..b94742395c9 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -79,13 +79,12 @@ _try_lottery: lla t0, __rel_dyn_start lla t1, __rel_dyn_end beq t0, t1, _relocate_done - j 5f 2: - REG_L t5, -(REGBYTES*2)(t0) /* t5 <-- relocation info:type */ + REG_L t5, REGBYTES(t0) /* t5 <-- relocation info:type */ li t3, R_RISCV_RELATIVE /* reloc type R_RISCV_RELATIVE */ bne t5, t3, 3f - REG_L t3, -(REGBYTES*3)(t0) - REG_L t5, -(REGBYTES)(t0) /* t5 <-- addend */ + REG_L t3, 0(t0) + REG_L t5, (REGBYTES * 2)(t0) /* t5 <-- addend */ add t5, t5, t2 add t3, t3, t2 REG_S t5, 0(t3) /* store runtime address to the GOT entry */ @@ -95,18 +94,17 @@ _try_lottery: lla t4, __dyn_sym_start 4: - REG_L t5, -(REGBYTES*2)(t0) /* t5 <-- relocation info:type */ srli t6, t5, SYM_INDEX /* t6 <--- sym table index */ andi t5, t5, 0xFF /* t5 <--- relocation type */ li t3, RELOC_TYPE bne t5, t3, 5f /* address R_RISCV_64 or R_RISCV_32 cases*/ - REG_L t3, -(REGBYTES*3)(t0) + REG_L t3, 0(t0) li t5, SYM_SIZE mul t6, t6, t5 add s5, t4, t6 - REG_L t6, -(REGBYTES)(t0) /* t0 <-- addend */ + REG_L t6, (REGBYTES * 2)(t0) /* t0 <-- addend */ REG_L t5, REGBYTES(s5) add t5, t5, t6 add t5, t5, t2 /* t5 <-- location to fix up in RAM */ @@ -114,8 +112,8 @@ _try_lottery: REG_S t5, 0(t3) /* store runtime address to the variable */ 5: - addi t0, t0, (REGBYTES*3) - ble t0, t1, 2b + addi t0, t0, (REGBYTES * 3) + blt t0, t1, 2b j _relocate_done _wait_relocate_copy_done: j _wait_for_boot_hart @@ -257,20 +255,28 @@ _bss_zero: /* Preload HART details * s7 -> HART Count * s8 -> HART Stack Size + * s9 -> Heap Size + * s10 -> Heap Offset */ lla a4, platform #if __riscv_xlen > 32 lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) + lwu s9, SBI_PLATFORM_HEAP_SIZE_OFFSET(a4) #else lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) + lw s9, SBI_PLATFORM_HEAP_SIZE_OFFSET(a4) #endif /* Setup scratch space for all the HARTs*/ lla tp, _fw_end mul a5, s7, s8 add tp, tp, a5 + /* Setup heap base address */ + lla s10, _fw_start + sub s10, tp, s10 + add tp, tp, s9 /* Keep a copy of tp */ add t3, tp, zero /* Counter */ @@ -285,8 +291,11 @@ _scratch_init: * t3 -> the firmware end address * s7 -> HART count * s8 -> HART stack size + * s9 -> Heap Size + * s10 -> Heap Offset */ add tp, t3, zero + sub tp, tp, s9 mul a5, s8, t1 sub tp, tp, a5 li a5, SBI_SCRATCH_SIZE @@ -298,6 +307,16 @@ _scratch_init: sub a5, t3, a4 REG_S a4, SBI_SCRATCH_FW_START_OFFSET(tp) REG_S a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp) + + /* Store R/W section's offset in scratch space */ + lla a4, __fw_rw_offset + REG_L a5, 0(a4) + REG_S a5, SBI_SCRATCH_FW_RW_OFFSET(tp) + + /* Store fw_heap_offset and fw_heap_size in scratch space */ + REG_S s10, SBI_SCRATCH_FW_HEAP_OFFSET(tp) + REG_S s9, SBI_SCRATCH_FW_HEAP_SIZE_OFFSET(tp) + /* Store next arg1 in scratch space */ MOV_3R s0, a0, s1, a1, s2, a2 call fw_next_arg1 @@ -422,9 +441,8 @@ _start_warm: li ra, 0 call _reset_regs - /* Disable and clear all interrupts */ + /* Disable all interrupts */ csrw CSR_MIE, zero - csrw CSR_MIP, zero /* Find HART count and HART stack size */ lla a4, platform @@ -453,7 +471,6 @@ _start_warm: add s9, s9, 4 add a4, a4, 1 blt a4, s7, 1b - li a4, -1 2: add s6, a4, zero 3: bge s6, s7, _start_hang @@ -519,6 +536,8 @@ _link_start: RISCV_PTR FW_TEXT_START _link_end: RISCV_PTR _fw_reloc_end +__fw_rw_offset: + RISCV_PTR _fw_rw_start - _fw_start .section .entry, "ax", %progbits .align 3 diff --git a/firmware/fw_base.ldS b/firmware/fw_base.ldS index 563ae252a3d..3d68484ba5e 100644 --- a/firmware/fw_base.ldS +++ b/firmware/fw_base.ldS @@ -30,17 +30,41 @@ /* Beginning of the read-only data sections */ + PROVIDE(_rodata_start = .); + .rodata : { - PROVIDE(_rodata_start = .); *(.rodata .rodata.*) . = ALIGN(8); - PROVIDE(_rodata_end = .); } + . = ALIGN(0x1000); /* Ensure next section is page aligned */ + + .dynsym : { + PROVIDE(__dyn_sym_start = .); + *(.dynsym) + PROVIDE(__dyn_sym_end = .); + } + + .rela.dyn : { + PROVIDE(__rel_dyn_start = .); + *(.rela*) + . = ALIGN(8); + PROVIDE(__rel_dyn_end = .); + } + + PROVIDE(_rodata_end = .); + /* End of the read-only data sections */ - . = ALIGN(0x1000); /* Ensure next section is page aligned */ + /* + * PMP regions must be to be power-of-2. RX/RW will have separate + * regions, so ensure that the split is power-of-2. + */ + . = ALIGN(1 << LOG2CEIL((SIZEOF(.rodata) + SIZEOF(.text) + + SIZEOF(.dynsym) + SIZEOF(.rela.dyn)))); + + PROVIDE(_fw_rw_start = .); /* Beginning of the read-write data sections */ @@ -59,19 +83,6 @@ PROVIDE(_data_end = .); } - .dynsym : { - PROVIDE(__dyn_sym_start = .); - *(.dynsym) - PROVIDE(__dyn_sym_end = .); - } - - .rela.dyn : { - PROVIDE(__rel_dyn_start = .); - *(.rela*) - . = ALIGN(8); - PROVIDE(__rel_dyn_end = .); - } - . = ALIGN(0x1000); /* Ensure next section is page aligned */ .bss : diff --git a/include/sbi/riscv_asm.h b/include/sbi/riscv_asm.h index 1ff36de76e3..2c34635a393 100644 --- a/include/sbi/riscv_asm.h +++ b/include/sbi/riscv_asm.h @@ -181,6 +181,12 @@ int misa_xlen(void); /* Get RISC-V ISA string representation */ void misa_string(int xlen, char *out, unsigned int out_sz); +/* Disable pmp entry at a given index */ +int pmp_disable(unsigned int n); + +/* Check if the matching field is set */ +int is_pmp_entry_mapped(unsigned long entry); + int pmp_set(unsigned int n, unsigned long prot, unsigned long addr, unsigned long log2len); diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h index b0f08c87676..f09a89afe82 100644 --- a/include/sbi/riscv_encoding.h +++ b/include/sbi/riscv_encoding.h @@ -602,6 +602,8 @@ /* Machine Counter Setup */ #define CSR_MCOUNTINHIBIT 0x320 +#define CSR_MCYCLECFG 0x321 +#define CSR_MINSTRETCFG 0x322 #define CSR_MHPMEVENT3 0x323 #define CSR_MHPMEVENT4 0x324 #define CSR_MHPMEVENT5 0x325 @@ -633,6 +635,8 @@ #define CSR_MHPMEVENT31 0x33f /* For RV32 */ +#define CSR_MCYCLECFGH 0x721 +#define CSR_MINSTRETCFGH 0x722 #define CSR_MHPMEVENT3H 0x723 #define CSR_MHPMEVENT4H 0x724 #define CSR_MHPMEVENT5H 0x725 @@ -663,6 +667,18 @@ #define CSR_MHPMEVENT30H 0x73e #define CSR_MHPMEVENT31H 0x73f +/* Machine Security Configuration CSR (mseccfg) */ +#define CSR_MSECCFG_LOWER 0x747 +#define CSR_MSECCFG_UPPER 0x757 +#define CSR_MSECCFG (CSR_MSECCFG_LOWER) + +#define MSECCFG_MML_SHIFT (0) +#define MSECCFG_MML (_UL(1) << MSECCFG_MML_SHIFT) +#define MSECCFG_MMWP_SHIFT (1) +#define MSECCFG_MMWP (_UL(1) << MSECCFG_MMWP_SHIFT) +#define MSECCFG_RLB_SHIFT (2) +#define MSECCFG_RLB (_UL(1) << MSECCFG_RLB_SHIFT) + /* Counter Overflow CSR */ #define CSR_SCOUNTOVF 0xda0 @@ -736,6 +752,8 @@ #define SMSTATEEN0_CS (_ULL(1) << SMSTATEEN0_CS_SHIFT) #define SMSTATEEN0_FCSR_SHIFT 1 #define SMSTATEEN0_FCSR (_ULL(1) << SMSTATEEN0_FCSR_SHIFT) +#define SMSTATEEN0_CONTEXT_SHIFT 57 +#define SMSTATEEN0_CONTEXT (_ULL(1) << SMSTATEEN0_CONTEXT_SHIFT) #define SMSTATEEN0_IMSIC_SHIFT 58 #define SMSTATEEN0_IMSIC (_ULL(1) << SMSTATEEN0_IMSIC_SHIFT) #define SMSTATEEN0_AIA_SHIFT 59 diff --git a/include/sbi/sbi_bitops.h b/include/sbi/sbi_bitops.h index 251e4d89d59..2e08947beed 100644 --- a/include/sbi/sbi_bitops.h +++ b/include/sbi/sbi_bitops.h @@ -12,13 +12,7 @@ #include -#if __SIZEOF_POINTER__ == 8 -#define BITS_PER_LONG 64 -#elif __SIZEOF_POINTER__ == 4 -#define BITS_PER_LONG 32 -#else -#error "Unexpected __SIZEOF_POINTER__" -#endif +#define BITS_PER_LONG (8 * __SIZEOF_LONG__) #define EXTRACT_FIELD(val, which) \ (((val) & (which)) / ((which) & ~((which)-1))) diff --git a/include/sbi/sbi_byteorder.h b/include/sbi/sbi_byteorder.h new file mode 100644 index 00000000000..15107e1ea3d --- /dev/null +++ b/include/sbi/sbi_byteorder.h @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ventana Micro Systems Inc. + */ + +#ifndef __SBI_BYTEORDER_H__ +#define __SBI_BYTEORDER_H__ + +#include + +#define BSWAP16(x) ((((x) & 0x00ff) << 8) | \ + (((x) & 0xff00) >> 8)) +#define BSWAP32(x) ((((x) & 0x000000ff) << 24) | \ + (((x) & 0x0000ff00) << 8) | \ + (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0xff000000) >> 24)) +#define BSWAP64(x) ((((x) & 0x00000000000000ffULL) << 56) | \ + (((x) & 0x000000000000ff00ULL) << 40) | \ + (((x) & 0x0000000000ff0000ULL) << 24) | \ + (((x) & 0x00000000ff000000ULL) << 8) | \ + (((x) & 0x000000ff00000000ULL) >> 8) | \ + (((x) & 0x0000ff0000000000ULL) >> 24) | \ + (((x) & 0x00ff000000000000ULL) >> 40) | \ + (((x) & 0xff00000000000000ULL) >> 56)) + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ /* CPU(little-endian) */ +#define cpu_to_be16(x) ((uint16_t)BSWAP16(x)) +#define cpu_to_be32(x) ((uint32_t)BSWAP32(x)) +#define cpu_to_be64(x) ((uint64_t)BSWAP64(x)) + +#define be16_to_cpu(x) ((uint16_t)BSWAP16(x)) +#define be32_to_cpu(x) ((uint32_t)BSWAP32(x)) +#define be64_to_cpu(x) ((uint64_t)BSWAP64(x)) + +#define cpu_to_le16(x) ((uint16_t)(x)) +#define cpu_to_le32(x) ((uint32_t)(x)) +#define cpu_to_le64(x) ((uint64_t)(x)) + +#define le16_to_cpu(x) ((uint16_t)(x)) +#define le32_to_cpu(x) ((uint32_t)(x)) +#define le64_to_cpu(x) ((uint64_t)(x)) +#else /* CPU(big-endian) */ +#define cpu_to_be16(x) ((uint16_t)(x)) +#define cpu_to_be32(x) ((uint32_t)(x)) +#define cpu_to_be64(x) ((uint64_t)(x)) + +#define be16_to_cpu(x) ((uint16_t)(x)) +#define be32_to_cpu(x) ((uint32_t)(x)) +#define be64_to_cpu(x) ((uint64_t)(x)) + +#define cpu_to_le16(x) ((uint16_t)BSWAP16(x)) +#define cpu_to_le32(x) ((uint32_t)BSWAP32(x)) +#define cpu_to_le64(x) ((uint64_t)BSWAP64(x)) + +#define le16_to_cpu(x) ((uint16_t)BSWAP16(x)) +#define le32_to_cpu(x) ((uint32_t)BSWAP32(x)) +#define le64_to_cpu(x) ((uint64_t)BSWAP64(x)) +#endif + +#endif /* __SBI_BYTEORDER_H__ */ diff --git a/include/sbi/sbi_console.h b/include/sbi/sbi_console.h index e15b55dcb54..0979765240c 100644 --- a/include/sbi/sbi_console.h +++ b/include/sbi/sbi_console.h @@ -19,6 +19,9 @@ struct sbi_console_device { /** Write a character to the console output */ void (*console_putc)(char ch); + /** Write a character string to the console output */ + unsigned long (*console_puts)(const char *str, unsigned long len); + /** Read a character from the console input */ int (*console_getc)(void); }; @@ -33,8 +36,12 @@ void sbi_putc(char ch); void sbi_puts(const char *str); +unsigned long sbi_nputs(const char *str, unsigned long len); + void sbi_gets(char *s, int maxwidth, char endchar); +unsigned long sbi_ngets(char *str, unsigned long len); + int __printf(2, 3) sbi_sprintf(char *out, const char *format, ...); int __printf(3, 4) sbi_snprintf(char *out, u32 out_sz, const char *format, ...); diff --git a/include/sbi/sbi_cppc.h b/include/sbi/sbi_cppc.h new file mode 100644 index 00000000000..edf73f52ac0 --- /dev/null +++ b/include/sbi/sbi_cppc.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ventana Micro Systems Inc. + * + */ + +#ifndef __SBI_CPPC_H__ +#define __SBI_CPPC_H__ + +#include + +/** CPPC device */ +struct sbi_cppc_device { + /** Name of the CPPC device */ + char name[32]; + + /** probe - returns register width if implemented, 0 otherwise */ + int (*cppc_probe)(unsigned long reg); + + /** read the cppc register*/ + int (*cppc_read)(unsigned long reg, uint64_t *val); + + /** write to the cppc register*/ + int (*cppc_write)(unsigned long reg, uint64_t val); +}; + +int sbi_cppc_probe(unsigned long reg); +int sbi_cppc_read(unsigned long reg, uint64_t *val); +int sbi_cppc_write(unsigned long reg, uint64_t val); + +const struct sbi_cppc_device *sbi_cppc_get_device(void); +void sbi_cppc_set_device(const struct sbi_cppc_device *dev); + +#endif diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h index f0d9289ec7c..da2a65a4d44 100644 --- a/include/sbi/sbi_domain.h +++ b/include/sbi/sbi_domain.h @@ -36,11 +36,125 @@ struct sbi_domain_memregion { */ unsigned long base; /** Flags representing memory region attributes */ -#define SBI_DOMAIN_MEMREGION_READABLE (1UL << 0) -#define SBI_DOMAIN_MEMREGION_WRITEABLE (1UL << 1) -#define SBI_DOMAIN_MEMREGION_EXECUTABLE (1UL << 2) -#define SBI_DOMAIN_MEMREGION_MMODE (1UL << 3) -#define SBI_DOMAIN_MEMREGION_ACCESS_MASK (0xfUL) +#define SBI_DOMAIN_MEMREGION_M_READABLE (1UL << 0) +#define SBI_DOMAIN_MEMREGION_M_WRITABLE (1UL << 1) +#define SBI_DOMAIN_MEMREGION_M_EXECUTABLE (1UL << 2) +#define SBI_DOMAIN_MEMREGION_SU_READABLE (1UL << 3) +#define SBI_DOMAIN_MEMREGION_SU_WRITABLE (1UL << 4) +#define SBI_DOMAIN_MEMREGION_SU_EXECUTABLE (1UL << 5) + +#define SBI_DOMAIN_MEMREGION_ACCESS_MASK (0x3fUL) +#define SBI_DOMAIN_MEMREGION_M_ACCESS_MASK (0x7UL) +#define SBI_DOMAIN_MEMREGION_SU_ACCESS_MASK (0x38UL) + +#define SBI_DOMAIN_MEMREGION_SU_ACCESS_SHIFT (3) + +#define SBI_DOMAIN_MEMREGION_SHARED_RDONLY \ + (SBI_DOMAIN_MEMREGION_M_READABLE | \ + SBI_DOMAIN_MEMREGION_SU_READABLE) + +#define SBI_DOMAIN_MEMREGION_SHARED_SUX_MRX \ + (SBI_DOMAIN_MEMREGION_M_READABLE | \ + SBI_DOMAIN_MEMREGION_M_EXECUTABLE | \ + SBI_DOMAIN_MEMREGION_SU_EXECUTABLE) + +#define SBI_DOMAIN_MEMREGION_SHARED_SUX_MX \ + (SBI_DOMAIN_MEMREGION_M_EXECUTABLE | \ + SBI_DOMAIN_MEMREGION_SU_EXECUTABLE) + +#define SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW \ + (SBI_DOMAIN_MEMREGION_M_READABLE | \ + SBI_DOMAIN_MEMREGION_M_WRITABLE | \ + SBI_DOMAIN_MEMREGION_SU_READABLE| \ + SBI_DOMAIN_MEMREGION_SU_WRITABLE) + +#define SBI_DOMAIN_MEMREGION_SHARED_SUR_MRW \ + (SBI_DOMAIN_MEMREGION_M_READABLE | \ + SBI_DOMAIN_MEMREGION_M_WRITABLE | \ + SBI_DOMAIN_MEMREGION_SU_READABLE) + + /* Shared read-only region between M and SU mode */ +#define SBI_DOMAIN_MEMREGION_IS_SUR_MR(__flags) \ + ((__flags & SBI_DOMAIN_MEMREGION_M_READABLE) && \ + (__flags & SBI_DOMAIN_MEMREGION_SU_READABLE)) + + /* Shared region: SU execute-only and M read/execute */ +#define SBI_DOMAIN_MEMREGION_IS_SUX_MRX(__flags) \ + ((__flags & SBI_DOMAIN_MEMREGION_M_READABLE) && \ + (__flags & SBI_DOMAIN_MEMREGION_M_EXECUTABLE) && \ + (__flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)) + + /* Shared region: SU and M execute-only */ +#define SBI_DOMAIN_MEMREGION_IS_SUX_MX(__flags) \ + ((__flags & SBI_DOMAIN_MEMREGION_M_EXECUTABLE) && \ + (__flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)) + + /* Shared region: SU and M read/write */ +#define SBI_DOMAIN_MEMREGION_IS_SURW_MRW(__flags) \ + ((__flags & SBI_DOMAIN_MEMREGION_M_READABLE) && \ + (__flags & SBI_DOMAIN_MEMREGION_M_WRITABLE) && \ + (__flags & SBI_DOMAIN_MEMREGION_SU_READABLE) & \ + (__flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)) + + /* Shared region: SU read-only and M read/write */ +#define SBI_DOMAIN_MEMREGION_IS_SUR_MRW(__flags) \ + ((__flags & SBI_DOMAIN_MEMREGION_M_READABLE) && \ + (__flags & SBI_DOMAIN_MEMREGION_M_WRITABLE) && \ + (__flags & SBI_DOMAIN_MEMREGION_SU_READABLE)) + + /* + * Check if region flags match with any of the above + * mentioned shared region type + */ +#define SBI_DOMAIN_MEMREGION_IS_SHARED(_flags) \ + (SBI_DOMAIN_MEMREGION_IS_SUR_MR(_flags) || \ + SBI_DOMAIN_MEMREGION_IS_SUX_MRX(_flags) || \ + SBI_DOMAIN_MEMREGION_IS_SUX_MX(_flags) || \ + SBI_DOMAIN_MEMREGION_IS_SURW_MRW(_flags)|| \ + SBI_DOMAIN_MEMREGION_IS_SUR_MRW(_flags)) + +#define SBI_DOMAIN_MEMREGION_M_ONLY_ACCESS(__flags) \ + ((__flags & SBI_DOMAIN_MEMREGION_M_ACCESS_MASK) && \ + !(__flags & SBI_DOMAIN_MEMREGION_SU_ACCESS_MASK)) + +#define SBI_DOMAIN_MEMREGION_SU_ONLY_ACCESS(__flags) \ + ((__flags & SBI_DOMAIN_MEMREGION_SU_ACCESS_MASK) && \ + !(__flags & SBI_DOMAIN_MEMREGION_M_ACCESS_MASK)) + +/** Bit to control if permissions are enforced on all modes */ +#define SBI_DOMAIN_MEMREGION_ENF_PERMISSIONS (1UL << 6) + +#define SBI_DOMAIN_MEMREGION_M_RWX \ + (SBI_DOMAIN_MEMREGION_M_READABLE | \ + SBI_DOMAIN_MEMREGION_M_WRITABLE | \ + SBI_DOMAIN_MEMREGION_M_EXECUTABLE) + +#define SBI_DOMAIN_MEMREGION_SU_RWX \ + (SBI_DOMAIN_MEMREGION_SU_READABLE | \ + SBI_DOMAIN_MEMREGION_SU_WRITABLE | \ + SBI_DOMAIN_MEMREGION_SU_EXECUTABLE) + +/* Unrestricted M-mode accesses but enfoced on SU-mode */ +#define SBI_DOMAIN_MEMREGION_READABLE \ + (SBI_DOMAIN_MEMREGION_SU_READABLE | \ + SBI_DOMAIN_MEMREGION_M_RWX) +#define SBI_DOMAIN_MEMREGION_WRITEABLE \ + (SBI_DOMAIN_MEMREGION_SU_WRITABLE | \ + SBI_DOMAIN_MEMREGION_M_RWX) +#define SBI_DOMAIN_MEMREGION_EXECUTABLE \ + (SBI_DOMAIN_MEMREGION_SU_EXECUTABLE | \ + SBI_DOMAIN_MEMREGION_M_RWX) + +/* Enforced accesses across all modes */ +#define SBI_DOMAIN_MEMREGION_ENF_READABLE \ + (SBI_DOMAIN_MEMREGION_SU_READABLE | \ + SBI_DOMAIN_MEMREGION_M_READABLE) +#define SBI_DOMAIN_MEMREGION_ENF_WRITABLE \ + (SBI_DOMAIN_MEMREGION_SU_WRITABLE | \ + SBI_DOMAIN_MEMREGION_M_WRITABLE) +#define SBI_DOMAIN_MEMREGION_ENF_EXECUTABLE \ + (SBI_DOMAIN_MEMREGION_SU_EXECUTABLE | \ + SBI_DOMAIN_MEMREGION_M_EXECUTABLE) #define SBI_DOMAIN_MEMREGION_MMIO (1UL << 31) unsigned long flags; @@ -78,17 +192,17 @@ struct sbi_domain { unsigned long next_mode; /** Is domain allowed to reset the system */ bool system_reset_allowed; + /** Is domain allowed to suspend the system */ + bool system_suspend_allowed; + /** Identifies whether to include the firmware region */ + bool fw_region_inited; }; /** The root domain instance */ extern struct sbi_domain root; -/** HART id to domain table */ -extern struct sbi_domain *hartid_to_domain_table[]; - /** Get pointer to sbi_domain from HART id */ -#define sbi_hartid_to_domain(__hartid) \ - hartid_to_domain_table[__hartid] +struct sbi_domain *sbi_hartid_to_domain(u32 hartid); /** Get pointer to sbi_domain for current HART */ #define sbi_domain_thishart_ptr() \ @@ -154,6 +268,21 @@ bool sbi_domain_check_addr(const struct sbi_domain *dom, unsigned long addr, unsigned long mode, unsigned long access_flags); +/** + * Check whether we can access specified address range for given mode and + * memory region flags under a domain + * @param dom pointer to domain + * @param addr the start of the address range to be checked + * @param size the size of the address range to be checked + * @param mode the privilege mode of access + * @param access_flags bitmask of domain access types (enum sbi_domain_access) + * @return TRUE if access allowed otherwise FALSE + */ +bool sbi_domain_check_addr_range(const struct sbi_domain *dom, + unsigned long addr, unsigned long size, + unsigned long mode, + unsigned long access_flags); + /** Dump domain details on the console */ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix); diff --git a/include/sbi/sbi_ecall.h b/include/sbi/sbi_ecall.h index ff9bf8e2b43..90f33bac0bc 100644 --- a/include/sbi/sbi_ecall.h +++ b/include/sbi/sbi_ecall.h @@ -21,10 +21,46 @@ struct sbi_trap_regs; struct sbi_trap_info; struct sbi_ecall_extension { + /* head is used by the extension list */ struct sbi_dlist head; + /* + * extid_start and extid_end specify the range for this extension. As + * the initial range may be wider than the valid runtime range, the + * register_extensions callback is responsible for narrowing the range + * before other callbacks may be invoked. + */ unsigned long extid_start; unsigned long extid_end; + /* + * register_extensions + * + * Calls sbi_ecall_register_extension() one or more times to register + * extension ID range(s) which should be handled by this extension. + * More than one sbi_ecall_extension struct and + * sbi_ecall_register_extension() call is necessary when the supported + * extension ID ranges have gaps. Additionally, extension availability + * must be checked before registering, which means, when this callback + * returns, only valid extension IDs from the initial range, which are + * also available, have been registered. + */ + int (* register_extensions)(void); + /* + * probe + * + * Implements the Base extension's probe function for the extension. As + * the register_extensions callback ensures that no other extension + * callbacks will be invoked when the extension is not available, then + * probe can never fail. However, an extension may choose to set + * out_val to a nonzero value other than one. In those cases, it should + * implement this callback. + */ int (* probe)(unsigned long extid, unsigned long *out_val); + /* + * handle + * + * This is the extension handler. register_extensions ensures it is + * never invoked with an invalid or unavailable extension ID. + */ int (* handle)(unsigned long extid, unsigned long funcid, const struct sbi_trap_regs *regs, unsigned long *out_val, diff --git a/include/sbi/sbi_ecall_interface.h b/include/sbi/sbi_ecall_interface.h index a3f2bf4bdf6..1fe469e3707 100644 --- a/include/sbi/sbi_ecall_interface.h +++ b/include/sbi/sbi_ecall_interface.h @@ -29,6 +29,9 @@ #define SBI_EXT_HSM 0x48534D #define SBI_EXT_SRST 0x53525354 #define SBI_EXT_PMU 0x504D55 +#define SBI_EXT_DBCN 0x4442434E +#define SBI_EXT_SUSP 0x53555350 +#define SBI_EXT_CPPC 0x43505043 /* SBI function IDs for BASE extension*/ #define SBI_EXT_BASE_GET_SPEC_VERSION 0x0 @@ -99,6 +102,7 @@ #define SBI_EXT_PMU_COUNTER_START 0x3 #define SBI_EXT_PMU_COUNTER_STOP 0x4 #define SBI_EXT_PMU_COUNTER_FW_READ 0x5 +#define SBI_EXT_PMU_COUNTER_FW_READ_HI 0x6 /** General pmu event codes specified in SBI PMU extension */ enum sbi_pmu_hw_generic_events_t { @@ -182,6 +186,17 @@ enum sbi_pmu_fw_event_code_id { SBI_PMU_FW_HFENCE_VVMA_ASID_SENT = 20, SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD = 21, SBI_PMU_FW_MAX, + /* + * Event codes 22 to 255 are reserved for future use. + * Event codes 256 to 65534 are reserved for SBI implementation + * specific custom firmware events. + */ + SBI_PMU_FW_RESERVED_MAX = 0xFFFE, + /* + * Event code 0xFFFF is used for platform specific firmware + * events where the event data contains any event specific information. + */ + SBI_PMU_FW_PLATFORM = 0xFFFF, }; /** SBI PMU event idx type */ @@ -200,10 +215,10 @@ enum sbi_pmu_ctr_type { }; /* Helper macros to decode event idx */ -#define SBI_PMU_EVENT_IDX_OFFSET 20 #define SBI_PMU_EVENT_IDX_MASK 0xFFFFF +#define SBI_PMU_EVENT_IDX_TYPE_OFFSET 16 +#define SBI_PMU_EVENT_IDX_TYPE_MASK (0xF << SBI_PMU_EVENT_IDX_TYPE_OFFSET) #define SBI_PMU_EVENT_IDX_CODE_MASK 0xFFFF -#define SBI_PMU_EVENT_IDX_TYPE_MASK 0xF0000 #define SBI_PMU_EVENT_RAW_IDX 0x20000 #define SBI_PMU_EVENT_IDX_INVALID 0xFFFFFFFF @@ -230,6 +245,51 @@ enum sbi_pmu_ctr_type { /* Flags defined for counter stop function */ #define SBI_PMU_STOP_FLAG_RESET (1 << 0) +/* SBI function IDs for DBCN extension */ +#define SBI_EXT_DBCN_CONSOLE_WRITE 0x0 +#define SBI_EXT_DBCN_CONSOLE_READ 0x1 +#define SBI_EXT_DBCN_CONSOLE_WRITE_BYTE 0x2 + +/* SBI function IDs for SUSP extension */ +#define SBI_EXT_SUSP_SUSPEND 0x0 + +#define SBI_SUSP_SLEEP_TYPE_SUSPEND 0x0 +#define SBI_SUSP_SLEEP_TYPE_LAST SBI_SUSP_SLEEP_TYPE_SUSPEND +#define SBI_SUSP_PLATFORM_SLEEP_START 0x80000000 + +/* SBI function IDs for CPPC extension */ +#define SBI_EXT_CPPC_PROBE 0x0 +#define SBI_EXT_CPPC_READ 0x1 +#define SBI_EXT_CPPC_READ_HI 0x2 +#define SBI_EXT_CPPC_WRITE 0x3 + +enum sbi_cppc_reg_id { + SBI_CPPC_HIGHEST_PERF = 0x00000000, + SBI_CPPC_NOMINAL_PERF = 0x00000001, + SBI_CPPC_LOW_NON_LINEAR_PERF = 0x00000002, + SBI_CPPC_LOWEST_PERF = 0x00000003, + SBI_CPPC_GUARANTEED_PERF = 0x00000004, + SBI_CPPC_DESIRED_PERF = 0x00000005, + SBI_CPPC_MIN_PERF = 0x00000006, + SBI_CPPC_MAX_PERF = 0x00000007, + SBI_CPPC_PERF_REDUC_TOLERANCE = 0x00000008, + SBI_CPPC_TIME_WINDOW = 0x00000009, + SBI_CPPC_CTR_WRAP_TIME = 0x0000000A, + SBI_CPPC_REFERENCE_CTR = 0x0000000B, + SBI_CPPC_DELIVERED_CTR = 0x0000000C, + SBI_CPPC_PERF_LIMITED = 0x0000000D, + SBI_CPPC_ENABLE = 0x0000000E, + SBI_CPPC_AUTO_SEL_ENABLE = 0x0000000F, + SBI_CPPC_AUTO_ACT_WINDOW = 0x00000010, + SBI_CPPC_ENERGY_PERF_PREFERENCE = 0x00000011, + SBI_CPPC_REFERENCE_PERF = 0x00000012, + SBI_CPPC_LOWEST_FREQ = 0x00000013, + SBI_CPPC_NOMINAL_FREQ = 0x00000014, + SBI_CPPC_ACPI_LAST = SBI_CPPC_NOMINAL_FREQ, + SBI_CPPC_TRANSITION_LATENCY = 0x80000000, + SBI_CPPC_NON_ACPI_LAST = SBI_CPPC_TRANSITION_LATENCY, +}; + /* SBI base specification related macros */ #define SBI_SPEC_VERSION_MAJOR_OFFSET 24 #define SBI_SPEC_VERSION_MAJOR_MASK 0x7f diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index 95b40e75933..4f9e14608f4 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -26,21 +26,42 @@ enum sbi_hart_priv_versions { /** Possible ISA extensions of a hart */ enum sbi_hart_extensions { - /** Hart has Sscofpmt extension */ - SBI_HART_EXT_SSCOFPMF = 0, - /** HART has HW time CSR (extension name not available) */ - SBI_HART_EXT_TIME, /** HART has AIA M-mode CSRs */ - SBI_HART_EXT_SMAIA, + SBI_HART_EXT_SMAIA = 0, + /** HART has Smepmp */ + SBI_HART_EXT_SMEPMP, /** HART has Smstateen CSR **/ SBI_HART_EXT_SMSTATEEN, + /** Hart has Sscofpmt extension */ + SBI_HART_EXT_SSCOFPMF, /** HART has Sstc extension */ SBI_HART_EXT_SSTC, + /** HART has Zicntr extension (i.e. HW cycle, time & instret CSRs) */ + SBI_HART_EXT_ZICNTR, + /** HART has Zihpm extension */ + SBI_HART_EXT_ZIHPM, + /** Hart has Smcntrpmf extension */ + SBI_HART_EXT_SMCNTRPMF, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, }; +/* + * Smepmp enforces access boundaries between M-mode and + * S/U-mode. When it is enabled, the PMPs are programmed + * such that M-mode doesn't have access to S/U-mode memory. + * + * To give M-mode R/W access to the shared memory between M and + * S/U-mode, first entry is reserved. It is disabled at boot. + * When shared memory access is required, the physical address + * should be programmed into the first PMP entry with R/W + * permissions to the M-mode. Once the work is done, it should be + * unmapped. sbi_hart_map_saddr/sbi_hart_unmap_saddr function + * pair should be used to map/unmap the shared memory. + */ +#define SBI_SMEPMP_RESV_ENTRY 0 + struct sbi_hart_features { bool detected; int priv_version; @@ -71,6 +92,8 @@ unsigned long sbi_hart_pmp_granularity(struct sbi_scratch *scratch); unsigned int sbi_hart_pmp_addrbits(struct sbi_scratch *scratch); unsigned int sbi_hart_mhpm_bits(struct sbi_scratch *scratch); int sbi_hart_pmp_configure(struct sbi_scratch *scratch); +int sbi_hart_map_saddr(unsigned long base, unsigned long size); +int sbi_hart_unmap_saddr(void); int sbi_hart_priv_version(struct sbi_scratch *scratch); void sbi_hart_get_priv_version_str(struct sbi_scratch *scratch, char *version_str, int nvstr); diff --git a/include/sbi/sbi_heap.h b/include/sbi/sbi_heap.h new file mode 100644 index 00000000000..88d176ef0ed --- /dev/null +++ b/include/sbi/sbi_heap.h @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ventana Micro Systems Inc. + * + * Authors: + * Anup Patel + */ + +#ifndef __SBI_HEAP_H__ +#define __SBI_HEAP_H__ + +#include + +struct sbi_scratch; + +/** Allocate from heap area */ +void *sbi_malloc(size_t size); + +/** Zero allocate from heap area */ +void *sbi_zalloc(size_t size); + +/** Allocate array from heap area */ +static inline void *sbi_calloc(size_t nitems, size_t size) +{ + return sbi_zalloc(nitems * size); +} + +/** Free-up to heap area */ +void sbi_free(void *ptr); + +/** Amount (in bytes) of free space in the heap area */ +unsigned long sbi_heap_free_space(void); + +/** Amount (in bytes) of used space in the heap area */ +unsigned long sbi_heap_used_space(void); + +/** Amount (in bytes) of reserved space in the heap area */ +unsigned long sbi_heap_reserved_space(void); + +/** Initialize heap area */ +int sbi_heap_init(struct sbi_scratch *scratch); + +#endif diff --git a/include/sbi/sbi_hsm.h b/include/sbi/sbi_hsm.h index d6cc468d052..4b5601ba40c 100644 --- a/include/sbi/sbi_hsm.h +++ b/include/sbi/sbi_hsm.h @@ -21,8 +21,12 @@ struct sbi_hsm_device { int (*hart_start)(u32 hartid, ulong saddr); /** - * Stop (or power-down) the current hart from running. This call - * doesn't expect to return if success. + * Stop (or power-down) the current hart from running. + * + * Return SBI_ENOTSUPP if the hart does not support platform-specific + * stop actions. + * + * For successful stop, the call won't return. */ int (*hart_stop)(void); @@ -59,15 +63,21 @@ void __noreturn sbi_hsm_exit(struct sbi_scratch *scratch); int sbi_hsm_hart_start(struct sbi_scratch *scratch, const struct sbi_domain *dom, - u32 hartid, ulong saddr, ulong smode, ulong priv); + u32 hartid, ulong saddr, ulong smode, ulong arg1); int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow); void sbi_hsm_hart_resume_start(struct sbi_scratch *scratch); -void sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch); +void __noreturn sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch, + u32 hartid); int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type, - ulong raddr, ulong rmode, ulong priv); + ulong raddr, ulong rmode, ulong arg1); +bool sbi_hsm_hart_change_state(struct sbi_scratch *scratch, long oldstate, + long newstate); +int __sbi_hsm_hart_get_state(u32 hartid); int sbi_hsm_hart_get_state(const struct sbi_domain *dom, u32 hartid); int sbi_hsm_hart_interruptible_mask(const struct sbi_domain *dom, ulong hbase, ulong *out_hmask); -void sbi_hsm_prepare_next_jump(struct sbi_scratch *scratch, u32 hartid); +void __sbi_hsm_suspend_non_ret_save(struct sbi_scratch *scratch); +void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch, + u32 hartid); #endif diff --git a/include/sbi/sbi_init.h b/include/sbi/sbi_init.h index 74eb1c07599..9640fee9143 100644 --- a/include/sbi/sbi_init.h +++ b/include/sbi/sbi_init.h @@ -16,6 +16,8 @@ struct sbi_scratch; void __noreturn sbi_init(struct sbi_scratch *scratch); +unsigned long sbi_entry_count(u32 hartid); + unsigned long sbi_init_count(u32 hartid); void __noreturn sbi_exit(struct sbi_scratch *scratch); diff --git a/include/sbi/sbi_ipi.h b/include/sbi/sbi_ipi.h index f6ac8072f68..c64f422c030 100644 --- a/include/sbi/sbi_ipi.h +++ b/include/sbi/sbi_ipi.h @@ -30,6 +30,12 @@ struct sbi_ipi_device { void (*ipi_clear)(u32 target_hart); }; +enum sbi_ipi_update_type { + SBI_IPI_UPDATE_SUCCESS, + SBI_IPI_UPDATE_BREAK, + SBI_IPI_UPDATE_RETRY, +}; + struct sbi_scratch; /** IPI event operations or callbacks */ @@ -41,6 +47,10 @@ struct sbi_ipi_event_ops { * Update callback to save/enqueue data for remote HART * Note: This is an optional callback and it is called just before * triggering IPI to remote HART. + * @return < 0, error or failure + * @return SBI_IPI_UPDATE_SUCCESS, success + * @return SBI_IPI_UPDATE_BREAK, break IPI, done on local hart + * @return SBI_IPI_UPDATE_RETRY, need retry */ int (* update)(struct sbi_scratch *scratch, struct sbi_scratch *remote_scratch, @@ -77,6 +87,8 @@ void sbi_ipi_process(void); int sbi_ipi_raw_send(u32 target_hart); +void sbi_ipi_raw_clear(u32 target_hart); + const struct sbi_ipi_device *sbi_ipi_get_device(void); void sbi_ipi_set_device(const struct sbi_ipi_device *dev); diff --git a/include/sbi/sbi_list.h b/include/sbi/sbi_list.h index fe735dff054..9e56c526faf 100644 --- a/include/sbi/sbi_list.h +++ b/include/sbi/sbi_list.h @@ -31,7 +31,7 @@ struct sbi_dlist _lname = SBI_LIST_HEAD_INIT(_lname) #define SBI_INIT_LIST_HEAD(ptr) \ do { \ (ptr)->next = ptr; (ptr)->prev = ptr; \ -} while (0); +} while (0) static inline void __sbi_list_add(struct sbi_dlist *new, struct sbi_dlist *prev, diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index 010e9cebdd7..3e9616f6daf 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -29,12 +29,16 @@ #define SBI_PLATFORM_HART_COUNT_OFFSET (0x50) /** Offset of hart_stack_size in struct sbi_platform */ #define SBI_PLATFORM_HART_STACK_SIZE_OFFSET (0x54) +/** Offset of heap_size in struct sbi_platform */ +#define SBI_PLATFORM_HEAP_SIZE_OFFSET (0x58) +/** Offset of reserved in struct sbi_platform */ +#define SBI_PLATFORM_RESERVED_OFFSET (0x5c) /** Offset of platform_ops_addr in struct sbi_platform */ -#define SBI_PLATFORM_OPS_OFFSET (0x58) +#define SBI_PLATFORM_OPS_OFFSET (0x60) /** Offset of firmware_context in struct sbi_platform */ -#define SBI_PLATFORM_FIRMWARE_CONTEXT_OFFSET (0x58 + __SIZEOF_POINTER__) +#define SBI_PLATFORM_FIRMWARE_CONTEXT_OFFSET (0x60 + __SIZEOF_POINTER__) /** Offset of hart_index2id in struct sbi_platform */ -#define SBI_PLATFORM_HART_INDEX2ID_OFFSET (0x58 + (__SIZEOF_POINTER__ * 2)) +#define SBI_PLATFORM_HART_INDEX2ID_OFFSET (0x60 + (__SIZEOF_POINTER__ * 2)) #define SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_DEFAULT (1UL << 12) @@ -65,6 +69,9 @@ enum sbi_platform_features { /** Platform functions */ struct sbi_platform_operations { + /* Check if specified HART is allowed to do cold boot */ + bool (*cold_boot_allowed)(u32 hartid); + /* Platform nascent initialization */ int (*nascent_init)(void); @@ -123,10 +130,10 @@ struct sbi_platform_operations { /** Exit platform timer for current HART */ void (*timer_exit)(void); - /** platform specific SBI extension implementation probe function */ - int (*vendor_ext_check)(long extid); + /** Check if SBI vendor extension is implemented or not */ + bool (*vendor_ext_check)(void); /** platform specific SBI extension implementation provider */ - int (*vendor_ext_provider)(long extid, long funcid, + int (*vendor_ext_provider)(long funcid, const struct sbi_trap_regs *regs, unsigned long *out_value, struct sbi_trap_info *out_trap); @@ -135,6 +142,10 @@ struct sbi_platform_operations { /** Platform default per-HART stack size for exception/interrupt handling */ #define SBI_PLATFORM_DEFAULT_HART_STACK_SIZE 8192 +/** Platform default heap size */ +#define SBI_PLATFORM_DEFAULT_HEAP_SIZE(__num_hart) \ + (0x8000 + 0x800 * (__num_hart)) + /** Representation of a platform */ struct sbi_platform { /** @@ -157,6 +168,10 @@ struct sbi_platform { u32 hart_count; /** Per-HART stack size for exception/interrupt handling */ u32 hart_stack_size; + /** Size of heap shared by all HARTs */ + u32 heap_size; + /** Reserved for future use */ + u32 reserved; /** Pointer to sbi platform operations */ unsigned long platform_ops_addr; /** Pointer to system firmware specific context */ @@ -356,6 +371,23 @@ static inline bool sbi_platform_hart_invalid(const struct sbi_platform *plat, return false; } +/** + * Check whether given HART is allowed to do cold boot + * + * @param plat pointer to struct sbi_platform + * @param hartid HART ID + * + * @return true if HART is allowed to do cold boot and false otherwise + */ +static inline bool sbi_platform_cold_boot_allowed( + const struct sbi_platform *plat, + u32 hartid) +{ + if (plat && sbi_platform_ops(plat)->cold_boot_allowed) + return sbi_platform_ops(plat)->cold_boot_allowed(hartid); + return true; +} + /** * Nascent (very early) initialization for current HART * @@ -616,27 +648,25 @@ static inline void sbi_platform_timer_exit(const struct sbi_platform *plat) } /** - * Check if a vendor extension is implemented or not. + * Check if SBI vendor extension is implemented or not. * * @param plat pointer to struct sbi_platform - * @param extid vendor SBI extension id * - * @return 0 if extid is not implemented and 1 if implemented + * @return false if not implemented and true if implemented */ -static inline int sbi_platform_vendor_ext_check(const struct sbi_platform *plat, - long extid) +static inline bool sbi_platform_vendor_ext_check( + const struct sbi_platform *plat) { if (plat && sbi_platform_ops(plat)->vendor_ext_check) - return sbi_platform_ops(plat)->vendor_ext_check(extid); + return sbi_platform_ops(plat)->vendor_ext_check(); - return 0; + return false; } /** * Invoke platform specific vendor SBI extension implementation. * * @param plat pointer to struct sbi_platform - * @param extid vendor SBI extension id * @param funcid SBI function id within the extension id * @param regs pointer to trap registers passed by the caller * @param out_value output value that can be filled by the callee @@ -646,14 +676,14 @@ static inline int sbi_platform_vendor_ext_check(const struct sbi_platform *plat, */ static inline int sbi_platform_vendor_ext_provider( const struct sbi_platform *plat, - long extid, long funcid, + long funcid, const struct sbi_trap_regs *regs, unsigned long *out_value, struct sbi_trap_info *out_trap) { if (plat && sbi_platform_ops(plat)->vendor_ext_provider) { - return sbi_platform_ops(plat)->vendor_ext_provider(extid, - funcid, regs, + return sbi_platform_ops(plat)->vendor_ext_provider(funcid, + regs, out_value, out_trap); } diff --git a/include/sbi/sbi_pmu.h b/include/sbi/sbi_pmu.h index c36524393bc..16f687733ba 100644 --- a/include/sbi/sbi_pmu.h +++ b/include/sbi/sbi_pmu.h @@ -30,37 +30,48 @@ struct sbi_pmu_device { /** * Validate event code of custom firmware event - * Note: SBI_PMU_FW_MAX <= event_idx_code */ - int (*fw_event_validate_code)(uint32_t event_idx_code); + int (*fw_event_validate_encoding)(uint32_t hartid, uint64_t event_data); /** * Match custom firmware counter with custom firmware event * Note: 0 <= counter_index < SBI_PMU_FW_CTR_MAX */ - bool (*fw_counter_match_code)(uint32_t counter_index, - uint32_t event_idx_code); + bool (*fw_counter_match_encoding)(uint32_t hartid, + uint32_t counter_index, + uint64_t event_data); + + /** + * Fetch the max width of this counter in number of bits. + */ + int (*fw_counter_width)(void); /** * Read value of custom firmware counter * Note: 0 <= counter_index < SBI_PMU_FW_CTR_MAX */ - uint64_t (*fw_counter_read_value)(uint32_t counter_index); + uint64_t (*fw_counter_read_value)(uint32_t hartid, + uint32_t counter_index); + + /** + * Write value to custom firmware counter + * Note: 0 <= counter_index < SBI_PMU_FW_CTR_MAX + */ + void (*fw_counter_write_value)(uint32_t hartid, uint32_t counter_index, + uint64_t value); /** * Start custom firmware counter - * Note: SBI_PMU_FW_MAX <= event_idx_code * Note: 0 <= counter_index < SBI_PMU_FW_CTR_MAX */ - int (*fw_counter_start)(uint32_t counter_index, - uint32_t event_idx_code, - uint64_t init_val, bool init_val_update); + int (*fw_counter_start)(uint32_t hartid, uint32_t counter_index, + uint64_t event_data); /** * Stop custom firmware counter * Note: 0 <= counter_index < SBI_PMU_FW_CTR_MAX */ - int (*fw_counter_stop)(uint32_t counter_index); + int (*fw_counter_stop)(uint32_t hartid, uint32_t counter_index); /** * Custom enable irq for hardware counter diff --git a/include/sbi/sbi_scratch.h b/include/sbi/sbi_scratch.h index 40a3bc93c6d..48014923e36 100644 --- a/include/sbi/sbi_scratch.h +++ b/include/sbi/sbi_scratch.h @@ -18,26 +18,32 @@ #define SBI_SCRATCH_FW_START_OFFSET (0 * __SIZEOF_POINTER__) /** Offset of fw_size member in sbi_scratch */ #define SBI_SCRATCH_FW_SIZE_OFFSET (1 * __SIZEOF_POINTER__) +/** Offset (in sbi_scratch) of the R/W Offset */ +#define SBI_SCRATCH_FW_RW_OFFSET (2 * __SIZEOF_POINTER__) +/** Offset of fw_heap_offset member in sbi_scratch */ +#define SBI_SCRATCH_FW_HEAP_OFFSET (3 * __SIZEOF_POINTER__) +/** Offset of fw_heap_size_offset member in sbi_scratch */ +#define SBI_SCRATCH_FW_HEAP_SIZE_OFFSET (4 * __SIZEOF_POINTER__) /** Offset of next_arg1 member in sbi_scratch */ -#define SBI_SCRATCH_NEXT_ARG1_OFFSET (2 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_NEXT_ARG1_OFFSET (5 * __SIZEOF_POINTER__) /** Offset of next_addr member in sbi_scratch */ -#define SBI_SCRATCH_NEXT_ADDR_OFFSET (3 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_NEXT_ADDR_OFFSET (6 * __SIZEOF_POINTER__) /** Offset of next_mode member in sbi_scratch */ -#define SBI_SCRATCH_NEXT_MODE_OFFSET (4 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_NEXT_MODE_OFFSET (7 * __SIZEOF_POINTER__) /** Offset of warmboot_addr member in sbi_scratch */ -#define SBI_SCRATCH_WARMBOOT_ADDR_OFFSET (5 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_WARMBOOT_ADDR_OFFSET (8 * __SIZEOF_POINTER__) /** Offset of platform_addr member in sbi_scratch */ -#define SBI_SCRATCH_PLATFORM_ADDR_OFFSET (6 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_PLATFORM_ADDR_OFFSET (9 * __SIZEOF_POINTER__) /** Offset of hartid_to_scratch member in sbi_scratch */ -#define SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET (7 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET (10 * __SIZEOF_POINTER__) /** Offset of trap_exit member in sbi_scratch */ -#define SBI_SCRATCH_TRAP_EXIT_OFFSET (8 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_TRAP_EXIT_OFFSET (11 * __SIZEOF_POINTER__) /** Offset of tmp0 member in sbi_scratch */ -#define SBI_SCRATCH_TMP0_OFFSET (9 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_TMP0_OFFSET (12 * __SIZEOF_POINTER__) /** Offset of options member in sbi_scratch */ -#define SBI_SCRATCH_OPTIONS_OFFSET (10 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_OPTIONS_OFFSET (13 * __SIZEOF_POINTER__) /** Offset of extra space in sbi_scratch */ -#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (11 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (14 * __SIZEOF_POINTER__) /** Maximum size of sbi_scratch (4KB) */ #define SBI_SCRATCH_SIZE (0x1000) @@ -53,6 +59,12 @@ struct sbi_scratch { unsigned long fw_start; /** Size (in bytes) of firmware linked to OpenSBI library */ unsigned long fw_size; + /** Offset (in bytes) of the R/W section */ + unsigned long fw_rw_offset; + /** Offset (in bytes) of the heap area */ + unsigned long fw_heap_offset; + /** Size (in bytes) of the heap area */ + unsigned long fw_heap_size; /** Arg1 (or 'a1' register) of next booting stage for this HART */ unsigned long next_arg1; /** Address of next booting stage for this HART */ @@ -163,6 +175,9 @@ unsigned long sbi_scratch_alloc_offset(unsigned long size); /** Free-up extra space in sbi_scratch */ void sbi_scratch_free_offset(unsigned long offset); +/** Amount (in bytes) of used space in in sbi_scratch */ +unsigned long sbi_scratch_used_space(void); + /** Get pointer from offset in sbi_scratch */ #define sbi_scratch_offset_ptr(scratch, offset) (void *)((char *)(scratch) + (offset)) @@ -170,6 +185,23 @@ void sbi_scratch_free_offset(unsigned long offset); #define sbi_scratch_thishart_offset_ptr(offset) \ (void *)((char *)sbi_scratch_thishart_ptr() + (offset)) +/** Allocate offset for a data type in sbi_scratch */ +#define sbi_scratch_alloc_type_offset(__type) \ + sbi_scratch_alloc_offset(sizeof(__type)) + +/** Read a data type from sbi_scratch at given offset */ +#define sbi_scratch_read_type(__scratch, __type, __offset) \ +({ \ + *((__type *)sbi_scratch_offset_ptr((__scratch), (__offset))); \ +}) + +/** Write a data type to sbi_scratch at given offset */ +#define sbi_scratch_write_type(__scratch, __type, __offset, __ptr) \ +do { \ + *((__type *)sbi_scratch_offset_ptr((__scratch), (__offset))) \ + = (__type)(__ptr); \ +} while (0) + /** HART id to scratch table */ extern struct sbi_scratch *hartid_to_scratch_table[]; diff --git a/include/sbi/sbi_system.h b/include/sbi/sbi_system.h index 84c281383d8..0fdcc98cce5 100644 --- a/include/sbi/sbi_system.h +++ b/include/sbi/sbi_system.h @@ -43,4 +43,38 @@ bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason); void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason); +/** System suspend device */ +struct sbi_system_suspend_device { + /** Name of the system suspend device */ + char name[32]; + + /** + * Check whether sleep type is supported by the device + * + * Returns 0 when @sleep_type supported, SBI_ERR_INVALID_PARAM + * when @sleep_type is reserved, or SBI_ERR_NOT_SUPPORTED when + * @sleep_type is not reserved and is implemented, but the + * platform doesn't support it due to missing dependencies. + */ + int (*system_suspend_check)(u32 sleep_type); + + /** + * Suspend the system + * + * @sleep_type: The sleep type identifier passed to the SBI call. + * @mmode_resume_addr: + * This is the same as sbi_scratch.warmboot_addr. Some platforms + * may not be able to return from system_suspend(), so they will + * jump directly to this address instead. Platforms which can + * return from system_suspend() may ignore this parameter. + */ + int (*system_suspend)(u32 sleep_type, unsigned long mmode_resume_addr); +}; + +const struct sbi_system_suspend_device *sbi_system_suspend_get_device(void); +void sbi_system_suspend_set_device(struct sbi_system_suspend_device *dev); +void sbi_system_suspend_test_enable(void); +bool sbi_system_suspend_supported(u32 sleep_type); +int sbi_system_suspend(u32 sleep_type, ulong resume_addr, ulong opaque); + #endif diff --git a/include/sbi/sbi_types.h b/include/sbi/sbi_types.h index 75cddddfe0d..def88bbad2c 100644 --- a/include/sbi/sbi_types.h +++ b/include/sbi/sbi_types.h @@ -54,6 +54,13 @@ typedef unsigned long virtual_size_t; typedef unsigned long physical_addr_t; typedef unsigned long physical_size_t; +typedef uint16_t le16_t; +typedef uint16_t be16_t; +typedef uint32_t le32_t; +typedef uint32_t be32_t; +typedef uint64_t le64_t; +typedef uint64_t be64_t; + #define true 1 #define false 0 @@ -62,6 +69,7 @@ typedef unsigned long physical_size_t; #define __packed __attribute__((packed)) #define __noreturn __attribute__((noreturn)) #define __aligned(x) __attribute__((aligned(x))) +#define __always_inline inline __attribute__((always_inline)) #define likely(x) __builtin_expect((x), 1) #define unlikely(x) __builtin_expect((x), 0) diff --git a/include/sbi/sbi_version.h b/include/sbi/sbi_version.h index dd75deae814..641cf8814b2 100644 --- a/include/sbi/sbi_version.h +++ b/include/sbi/sbi_version.h @@ -11,7 +11,7 @@ #define __SBI_VERSION_H__ #define OPENSBI_VERSION_MAJOR 1 -#define OPENSBI_VERSION_MINOR 2 +#define OPENSBI_VERSION_MINOR 3 /** * OpenSBI 32-bit version with: diff --git a/include/sbi_utils/fdt/fdt_fixup.h b/include/sbi_utils/fdt/fdt_fixup.h index fb076bad76c..ecd55a724a8 100644 --- a/include/sbi_utils/fdt/fdt_fixup.h +++ b/include/sbi_utils/fdt/fdt_fixup.h @@ -9,6 +9,29 @@ #ifndef __FDT_FIXUP_H__ #define __FDT_FIXUP_H__ +struct sbi_cpu_idle_state { + const char *name; + uint32_t suspend_param; + bool local_timer_stop; + uint32_t entry_latency_us; + uint32_t exit_latency_us; + uint32_t min_residency_us; + uint32_t wakeup_latency_us; +}; + +/** + * Add CPU idle states to cpu nodes in the DT + * + * Add information about CPU idle states to the devicetree. This function + * assumes that CPU idle states are not already present in the devicetree, and + * that all CPU states are equally applicable to all CPUs. + * + * @param fdt: device tree blob + * @param states: array of idle state descriptions, ending with empty element + * @return zero on success and -ve on failure + */ +int fdt_add_cpu_idle_states(void *dtb, const struct sbi_cpu_idle_state *state); + /** * Fix up the CPU node in the device tree * @@ -70,20 +93,6 @@ void fdt_plic_fixup(void *fdt); */ int fdt_reserved_memory_fixup(void *fdt); -/** - * Fix up the reserved memory subnodes in the device tree - * - * This routine adds the no-map property to the reserved memory subnodes so - * that the OS does not map those PMP protected memory regions. - * - * Platform codes must call this helper in their final_init() after fdt_fixups() - * if the OS should not map the PMP protected reserved regions. - * - * @param fdt: device tree blob - * @return zero on success and -ve on failure - */ -int fdt_reserved_memory_nomap_fixup(void *fdt); - /** * General device tree fix-up * diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h index 09f3095d0d9..5c928ffd72c 100644 --- a/include/sbi_utils/fdt/fdt_helper.h +++ b/include/sbi_utils/fdt/fdt_helper.h @@ -11,7 +11,7 @@ #define __FDT_HELPER_H__ #include -#include +#include struct fdt_match { const char *compatible; @@ -56,6 +56,9 @@ int fdt_parse_max_enabled_hart_id(void *fdt, u32 *max_hartid); int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq); +int fdt_parse_isa_extensions(void *fdt, unsigned int hard_id, + unsigned long *extensions); + int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset, struct platform_uart_data *uart); @@ -109,7 +112,7 @@ int fdt_parse_compat_addr(void *fdt, uint64_t *addr, static inline void *fdt_get_address(void) { - return sbi_scratch_thishart_arg1_ptr(); + return (void *)root.next_arg1; } #endif /* __FDT_HELPER_H__ */ diff --git a/include/sbi_utils/i2c/dw_i2c.h b/include/sbi_utils/i2c/dw_i2c.h new file mode 100644 index 00000000000..88703e057ff --- /dev/null +++ b/include/sbi_utils/i2c/dw_i2c.h @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 StarFive Technology Co., Ltd. + * + * Author: Minda Chen + */ + +#ifndef __DW_I2C_H__ +#define __DW_I2C_H__ + +#include + +int dw_i2c_init(struct i2c_adapter *, int nodeoff); + +struct dw_i2c_adapter { + unsigned long addr; + struct i2c_adapter adapter; +}; + +#endif diff --git a/include/sbi_utils/sys/atcsmu.h b/include/sbi_utils/sys/atcsmu.h new file mode 100644 index 00000000000..07be22eef62 --- /dev/null +++ b/include/sbi_utils/sys/atcsmu.h @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2023 Andes Technology Corporation + */ + +#ifndef _SYS_ATCSMU_H +#define _SYS_ATCSMU_H + +#include + +/* clang-format off */ + +#define PCS0_WE_OFFSET 0x90 +#define PCSm_WE_OFFSET(i) ((i + 3) * 0x20 + PCS0_WE_OFFSET) + +#define PCS0_CTL_OFFSET 0x94 +#define PCSm_CTL_OFFSET(i) ((i + 3) * 0x20 + PCS0_CTL_OFFSET) +#define PCS_CTL_CMD_SHIFT 0 +#define PCS_CTL_PARAM_SHIFT 3 +#define SLEEP_CMD 0x3 +#define WAKEUP_CMD (0x0 | (1 << PCS_CTL_PARAM_SHIFT)) +#define LIGHTSLEEP_MODE 0 +#define DEEPSLEEP_MODE 1 +#define LIGHT_SLEEP_CMD (SLEEP_CMD | (LIGHTSLEEP_MODE << PCS_CTL_PARAM_SHIFT)) +#define DEEP_SLEEP_CMD (SLEEP_CMD | (DEEPSLEEP_MODE << PCS_CTL_PARAM_SHIFT)) + +#define PCS0_CFG_OFFSET 0x80 +#define PCSm_CFG_OFFSET(i) ((i + 3) * 0x20 + PCS0_CFG_OFFSET) +#define PCS_CFG_LIGHT_SLEEP_SHIFT 2 +#define PCS_CFG_LIGHT_SLEEP (1 << PCS_CFG_LIGHT_SLEEP_SHIFT) +#define PCS_CFG_DEEP_SLEEP_SHIFT 3 +#define PCS_CFG_DEEP_SLEEP (1 << PCS_CFG_DEEP_SLEEP_SHIFT) + +#define RESET_VEC_LO_OFFSET 0x50 +#define RESET_VEC_HI_OFFSET 0x60 +#define RESET_VEC_8CORE_OFFSET 0x1a0 +#define HARTn_RESET_VEC_LO(n) (RESET_VEC_LO_OFFSET + \ + ((n) < 4 ? 0 : RESET_VEC_8CORE_OFFSET) + \ + ((n) * 0x4)) +#define HARTn_RESET_VEC_HI(n) (RESET_VEC_HI_OFFSET + \ + ((n) < 4 ? 0 : RESET_VEC_8CORE_OFFSET) + \ + ((n) * 0x4)) + +#define PCS_MAX_NR 8 +#define FLASH_BASE 0x80000000ULL + +/* clang-format on */ + +struct smu_data { + unsigned long addr; +}; + +int smu_set_wakeup_events(struct smu_data *smu, u32 events, u32 hartid); +bool smu_support_sleep_mode(struct smu_data *smu, u32 sleep_mode, u32 hartid); +int smu_set_command(struct smu_data *smu, u32 pcs_ctl, u32 hartid); +int smu_set_reset_vector(struct smu_data *smu, ulong wakeup_addr, u32 hartid); + +#endif /* _SYS_ATCSMU_H */ diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig index df74bba3854..477775e21ea 100644 --- a/lib/sbi/Kconfig +++ b/lib/sbi/Kconfig @@ -22,10 +22,22 @@ config SBI_ECALL_SRST bool "System Reset extension" default y +config SBI_ECALL_SUSP + bool "System Suspend extension" + default y + config SBI_ECALL_PMU bool "Performance Monitoring Unit extension" default y +config SBI_ECALL_DBCN + bool "Debug Console extension" + default y + +config SBI_ECALL_CPPC + bool "CPPC extension" + default y + config SBI_ECALL_LEGACY bool "SBI v0.1 legacy extensions" default y diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index c774ebbcd14..c6991870191 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -34,9 +34,18 @@ libsbi-objs-$(CONFIG_SBI_ECALL_HSM) += sbi_ecall_hsm.o carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_SRST) += ecall_srst libsbi-objs-$(CONFIG_SBI_ECALL_SRST) += sbi_ecall_srst.o +carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_SUSP) += ecall_susp +libsbi-objs-$(CONFIG_SBI_ECALL_SUSP) += sbi_ecall_susp.o + carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_PMU) += ecall_pmu libsbi-objs-$(CONFIG_SBI_ECALL_PMU) += sbi_ecall_pmu.o +carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_DBCN) += ecall_dbcn +libsbi-objs-$(CONFIG_SBI_ECALL_DBCN) += sbi_ecall_dbcn.o + +carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_CPPC) += ecall_cppc +libsbi-objs-$(CONFIG_SBI_ECALL_CPPC) += sbi_ecall_cppc.o + carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_LEGACY) += ecall_legacy libsbi-objs-$(CONFIG_SBI_ECALL_LEGACY) += sbi_ecall_legacy.o @@ -50,6 +59,7 @@ libsbi-objs-y += sbi_domain.o libsbi-objs-y += sbi_emulate_csr.o libsbi-objs-y += sbi_fifo.o libsbi-objs-y += sbi_hart.o +libsbi-objs-y += sbi_heap.o libsbi-objs-y += sbi_math.o libsbi-objs-y += sbi_hfence.o libsbi-objs-y += sbi_hsm.o @@ -68,3 +78,4 @@ libsbi-objs-y += sbi_tlb.o libsbi-objs-y += sbi_trap.o libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o +libsbi-objs-y += sbi_cppc.o diff --git a/lib/sbi/riscv_asm.c b/lib/sbi/riscv_asm.c index a09cf78e9c1..05b8c7ce6e3 100644 --- a/lib/sbi/riscv_asm.c +++ b/lib/sbi/riscv_asm.c @@ -128,6 +128,8 @@ unsigned long csr_read_num(int csr_num) switchcase_csr_read_8(CSR_MHPMCOUNTER8, ret) switchcase_csr_read_16(CSR_MHPMCOUNTER16, ret) switchcase_csr_read(CSR_MCOUNTINHIBIT, ret) + switchcase_csr_read(CSR_MCYCLECFG, ret) + switchcase_csr_read(CSR_MINSTRETCFG, ret) switchcase_csr_read(CSR_MHPMEVENT3, ret) switchcase_csr_read_4(CSR_MHPMEVENT4, ret) switchcase_csr_read_8(CSR_MHPMEVENT8, ret) @@ -139,6 +141,12 @@ unsigned long csr_read_num(int csr_num) switchcase_csr_read_4(CSR_MHPMCOUNTER4H, ret) switchcase_csr_read_8(CSR_MHPMCOUNTER8H, ret) switchcase_csr_read_16(CSR_MHPMCOUNTER16H, ret) + /** + * The CSR range M[CYCLE, INSTRET]CFGH are available only if smcntrpmf + * extension is present. The caller must ensure that. + */ + switchcase_csr_read(CSR_MCYCLECFGH, ret) + switchcase_csr_read(CSR_MINSTRETCFGH, ret) /** * The CSR range MHPMEVENT[3-16]H are available only if sscofpmf * extension is present. The caller must ensure that. @@ -152,7 +160,7 @@ unsigned long csr_read_num(int csr_num) default: sbi_panic("%s: Unknown CSR %#x", __func__, csr_num); break; - }; + } return ret; @@ -206,12 +214,16 @@ void csr_write_num(int csr_num, unsigned long val) switchcase_csr_write_4(CSR_MHPMCOUNTER4H, val) switchcase_csr_write_8(CSR_MHPMCOUNTER8H, val) switchcase_csr_write_16(CSR_MHPMCOUNTER16H, val) + switchcase_csr_write(CSR_MCYCLECFGH, val) + switchcase_csr_write(CSR_MINSTRETCFGH, val) switchcase_csr_write(CSR_MHPMEVENT3H, val) switchcase_csr_write_4(CSR_MHPMEVENT4H, val) switchcase_csr_write_8(CSR_MHPMEVENT8H, val) switchcase_csr_write_16(CSR_MHPMEVENT16H, val) #endif switchcase_csr_write(CSR_MCOUNTINHIBIT, val) + switchcase_csr_write(CSR_MCYCLECFG, val) + switchcase_csr_write(CSR_MINSTRETCFG, val) switchcase_csr_write(CSR_MHPMEVENT3, val) switchcase_csr_write_4(CSR_MHPMEVENT4, val) switchcase_csr_write_8(CSR_MHPMEVENT8, val) @@ -220,7 +232,7 @@ void csr_write_num(int csr_num, unsigned long val) default: sbi_panic("%s: Unknown CSR %#x", __func__, csr_num); break; - }; + } #undef switchcase_csr_write_64 #undef switchcase_csr_write_32 @@ -246,6 +258,48 @@ static unsigned long ctz(unsigned long x) return ret; } +int pmp_disable(unsigned int n) +{ + int pmpcfg_csr, pmpcfg_shift; + unsigned long cfgmask, pmpcfg; + + if (n >= PMP_COUNT) + return SBI_EINVAL; + +#if __riscv_xlen == 32 + pmpcfg_csr = CSR_PMPCFG0 + (n >> 2); + pmpcfg_shift = (n & 3) << 3; +#elif __riscv_xlen == 64 + pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1; + pmpcfg_shift = (n & 7) << 3; +#else +# error "Unexpected __riscv_xlen" +#endif + + /* Clear the address matching bits to disable the pmp entry */ + cfgmask = ~(0xffUL << pmpcfg_shift); + pmpcfg = (csr_read_num(pmpcfg_csr) & cfgmask); + + csr_write_num(pmpcfg_csr, pmpcfg); + + return SBI_OK; +} + +int is_pmp_entry_mapped(unsigned long entry) +{ + unsigned long prot; + unsigned long addr; + unsigned long log2len; + + pmp_get(entry, &prot, &addr, &log2len); + + /* If address matching bits are non-zero, the entry is enable */ + if (prot & PMP_A) + return true; + + return false; +} + int pmp_set(unsigned int n, unsigned long prot, unsigned long addr, unsigned long log2len) { diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c index bb6e1ef159a..2b3b0a3fe80 100644 --- a/lib/sbi/sbi_console.c +++ b/lib/sbi/sbi_console.c @@ -12,8 +12,13 @@ #include #include #include +#include + +#define CONSOLE_TBUF_MAX 256 static const struct sbi_console_device *console_dev = NULL; +static char console_tbuf[CONSOLE_TBUF_MAX]; +static u32 console_tbuf_len; static spinlock_t console_out_lock = SPIN_LOCK_INITIALIZER; bool sbi_isprintable(char c) @@ -41,14 +46,47 @@ void sbi_putc(char ch) } } +static unsigned long nputs(const char *str, unsigned long len) +{ + unsigned long i, ret; + + if (console_dev && console_dev->console_puts) { + ret = console_dev->console_puts(str, len); + } else { + for (i = 0; i < len; i++) + sbi_putc(str[i]); + ret = len; + } + + return ret; +} + +static void nputs_all(const char *str, unsigned long len) +{ + unsigned long p = 0; + + while (p < len) + p += nputs(&str[p], len - p); +} + void sbi_puts(const char *str) { + unsigned long len = sbi_strlen(str); + spin_lock(&console_out_lock); - while (*str) { - sbi_putc(*str); - str++; - } + nputs_all(str, len); + spin_unlock(&console_out_lock); +} + +unsigned long sbi_nputs(const char *str, unsigned long len) +{ + unsigned long ret; + + spin_lock(&console_out_lock); + ret = nputs(str, len); spin_unlock(&console_out_lock); + + return ret; } void sbi_gets(char *s, int maxwidth, char endchar) @@ -64,9 +102,26 @@ void sbi_gets(char *s, int maxwidth, char endchar) *retval = '\0'; } +unsigned long sbi_ngets(char *str, unsigned long len) +{ + int ch; + unsigned long i; + + for (i = 0; i < len; i++) { + ch = sbi_getc(); + if (ch < 0) + break; + str[i] = ch; + } + + return i; +} + #define PAD_RIGHT 1 #define PAD_ZERO 2 #define PAD_ALTERNATE 4 +#define PAD_SIGN 8 +#define USE_TBUF 16 #define PRINT_BUF_LEN 64 #define va_start(v, l) __builtin_va_start((v), l) @@ -74,7 +129,7 @@ void sbi_gets(char *s, int maxwidth, char endchar) #define va_arg __builtin_va_arg typedef __builtin_va_list va_list; -static void printc(char **out, u32 *out_len, char ch) +static void printc(char **out, u32 *out_len, char ch, int flags) { if (!out) { sbi_putc(ch); @@ -88,60 +143,66 @@ static void printc(char **out, u32 *out_len, char ch) if (!out_len || *out_len > 1) { *(*out)++ = ch; **out = '\0'; + if (out_len) { + --(*out_len); + if ((flags & USE_TBUF) && *out_len == 1) { + nputs_all(console_tbuf, CONSOLE_TBUF_MAX - *out_len); + *out = console_tbuf; + *out_len = CONSOLE_TBUF_MAX; + } + } } - - if (out_len && *out_len > 0) - --(*out_len); } static int prints(char **out, u32 *out_len, const char *string, int width, int flags) { - int pc = 0; - char padchar = ' '; - - if (width > 0) { - int len = 0; - const char *ptr; - for (ptr = string; *ptr; ++ptr) - ++len; - if (len >= width) - width = 0; - else - width -= len; - if (flags & PAD_ZERO) - padchar = '0'; - } + int pc = 0; + width -= sbi_strlen(string); if (!(flags & PAD_RIGHT)) { for (; width > 0; --width) { - printc(out, out_len, padchar); + printc(out, out_len, flags & PAD_ZERO ? '0' : ' ', flags); ++pc; } } for (; *string; ++string) { - printc(out, out_len, *string); + printc(out, out_len, *string, flags); ++pc; } for (; width > 0; --width) { - printc(out, out_len, padchar); + printc(out, out_len, ' ', flags); ++pc; } return pc; } -static int printi(char **out, u32 *out_len, long long i, int b, int sg, - int width, int flags, int letbase) +static int printi(char **out, u32 *out_len, long long i, + int width, int flags, int type) { - char print_buf[PRINT_BUF_LEN]; - char *s; - int neg = 0, pc = 0; - u64 t; - unsigned long long u = i; - - if (sg && b == 10 && i < 0) { - neg = 1; - u = -i; + int pc = 0; + char *s, sign = 0, letbase, print_buf[PRINT_BUF_LEN]; + unsigned long long u, b, t; + + b = 10; + letbase = 'a'; + if (type == 'o') + b = 8; + else if (type == 'x' || type == 'X' || type == 'p' || type == 'P') { + b = 16; + letbase &= ~0x20; + letbase |= type & 0x20; + } + + u = i; + sign = 0; + if (type == 'i' || type == 'd') { + if ((flags & PAD_SIGN) && i > 0) + sign = '+'; + if (i < 0) { + sign = '-'; + u = -i; + } } s = print_buf + PRINT_BUF_LEN - 1; @@ -159,23 +220,33 @@ static int printi(char **out, u32 *out_len, long long i, int b, int sg, } } - if (flags & PAD_ALTERNATE) { - if ((b == 16) && (letbase == 'A')) { - *--s = 'X'; - } else if ((b == 16) && (letbase == 'a')) { - *--s = 'x'; - } - *--s = '0'; - } - - if (neg) { - if (width && (flags & PAD_ZERO)) { - printc(out, out_len, '-'); + if (flags & PAD_ZERO) { + if (sign) { + printc(out, out_len, sign, flags); ++pc; --width; - } else { - *--s = '-'; } + if (i && (flags & PAD_ALTERNATE)) { + if (b == 16 || b == 8) { + printc(out, out_len, '0', flags); + ++pc; + --width; + } + if (b == 16) { + printc(out, out_len, 'x' - 'a' + letbase, flags); + ++pc; + --width; + } + } + } else { + if (i && (flags & PAD_ALTERNATE)) { + if (b == 16) + *--s = 'x' - 'a' + letbase; + if (b == 16 || b == 8) + *--s = '0'; + } + if (sign) + *--s = sign; } return pc + prints(out, out_len, s, width, flags); @@ -183,32 +254,68 @@ static int printi(char **out, u32 *out_len, long long i, int b, int sg, static int print(char **out, u32 *out_len, const char *format, va_list args) { - int width, flags; - int pc = 0; - char scr[2]; - unsigned long long tmp; + bool flags_done; + int width, flags, pc = 0; + char type, scr[2], *tout; + bool use_tbuf = (!out) ? true : false; + + /* + * The console_tbuf is protected by console_out_lock and + * print() is always called with console_out_lock held + * when out == NULL. + */ + if (use_tbuf) { + console_tbuf_len = CONSOLE_TBUF_MAX; + tout = console_tbuf; + out = &tout; + out_len = &console_tbuf_len; + } + + /* handle special case: *out_len == 1*/ + if (out) { + if(!out_len || *out_len) + **out = '\0'; + } for (; *format != 0; ++format) { + width = flags = 0; + if (use_tbuf) + flags |= USE_TBUF; if (*format == '%') { ++format; - width = flags = 0; if (*format == '\0') break; if (*format == '%') goto literal; /* Get flags */ - if (*format == '-') { - ++format; - flags = PAD_RIGHT; - } - if (*format == '#') { - ++format; - flags |= PAD_ALTERNATE; - } - while (*format == '0') { - ++format; - flags |= PAD_ZERO; + flags_done = false; + while (!flags_done) { + switch (*format) { + case '-': + flags |= PAD_RIGHT; + break; + case '+': + flags |= PAD_SIGN; + break; + case '#': + flags |= PAD_ALTERNATE; + break; + case '0': + flags |= PAD_ZERO; + break; + case ' ': + case '\'': + /* Ignored flags, do nothing */ + break; + default: + flags_done = true; + break; + } + if (!flags_done) + ++format; } + if (flags & PAD_RIGHT) + flags &= ~PAD_ZERO; /* Get width */ for (; *format >= '0' && *format <= '9'; ++format) { width *= 10; @@ -222,83 +329,47 @@ static int print(char **out, u32 *out_len, const char *format, va_list args) } if ((*format == 'd') || (*format == 'i')) { pc += printi(out, out_len, va_arg(args, int), - 10, 1, width, flags, '0'); - continue; - } - if (*format == 'x') { - pc += printi(out, out_len, - va_arg(args, unsigned int), 16, 0, - width, flags, 'a'); + width, flags, *format); continue; } - if (*format == 'X') { - pc += printi(out, out_len, - va_arg(args, unsigned int), 16, 0, - width, flags, 'A'); + if ((*format == 'u') || (*format == 'o') + || (*format == 'x') || (*format == 'X')) { + pc += printi(out, out_len, va_arg(args, unsigned int), + width, flags, *format); continue; } - if (*format == 'u') { - pc += printi(out, out_len, - va_arg(args, unsigned int), 10, 0, - width, flags, 'a'); + if ((*format == 'p') || (*format == 'P')) { + pc += printi(out, out_len, (uintptr_t)va_arg(args, void*), + width, flags, *format); continue; } - if (*format == 'p') { - pc += printi(out, out_len, - va_arg(args, unsigned long), 16, 0, - width, flags, 'a'); - continue; - } - if (*format == 'P') { - pc += printi(out, out_len, - va_arg(args, unsigned long), 16, 0, - width, flags, 'A'); - continue; - } - if (*format == 'l' && *(format + 1) == 'l') { - tmp = va_arg(args, unsigned long long); - if (*(format + 2) == 'u') { - format += 2; - pc += printi(out, out_len, tmp, 10, 0, - width, flags, 'a'); - } else if (*(format + 2) == 'x') { - format += 2; - pc += printi(out, out_len, tmp, 16, 0, - width, flags, 'a'); - } else if (*(format + 2) == 'X') { - format += 2; - pc += printi(out, out_len, tmp, 16, 0, - width, flags, 'A'); - } else { - format += 1; - pc += printi(out, out_len, tmp, 10, 1, - width, flags, '0'); + if (*format == 'l') { + type = 'i'; + if (format[1] == 'l') { + ++format; + if ((format[1] == 'u') || (format[1] == 'o') + || (format[1] == 'd') || (format[1] == 'i') + || (format[1] == 'x') || (format[1] == 'X')) { + ++format; + type = *format; + } + pc += printi(out, out_len, va_arg(args, long long), + width, flags, type); + continue; } - continue; - } else if (*format == 'l') { - if (*(format + 1) == 'u') { - format += 1; - pc += printi( - out, out_len, - va_arg(args, unsigned long), 10, - 0, width, flags, 'a'); - } else if (*(format + 1) == 'x') { - format += 1; - pc += printi( - out, out_len, - va_arg(args, unsigned long), 16, - 0, width, flags, 'a'); - } else if (*(format + 1) == 'X') { - format += 1; - pc += printi( - out, out_len, - va_arg(args, unsigned long), 16, - 0, width, flags, 'A'); - } else { - pc += printi(out, out_len, - va_arg(args, long), 10, 1, - width, flags, '0'); + if ((format[1] == 'u') || (format[1] == 'o') + || (format[1] == 'd') || (format[1] == 'i') + || (format[1] == 'x') || (format[1] == 'X')) { + ++format; + type = *format; } + if ((type == 'd') || (type == 'i')) + pc += printi(out, out_len, va_arg(args, long), + width, flags, type); + else + pc += printi(out, out_len, va_arg(args, unsigned long), + width, flags, type); + continue; } if (*format == 'c') { /* char are converted to int then pushed on the stack */ @@ -309,11 +380,14 @@ static int print(char **out, u32 *out_len, const char *format, va_list args) } } else { literal: - printc(out, out_len, *format); + printc(out, out_len, *format, flags); ++pc; } } + if (use_tbuf && console_tbuf_len < CONSOLE_TBUF_MAX) + nputs_all(console_tbuf, CONSOLE_TBUF_MAX - console_tbuf_len); + return pc; } @@ -407,5 +481,11 @@ void sbi_console_set_device(const struct sbi_console_device *dev) int sbi_console_init(struct sbi_scratch *scratch) { - return sbi_platform_console_init(sbi_platform_ptr(scratch)); + int rc = sbi_platform_console_init(sbi_platform_ptr(scratch)); + + /* console is not a necessary device */ + if (rc == SBI_ENODEV) + return 0; + + return rc; } diff --git a/lib/sbi/sbi_cppc.c b/lib/sbi/sbi_cppc.c new file mode 100644 index 00000000000..afb60b332c2 --- /dev/null +++ b/lib/sbi/sbi_cppc.c @@ -0,0 +1,110 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ventana Micro Systems Inc. + * + */ + +#include +#include + +static const struct sbi_cppc_device *cppc_dev = NULL; + +const struct sbi_cppc_device *sbi_cppc_get_device(void) +{ + return cppc_dev; +} + +void sbi_cppc_set_device(const struct sbi_cppc_device *dev) +{ + if (!dev || cppc_dev) + return; + + cppc_dev = dev; +} + +static bool sbi_cppc_is_reserved(unsigned long reg) +{ + if ((reg > SBI_CPPC_ACPI_LAST && reg < SBI_CPPC_TRANSITION_LATENCY) || + reg > SBI_CPPC_NON_ACPI_LAST) + return true; + + return false; +} + +static bool sbi_cppc_readable(unsigned long reg) +{ + /* there are no write-only cppc registers currently */ + return true; +} + +static bool sbi_cppc_writable(unsigned long reg) +{ + switch (reg) { + case SBI_CPPC_HIGHEST_PERF: + case SBI_CPPC_NOMINAL_PERF: + case SBI_CPPC_LOW_NON_LINEAR_PERF: + case SBI_CPPC_LOWEST_PERF: + case SBI_CPPC_GUARANTEED_PERF: + case SBI_CPPC_CTR_WRAP_TIME: + case SBI_CPPC_REFERENCE_CTR: + case SBI_CPPC_DELIVERED_CTR: + case SBI_CPPC_REFERENCE_PERF: + case SBI_CPPC_LOWEST_FREQ: + case SBI_CPPC_NOMINAL_FREQ: + case SBI_CPPC_TRANSITION_LATENCY: + return false; + } + + return true; +} + +int sbi_cppc_probe(unsigned long reg) +{ + if (!cppc_dev || !cppc_dev->cppc_probe) + return SBI_EFAIL; + + /* Check whether register is reserved */ + if (sbi_cppc_is_reserved(reg)) + return SBI_ERR_INVALID_PARAM; + + return cppc_dev->cppc_probe(reg); +} + +int sbi_cppc_read(unsigned long reg, uint64_t *val) +{ + int ret; + + if (!cppc_dev || !cppc_dev->cppc_read) + return SBI_EFAIL; + + /* Check whether register is implemented */ + ret = sbi_cppc_probe(reg); + if (ret <= 0) + return SBI_ERR_NOT_SUPPORTED; + + /* Check whether the register is write-only */ + if (!sbi_cppc_readable(reg)) + return SBI_ERR_DENIED; + + return cppc_dev->cppc_read(reg, val); +} + +int sbi_cppc_write(unsigned long reg, uint64_t val) +{ + int ret; + + if (!cppc_dev || !cppc_dev->cppc_write) + return SBI_EFAIL; + + /* Check whether register is implemented */ + ret = sbi_cppc_probe(reg); + if (ret <= 0) + return SBI_ERR_NOT_SUPPORTED; + + /* Check whether the register is read-only */ + if (!sbi_cppc_writable(reg)) + return SBI_ERR_DENIED; + + return cppc_dev->cppc_write(reg, val); +} diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c index 19e2029e6c4..acd0f74c301 100644 --- a/lib/sbi/sbi_domain.c +++ b/lib/sbi/sbi_domain.c @@ -11,31 +11,57 @@ #include #include #include +#include #include #include #include #include #include -struct sbi_domain *hartid_to_domain_table[SBI_HARTMASK_MAX_BITS] = { 0 }; -struct sbi_domain *domidx_to_domain_table[SBI_DOMAIN_MAX_INDEX] = { 0 }; +/* + * We allocate an extra element because sbi_domain_for_each() expects + * the array to be null-terminated. + */ +struct sbi_domain *domidx_to_domain_table[SBI_DOMAIN_MAX_INDEX + 1] = { 0 }; static u32 domain_count = 0; static bool domain_finalized = false; -static struct sbi_hartmask root_hmask = { 0 }; - #define ROOT_REGION_MAX 16 static u32 root_memregs_count = 0; -static struct sbi_domain_memregion root_fw_region; -static struct sbi_domain_memregion root_memregs[ROOT_REGION_MAX + 1] = { 0 }; struct sbi_domain root = { .name = "root", - .possible_harts = &root_hmask, - .regions = root_memregs, + .possible_harts = NULL, + .regions = NULL, .system_reset_allowed = true, + .system_suspend_allowed = true, + .fw_region_inited = false, }; +static unsigned long domain_hart_ptr_offset; + +struct sbi_domain *sbi_hartid_to_domain(u32 hartid) +{ + struct sbi_scratch *scratch; + + scratch = sbi_hartid_to_scratch(hartid); + if (!scratch || !domain_hart_ptr_offset) + return NULL; + + return sbi_scratch_read_type(scratch, void *, domain_hart_ptr_offset); +} + +static void update_hartid_to_domain(u32 hartid, struct sbi_domain *dom) +{ + struct sbi_scratch *scratch; + + scratch = sbi_hartid_to_scratch(hartid); + if (!scratch) + return; + + sbi_scratch_write_type(scratch, void *, domain_hart_ptr_offset, dom); +} + bool sbi_domain_is_assigned_hart(const struct sbi_domain *dom, u32 hartid) { if (dom) @@ -64,14 +90,6 @@ ulong sbi_domain_get_assigned_hartmask(const struct sbi_domain *dom, return ret; } -static void domain_memregion_initfw(struct sbi_domain_memregion *reg) -{ - if (!reg) - return; - - sbi_memcpy(reg, &root_fw_region, sizeof(*reg)); -} - void sbi_domain_memregion_init(unsigned long addr, unsigned long size, unsigned long flags, @@ -107,24 +125,34 @@ bool sbi_domain_check_addr(const struct sbi_domain *dom, { bool rmmio, mmio = false; struct sbi_domain_memregion *reg; - unsigned long rstart, rend, rflags, rwx = 0; + unsigned long rstart, rend, rflags, rwx = 0, rrwx = 0; if (!dom) return false; + /* + * Use M_{R/W/X} bits because the SU-bits are at the + * same relative offsets. If the mode is not M, the SU + * bits will fall at same offsets after the shift. + */ if (access_flags & SBI_DOMAIN_READ) - rwx |= SBI_DOMAIN_MEMREGION_READABLE; + rwx |= SBI_DOMAIN_MEMREGION_M_READABLE; + if (access_flags & SBI_DOMAIN_WRITE) - rwx |= SBI_DOMAIN_MEMREGION_WRITEABLE; + rwx |= SBI_DOMAIN_MEMREGION_M_WRITABLE; + if (access_flags & SBI_DOMAIN_EXECUTE) - rwx |= SBI_DOMAIN_MEMREGION_EXECUTABLE; + rwx |= SBI_DOMAIN_MEMREGION_M_EXECUTABLE; + if (access_flags & SBI_DOMAIN_MMIO) mmio = true; sbi_domain_for_each_memregion(dom, reg) { rflags = reg->flags; - if (mode == PRV_M && !(rflags & SBI_DOMAIN_MEMREGION_MMODE)) - continue; + rrwx = (mode == PRV_M ? + (rflags & SBI_DOMAIN_MEMREGION_M_ACCESS_MASK) : + (rflags & SBI_DOMAIN_MEMREGION_SU_ACCESS_MASK) + >> SBI_DOMAIN_MEMREGION_SU_ACCESS_SHIFT); rstart = reg->base; rend = (reg->order < __riscv_xlen) ? @@ -133,7 +161,7 @@ bool sbi_domain_check_addr(const struct sbi_domain *dom, rmmio = (rflags & SBI_DOMAIN_MEMREGION_MMIO) ? true : false; if (mmio != rmmio) return false; - return ((rflags & rwx) == rwx) ? true : false; + return ((rrwx & rwx) == rwx) ? true : false; } } @@ -198,11 +226,48 @@ static bool is_region_before(const struct sbi_domain_memregion *regA, return false; } +static const struct sbi_domain_memregion *find_region( + const struct sbi_domain *dom, + unsigned long addr) +{ + unsigned long rstart, rend; + struct sbi_domain_memregion *reg; + + sbi_domain_for_each_memregion(dom, reg) { + rstart = reg->base; + rend = (reg->order < __riscv_xlen) ? + rstart + ((1UL << reg->order) - 1) : -1UL; + if (rstart <= addr && addr <= rend) + return reg; + } + + return NULL; +} + +static const struct sbi_domain_memregion *find_next_subset_region( + const struct sbi_domain *dom, + const struct sbi_domain_memregion *reg, + unsigned long addr) +{ + struct sbi_domain_memregion *sreg, *ret = NULL; + + sbi_domain_for_each_memregion(dom, sreg) { + if (sreg == reg || (sreg->base <= addr) || + !is_region_subset(sreg, reg)) + continue; + + if (!ret || (sreg->base < ret->base) || + ((sreg->base == ret->base) && (sreg->order < ret->order))) + ret = sreg; + } + + return ret; +} + static int sanitize_domain(const struct sbi_platform *plat, struct sbi_domain *dom) { u32 i, j, count; - bool have_fw_reg; struct sbi_domain_memregion treg, *reg, *reg1; /* Check possible HARTs */ @@ -217,7 +282,7 @@ static int sanitize_domain(const struct sbi_platform *plat, "hart %d\n", __func__, dom->name, i); return SBI_EINVAL; } - }; + } /* Check memory regions */ if (!dom->regions) { @@ -235,17 +300,13 @@ static int sanitize_domain(const struct sbi_platform *plat, } } - /* Count memory regions and check presence of firmware region */ + /* Count memory regions */ count = 0; - have_fw_reg = false; - sbi_domain_for_each_memregion(dom, reg) { - if (reg->order == root_fw_region.order && - reg->base == root_fw_region.base && - reg->flags == root_fw_region.flags) - have_fw_reg = true; + sbi_domain_for_each_memregion(dom, reg) count++; - } - if (!have_fw_reg) { + + /* Check presence of firmware regions */ + if (!dom->fw_region_inited) { sbi_printf("%s: %s does not have firmware region\n", __func__, dom->name); return SBI_EINVAL; @@ -285,7 +346,7 @@ static int sanitize_domain(const struct sbi_platform *plat, /* * Check next mode * - * We only allow next mode to be S-mode or U-mode.so that we can + * We only allow next mode to be S-mode or U-mode, so that we can * protect M-mode context and enforce checks on memory accesses. */ if (dom->next_mode != PRV_S && @@ -295,7 +356,7 @@ static int sanitize_domain(const struct sbi_platform *plat, return SBI_EINVAL; } - /* Check next address and next mode*/ + /* Check next address and next mode */ if (!sbi_domain_check_addr(dom, dom->next_addr, dom->next_mode, SBI_DOMAIN_EXECUTE)) { sbi_printf("%s: %s next booting stage address 0x%lx can't " @@ -306,6 +367,37 @@ static int sanitize_domain(const struct sbi_platform *plat, return 0; } +bool sbi_domain_check_addr_range(const struct sbi_domain *dom, + unsigned long addr, unsigned long size, + unsigned long mode, + unsigned long access_flags) +{ + unsigned long max = addr + size; + const struct sbi_domain_memregion *reg, *sreg; + + if (!dom) + return false; + + while (addr < max) { + reg = find_region(dom, addr); + if (!reg) + return false; + + if (!sbi_domain_check_addr(dom, addr, mode, access_flags)) + return false; + + sreg = find_next_subset_region(dom, reg, addr); + if (sreg) + addr = sreg->base; + else if (reg->order < __riscv_xlen) + addr = reg->base + (1UL << reg->order); + else + break; + } + + return true; +} + void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix) { u32 i, k; @@ -335,15 +427,25 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix) dom->index, i, suffix, rstart, rend); k = 0; - if (reg->flags & SBI_DOMAIN_MEMREGION_MMODE) - sbi_printf("%cM", (k++) ? ',' : '('); + + sbi_printf("M: "); if (reg->flags & SBI_DOMAIN_MEMREGION_MMIO) sbi_printf("%cI", (k++) ? ',' : '('); - if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE) + if (reg->flags & SBI_DOMAIN_MEMREGION_M_READABLE) sbi_printf("%cR", (k++) ? ',' : '('); - if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE) + if (reg->flags & SBI_DOMAIN_MEMREGION_M_WRITABLE) sbi_printf("%cW", (k++) ? ',' : '('); - if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE) + if (reg->flags & SBI_DOMAIN_MEMREGION_M_EXECUTABLE) + sbi_printf("%cX", (k++) ? ',' : '('); + sbi_printf("%s ", (k++) ? ")" : "()"); + + k = 0; + sbi_printf("S/U: "); + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) + sbi_printf("%cR", (k++) ? ',' : '('); + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) + sbi_printf("%cW", (k++) ? ',' : '('); + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE) sbi_printf("%cX", (k++) ? ',' : '('); sbi_printf("%s\n", (k++) ? ")" : "()"); @@ -370,10 +472,13 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix) default: sbi_printf("Unknown\n"); break; - }; + } sbi_printf("Domain%d SysReset %s: %s\n", dom->index, suffix, (dom->system_reset_allowed) ? "yes" : "no"); + + sbi_printf("Domain%d SysSuspend %s: %s\n", + dom->index, suffix, (dom->system_suspend_allowed) ? "yes" : "no"); } void sbi_domain_dump_all(const char *suffix) @@ -437,11 +542,11 @@ int sbi_domain_register(struct sbi_domain *dom, if (!sbi_hartmask_test_hart(i, dom->possible_harts)) continue; - tdom = hartid_to_domain_table[i]; + tdom = sbi_hartid_to_domain(i); if (tdom) sbi_hartmask_clear_hart(i, &tdom->assigned_harts); - hartid_to_domain_table[i] = dom; + update_hartid_to_domain(i, dom); sbi_hartmask_set_hart(i, &dom->assigned_harts); /* @@ -467,8 +572,7 @@ int sbi_domain_root_add_memregion(const struct sbi_domain_memregion *reg) const struct sbi_platform *plat = sbi_platform_thishart_ptr(); /* Sanity checks */ - if (!reg || domain_finalized || - (root.regions != root_memregs) || + if (!reg || domain_finalized || !root.regions || (ROOT_REGION_MAX <= root_memregs_count)) return SBI_EINVAL; @@ -483,10 +587,10 @@ int sbi_domain_root_add_memregion(const struct sbi_domain_memregion *reg) } /* Append the memregion to root memregions */ - nreg = &root_memregs[root_memregs_count]; + nreg = &root.regions[root_memregs_count]; sbi_memcpy(nreg, reg, sizeof(*reg)); root_memregs_count++; - root_memregs[root_memregs_count].order = 0; + root.regions[root_memregs_count].order = 0; /* Sort and optimize root regions */ do { @@ -616,18 +720,69 @@ int sbi_domain_finalize(struct sbi_scratch *scratch, u32 cold_hartid) int sbi_domain_init(struct sbi_scratch *scratch, u32 cold_hartid) { u32 i; + int rc; + struct sbi_hartmask *root_hmask; + struct sbi_domain_memregion *root_memregs; const struct sbi_platform *plat = sbi_platform_ptr(scratch); + if (scratch->fw_rw_offset == 0 || + (scratch->fw_rw_offset & (scratch->fw_rw_offset - 1)) != 0) { + sbi_printf("%s: fw_rw_offset is not a power of 2 (0x%lx)\n", + __func__, scratch->fw_rw_offset); + return SBI_EINVAL; + } + + if ((scratch->fw_start & (scratch->fw_rw_offset - 1)) != 0) { + sbi_printf("%s: fw_start and fw_rw_offset not aligned\n", + __func__); + return SBI_EINVAL; + } + + domain_hart_ptr_offset = sbi_scratch_alloc_type_offset(void *); + if (!domain_hart_ptr_offset) + return SBI_ENOMEM; + + root_memregs = sbi_calloc(sizeof(*root_memregs), ROOT_REGION_MAX + 1); + if (!root_memregs) { + sbi_printf("%s: no memory for root regions\n", __func__); + rc = SBI_ENOMEM; + goto fail_free_domain_hart_ptr_offset; + } + root.regions = root_memregs; + + root_hmask = sbi_zalloc(sizeof(*root_hmask)); + if (!root_hmask) { + sbi_printf("%s: no memory for root hartmask\n", __func__); + rc = SBI_ENOMEM; + goto fail_free_root_memregs; + } + root.possible_harts = root_hmask; + /* Root domain firmware memory region */ - sbi_domain_memregion_init(scratch->fw_start, scratch->fw_size, 0, - &root_fw_region); - domain_memregion_initfw(&root_memregs[root_memregs_count++]); + sbi_domain_memregion_init(scratch->fw_start, scratch->fw_rw_offset, + (SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_EXECUTABLE), + &root_memregs[root_memregs_count++]); + + sbi_domain_memregion_init((scratch->fw_start + scratch->fw_rw_offset), + (scratch->fw_size - scratch->fw_rw_offset), + (SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_WRITABLE), + &root_memregs[root_memregs_count++]); + + root.fw_region_inited = true; - /* Root domain allow everything memory region */ + /* + * Allow SU RWX on rest of the memory region. Since pmp entries + * have implicit priority on index, previous entries will + * deny access to SU on M-mode region. Also, M-mode will not + * have access to SU region while previous entries will allow + * access to M-mode regions. + */ sbi_domain_memregion_init(0, ~0UL, - (SBI_DOMAIN_MEMREGION_READABLE | - SBI_DOMAIN_MEMREGION_WRITEABLE | - SBI_DOMAIN_MEMREGION_EXECUTABLE), + (SBI_DOMAIN_MEMREGION_SU_READABLE | + SBI_DOMAIN_MEMREGION_SU_WRITABLE | + SBI_DOMAIN_MEMREGION_SU_EXECUTABLE), &root_memregs[root_memregs_count++]); /* Root domain memory region end */ @@ -645,8 +800,21 @@ int sbi_domain_init(struct sbi_scratch *scratch, u32 cold_hartid) for (i = 0; i < SBI_HARTMASK_MAX_BITS; i++) { if (sbi_platform_hart_invalid(plat, i)) continue; - sbi_hartmask_set_hart(i, &root_hmask); + sbi_hartmask_set_hart(i, root_hmask); } - return sbi_domain_register(&root, &root_hmask); + /* Finally register the root domain */ + rc = sbi_domain_register(&root, root_hmask); + if (rc) + goto fail_free_root_hmask; + + return 0; + +fail_free_root_hmask: + sbi_free(root_hmask); +fail_free_root_memregs: + sbi_free(root_memregs); +fail_free_domain_hart_ptr_offset: + sbi_scratch_free_offset(domain_hart_ptr_offset); + return rc; } diff --git a/lib/sbi/sbi_ecall.c b/lib/sbi/sbi_ecall.c index 27ce5d49342..3eb4f0addb0 100644 --- a/lib/sbi/sbi_ecall.c +++ b/lib/sbi/sbi_ecall.c @@ -120,7 +120,9 @@ int sbi_ecall_handler(struct sbi_trap_regs *regs) trap.epc = regs->mepc; sbi_trap_redirect(regs, &trap); } else { - if (ret < SBI_LAST_ERR) { + if (ret < SBI_LAST_ERR || + (extension_id != SBI_EXT_0_1_CONSOLE_GETCHAR && + SBI_SUCCESS < ret)) { sbi_printf("%s: Invalid error %d for ext=0x%lx " "func=0x%lx\n", __func__, ret, extension_id, func_id); @@ -152,7 +154,10 @@ int sbi_ecall_init(void) for (i = 0; i < sbi_ecall_exts_size; i++) { ext = sbi_ecall_exts[i]; - ret = sbi_ecall_register_extension(ext); + ret = SBI_ENODEV; + + if (ext->register_extensions) + ret = ext->register_extensions(); if (ret) return ret; } diff --git a/lib/sbi/sbi_ecall_base.c b/lib/sbi/sbi_ecall_base.c index 786d2ac6792..74f05eb26a3 100644 --- a/lib/sbi/sbi_ecall_base.c +++ b/lib/sbi/sbi_ecall_base.c @@ -72,8 +72,16 @@ static int sbi_ecall_base_handler(unsigned long extid, unsigned long funcid, return ret; } +struct sbi_ecall_extension ecall_base; + +static int sbi_ecall_base_register_extensions(void) +{ + return sbi_ecall_register_extension(&ecall_base); +} + struct sbi_ecall_extension ecall_base = { - .extid_start = SBI_EXT_BASE, - .extid_end = SBI_EXT_BASE, - .handle = sbi_ecall_base_handler, + .extid_start = SBI_EXT_BASE, + .extid_end = SBI_EXT_BASE, + .register_extensions = sbi_ecall_base_register_extensions, + .handle = sbi_ecall_base_handler, }; diff --git a/lib/sbi/sbi_ecall_cppc.c b/lib/sbi/sbi_ecall_cppc.c new file mode 100644 index 00000000000..b54d54ec684 --- /dev/null +++ b/lib/sbi/sbi_ecall_cppc.c @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ventana Micro Systems Inc. + * + */ + +#include +#include +#include +#include +#include + +static int sbi_ecall_cppc_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + int ret = 0; + uint64_t temp; + + switch (funcid) { + case SBI_EXT_CPPC_READ: + ret = sbi_cppc_read(regs->a0, &temp); + *out_val = temp; + break; + case SBI_EXT_CPPC_READ_HI: +#if __riscv_xlen == 32 + ret = sbi_cppc_read(regs->a0, &temp); + *out_val = temp >> 32; +#else + *out_val = 0; +#endif + break; + case SBI_EXT_CPPC_WRITE: + ret = sbi_cppc_write(regs->a0, regs->a1); + break; + case SBI_EXT_CPPC_PROBE: + ret = sbi_cppc_probe(regs->a0); + if (ret >= 0) { + *out_val = ret; + ret = 0; + } + break; + default: + ret = SBI_ENOTSUPP; + } + + return ret; +} + +struct sbi_ecall_extension ecall_cppc; + +static int sbi_ecall_cppc_register_extensions(void) +{ + if (!sbi_cppc_get_device()) + return 0; + + return sbi_ecall_register_extension(&ecall_cppc); +} + +struct sbi_ecall_extension ecall_cppc = { + .extid_start = SBI_EXT_CPPC, + .extid_end = SBI_EXT_CPPC, + .register_extensions = sbi_ecall_cppc_register_extensions, + .handle = sbi_ecall_cppc_handler, +}; diff --git a/lib/sbi/sbi_ecall_dbcn.c b/lib/sbi/sbi_ecall_dbcn.c new file mode 100644 index 00000000000..18cd6c8b5cc --- /dev/null +++ b/lib/sbi/sbi_ecall_dbcn.c @@ -0,0 +1,82 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Ventana Micro Systems Inc. + * + * Authors: + * Anup Patel + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int sbi_ecall_dbcn_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + ulong smode = (csr_read(CSR_MSTATUS) & MSTATUS_MPP) >> + MSTATUS_MPP_SHIFT; + + switch (funcid) { + case SBI_EXT_DBCN_CONSOLE_WRITE: + case SBI_EXT_DBCN_CONSOLE_READ: + /* + * On RV32, the M-mode can only access the first 4GB of + * the physical address space because M-mode does not have + * MMU to access full 34-bit physical address space. + * + * Based on above, we simply fail if the upper 32bits of + * the physical address (i.e. a2 register) is non-zero on + * RV32. + * + * Analogously, we fail if the upper 64bit of the + * physical address (i.e. a2 register) is non-zero on + * RV64. + */ + if (regs->a2) + return SBI_ERR_FAILED; + + if (!sbi_domain_check_addr_range(sbi_domain_thishart_ptr(), + regs->a1, regs->a0, smode, + SBI_DOMAIN_READ|SBI_DOMAIN_WRITE)) + return SBI_ERR_INVALID_PARAM; + sbi_hart_map_saddr(regs->a1, regs->a0); + if (funcid == SBI_EXT_DBCN_CONSOLE_WRITE) + *out_val = sbi_nputs((const char *)regs->a1, regs->a0); + else + *out_val = sbi_ngets((char *)regs->a1, regs->a0); + sbi_hart_unmap_saddr(); + return 0; + case SBI_EXT_DBCN_CONSOLE_WRITE_BYTE: + sbi_putc(regs->a0); + return 0; + default: + break; + } + + return SBI_ENOTSUPP; +} + +struct sbi_ecall_extension ecall_dbcn; + +static int sbi_ecall_dbcn_register_extensions(void) +{ + if (!sbi_console_get_device()) + return 0; + + return sbi_ecall_register_extension(&ecall_dbcn); +} + +struct sbi_ecall_extension ecall_dbcn = { + .extid_start = SBI_EXT_DBCN, + .extid_end = SBI_EXT_DBCN, + .register_extensions = sbi_ecall_dbcn_register_extensions, + .handle = sbi_ecall_dbcn_handler, +}; diff --git a/lib/sbi/sbi_ecall_hsm.c b/lib/sbi/sbi_ecall_hsm.c index 703a824493b..20705c39513 100644 --- a/lib/sbi/sbi_ecall_hsm.c +++ b/lib/sbi/sbi_ecall_hsm.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -45,7 +44,8 @@ static int sbi_ecall_hsm_handler(unsigned long extid, unsigned long funcid, break; default: ret = SBI_ENOTSUPP; - }; + } + if (ret >= 0) { *out_val = ret; ret = 0; @@ -54,8 +54,16 @@ static int sbi_ecall_hsm_handler(unsigned long extid, unsigned long funcid, return ret; } +struct sbi_ecall_extension ecall_hsm; + +static int sbi_ecall_hsm_register_extensions(void) +{ + return sbi_ecall_register_extension(&ecall_hsm); +} + struct sbi_ecall_extension ecall_hsm = { - .extid_start = SBI_EXT_HSM, - .extid_end = SBI_EXT_HSM, - .handle = sbi_ecall_hsm_handler, + .extid_start = SBI_EXT_HSM, + .extid_end = SBI_EXT_HSM, + .register_extensions = sbi_ecall_hsm_register_extensions, + .handle = sbi_ecall_hsm_handler, }; diff --git a/lib/sbi/sbi_ecall_ipi.c b/lib/sbi/sbi_ecall_ipi.c index f4797e117a3..a40d6b8cc8a 100644 --- a/lib/sbi/sbi_ecall_ipi.c +++ b/lib/sbi/sbi_ecall_ipi.c @@ -29,8 +29,16 @@ static int sbi_ecall_ipi_handler(unsigned long extid, unsigned long funcid, return ret; } +struct sbi_ecall_extension ecall_ipi; + +static int sbi_ecall_ipi_register_extensions(void) +{ + return sbi_ecall_register_extension(&ecall_ipi); +} + struct sbi_ecall_extension ecall_ipi = { - .extid_start = SBI_EXT_IPI, - .extid_end = SBI_EXT_IPI, - .handle = sbi_ecall_ipi_handler, + .extid_start = SBI_EXT_IPI, + .extid_end = SBI_EXT_IPI, + .register_extensions = sbi_ecall_ipi_register_extensions, + .handle = sbi_ecall_ipi_handler, }; diff --git a/lib/sbi/sbi_ecall_legacy.c b/lib/sbi/sbi_ecall_legacy.c index e20de766d27..99e862e0f19 100644 --- a/lib/sbi/sbi_ecall_legacy.c +++ b/lib/sbi/sbi_ecall_legacy.c @@ -112,13 +112,21 @@ static int sbi_ecall_legacy_handler(unsigned long extid, unsigned long funcid, break; default: ret = SBI_ENOTSUPP; - }; + } return ret; } +struct sbi_ecall_extension ecall_legacy; + +static int sbi_ecall_legacy_register_extensions(void) +{ + return sbi_ecall_register_extension(&ecall_legacy); +} + struct sbi_ecall_extension ecall_legacy = { - .extid_start = SBI_EXT_0_1_SET_TIMER, - .extid_end = SBI_EXT_0_1_SHUTDOWN, - .handle = sbi_ecall_legacy_handler, + .extid_start = SBI_EXT_0_1_SET_TIMER, + .extid_end = SBI_EXT_0_1_SHUTDOWN, + .register_extensions = sbi_ecall_legacy_register_extensions, + .handle = sbi_ecall_legacy_handler, }; diff --git a/lib/sbi/sbi_ecall_pmu.c b/lib/sbi/sbi_ecall_pmu.c index 826c8a89bf0..1d5d512eeed 100644 --- a/lib/sbi/sbi_ecall_pmu.c +++ b/lib/sbi/sbi_ecall_pmu.c @@ -54,6 +54,14 @@ static int sbi_ecall_pmu_handler(unsigned long extid, unsigned long funcid, ret = sbi_pmu_ctr_fw_read(regs->a0, &temp); *out_val = temp; break; + case SBI_EXT_PMU_COUNTER_FW_READ_HI: +#if __riscv_xlen == 32 + ret = sbi_pmu_ctr_fw_read(regs->a0, &temp); + *out_val = temp >> 32; +#else + *out_val = 0; +#endif + break; case SBI_EXT_PMU_COUNTER_START: #if __riscv_xlen == 32 @@ -68,21 +76,21 @@ static int sbi_ecall_pmu_handler(unsigned long extid, unsigned long funcid, break; default: ret = SBI_ENOTSUPP; - }; + } return ret; } -static int sbi_ecall_pmu_probe(unsigned long extid, unsigned long *out_val) +struct sbi_ecall_extension ecall_pmu; + +static int sbi_ecall_pmu_register_extensions(void) { - /* PMU extension is always enabled */ - *out_val = 1; - return 0; + return sbi_ecall_register_extension(&ecall_pmu); } struct sbi_ecall_extension ecall_pmu = { - .extid_start = SBI_EXT_PMU, - .extid_end = SBI_EXT_PMU, - .handle = sbi_ecall_pmu_handler, - .probe = sbi_ecall_pmu_probe, + .extid_start = SBI_EXT_PMU, + .extid_end = SBI_EXT_PMU, + .register_extensions = sbi_ecall_pmu_register_extensions, + .handle = sbi_ecall_pmu_handler, }; diff --git a/lib/sbi/sbi_ecall_rfence.c b/lib/sbi/sbi_ecall_rfence.c index 8f0e3d7bacf..22c66522782 100644 --- a/lib/sbi/sbi_ecall_rfence.c +++ b/lib/sbi/sbi_ecall_rfence.c @@ -74,13 +74,21 @@ static int sbi_ecall_rfence_handler(unsigned long extid, unsigned long funcid, break; default: ret = SBI_ENOTSUPP; - }; + } return ret; } +struct sbi_ecall_extension ecall_rfence; + +static int sbi_ecall_rfence_register_extensions(void) +{ + return sbi_ecall_register_extension(&ecall_rfence); +} + struct sbi_ecall_extension ecall_rfence = { - .extid_start = SBI_EXT_RFENCE, - .extid_end = SBI_EXT_RFENCE, - .handle = sbi_ecall_rfence_handler, + .extid_start = SBI_EXT_RFENCE, + .extid_end = SBI_EXT_RFENCE, + .register_extensions = sbi_ecall_rfence_register_extensions, + .handle = sbi_ecall_rfence_handler, }; diff --git a/lib/sbi/sbi_ecall_srst.c b/lib/sbi/sbi_ecall_srst.c index 93b012ce024..dcd560d22f9 100644 --- a/lib/sbi/sbi_ecall_srst.c +++ b/lib/sbi/sbi_ecall_srst.c @@ -48,28 +48,36 @@ static int sbi_ecall_srst_handler(unsigned long extid, unsigned long funcid, return SBI_ENOTSUPP; } -static int sbi_ecall_srst_probe(unsigned long extid, unsigned long *out_val) +static bool srst_available(void) { - u32 type, count = 0; + u32 type; /* * At least one standard reset types should be supported by * the platform for SBI SRST extension to be usable. */ - for (type = 0; type <= SBI_SRST_RESET_TYPE_LAST; type++) { if (sbi_system_reset_supported(type, SBI_SRST_RESET_REASON_NONE)) - count++; + return true; } - *out_val = (count) ? 1 : 0; - return 0; + return false; +} + +struct sbi_ecall_extension ecall_srst; + +static int sbi_ecall_srst_register_extensions(void) +{ + if (!srst_available()) + return 0; + + return sbi_ecall_register_extension(&ecall_srst); } struct sbi_ecall_extension ecall_srst = { - .extid_start = SBI_EXT_SRST, - .extid_end = SBI_EXT_SRST, - .handle = sbi_ecall_srst_handler, - .probe = sbi_ecall_srst_probe, + .extid_start = SBI_EXT_SRST, + .extid_end = SBI_EXT_SRST, + .register_extensions = sbi_ecall_srst_register_extensions, + .handle = sbi_ecall_srst_handler, }; diff --git a/lib/sbi/sbi_ecall_susp.c b/lib/sbi/sbi_ecall_susp.c new file mode 100644 index 00000000000..2bfd99ae872 --- /dev/null +++ b/lib/sbi/sbi_ecall_susp.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: BSD-2-Clause +#include +#include +#include +#include +#include + +static int sbi_ecall_susp_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + int ret = SBI_ENOTSUPP; + + if (funcid == SBI_EXT_SUSP_SUSPEND) + ret = sbi_system_suspend(regs->a0, regs->a1, regs->a2); + + if (ret >= 0) { + *out_val = ret; + ret = 0; + } + + return ret; +} + +static bool susp_available(void) +{ + u32 type; + + /* + * At least one suspend type should be supported by the + * platform for the SBI SUSP extension to be usable. + */ + for (type = 0; type <= SBI_SUSP_SLEEP_TYPE_LAST; type++) { + if (sbi_system_suspend_supported(type)) + return true; + } + + return false; +} + +struct sbi_ecall_extension ecall_susp; + +static int sbi_ecall_susp_register_extensions(void) +{ + if (!susp_available()) + return 0; + + return sbi_ecall_register_extension(&ecall_susp); +} + +struct sbi_ecall_extension ecall_susp = { + .extid_start = SBI_EXT_SUSP, + .extid_end = SBI_EXT_SUSP, + .register_extensions = sbi_ecall_susp_register_extensions, + .handle = sbi_ecall_susp_handler, +}; diff --git a/lib/sbi/sbi_ecall_time.c b/lib/sbi/sbi_ecall_time.c index 668cb17680c..e79196f77ad 100644 --- a/lib/sbi/sbi_ecall_time.c +++ b/lib/sbi/sbi_ecall_time.c @@ -33,8 +33,16 @@ static int sbi_ecall_time_handler(unsigned long extid, unsigned long funcid, return ret; } +struct sbi_ecall_extension ecall_time; + +static int sbi_ecall_time_register_extensions(void) +{ + return sbi_ecall_register_extension(&ecall_time); +} + struct sbi_ecall_extension ecall_time = { - .extid_start = SBI_EXT_TIME, - .extid_end = SBI_EXT_TIME, - .handle = sbi_ecall_time_handler, + .extid_start = SBI_EXT_TIME, + .extid_end = SBI_EXT_TIME, + .register_extensions = sbi_ecall_time_register_extensions, + .handle = sbi_ecall_time_handler, }; diff --git a/lib/sbi/sbi_ecall_vendor.c b/lib/sbi/sbi_ecall_vendor.c index 9252829633a..700f475f0a8 100644 --- a/lib/sbi/sbi_ecall_vendor.c +++ b/lib/sbi/sbi_ecall_vendor.c @@ -13,13 +13,13 @@ #include #include #include +#include -static int sbi_ecall_vendor_probe(unsigned long extid, - unsigned long *out_val) +static inline unsigned long sbi_ecall_vendor_id(void) { - *out_val = sbi_platform_vendor_ext_check(sbi_platform_thishart_ptr(), - extid); - return 0; + return SBI_EXT_VENDOR_START + + (csr_read(CSR_MVENDORID) & + (SBI_EXT_VENDOR_END - SBI_EXT_VENDOR_START)); } static int sbi_ecall_vendor_handler(unsigned long extid, unsigned long funcid, @@ -28,13 +28,28 @@ static int sbi_ecall_vendor_handler(unsigned long extid, unsigned long funcid, struct sbi_trap_info *out_trap) { return sbi_platform_vendor_ext_provider(sbi_platform_thishart_ptr(), - extid, funcid, regs, + funcid, regs, out_val, out_trap); } +struct sbi_ecall_extension ecall_vendor; + +static int sbi_ecall_vendor_register_extensions(void) +{ + unsigned long extid = sbi_ecall_vendor_id(); + + if (!sbi_platform_vendor_ext_check(sbi_platform_thishart_ptr())) + return 0; + + ecall_vendor.extid_start = extid; + ecall_vendor.extid_end = extid; + + return sbi_ecall_register_extension(&ecall_vendor); +} + struct sbi_ecall_extension ecall_vendor = { - .extid_start = SBI_EXT_VENDOR_START, - .extid_end = SBI_EXT_VENDOR_END, - .probe = sbi_ecall_vendor_probe, - .handle = sbi_ecall_vendor_handler, + .extid_start = SBI_EXT_VENDOR_START, + .extid_end = SBI_EXT_VENDOR_END, + .register_extensions = sbi_ecall_vendor_register_extensions, + .handle = sbi_ecall_vendor_handler, }; diff --git a/lib/sbi/sbi_emulate_csr.c b/lib/sbi/sbi_emulate_csr.c index 64bebd8da7a..da811653661 100644 --- a/lib/sbi/sbi_emulate_csr.c +++ b/lib/sbi/sbi_emulate_csr.c @@ -149,7 +149,7 @@ int sbi_emulate_csr_read(int csr_num, struct sbi_trap_regs *regs, default: ret = SBI_ENOTSUPP; break; - }; + } if (ret) sbi_dprintf("%s: hartid%d: invalid csr_num=0x%x\n", @@ -187,7 +187,7 @@ int sbi_emulate_csr_write(int csr_num, struct sbi_trap_regs *regs, default: ret = SBI_ENOTSUPP; break; - }; + } if (ret) sbi_dprintf("%s: hartid%d: invalid csr_num=0x%x\n", diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 5447c523987..aadb53c1dd3 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -90,6 +90,7 @@ static void mstatus_init(struct sbi_scratch *scratch) mstateen_val |= ((uint64_t)csr_read(CSR_MSTATEEN0H)) << 32; #endif mstateen_val |= SMSTATEEN_STATEN; + mstateen_val |= SMSTATEEN0_CONTEXT; mstateen_val |= SMSTATEEN0_HSENVCFG; if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMAIA)) @@ -283,11 +284,117 @@ unsigned int sbi_hart_mhpm_bits(struct sbi_scratch *scratch) return hfeatures->mhpm_bits; } +/* + * Returns Smepmp flags for a given domain and region based on permissions. + */ +unsigned int sbi_hart_get_smepmp_flags(struct sbi_scratch *scratch, + struct sbi_domain *dom, + struct sbi_domain_memregion *reg) +{ + unsigned int pmp_flags = 0; + + if (SBI_DOMAIN_MEMREGION_IS_SHARED(reg->flags)) { + /* Read only for both M and SU modes */ + if (SBI_DOMAIN_MEMREGION_IS_SUR_MR(reg->flags)) + pmp_flags = (PMP_R | PMP_W | PMP_X); + + /* Execute for SU but Read/Execute for M mode */ + else if (SBI_DOMAIN_MEMREGION_IS_SUX_MRX(reg->flags)) + /* locked region */ + pmp_flags = (PMP_L | PMP_W | PMP_X); + + /* Execute only for both M and SU modes */ + else if (SBI_DOMAIN_MEMREGION_IS_SUX_MX(reg->flags)) + pmp_flags = (PMP_L | PMP_W); + + /* Read/Write for both M and SU modes */ + else if (SBI_DOMAIN_MEMREGION_IS_SURW_MRW(reg->flags)) + pmp_flags = (PMP_W | PMP_X); + + /* Read only for SU mode but Read/Write for M mode */ + else if (SBI_DOMAIN_MEMREGION_IS_SUR_MRW(reg->flags)) + pmp_flags = (PMP_W); + } else if (SBI_DOMAIN_MEMREGION_M_ONLY_ACCESS(reg->flags)) { + /* + * When smepmp is supported and used, M region cannot have RWX + * permissions on any region. + */ + if ((reg->flags & SBI_DOMAIN_MEMREGION_M_ACCESS_MASK) + == SBI_DOMAIN_MEMREGION_M_RWX) { + sbi_printf("%s: M-mode only regions cannot have" + "RWX permissions\n", __func__); + return 0; + } + + /* M-mode only access regions are always locked */ + pmp_flags |= PMP_L; + + if (reg->flags & SBI_DOMAIN_MEMREGION_M_READABLE) + pmp_flags |= PMP_R; + if (reg->flags & SBI_DOMAIN_MEMREGION_M_WRITABLE) + pmp_flags |= PMP_W; + if (reg->flags & SBI_DOMAIN_MEMREGION_M_EXECUTABLE) + pmp_flags |= PMP_X; + } else if (SBI_DOMAIN_MEMREGION_SU_ONLY_ACCESS(reg->flags)) { + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) + pmp_flags |= PMP_R; + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) + pmp_flags |= PMP_W; + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE) + pmp_flags |= PMP_X; + } + + return pmp_flags; +} + +int sbi_hart_map_saddr(unsigned long addr, unsigned long size) +{ + /* shared R/W access for M and S/U mode */ + unsigned int pmp_flags = (PMP_W | PMP_X); + unsigned long order, base = 0; + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + /* If Smepmp is not supported no special mapping is required */ + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMEPMP)) + return SBI_OK; + + if (is_pmp_entry_mapped(SBI_SMEPMP_RESV_ENTRY)) + return SBI_ENOSPC; + + for (order = log2roundup(size) ; order <= __riscv_xlen; order++) { + if (order < __riscv_xlen) { + base = addr & ~((1UL << order) - 1UL); + if ((base <= addr) && + (addr < (base + (1UL << order))) && + (base <= (addr + size - 1UL)) && + ((addr + size - 1UL) < (base + (1UL << order)))) + break; + } else { + return SBI_EFAIL; + } + } + + pmp_set(SBI_SMEPMP_RESV_ENTRY, pmp_flags, base, order); + + return SBI_OK; +} + +int sbi_hart_unmap_saddr(void) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMEPMP)) + return SBI_OK; + + return pmp_disable(SBI_SMEPMP_RESV_ENTRY); +} + int sbi_hart_pmp_configure(struct sbi_scratch *scratch) { struct sbi_domain_memregion *reg; struct sbi_domain *dom = sbi_domain_thishart_ptr(); - unsigned int pmp_idx = 0, pmp_flags, pmp_bits, pmp_gran_log2; + unsigned int pmp_idx = 0; + unsigned int pmp_flags, pmp_bits, pmp_gran_log2; unsigned int pmp_count = sbi_hart_pmp_count(scratch); unsigned long pmp_addr = 0, pmp_addr_max = 0; @@ -298,30 +405,68 @@ int sbi_hart_pmp_configure(struct sbi_scratch *scratch) pmp_bits = sbi_hart_pmp_addrbits(scratch) - 1; pmp_addr_max = (1UL << pmp_bits) | ((1UL << pmp_bits) - 1); + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMEPMP)) { + /* Reserve first entry for dynamic shared mappings */ + pmp_idx = SBI_SMEPMP_RESV_ENTRY + 1; + + /* + * Set the RLB and clear MML so that, we can write to + * entries without enforcement even if some entries + * are locked. + */ + csr_set(CSR_MSECCFG, MSECCFG_RLB); + csr_clear(CSR_MSECCFG, MSECCFG_MML); + + /* Disable the reserved entry */ + pmp_disable(SBI_SMEPMP_RESV_ENTRY); + } + sbi_domain_for_each_memregion(dom, reg) { if (pmp_count <= pmp_idx) break; pmp_flags = 0; - if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE) - pmp_flags |= PMP_R; - if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE) - pmp_flags |= PMP_W; - if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE) - pmp_flags |= PMP_X; - if (reg->flags & SBI_DOMAIN_MEMREGION_MMODE) - pmp_flags |= PMP_L; + + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMEPMP)) { + pmp_flags = sbi_hart_get_smepmp_flags(scratch, dom, reg); + + if (pmp_flags == 0) + return 0; + } else { + /* + * If permissions are to be enforced for all modes on + * this region, the lock bit should be set. + */ + if (reg->flags & SBI_DOMAIN_MEMREGION_ENF_PERMISSIONS) + pmp_flags |= PMP_L; + + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) + pmp_flags |= PMP_R; + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) + pmp_flags |= PMP_W; + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE) + pmp_flags |= PMP_X; + } pmp_addr = reg->base >> PMP_SHIFT; - if (pmp_gran_log2 <= reg->order && pmp_addr < pmp_addr_max) + if (pmp_gran_log2 <= reg->order && pmp_addr < pmp_addr_max) { pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order); - else { - sbi_printf("Can not configure pmp for domain %s", dom->name); - sbi_printf(" because memory region address %lx or size %lx is not in range\n", - reg->base, reg->order); + } else { + sbi_printf("Can not configure pmp for domain %s because" + " memory region address 0x%lx or size 0x%lx " + "is not in range.\n", dom->name, reg->base, + reg->order); } } + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMEPMP)) { + /* + * All entries are programmed. Enable MML bit. + * Keep the RLB bit so that dynamic mappings can be done. + */ + csr_set(CSR_MSECCFG, (MSECCFG_RLB | MSECCFG_MML)); + } + /* * As per section 3.7.2 of privileged specification v1.12, * virtual address translations can be speculatively performed @@ -431,20 +576,29 @@ static inline char *sbi_hart_extension_id2string(int ext) char *estr = NULL; switch (ext) { - case SBI_HART_EXT_SSCOFPMF: - estr = "sscofpmf"; - break; - case SBI_HART_EXT_TIME: - estr = "time"; - break; case SBI_HART_EXT_SMAIA: estr = "smaia"; break; + case SBI_HART_EXT_SMSTATEEN: + estr = "smstateen"; + break; + case SBI_HART_EXT_SSCOFPMF: + estr = "sscofpmf"; + break; case SBI_HART_EXT_SSTC: estr = "sstc"; break; - case SBI_HART_EXT_SMSTATEEN: - estr = "smstateen"; + case SBI_HART_EXT_ZICNTR: + estr = "zicntr"; + break; + case SBI_HART_EXT_ZIHPM: + estr = "zihpm"; + break; + case SBI_HART_EXT_SMEPMP: + estr = "smepmp"; + break; + case SBI_HART_EXT_SMCNTRPMF: + estr = "smcntrpmf"; break; default: break; @@ -517,7 +671,7 @@ static unsigned long hart_pmp_get_allowed_addr(void) return val; } -static int hart_pmu_get_allowed_bits(void) +static int hart_mhpm_get_allowed_bits(void) { unsigned long val = ~(0UL); struct sbi_trap_info trap = {0}; @@ -618,7 +772,7 @@ static int hart_detect_features(struct sbi_scratch *scratch) /* Detect number of MHPM counters */ __check_csr(CSR_MHPMCOUNTER3, 0, 1UL, mhpm_count, __mhpm_skip); - hfeatures->mhpm_bits = hart_pmu_get_allowed_bits(); + hfeatures->mhpm_bits = hart_mhpm_get_allowed_bits(); __check_csr_4(CSR_MHPMCOUNTER4, 0, 1UL, mhpm_count, __mhpm_skip); __check_csr_8(CSR_MHPMCOUNTER8, 0, 1UL, mhpm_count, __mhpm_skip); @@ -657,7 +811,7 @@ static int hart_detect_features(struct sbi_scratch *scratch) hfeatures->priv_version = SBI_HART_PRIV_VER_1_12; /* Counter overflow/filtering is not useful without mcounter/inhibit */ - if (hfeatures->priv_version >= SBI_HART_PRIV_VER_1_12) { + if (hfeatures->priv_version >= SBI_HART_PRIV_VER_1_11) { /* Detect if hart supports sscofpmf */ csr_read_allowed(CSR_SCOUNTOVF, (unsigned long)&trap); if (!trap.cause) @@ -669,7 +823,7 @@ static int hart_detect_features(struct sbi_scratch *scratch) csr_read_allowed(CSR_TIME, (unsigned long)&trap); if (!trap.cause) __sbi_hart_update_extension(hfeatures, - SBI_HART_EXT_TIME, true); + SBI_HART_EXT_ZICNTR, true); /* Detect if hart has AIA local interrupt CSRs */ csr_read_allowed(CSR_MTOPI, (unsigned long)&trap); @@ -693,12 +847,25 @@ static int hart_detect_features(struct sbi_scratch *scratch) SBI_HART_EXT_SMSTATEEN, true); } + /* Detect if hart supports smcntrpmf */ + if (hfeatures->priv_version >= SBI_HART_PRIV_VER_1_12) { + csr_read_allowed(CSR_MCYCLECFG, (unsigned long)&trap); + if (!trap.cause) + __sbi_hart_update_extension(hfeatures, + SBI_HART_EXT_SMCNTRPMF, true); + } + /* Let platform populate extensions */ rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr(), hfeatures); if (rc) return rc; + /* Extensions implied by other extensions and features */ + if (hfeatures->mhpm_count) + __sbi_hart_update_extension(hfeatures, + SBI_HART_EXT_ZIHPM, true); + /* Mark hart feature detection done */ hfeatures->detected = true; @@ -726,6 +893,12 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot) { int rc; + /* + * Clear mip CSR before proceeding with init to avoid any spurious + * external interrupts in S-mode. + */ + csr_write(CSR_MIP, 0); + if (cold_boot) { if (misa_extension('H')) sbi_hart_expected_trap = &__sbi_expected_trap_hext; diff --git a/lib/sbi/sbi_heap.c b/lib/sbi/sbi_heap.c new file mode 100644 index 00000000000..698c3775d99 --- /dev/null +++ b/lib/sbi/sbi_heap.c @@ -0,0 +1,206 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ventana Micro Systems Inc. + * + * Authors: + * Anup Patel + */ + +#include +#include +#include +#include +#include +#include + +/* Alignment of heap base address and size */ +#define HEAP_BASE_ALIGN 1024 +/* Minimum size and alignment of heap allocations */ +#define HEAP_ALLOC_ALIGN 64 +#define HEAP_HOUSEKEEPING_FACTOR 16 + +struct heap_node { + struct sbi_dlist head; + unsigned long addr; + unsigned long size; +}; + +struct heap_control { + spinlock_t lock; + unsigned long base; + unsigned long size; + unsigned long hkbase; + unsigned long hksize; + struct sbi_dlist free_node_list; + struct sbi_dlist free_space_list; + struct sbi_dlist used_space_list; +}; + +static struct heap_control hpctrl; + +void *sbi_malloc(size_t size) +{ + void *ret = NULL; + struct heap_node *n, *np; + + if (!size) + return NULL; + + size += HEAP_ALLOC_ALIGN - 1; + size &= ~((unsigned long)HEAP_ALLOC_ALIGN - 1); + + spin_lock(&hpctrl.lock); + + np = NULL; + sbi_list_for_each_entry(n, &hpctrl.free_space_list, head) { + if (size <= n->size) { + np = n; + break; + } + } + if (np) { + if ((size < np->size) && + !sbi_list_empty(&hpctrl.free_node_list)) { + n = sbi_list_first_entry(&hpctrl.free_node_list, + struct heap_node, head); + sbi_list_del(&n->head); + n->addr = np->addr + np->size - size; + n->size = size; + np->size -= size; + sbi_list_add_tail(&n->head, &hpctrl.used_space_list); + ret = (void *)n->addr; + } else if (size == np->size) { + sbi_list_del(&np->head); + sbi_list_add_tail(&np->head, &hpctrl.used_space_list); + ret = (void *)np->addr; + } + } + + spin_unlock(&hpctrl.lock); + + return ret; +} + +void *sbi_zalloc(size_t size) +{ + void *ret = sbi_malloc(size); + + if (ret) + sbi_memset(ret, 0, size); + return ret; +} + +void sbi_free(void *ptr) +{ + struct heap_node *n, *np; + + if (!ptr) + return; + + spin_lock(&hpctrl.lock); + + np = NULL; + sbi_list_for_each_entry(n, &hpctrl.used_space_list, head) { + if ((n->addr <= (unsigned long)ptr) && + ((unsigned long)ptr < (n->addr + n->size))) { + np = n; + break; + } + } + if (!np) { + spin_unlock(&hpctrl.lock); + return; + } + + sbi_list_del(&np->head); + + sbi_list_for_each_entry(n, &hpctrl.free_space_list, head) { + if ((np->addr + np->size) == n->addr) { + n->addr = np->addr; + n->size += np->size; + sbi_list_add_tail(&np->head, &hpctrl.free_node_list); + np = NULL; + break; + } else if (np->addr == (n->addr + n->size)) { + n->size += np->size; + sbi_list_add_tail(&np->head, &hpctrl.free_node_list); + np = NULL; + break; + } else if ((n->addr + n->size) < np->addr) { + sbi_list_add(&np->head, &n->head); + np = NULL; + break; + } + } + if (np) + sbi_list_add_tail(&np->head, &hpctrl.free_space_list); + + spin_unlock(&hpctrl.lock); +} + +unsigned long sbi_heap_free_space(void) +{ + struct heap_node *n; + unsigned long ret = 0; + + spin_lock(&hpctrl.lock); + sbi_list_for_each_entry(n, &hpctrl.free_space_list, head) + ret += n->size; + spin_unlock(&hpctrl.lock); + + return ret; +} + +unsigned long sbi_heap_used_space(void) +{ + return hpctrl.size - hpctrl.hksize - sbi_heap_free_space(); +} + +unsigned long sbi_heap_reserved_space(void) +{ + return hpctrl.hksize; +} + +int sbi_heap_init(struct sbi_scratch *scratch) +{ + unsigned long i; + struct heap_node *n; + + /* Sanity checks on heap offset and size */ + if (!scratch->fw_heap_size || + (scratch->fw_heap_size & (HEAP_BASE_ALIGN - 1)) || + (scratch->fw_heap_offset < scratch->fw_rw_offset) || + (scratch->fw_size < (scratch->fw_heap_offset + scratch->fw_heap_size)) || + (scratch->fw_heap_offset & (HEAP_BASE_ALIGN - 1))) + return SBI_EINVAL; + + /* Initialize heap control */ + SPIN_LOCK_INIT(hpctrl.lock); + hpctrl.base = scratch->fw_start + scratch->fw_heap_offset; + hpctrl.size = scratch->fw_heap_size; + hpctrl.hkbase = hpctrl.base; + hpctrl.hksize = hpctrl.size / HEAP_HOUSEKEEPING_FACTOR; + hpctrl.hksize &= ~((unsigned long)HEAP_BASE_ALIGN - 1); + SBI_INIT_LIST_HEAD(&hpctrl.free_node_list); + SBI_INIT_LIST_HEAD(&hpctrl.free_space_list); + SBI_INIT_LIST_HEAD(&hpctrl.used_space_list); + + /* Prepare free node list */ + for (i = 0; i < (hpctrl.hksize / sizeof(*n)); i++) { + n = (struct heap_node *)(hpctrl.hkbase + (sizeof(*n) * i)); + SBI_INIT_LIST_HEAD(&n->head); + n->addr = n->size = 0; + sbi_list_add_tail(&n->head, &hpctrl.free_node_list); + } + + /* Prepare free space list */ + n = sbi_list_first_entry(&hpctrl.free_node_list, + struct heap_node, head); + sbi_list_del(&n->head); + n->addr = hpctrl.hkbase + hpctrl.hksize; + n->size = hpctrl.size - hpctrl.hksize; + sbi_list_add_tail(&n->head, &hpctrl.free_space_list); + + return 0; +} diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c index 836008fc92d..f870ca72baf 100644 --- a/lib/sbi/sbi_hsm.c +++ b/lib/sbi/sbi_hsm.c @@ -26,6 +26,15 @@ #include #include +#define __sbi_hsm_hart_change_state(hdata, oldstate, newstate) \ +({ \ + long state = atomic_cmpxchg(&(hdata)->state, oldstate, newstate); \ + if (state != (oldstate)) \ + sbi_printf("%s: ERR: The hart is in invalid state [%lu]\n", \ + __func__, state); \ + state == (oldstate); \ +}) + static const struct sbi_hsm_device *hsm_dev = NULL; static unsigned long hart_data_offset; @@ -35,9 +44,18 @@ struct sbi_hsm_data { unsigned long suspend_type; unsigned long saved_mie; unsigned long saved_mip; + atomic_t start_ticket; }; -static inline int __sbi_hsm_hart_get_state(u32 hartid) +bool sbi_hsm_hart_change_state(struct sbi_scratch *scratch, long oldstate, + long newstate) +{ + struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, + hart_data_offset); + return __sbi_hsm_hart_change_state(hdata, oldstate, newstate); +} + +int __sbi_hsm_hart_get_state(u32 hartid) { struct sbi_hsm_data *hdata; struct sbi_scratch *scratch; @@ -58,6 +76,32 @@ int sbi_hsm_hart_get_state(const struct sbi_domain *dom, u32 hartid) return __sbi_hsm_hart_get_state(hartid); } +/* + * Try to acquire the ticket for the given target hart to make sure only + * one hart prepares the start of the target hart. + * Returns true if the ticket has been acquired, false otherwise. + * + * The function has "acquire" semantics: no memory operations following it + * in the current hart can be seen before it by other harts. + * atomic_cmpxchg() provides the memory barriers needed for that. + */ +static bool hsm_start_ticket_acquire(struct sbi_hsm_data *hdata) +{ + return (atomic_cmpxchg(&hdata->start_ticket, 0, 1) == 0); +} + +/* + * Release the ticket for the given target hart. + * + * The function has "release" semantics: no memory operations preceding it + * in the current hart can be seen after it by other harts. + */ +static void hsm_start_ticket_release(struct sbi_hsm_data *hdata) +{ + RISCV_FENCE(rw, w); + atomic_write(&hdata->start_ticket, 0); +} + /** * Get ulong HART mask for given HART base ID * @param dom the domain to be used for output HART mask @@ -93,16 +137,25 @@ int sbi_hsm_hart_interruptible_mask(const struct sbi_domain *dom, return 0; } -void sbi_hsm_prepare_next_jump(struct sbi_scratch *scratch, u32 hartid) +void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch, + u32 hartid) { - u32 oldstate; + unsigned long next_arg1; + unsigned long next_addr; + unsigned long next_mode; struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, hart_data_offset); - oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_START_PENDING, - SBI_HSM_STATE_STARTED); - if (oldstate != SBI_HSM_STATE_START_PENDING) + if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_START_PENDING, + SBI_HSM_STATE_STARTED)) sbi_hart_hang(); + + next_arg1 = scratch->next_arg1; + next_addr = scratch->next_addr; + next_mode = scratch->next_mode; + hsm_start_ticket_release(hdata); + + sbi_hart_switch_mode(hartid, next_arg1, next_addr, next_mode, false); } static void sbi_hsm_hart_wait(struct sbi_scratch *scratch, u32 hartid) @@ -116,10 +169,10 @@ static void sbi_hsm_hart_wait(struct sbi_scratch *scratch, u32 hartid) /* Set MSIE and MEIE bits to receive IPI */ csr_set(CSR_MIE, MIP_MSIP | MIP_MEIP); - /* Wait for hart_add call*/ + /* Wait for state transition requested by sbi_hsm_hart_start() */ while (atomic_read(&hdata->state) != SBI_HSM_STATE_START_PENDING) { wfi(); - }; + } /* Restore MIE CSR */ csr_write(CSR_MIE, saved_mie); @@ -207,6 +260,7 @@ int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot) (i == hartid) ? SBI_HSM_STATE_START_PENDING : SBI_HSM_STATE_STOPPED); + ATOMIC_INIT(&hdata->start_ticket, 0); } } else { sbi_hsm_hart_wait(scratch, hartid); @@ -217,20 +271,17 @@ int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot) void __noreturn sbi_hsm_exit(struct sbi_scratch *scratch) { - u32 hstate; struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, hart_data_offset); void (*jump_warmboot)(void) = (void (*)(void))scratch->warmboot_addr; - hstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_STOP_PENDING, - SBI_HSM_STATE_STOPPED); - if (hstate != SBI_HSM_STATE_STOP_PENDING) + if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_STOP_PENDING, + SBI_HSM_STATE_STOPPED)) goto fail_exit; if (hsm_device_has_hart_hotplug()) { - hsm_device_hart_stop(); - /* It should never reach here */ - goto fail_exit; + if (hsm_device_hart_stop() != SBI_ENOTSUPP) + goto fail_exit; } /** @@ -248,12 +299,13 @@ void __noreturn sbi_hsm_exit(struct sbi_scratch *scratch) int sbi_hsm_hart_start(struct sbi_scratch *scratch, const struct sbi_domain *dom, - u32 hartid, ulong saddr, ulong smode, ulong priv) + u32 hartid, ulong saddr, ulong smode, ulong arg1) { - unsigned long init_count; + unsigned long init_count, entry_count; unsigned int hstate; struct sbi_scratch *rscratch; struct sbi_hsm_data *hdata; + int rc; /* For now, we only allow start mode to be S-mode or U-mode. */ if (smode != PRV_S && smode != PRV_U) @@ -267,39 +319,55 @@ int sbi_hsm_hart_start(struct sbi_scratch *scratch, rscratch = sbi_hartid_to_scratch(hartid); if (!rscratch) return SBI_EINVAL; + hdata = sbi_scratch_offset_ptr(rscratch, hart_data_offset); + if (!hsm_start_ticket_acquire(hdata)) + return SBI_EINVAL; + + init_count = sbi_init_count(hartid); + entry_count = sbi_entry_count(hartid); + + rscratch->next_arg1 = arg1; + rscratch->next_addr = saddr; + rscratch->next_mode = smode; + + /* + * atomic_cmpxchg() is an implicit barrier. It makes sure that + * other harts see reading of init_count and writing to *rscratch + * before hdata->state is set to SBI_HSM_STATE_START_PENDING. + */ hstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_STOPPED, SBI_HSM_STATE_START_PENDING); - if (hstate == SBI_HSM_STATE_STARTED) - return SBI_EALREADY; + if (hstate == SBI_HSM_STATE_STARTED) { + rc = SBI_EALREADY; + goto err; + } /** * if a hart is already transition to start or stop, another start call * is considered as invalid request. */ - if (hstate != SBI_HSM_STATE_STOPPED) - return SBI_EINVAL; - - init_count = sbi_init_count(hartid); - rscratch->next_arg1 = priv; - rscratch->next_addr = saddr; - rscratch->next_mode = smode; + if (hstate != SBI_HSM_STATE_STOPPED) { + rc = SBI_EINVAL; + goto err; + } - if (hsm_device_has_hart_hotplug() || + if ((hsm_device_has_hart_hotplug() && (entry_count == init_count)) || (hsm_device_has_hart_secondary_boot() && !init_count)) { - return hsm_device_hart_start(hartid, scratch->warmboot_addr); + rc = hsm_device_hart_start(hartid, scratch->warmboot_addr); } else { - int rc = sbi_ipi_raw_send(hartid); - if (rc) - return rc; + rc = sbi_ipi_raw_send(hartid); } - return 0; + if (!rc) + return 0; +err: + hsm_start_ticket_release(hdata); + return rc; } int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow) { - int oldstate; const struct sbi_domain *dom = sbi_domain_thishart_ptr(); struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, hart_data_offset); @@ -307,13 +375,9 @@ int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow) if (!dom) return SBI_EFAIL; - oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_STARTED, - SBI_HSM_STATE_STOP_PENDING); - if (oldstate != SBI_HSM_STATE_STARTED) { - sbi_printf("%s: ERR: The hart is in invalid state [%u]\n", - __func__, oldstate); + if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_STARTED, + SBI_HSM_STATE_STOP_PENDING)) return SBI_EFAIL; - } if (exitnow) sbi_exit(scratch); @@ -329,7 +393,7 @@ static int __sbi_hsm_suspend_default(struct sbi_scratch *scratch) return 0; } -static void __sbi_hsm_suspend_non_ret_save(struct sbi_scratch *scratch) +void __sbi_hsm_suspend_non_ret_save(struct sbi_scratch *scratch) { struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, hart_data_offset); @@ -358,62 +422,55 @@ static void __sbi_hsm_suspend_non_ret_restore(struct sbi_scratch *scratch) hart_data_offset); csr_write(CSR_MIE, hdata->saved_mie); - csr_write(CSR_MIP, (hdata->saved_mip & (MIP_SSIP | MIP_STIP))); + csr_set(CSR_MIP, (hdata->saved_mip & (MIP_SSIP | MIP_STIP))); } void sbi_hsm_hart_resume_start(struct sbi_scratch *scratch) { - int oldstate; struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, hart_data_offset); /* If current HART was SUSPENDED then set RESUME_PENDING state */ - oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_SUSPENDED, - SBI_HSM_STATE_RESUME_PENDING); - if (oldstate != SBI_HSM_STATE_SUSPENDED) { - sbi_printf("%s: ERR: The hart is in invalid state [%u]\n", - __func__, oldstate); + if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_SUSPENDED, + SBI_HSM_STATE_RESUME_PENDING)) sbi_hart_hang(); - } hsm_device_hart_resume(); } -void sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch) +void __noreturn sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch, + u32 hartid) { - u32 oldstate; struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, hart_data_offset); /* If current HART was RESUME_PENDING then set STARTED state */ - oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_RESUME_PENDING, - SBI_HSM_STATE_STARTED); - if (oldstate != SBI_HSM_STATE_RESUME_PENDING) { - sbi_printf("%s: ERR: The hart is in invalid state [%u]\n", - __func__, oldstate); + if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_RESUME_PENDING, + SBI_HSM_STATE_STARTED)) sbi_hart_hang(); - } /* * Restore some of the M-mode CSRs which we are re-configured by * the warm-boot sequence. */ __sbi_hsm_suspend_non_ret_restore(scratch); + + sbi_hart_switch_mode(hartid, scratch->next_arg1, + scratch->next_addr, + scratch->next_mode, false); } int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type, - ulong raddr, ulong rmode, ulong priv) + ulong raddr, ulong rmode, ulong arg1) { - int oldstate, ret; + int ret; const struct sbi_domain *dom = sbi_domain_thishart_ptr(); struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, hart_data_offset); - /* For now, we only allow suspend from S-mode or U-mode. */ - /* Sanity check on domain assigned to current HART */ if (!dom) - return SBI_EINVAL; + return SBI_EFAIL; /* Sanity check on suspend type */ if (SBI_HSM_SUSPEND_RET_DEFAULT < suspend_type && @@ -425,27 +482,26 @@ int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type, /* Additional sanity check for non-retentive suspend */ if (suspend_type & SBI_HSM_SUSP_NON_RET_BIT) { + /* + * For now, we only allow non-retentive suspend from + * S-mode or U-mode. + */ if (rmode != PRV_S && rmode != PRV_U) - return SBI_EINVAL; + return SBI_EFAIL; if (dom && !sbi_domain_check_addr(dom, raddr, rmode, SBI_DOMAIN_EXECUTE)) return SBI_EINVALID_ADDR; } /* Save the resume address and resume mode */ - scratch->next_arg1 = priv; + scratch->next_arg1 = arg1; scratch->next_addr = raddr; scratch->next_mode = rmode; /* Directly move from STARTED to SUSPENDED state */ - oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_STARTED, - SBI_HSM_STATE_SUSPENDED); - if (oldstate != SBI_HSM_STATE_STARTED) { - sbi_printf("%s: ERR: The hart is in invalid state [%u]\n", - __func__, oldstate); - ret = SBI_EDENIED; - goto fail_restore_state; - } + if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_STARTED, + SBI_HSM_STATE_SUSPENDED)) + return SBI_EFAIL; /* Save the suspend type */ hdata->suspend_type = suspend_type; @@ -480,18 +536,13 @@ int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type, jump_warmboot(); } -fail_restore_state: /* * We might have successfully resumed from retentive suspend * or suspend failed. In both cases, we restore state of hart. */ - oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_SUSPENDED, - SBI_HSM_STATE_STARTED); - if (oldstate != SBI_HSM_STATE_SUSPENDED) { - sbi_printf("%s: ERR: The hart is in invalid state [%u]\n", - __func__, oldstate); + if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_SUSPENDED, + SBI_HSM_STATE_STARTED)) sbi_hart_hang(); - } return ret; } diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c index 9691bce930f..2be47575a36 100644 --- a/lib/sbi/sbi_illegal_insn.c +++ b/lib/sbi/sbi_illegal_insn.c @@ -90,7 +90,7 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs) break; default: return truly_illegal_insn(insn, regs); - }; + } if (do_write && sbi_emulate_csr_write(csr_num, regs, new_csr_val)) return truly_illegal_insn(insn, regs); diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c index ded3da00a93..35e66333501 100644 --- a/lib/sbi/sbi_init.c +++ b/lib/sbi/sbi_init.c @@ -12,10 +12,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -69,6 +71,8 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch) const struct sbi_timer_device *tdev; const struct sbi_console_device *cdev; const struct sbi_system_reset_device *srdev; + const struct sbi_system_suspend_device *susp_dev; + const struct sbi_cppc_device *cppc_dev; const struct sbi_platform *plat = sbi_platform_ptr(scratch); if (scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS) @@ -103,11 +107,32 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch) srdev = sbi_system_reset_get_device(SBI_SRST_RESET_TYPE_SHUTDOWN, 0); sbi_printf("Platform Shutdown Device : %s\n", (srdev) ? srdev->name : "---"); + susp_dev = sbi_system_suspend_get_device(); + sbi_printf("Platform Suspend Device : %s\n", + (susp_dev) ? susp_dev->name : "---"); + cppc_dev = sbi_cppc_get_device(); + sbi_printf("Platform CPPC Device : %s\n", + (cppc_dev) ? cppc_dev->name : "---"); /* Firmware details */ sbi_printf("Firmware Base : 0x%lx\n", scratch->fw_start); sbi_printf("Firmware Size : %d KB\n", (u32)(scratch->fw_size / 1024)); + sbi_printf("Firmware RW Offset : 0x%lx\n", scratch->fw_rw_offset); + sbi_printf("Firmware RW Size : %d KB\n", + (u32)((scratch->fw_size - scratch->fw_rw_offset) / 1024)); + sbi_printf("Firmware Heap Offset : 0x%lx\n", scratch->fw_heap_offset); + sbi_printf("Firmware Heap Size : " + "%d KB (total), %d KB (reserved), %d KB (used), %d KB (free)\n", + (u32)(scratch->fw_heap_size / 1024), + (u32)(sbi_heap_reserved_space() / 1024), + (u32)(sbi_heap_used_space() / 1024), + (u32)(sbi_heap_free_space() / 1024)); + sbi_printf("Firmware Scratch Size : " + "%d B (total), %d B (used), %d B (free)\n", + SBI_SCRATCH_SIZE, + (u32)sbi_scratch_used_space(), + (u32)(SBI_SCRATCH_SIZE - sbi_scratch_used_space())); /* SBI details */ sbi_printf("Runtime SBI Version : %d.%d\n", @@ -190,7 +215,7 @@ static void wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid) wfi(); cmip = csr_read(CSR_MIP); } while (!(cmip & (MIP_MSIP | MIP_MEIP))); - }; + } /* Acquire coldboot lock */ spin_lock(&coldboot_lock); @@ -233,12 +258,13 @@ static void wake_coldboot_harts(struct sbi_scratch *scratch, u32 hartid) spin_unlock(&coldboot_lock); } +static unsigned long entry_count_offset; static unsigned long init_count_offset; static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) { int rc; - unsigned long *init_count; + unsigned long *count; const struct sbi_platform *plat = sbi_platform_ptr(scratch); /* Note: This has to be first thing in coldboot init sequence */ @@ -247,14 +273,26 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) sbi_hart_hang(); /* Note: This has to be second thing in coldboot init sequence */ + rc = sbi_heap_init(scratch); + if (rc) + sbi_hart_hang(); + + /* Note: This has to be the third thing in coldboot init sequence */ rc = sbi_domain_init(scratch, hartid); if (rc) sbi_hart_hang(); + entry_count_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__); + if (!entry_count_offset) + sbi_hart_hang(); + init_count_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__); if (!init_count_offset) sbi_hart_hang(); + count = sbi_scratch_offset_ptr(scratch, entry_count_offset); + (*count)++; + rc = sbi_hsm_init(scratch, hartid, true); if (rc) sbi_hart_hang(); @@ -272,8 +310,11 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) sbi_hart_hang(); rc = sbi_pmu_init(scratch, true); - if (rc) + if (rc) { + sbi_printf("%s: pmu init failed (error %d)\n", + __func__, rc); sbi_hart_hang(); + } sbi_boot_print_banner(scratch); @@ -302,12 +343,6 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) sbi_hart_hang(); } - rc = sbi_ecall_init(); - if (rc) { - sbi_printf("%s: ecall init failed (error %d)\n", __func__, rc); - sbi_hart_hang(); - } - /* * Note: Finalize domains after HSM initialization so that we * can startup non-root domains. @@ -321,21 +356,26 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) sbi_hart_hang(); } - rc = sbi_hart_pmp_configure(scratch); + /* + * Note: Platform final initialization should be after finalizing + * domains so that it sees correct domain assignment and PMP + * configuration for FDT fixups. + */ + rc = sbi_platform_final_init(plat, true); if (rc) { - sbi_printf("%s: PMP configure failed (error %d)\n", + sbi_printf("%s: platform final init failed (error %d)\n", __func__, rc); sbi_hart_hang(); } /* - * Note: Platform final initialization should be last so that - * it sees correct domain assignment and PMP configuration. + * Note: Ecall initialization should be after platform final + * initialization so that all available platform devices are + * already registered. */ - rc = sbi_platform_final_init(plat, true); + rc = sbi_ecall_init(); if (rc) { - sbi_printf("%s: platform final init failed (error %d)\n", - __func__, rc); + sbi_printf("%s: ecall init failed (error %d)\n", __func__, rc); sbi_hart_hang(); } @@ -345,25 +385,38 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) sbi_boot_print_hart(scratch, hartid); + /* + * Configure PMP at last because if SMEPMP is detected, + * M-mode access to the S/U space will be rescinded. + */ + rc = sbi_hart_pmp_configure(scratch); + if (rc) { + sbi_printf("%s: PMP configure failed (error %d)\n", + __func__, rc); + sbi_hart_hang(); + } + wake_coldboot_harts(scratch, hartid); - init_count = sbi_scratch_offset_ptr(scratch, init_count_offset); - (*init_count)++; + count = sbi_scratch_offset_ptr(scratch, init_count_offset); + (*count)++; - sbi_hsm_prepare_next_jump(scratch, hartid); - sbi_hart_switch_mode(hartid, scratch->next_arg1, scratch->next_addr, - scratch->next_mode, false); + sbi_hsm_hart_start_finish(scratch, hartid); } -static void init_warm_startup(struct sbi_scratch *scratch, u32 hartid) +static void __noreturn init_warm_startup(struct sbi_scratch *scratch, + u32 hartid) { int rc; - unsigned long *init_count; + unsigned long *count; const struct sbi_platform *plat = sbi_platform_ptr(scratch); - if (!init_count_offset) + if (!entry_count_offset || !init_count_offset) sbi_hart_hang(); + count = sbi_scratch_offset_ptr(scratch, entry_count_offset); + (*count)++; + rc = sbi_hsm_init(scratch, hartid, false); if (rc) sbi_hart_hang(); @@ -396,21 +449,26 @@ static void init_warm_startup(struct sbi_scratch *scratch, u32 hartid) if (rc) sbi_hart_hang(); - rc = sbi_hart_pmp_configure(scratch); + rc = sbi_platform_final_init(plat, false); if (rc) sbi_hart_hang(); - rc = sbi_platform_final_init(plat, false); + /* + * Configure PMP at last because if SMEPMP is detected, + * M-mode access to the S/U space will be rescinded. + */ + rc = sbi_hart_pmp_configure(scratch); if (rc) sbi_hart_hang(); - init_count = sbi_scratch_offset_ptr(scratch, init_count_offset); - (*init_count)++; + count = sbi_scratch_offset_ptr(scratch, init_count_offset); + (*count)++; - sbi_hsm_prepare_next_jump(scratch, hartid); + sbi_hsm_hart_start_finish(scratch, hartid); } -static void init_warm_resume(struct sbi_scratch *scratch) +static void __noreturn init_warm_resume(struct sbi_scratch *scratch, + u32 hartid) { int rc; @@ -424,7 +482,7 @@ static void init_warm_resume(struct sbi_scratch *scratch) if (rc) sbi_hart_hang(); - sbi_hsm_hart_resume_finish(scratch); + sbi_hsm_hart_resume_finish(scratch, hartid); } static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid) @@ -437,14 +495,12 @@ static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid) if (hstate < 0) sbi_hart_hang(); - if (hstate == SBI_HSM_STATE_SUSPENDED) - init_warm_resume(scratch); - else + if (hstate == SBI_HSM_STATE_SUSPENDED) { + init_warm_resume(scratch, hartid); + } else { + sbi_ipi_raw_clear(hartid); init_warm_startup(scratch, hartid); - - sbi_hart_switch_mode(hartid, scratch->next_arg1, - scratch->next_addr, - scratch->next_mode, false); + } } static atomic_t coldboot_lottery = ATOMIC_INITIALIZER(0); @@ -498,8 +554,11 @@ void __noreturn sbi_init(struct sbi_scratch *scratch) * HARTs which satisfy above condition. */ - if (next_mode_supported && atomic_xchg(&coldboot_lottery, 1) == 0) - coldboot = true; + if (sbi_platform_cold_boot_allowed(plat, hartid)) { + if (next_mode_supported && + atomic_xchg(&coldboot_lottery, 1) == 0) + coldboot = true; + } /* * Do platform specific nascent (very early) initialization so @@ -515,6 +574,23 @@ void __noreturn sbi_init(struct sbi_scratch *scratch) init_warmboot(scratch, hartid); } +unsigned long sbi_entry_count(u32 hartid) +{ + struct sbi_scratch *scratch; + unsigned long *entry_count; + + if (!entry_count_offset) + return 0; + + scratch = sbi_hartid_to_scratch(hartid); + if (!scratch) + return 0; + + entry_count = sbi_scratch_offset_ptr(scratch, entry_count_offset); + + return *entry_count; +} + unsigned long sbi_init_count(u32 hartid) { struct sbi_scratch *scratch; diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c index 1bcc2e40c90..ad09154521f 100644 --- a/lib/sbi/sbi_ipi.c +++ b/lib/sbi/sbi_ipi.c @@ -53,7 +53,7 @@ static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid, if (ipi_ops->update) { ret = ipi_ops->update(scratch, remote_scratch, remote_hartid, data); - if (ret < 0) + if (ret != SBI_IPI_UPDATE_SUCCESS) return ret; } @@ -69,6 +69,18 @@ static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid, sbi_pmu_ctr_incr_fw(SBI_PMU_FW_IPI_SENT); + return 0; +} + +static int sbi_ipi_sync(struct sbi_scratch *scratch, u32 event) +{ + const struct sbi_ipi_event_ops *ipi_ops; + + if ((SBI_IPI_EVENT_MAX <= event) || + !ipi_ops_array[event]) + return SBI_EINVAL; + ipi_ops = ipi_ops_array[event]; + if (ipi_ops->sync) ipi_ops->sync(scratch); @@ -83,33 +95,49 @@ static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid, int sbi_ipi_send_many(ulong hmask, ulong hbase, u32 event, void *data) { int rc; + bool retry_needed; ulong i, m; + struct sbi_hartmask target_mask = {0}; struct sbi_domain *dom = sbi_domain_thishart_ptr(); struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + /* Find the target harts */ if (hbase != -1UL) { rc = sbi_hsm_hart_interruptible_mask(dom, hbase, &m); if (rc) return rc; m &= hmask; - /* Send IPIs */ for (i = hbase; m; i++, m >>= 1) { if (m & 1UL) - sbi_ipi_send(scratch, i, event, data); + sbi_hartmask_set_hart(i, &target_mask); } } else { hbase = 0; while (!sbi_hsm_hart_interruptible_mask(dom, hbase, &m)) { - /* Send IPIs */ for (i = hbase; m; i++, m >>= 1) { if (m & 1UL) - sbi_ipi_send(scratch, i, event, data); + sbi_hartmask_set_hart(i, &target_mask); } hbase += BITS_PER_LONG; } } + /* Send IPIs */ + do { + retry_needed = false; + sbi_hartmask_for_each_hart(i, &target_mask) { + rc = sbi_ipi_send(scratch, i, event, data); + if (rc == SBI_IPI_UPDATE_RETRY) + retry_needed = true; + else + sbi_hartmask_clear_hart(i, &target_mask); + } + } while (retry_needed); + + /* Sync IPIs */ + sbi_ipi_sync(scratch, event); + return 0; } @@ -195,17 +223,14 @@ void sbi_ipi_process(void) ipi_type = atomic_raw_xchg_ulong(&ipi_data->ipi_type, 0); ipi_event = 0; while (ipi_type) { - if (!(ipi_type & 1UL)) - goto skip; - - ipi_ops = ipi_ops_array[ipi_event]; - if (ipi_ops && ipi_ops->process) - ipi_ops->process(scratch); - -skip: + if (ipi_type & 1UL) { + ipi_ops = ipi_ops_array[ipi_event]; + if (ipi_ops && ipi_ops->process) + ipi_ops->process(scratch); + } ipi_type = ipi_type >> 1; ipi_event++; - }; + } } int sbi_ipi_raw_send(u32 target_hart) @@ -217,6 +242,12 @@ int sbi_ipi_raw_send(u32 target_hart) return 0; } +void sbi_ipi_raw_clear(u32 target_hart) +{ + if (ipi_dev && ipi_dev->ipi_clear) + ipi_dev->ipi_clear(target_hart); +} + const struct sbi_ipi_device *sbi_ipi_get_device(void) { return ipi_dev; diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c index 154dbdac549..a82f6941e4c 100644 --- a/lib/sbi/sbi_pmu.c +++ b/lib/sbi/sbi_pmu.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -50,23 +50,43 @@ union sbi_pmu_ctr_info { }; }; -/* Platform specific PMU device */ -static const struct sbi_pmu_device *pmu_dev = NULL; - -/* Mapping between event range and possible counters */ -static struct sbi_pmu_hw_event hw_event_map[SBI_PMU_HW_EVENT_MAX] = {0}; - -/* counter to enabled event mapping */ -static uint32_t active_events[SBI_HARTMASK_MAX_BITS][SBI_PMU_HW_CTR_MAX + SBI_PMU_FW_CTR_MAX]; - -/* Bitmap of firmware counters started on each HART */ #if SBI_PMU_FW_CTR_MAX >= BITS_PER_LONG #error "Can't handle firmware counters beyond BITS_PER_LONG" #endif -static unsigned long fw_counters_started[SBI_HARTMASK_MAX_BITS]; -/* Values of firmwares counters on each HART */ -static uint64_t fw_counters_value[SBI_HARTMASK_MAX_BITS][SBI_PMU_FW_CTR_MAX] = {0}; +/** Per-HART state of the PMU counters */ +struct sbi_pmu_hart_state { + /* HART to which this state belongs */ + uint32_t hartid; + /* Counter to enabled event mapping */ + uint32_t active_events[SBI_PMU_HW_CTR_MAX + SBI_PMU_FW_CTR_MAX]; + /* Bitmap of firmware counters started */ + unsigned long fw_counters_started; + /* + * Counter values for SBI firmware events and event codes + * for platform firmware events. Both are mutually exclusive + * and hence can optimally share the same memory. + */ + uint64_t fw_counters_data[SBI_PMU_FW_CTR_MAX]; +}; + +/** Offset of pointer to PMU HART state in scratch space */ +static unsigned long phs_ptr_offset; + +#define pmu_get_hart_state_ptr(__scratch) \ + sbi_scratch_read_type((__scratch), void *, phs_ptr_offset) + +#define pmu_thishart_state_ptr() \ + pmu_get_hart_state_ptr(sbi_scratch_thishart_ptr()) + +#define pmu_set_hart_state_ptr(__scratch, __phs) \ + sbi_scratch_write_type((__scratch), void *, phs_ptr_offset, (__phs)) + +/* Platform specific PMU device */ +static const struct sbi_pmu_device *pmu_dev = NULL; + +/* Mapping between event range and possible counters */ +static struct sbi_pmu_hw_event *hw_event_map; /* Maximum number of hardware events available */ static uint32_t num_hw_events; @@ -77,7 +97,8 @@ static uint32_t num_hw_ctrs; static uint32_t total_ctrs; /* Helper macros to retrieve event idx and code type */ -#define get_cidx_type(x) ((x & SBI_PMU_EVENT_IDX_TYPE_MASK) >> 16) +#define get_cidx_type(x) \ + (((x) & SBI_PMU_EVENT_IDX_TYPE_MASK) >> SBI_PMU_EVENT_IDX_TYPE_OFFSET) #define get_cidx_code(x) (x & SBI_PMU_EVENT_IDX_CODE_MASK) /** @@ -106,7 +127,8 @@ static bool pmu_event_select_overlap(struct sbi_pmu_hw_event *evt, return false; } -static int pmu_event_validate(unsigned long event_idx) +static int pmu_event_validate(struct sbi_pmu_hart_state *phs, + unsigned long event_idx, uint64_t edata) { uint32_t event_idx_type = get_cidx_type(event_idx); uint32_t event_idx_code = get_cidx_code(event_idx); @@ -118,9 +140,15 @@ static int pmu_event_validate(unsigned long event_idx) event_idx_code_max = SBI_PMU_HW_GENERAL_MAX; break; case SBI_PMU_EVENT_TYPE_FW: - if (SBI_PMU_FW_MAX <= event_idx_code && - pmu_dev && pmu_dev->fw_event_validate_code) - return pmu_dev->fw_event_validate_code(event_idx_code); + if ((event_idx_code >= SBI_PMU_FW_MAX && + event_idx_code <= SBI_PMU_FW_RESERVED_MAX) || + event_idx_code > SBI_PMU_FW_PLATFORM) + return SBI_EINVAL; + + if (SBI_PMU_FW_PLATFORM == event_idx_code && + pmu_dev && pmu_dev->fw_event_validate_encoding) + return pmu_dev->fw_event_validate_encoding(phs->hartid, + edata); else event_idx_code_max = SBI_PMU_FW_MAX; break; @@ -153,16 +181,16 @@ static int pmu_event_validate(unsigned long event_idx) return SBI_EINVAL; } -static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code) +static int pmu_ctr_validate(struct sbi_pmu_hart_state *phs, + uint32_t cidx, uint32_t *event_idx_code) { uint32_t event_idx_val; uint32_t event_idx_type; - u32 hartid = current_hartid(); if (cidx >= total_ctrs) return SBI_EINVAL; - event_idx_val = active_events[hartid][cidx]; + event_idx_val = phs->active_events[cidx]; event_idx_type = get_cidx_type(event_idx_val); if (event_idx_val == SBI_PMU_EVENT_IDX_INVALID || event_idx_type >= SBI_PMU_EVENT_TYPE_MAX) @@ -177,18 +205,26 @@ int sbi_pmu_ctr_fw_read(uint32_t cidx, uint64_t *cval) { int event_idx_type; uint32_t event_code; - u32 hartid = current_hartid(); + struct sbi_pmu_hart_state *phs = pmu_thishart_state_ptr(); - event_idx_type = pmu_ctr_validate(cidx, &event_code); + event_idx_type = pmu_ctr_validate(phs, cidx, &event_code); if (event_idx_type != SBI_PMU_EVENT_TYPE_FW) return SBI_EINVAL; - if (SBI_PMU_FW_MAX <= event_code && - pmu_dev && pmu_dev->fw_counter_read_value) - fw_counters_value[hartid][cidx - num_hw_ctrs] = - pmu_dev->fw_counter_read_value(cidx - num_hw_ctrs); + if ((event_code >= SBI_PMU_FW_MAX && + event_code <= SBI_PMU_FW_RESERVED_MAX) || + event_code > SBI_PMU_FW_PLATFORM) + return SBI_EINVAL; - *cval = fw_counters_value[hartid][cidx - num_hw_ctrs]; + if (SBI_PMU_FW_PLATFORM == event_code) { + if (pmu_dev && pmu_dev->fw_counter_read_value) + *cval = pmu_dev->fw_counter_read_value(phs->hartid, + cidx - + num_hw_ctrs); + else + *cval = 0; + } else + *cval = phs->fw_counters_data[cidx - num_hw_ctrs]; return 0; } @@ -356,24 +392,37 @@ int sbi_pmu_irq_bit(void) return 0; } -static int pmu_ctr_start_fw(uint32_t cidx, uint32_t event_code, - uint64_t ival, bool ival_update) +static int pmu_ctr_start_fw(struct sbi_pmu_hart_state *phs, + uint32_t cidx, uint32_t event_code, + uint64_t event_data, uint64_t ival, + bool ival_update) { - int ret; - u32 hartid = current_hartid(); + if ((event_code >= SBI_PMU_FW_MAX && + event_code <= SBI_PMU_FW_RESERVED_MAX) || + event_code > SBI_PMU_FW_PLATFORM) + return SBI_EINVAL; - if (SBI_PMU_FW_MAX <= event_code && - pmu_dev && pmu_dev->fw_counter_start) { - ret = pmu_dev->fw_counter_start(cidx - num_hw_ctrs, - event_code, - ival, ival_update); - if (ret) - return ret; + if (SBI_PMU_FW_PLATFORM == event_code) { + if (!pmu_dev || + !pmu_dev->fw_counter_write_value || + !pmu_dev->fw_counter_start) { + return SBI_EINVAL; + } + + if (ival_update) + pmu_dev->fw_counter_write_value(phs->hartid, + cidx - num_hw_ctrs, + ival); + + return pmu_dev->fw_counter_start(phs->hartid, + cidx - num_hw_ctrs, + event_data); + } else { + if (ival_update) + phs->fw_counters_data[cidx - num_hw_ctrs] = ival; } - if (ival_update) - fw_counters_value[hartid][cidx - num_hw_ctrs] = ival; - fw_counters_started[hartid] |= BIT(cidx - num_hw_ctrs); + phs->fw_counters_started |= BIT(cidx - num_hw_ctrs); return 0; } @@ -381,11 +430,13 @@ static int pmu_ctr_start_fw(uint32_t cidx, uint32_t event_code, int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask, unsigned long flags, uint64_t ival) { + struct sbi_pmu_hart_state *phs = pmu_thishart_state_ptr(); int event_idx_type; uint32_t event_code; int ret = SBI_EINVAL; bool bUpdate = false; int i, cidx; + uint64_t edata; if ((cbase + sbi_fls(cmask)) >= total_ctrs) return ret; @@ -395,12 +446,17 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask, for_each_set_bit(i, &cmask, total_ctrs) { cidx = i + cbase; - event_idx_type = pmu_ctr_validate(cidx, &event_code); + event_idx_type = pmu_ctr_validate(phs, cidx, &event_code); if (event_idx_type < 0) /* Continue the start operation for other counters */ continue; - else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW) - ret = pmu_ctr_start_fw(cidx, event_code, ival, bUpdate); + else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW) { + edata = (event_code == SBI_PMU_FW_PLATFORM) ? + phs->fw_counters_data[cidx - num_hw_ctrs] + : 0x0; + ret = pmu_ctr_start_fw(phs, cidx, event_code, edata, + ival, bUpdate); + } else ret = pmu_ctr_start_hw(cidx, ival, bUpdate); } @@ -430,18 +486,24 @@ static int pmu_ctr_stop_hw(uint32_t cidx) return SBI_EALREADY_STOPPED; } -static int pmu_ctr_stop_fw(uint32_t cidx, uint32_t event_code) +static int pmu_ctr_stop_fw(struct sbi_pmu_hart_state *phs, + uint32_t cidx, uint32_t event_code) { int ret; - if (SBI_PMU_FW_MAX <= event_code && + if ((event_code >= SBI_PMU_FW_MAX && + event_code <= SBI_PMU_FW_RESERVED_MAX) || + event_code > SBI_PMU_FW_PLATFORM) + return SBI_EINVAL; + + if (SBI_PMU_FW_PLATFORM == event_code && pmu_dev && pmu_dev->fw_counter_stop) { - ret = pmu_dev->fw_counter_stop(cidx - num_hw_ctrs); + ret = pmu_dev->fw_counter_stop(phs->hartid, cidx - num_hw_ctrs); if (ret) return ret; } - fw_counters_started[current_hartid()] &= ~BIT(cidx - num_hw_ctrs); + phs->fw_counters_started &= ~BIT(cidx - num_hw_ctrs); return 0; } @@ -465,7 +527,7 @@ static int pmu_reset_hw_mhpmevent(int ctr_idx) int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask, unsigned long flag) { - u32 hartid = current_hartid(); + struct sbi_pmu_hart_state *phs = pmu_thishart_state_ptr(); int ret = SBI_EINVAL; int event_idx_type; uint32_t event_code; @@ -476,18 +538,18 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask, for_each_set_bit(i, &cmask, total_ctrs) { cidx = i + cbase; - event_idx_type = pmu_ctr_validate(cidx, &event_code); + event_idx_type = pmu_ctr_validate(phs, cidx, &event_code); if (event_idx_type < 0) /* Continue the stop operation for other counters */ continue; else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW) - ret = pmu_ctr_stop_fw(cidx, event_code); + ret = pmu_ctr_stop_fw(phs, cidx, event_code); else ret = pmu_ctr_stop_hw(cidx); - if (flag & SBI_PMU_STOP_FLAG_RESET) { - active_events[hartid][cidx] = SBI_PMU_EVENT_IDX_INVALID; + if (cidx > (CSR_INSTRET - CSR_CYCLE) && flag & SBI_PMU_STOP_FLAG_RESET) { + phs->active_events[cidx] = SBI_PMU_EVENT_IDX_INVALID; pmu_reset_hw_mhpmevent(cidx); } } @@ -547,6 +609,44 @@ static int pmu_update_hw_mhpmevent(struct sbi_pmu_hw_event *hw_evt, int ctr_idx, return 0; } +static int pmu_fixed_ctr_update_inhibit_bits(int fixed_ctr, unsigned long flags) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + uint64_t cfg_val = 0, cfg_csr_no; +#if __riscv_xlen == 32 + uint64_t cfgh_csr_no; +#endif + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMCNTRPMF)) + return fixed_ctr; + + switch (fixed_ctr) { + case 0: + cfg_csr_no = CSR_MCYCLECFG; +#if __riscv_xlen == 32 + cfgh_csr_no = CSR_MCYCLECFGH; +#endif + break; + case 2: + cfg_csr_no = CSR_MINSTRETCFG; +#if __riscv_xlen == 32 + cfgh_csr_no = CSR_MINSTRETCFGH; +#endif + break; + default: + return SBI_EFAIL; + } + + cfg_val |= MHPMEVENT_MINH; + pmu_update_inhibit_flags(flags, &cfg_val); +#if __riscv_xlen == 32 + csr_write_num(cfg_csr_no, cfg_val & 0xFFFFFFFF); + csr_write_num(cfgh_csr_no, cfg_val >> BITS_PER_LONG); +#else + csr_write_num(cfg_csr_no, cfg_val); +#endif + return fixed_ctr; +} + static int pmu_ctr_find_fixed_fw(unsigned long evt_idx_code) { /* Non-programmables counters are enabled always. No need to do lookup */ @@ -558,14 +658,15 @@ static int pmu_ctr_find_fixed_fw(unsigned long evt_idx_code) return SBI_EINVAL; } -static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned long flags, +static int pmu_ctr_find_hw(struct sbi_pmu_hart_state *phs, + unsigned long cbase, unsigned long cmask, + unsigned long flags, unsigned long event_idx, uint64_t data) { unsigned long ctr_mask; int i, ret = 0, fixed_ctr, ctr_idx = SBI_ENOTSUPP; struct sbi_pmu_hw_event *temp; unsigned long mctr_inhbt = 0; - u32 hartid = current_hartid(); struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); if (cbase >= num_hw_ctrs) @@ -578,7 +679,7 @@ static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned lo fixed_ctr = pmu_ctr_find_fixed_fw(event_idx); if (fixed_ctr >= 0 && !sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF)) - return fixed_ctr; + return pmu_fixed_ctr_update_inhibit_bits(fixed_ctr, flags); if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11) mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT); @@ -604,7 +705,7 @@ static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned lo * Some of the platform may not support mcountinhibit. * Checking the active_events is enough for them */ - if (active_events[hartid][cbase] != SBI_PMU_EVENT_IDX_INVALID) + if (phs->active_events[cbase] != SBI_PMU_EVENT_IDX_INVALID) continue; /* If mcountinhibit is supported, the bit must be enabled */ if ((sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11) && @@ -621,7 +722,7 @@ static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned lo * Return the fixed counter as they are mandatory anyways. */ if (fixed_ctr >= 0) - return fixed_ctr; + return pmu_fixed_ctr_update_inhibit_bits(fixed_ctr, flags); else return SBI_EFAIL; } @@ -639,21 +740,28 @@ static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned lo * Thus, select the first available fw counter after sanity * check. */ -static int pmu_ctr_find_fw(unsigned long cbase, unsigned long cmask, - uint32_t event_code, u32 hartid) +static int pmu_ctr_find_fw(struct sbi_pmu_hart_state *phs, + unsigned long cbase, unsigned long cmask, + uint32_t event_code, uint64_t edata) { int i, cidx; + if ((event_code >= SBI_PMU_FW_MAX && + event_code <= SBI_PMU_FW_RESERVED_MAX) || + event_code > SBI_PMU_FW_PLATFORM) + return SBI_EINVAL; + for_each_set_bit(i, &cmask, BITS_PER_LONG) { cidx = i + cbase; if (cidx < num_hw_ctrs || total_ctrs <= cidx) continue; - if (active_events[hartid][i] != SBI_PMU_EVENT_IDX_INVALID) + if (phs->active_events[i] != SBI_PMU_EVENT_IDX_INVALID) continue; - if (SBI_PMU_FW_MAX <= event_code && - pmu_dev && pmu_dev->fw_counter_match_code) { - if (!pmu_dev->fw_counter_match_code(cidx - num_hw_ctrs, - event_code)) + if (SBI_PMU_FW_PLATFORM == event_code && + pmu_dev && pmu_dev->fw_counter_match_encoding) { + if (!pmu_dev->fw_counter_match_encoding(phs->hartid, + cidx - num_hw_ctrs, + edata)) continue; } @@ -667,15 +775,15 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask, unsigned long flags, unsigned long event_idx, uint64_t event_data) { - int ret, ctr_idx = SBI_ENOTSUPP; - u32 event_code, hartid = current_hartid(); - int event_type; + struct sbi_pmu_hart_state *phs = pmu_thishart_state_ptr(); + int ret, event_type, ctr_idx = SBI_ENOTSUPP; + u32 event_code; /* Do a basic sanity check of counter base & mask */ if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs) return SBI_EINVAL; - event_type = pmu_event_validate(event_idx); + event_type = pmu_event_validate(phs, event_idx, event_data); if (event_type < 0) return SBI_EINVAL; event_code = get_cidx_code(event_idx); @@ -684,25 +792,34 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask, /* The caller wants to skip the match because it already knows the * counter idx for the given event. Verify that the counter idx * is still valid. + * As per the specification, we should "unconditionally select + * the first counter from the set of counters specified by the + * counter_idx_base and counter_idx_mask". */ - if (active_events[hartid][cidx_base] == SBI_PMU_EVENT_IDX_INVALID) + unsigned long cidx_first = cidx_base + sbi_ffs(cidx_mask); + + if (phs->active_events[cidx_first] == SBI_PMU_EVENT_IDX_INVALID) return SBI_EINVAL; - ctr_idx = cidx_base; + ctr_idx = cidx_first; goto skip_match; } if (event_type == SBI_PMU_EVENT_TYPE_FW) { /* Any firmware counter can be used track any firmware event */ - ctr_idx = pmu_ctr_find_fw(cidx_base, cidx_mask, event_code, hartid); + ctr_idx = pmu_ctr_find_fw(phs, cidx_base, cidx_mask, + event_code, event_data); + if (event_code == SBI_PMU_FW_PLATFORM) + phs->fw_counters_data[ctr_idx - num_hw_ctrs] = + event_data; } else { - ctr_idx = pmu_ctr_find_hw(cidx_base, cidx_mask, flags, event_idx, - event_data); + ctr_idx = pmu_ctr_find_hw(phs, cidx_base, cidx_mask, flags, + event_idx, event_data); } if (ctr_idx < 0) return SBI_ENOTSUPP; - active_events[hartid][ctr_idx] = event_idx; + phs->active_events[ctr_idx] = event_idx; skip_match: if (event_type == SBI_PMU_EVENT_TYPE_HW) { if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE) @@ -711,18 +828,17 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask, pmu_ctr_start_hw(ctr_idx, 0, false); } else if (event_type == SBI_PMU_EVENT_TYPE_FW) { if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE) - fw_counters_value[hartid][ctr_idx - num_hw_ctrs] = 0; + phs->fw_counters_data[ctr_idx - num_hw_ctrs] = 0; if (flags & SBI_PMU_CFG_FLAG_AUTO_START) { - if (SBI_PMU_FW_MAX <= event_code && + if (SBI_PMU_FW_PLATFORM == event_code && pmu_dev && pmu_dev->fw_counter_start) { ret = pmu_dev->fw_counter_start( - ctr_idx - num_hw_ctrs, event_code, - fw_counters_value[hartid][ctr_idx - num_hw_ctrs], - true); + phs->hartid, + ctr_idx - num_hw_ctrs, event_data); if (ret) return ret; } - fw_counters_started[hartid] |= BIT(ctr_idx - num_hw_ctrs); + phs->fw_counters_started |= BIT(ctr_idx - num_hw_ctrs); } } @@ -731,19 +847,20 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask, int sbi_pmu_ctr_incr_fw(enum sbi_pmu_fw_event_code_id fw_id) { - u32 cidx, hartid = current_hartid(); + u32 cidx; uint64_t *fcounter = NULL; + struct sbi_pmu_hart_state *phs = pmu_thishart_state_ptr(); - if (likely(!fw_counters_started[hartid])) + if (likely(!phs->fw_counters_started)) return 0; if (unlikely(fw_id >= SBI_PMU_FW_MAX)) return SBI_EINVAL; for (cidx = num_hw_ctrs; cidx < total_ctrs; cidx++) { - if (get_cidx_code(active_events[hartid][cidx]) == fw_id && - (fw_counters_started[hartid] & BIT(cidx - num_hw_ctrs))) { - fcounter = &fw_counters_value[hartid][cidx - num_hw_ctrs]; + if (get_cidx_code(phs->active_events[cidx]) == fw_id && + (phs->fw_counters_started & BIT(cidx - num_hw_ctrs))) { + fcounter = &phs->fw_counters_data[cidx - num_hw_ctrs]; break; } } @@ -761,6 +878,7 @@ unsigned long sbi_pmu_num_ctr(void) int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info) { + int width; union sbi_pmu_ctr_info cinfo = {0}; struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); @@ -782,6 +900,11 @@ int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info) cinfo.type = SBI_PMU_CTR_TYPE_FW; /* Firmware counters are always 64 bits wide */ cinfo.width = 63; + if (pmu_dev && pmu_dev->fw_counter_width) { + width = pmu_dev->fw_counter_width(); + if (width) + cinfo.width = width - 1; + } } *ctr_info = cinfo.value; @@ -789,16 +912,16 @@ int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info) return 0; } -static void pmu_reset_event_map(u32 hartid) +static void pmu_reset_event_map(struct sbi_pmu_hart_state *phs) { int j; /* Initialize the counter to event mapping table */ for (j = 3; j < total_ctrs; j++) - active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID; + phs->active_events[j] = SBI_PMU_EVENT_IDX_INVALID; for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++) - fw_counters_value[hartid][j] = 0; - fw_counters_started[hartid] = 0; + phs->fw_counters_data[j] = 0; + phs->fw_counters_started = 0; } const struct sbi_pmu_device *sbi_pmu_get_device(void) @@ -816,39 +939,60 @@ void sbi_pmu_set_device(const struct sbi_pmu_device *dev) void sbi_pmu_exit(struct sbi_scratch *scratch) { - u32 hartid = current_hartid(); - if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11) csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8); if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10) csr_write(CSR_MCOUNTEREN, -1); - pmu_reset_event_map(hartid); + + pmu_reset_event_map(pmu_get_hart_state_ptr(scratch)); } int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot) { + struct sbi_pmu_hart_state *phs; const struct sbi_platform *plat; - u32 hartid = current_hartid(); if (cold_boot) { + hw_event_map = sbi_calloc(sizeof(*hw_event_map), + SBI_PMU_HW_EVENT_MAX); + if (!hw_event_map) + return SBI_ENOMEM; + + phs_ptr_offset = sbi_scratch_alloc_type_offset(void *); + if (!phs_ptr_offset) { + sbi_free(hw_event_map); + return SBI_ENOMEM; + } + plat = sbi_platform_ptr(scratch); /* Initialize hw pmu events */ sbi_platform_pmu_init(plat); /* mcycle & minstret is available always */ num_hw_ctrs = sbi_hart_mhpm_count(scratch) + 3; + if (num_hw_ctrs > SBI_PMU_HW_CTR_MAX) + return SBI_EINVAL; total_ctrs = num_hw_ctrs + SBI_PMU_FW_CTR_MAX; } - pmu_reset_event_map(hartid); + phs = pmu_get_hart_state_ptr(scratch); + if (!phs) { + phs = sbi_zalloc(sizeof(*phs)); + if (!phs) + return SBI_ENOMEM; + phs->hartid = current_hartid(); + pmu_set_hart_state_ptr(scratch, phs); + } + + pmu_reset_event_map(phs); /* First three counters are fixed by the priv spec and we enable it by default */ - active_events[hartid][0] = SBI_PMU_EVENT_TYPE_HW << SBI_PMU_EVENT_IDX_OFFSET | - SBI_PMU_HW_CPU_CYCLES; - active_events[hartid][1] = SBI_PMU_EVENT_IDX_INVALID; - active_events[hartid][2] = SBI_PMU_EVENT_TYPE_HW << SBI_PMU_EVENT_IDX_OFFSET | - SBI_PMU_HW_INSTRUCTIONS; + phs->active_events[0] = (SBI_PMU_EVENT_TYPE_HW << SBI_PMU_EVENT_IDX_TYPE_OFFSET) | + SBI_PMU_HW_CPU_CYCLES; + phs->active_events[1] = SBI_PMU_EVENT_IDX_INVALID; + phs->active_events[2] = (SBI_PMU_EVENT_TYPE_HW << SBI_PMU_EVENT_IDX_TYPE_OFFSET) | + SBI_PMU_HW_INSTRUCTIONS; return 0; } diff --git a/lib/sbi/sbi_scratch.c b/lib/sbi/sbi_scratch.c index 87b34c6d4de..87ef84cafd3 100644 --- a/lib/sbi/sbi_scratch.c +++ b/lib/sbi/sbi_scratch.c @@ -59,8 +59,8 @@ unsigned long sbi_scratch_alloc_offset(unsigned long size) if (!size) return 0; - if (size & (__SIZEOF_POINTER__ - 1)) - size = (size & ~(__SIZEOF_POINTER__ - 1)) + __SIZEOF_POINTER__; + size += __SIZEOF_POINTER__ - 1; + size &= ~((unsigned long)__SIZEOF_POINTER__ - 1); spin_lock(&extra_lock); @@ -97,3 +97,14 @@ void sbi_scratch_free_offset(unsigned long offset) * brain-dead allocator. */ } + +unsigned long sbi_scratch_used_space(void) +{ + unsigned long ret = 0; + + spin_lock(&extra_lock); + ret = extra_offset; + spin_unlock(&extra_lock); + + return ret; +} diff --git a/lib/sbi/sbi_system.c b/lib/sbi/sbi_system.c index f37c811d0bf..d803ffa8418 100644 --- a/lib/sbi/sbi_system.c +++ b/lib/sbi/sbi_system.c @@ -17,6 +17,7 @@ #include #include #include +#include static SBI_LIST_HEAD(reset_devices_list); @@ -92,3 +93,116 @@ void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason) /* If platform specific reset did not work then do sbi_exit() */ sbi_exit(scratch); } + +static const struct sbi_system_suspend_device *suspend_dev = NULL; + +const struct sbi_system_suspend_device *sbi_system_suspend_get_device(void) +{ + return suspend_dev; +} + +void sbi_system_suspend_set_device(struct sbi_system_suspend_device *dev) +{ + if (!dev || suspend_dev) + return; + + suspend_dev = dev; +} + +static int sbi_system_suspend_test_check(u32 sleep_type) +{ + return sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND ? 0 : SBI_EINVAL; +} + +static int sbi_system_suspend_test_suspend(u32 sleep_type, + unsigned long mmode_resume_addr) +{ + if (sleep_type != SBI_SUSP_SLEEP_TYPE_SUSPEND) + return SBI_EINVAL; + + sbi_timer_mdelay(5000); + + /* Wait for interrupt */ + wfi(); + + return SBI_OK; +} + +static struct sbi_system_suspend_device sbi_system_suspend_test = { + .name = "system-suspend-test", + .system_suspend_check = sbi_system_suspend_test_check, + .system_suspend = sbi_system_suspend_test_suspend, +}; + +void sbi_system_suspend_test_enable(void) +{ + sbi_system_suspend_set_device(&sbi_system_suspend_test); +} + +bool sbi_system_suspend_supported(u32 sleep_type) +{ + return suspend_dev && suspend_dev->system_suspend_check && + suspend_dev->system_suspend_check(sleep_type) == 0; +} + +int sbi_system_suspend(u32 sleep_type, ulong resume_addr, ulong opaque) +{ + const struct sbi_domain *dom = sbi_domain_thishart_ptr(); + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + void (*jump_warmboot)(void) = (void (*)(void))scratch->warmboot_addr; + unsigned int hartid = current_hartid(); + unsigned long prev_mode; + unsigned long i; + int ret; + + if (!dom || !dom->system_suspend_allowed) + return SBI_EFAIL; + + if (!suspend_dev || !suspend_dev->system_suspend || + !suspend_dev->system_suspend_check) + return SBI_EFAIL; + + ret = suspend_dev->system_suspend_check(sleep_type); + if (ret != SBI_OK) + return ret; + + prev_mode = (csr_read(CSR_MSTATUS) & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT; + if (prev_mode != PRV_S && prev_mode != PRV_U) + return SBI_EFAIL; + + sbi_hartmask_for_each_hart(i, &dom->assigned_harts) { + if (i == hartid) + continue; + if (__sbi_hsm_hart_get_state(i) != SBI_HSM_STATE_STOPPED) + return SBI_EFAIL; + } + + if (!sbi_domain_check_addr(dom, resume_addr, prev_mode, + SBI_DOMAIN_EXECUTE)) + return SBI_EINVALID_ADDR; + + if (!sbi_hsm_hart_change_state(scratch, SBI_HSM_STATE_STARTED, + SBI_HSM_STATE_SUSPENDED)) + return SBI_EFAIL; + + /* Prepare for resume */ + scratch->next_mode = prev_mode; + scratch->next_addr = resume_addr; + scratch->next_arg1 = opaque; + + __sbi_hsm_suspend_non_ret_save(scratch); + + /* Suspend */ + ret = suspend_dev->system_suspend(sleep_type, scratch->warmboot_addr); + if (ret != SBI_OK) { + if (!sbi_hsm_hart_change_state(scratch, SBI_HSM_STATE_SUSPENDED, + SBI_HSM_STATE_STARTED)) + sbi_hart_hang(); + return ret; + } + + /* Resume */ + jump_warmboot(); + + __builtin_unreachable(); +} diff --git a/lib/sbi/sbi_timer.c b/lib/sbi/sbi_timer.c index 4b24cbeb7c2..7b618de1e7f 100644 --- a/lib/sbi/sbi_timer.c +++ b/lib/sbi/sbi_timer.c @@ -188,7 +188,7 @@ int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot) if (!time_delta_off) return SBI_ENOMEM; - if (sbi_hart_has_extension(scratch, SBI_HART_EXT_TIME)) + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_ZICNTR)) get_time_val = get_ticks; } else { if (!time_delta_off) diff --git a/lib/sbi/sbi_tlb.c b/lib/sbi/sbi_tlb.c index 4c142ea3e66..26a87f32930 100644 --- a/lib/sbi/sbi_tlb.c +++ b/lib/sbi/sbi_tlb.c @@ -215,7 +215,7 @@ static void tlb_entry_process(struct sbi_tlb_info *tinfo) { u32 rhartid; struct sbi_scratch *rscratch = NULL; - unsigned long *rtlb_sync = NULL; + atomic_t *rtlb_sync = NULL; tinfo->local_fn(tinfo); @@ -225,47 +225,40 @@ static void tlb_entry_process(struct sbi_tlb_info *tinfo) continue; rtlb_sync = sbi_scratch_offset_ptr(rscratch, tlb_sync_off); - while (atomic_raw_xchg_ulong(rtlb_sync, 1)) ; + atomic_sub_return(rtlb_sync, 1); } } -static void tlb_process_count(struct sbi_scratch *scratch, int count) +static bool tlb_process_once(struct sbi_scratch *scratch) { struct sbi_tlb_info tinfo; - unsigned int deq_count = 0; struct sbi_fifo *tlb_fifo = sbi_scratch_offset_ptr(scratch, tlb_fifo_off); - while (!sbi_fifo_dequeue(tlb_fifo, &tinfo)) { + if (!sbi_fifo_dequeue(tlb_fifo, &tinfo)) { tlb_entry_process(&tinfo); - deq_count++; - if (deq_count > count) - break; - + return true; } + + return false; } static void tlb_process(struct sbi_scratch *scratch) { - struct sbi_tlb_info tinfo; - struct sbi_fifo *tlb_fifo = - sbi_scratch_offset_ptr(scratch, tlb_fifo_off); - - while (!sbi_fifo_dequeue(tlb_fifo, &tinfo)) - tlb_entry_process(&tinfo); + while (tlb_process_once(scratch)); } static void tlb_sync(struct sbi_scratch *scratch) { - unsigned long *tlb_sync = + atomic_t *tlb_sync = sbi_scratch_offset_ptr(scratch, tlb_sync_off); - while (!atomic_raw_xchg_ulong(tlb_sync, 0)) { + while (atomic_read(tlb_sync) > 0) { /* * While we are waiting for remote hart to set the sync, * consume fifo requests to avoid deadlock. */ - tlb_process_count(scratch, 1); + tlb_process_once(scratch); } return; @@ -343,6 +336,7 @@ static int tlb_update(struct sbi_scratch *scratch, u32 remote_hartid, void *data) { int ret; + atomic_t *tlb_sync; struct sbi_fifo *tlb_fifo_r; struct sbi_tlb_info *tinfo = data; u32 curr_hartid = current_hartid(); @@ -363,17 +357,14 @@ static int tlb_update(struct sbi_scratch *scratch, */ if (remote_hartid == curr_hartid) { tinfo->local_fn(tinfo); - return -1; + return SBI_IPI_UPDATE_BREAK; } tlb_fifo_r = sbi_scratch_offset_ptr(remote_scratch, tlb_fifo_off); ret = sbi_fifo_inplace_update(tlb_fifo_r, data, tlb_update_cb); - if (ret != SBI_FIFO_UNCHANGED) { - return 1; - } - while (sbi_fifo_enqueue(tlb_fifo_r, data) < 0) { + if (ret == SBI_FIFO_UNCHANGED && sbi_fifo_enqueue(tlb_fifo_r, data) < 0) { /** * For now, Busy loop until there is space in the fifo. * There may be case where target hart is also @@ -382,12 +373,16 @@ static int tlb_update(struct sbi_scratch *scratch, * TODO: Introduce a wait/wakeup event mechanism to handle * this properly. */ - tlb_process_count(scratch, 1); + tlb_process_once(scratch); sbi_dprintf("hart%d: hart%d tlb fifo full\n", curr_hartid, remote_hartid); + return SBI_IPI_UPDATE_RETRY; } - return 0; + tlb_sync = sbi_scratch_offset_ptr(scratch, tlb_sync_off); + atomic_add_return(tlb_sync, 1); + + return SBI_IPI_UPDATE_SUCCESS; } static struct sbi_ipi_event_ops tlb_ops = { @@ -413,7 +408,7 @@ int sbi_tlb_init(struct sbi_scratch *scratch, bool cold_boot) { int ret; void *tlb_mem; - unsigned long *tlb_sync; + atomic_t *tlb_sync; struct sbi_fifo *tlb_q; const struct sbi_platform *plat = sbi_platform_ptr(scratch); @@ -455,7 +450,7 @@ int sbi_tlb_init(struct sbi_scratch *scratch, bool cold_boot) tlb_q = sbi_scratch_offset_ptr(scratch, tlb_fifo_off); tlb_mem = sbi_scratch_offset_ptr(scratch, tlb_fifo_mem_off); - *tlb_sync = 0; + ATOMIC_INIT(tlb_sync, 0); sbi_fifo_init(tlb_q, tlb_mem, SBI_TLB_FIFO_NUM_ENTRIES, SBI_TLB_INFO_SIZE); diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c index 025cf614c27..dbf307c6894 100644 --- a/lib/sbi/sbi_trap.c +++ b/lib/sbi/sbi_trap.c @@ -212,7 +212,7 @@ static int sbi_trap_nonaia_irq(struct sbi_trap_regs *regs, ulong mcause) return sbi_irqchip_process(regs); default: return SBI_ENOENT; - }; + } return 0; } @@ -320,7 +320,7 @@ struct sbi_trap_regs *sbi_trap_handler(struct sbi_trap_regs *regs) rc = sbi_trap_redirect(regs, &trap); break; - }; + } trap_error: if (rc) diff --git a/lib/sbi/sbi_unpriv.c b/lib/sbi/sbi_unpriv.c index 9a34a717b00..2a55d9dbf11 100644 --- a/lib/sbi/sbi_unpriv.c +++ b/lib/sbi/sbi_unpriv.c @@ -163,7 +163,7 @@ ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap) break; default: break; - }; + } return insn; } diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c index 35462a2008a..788683d27c1 100644 --- a/lib/utils/fdt/fdt_domain.c +++ b/lib/utils/fdt/fdt_domain.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -219,14 +220,13 @@ void fdt_domain_fixup(void *fdt) fdt_nop_node(fdt, poffset); } -#define FDT_DOMAIN_MAX_COUNT 8 #define FDT_DOMAIN_REGION_MAX_COUNT 16 -static u32 fdt_domains_count; -static struct sbi_domain fdt_domains[FDT_DOMAIN_MAX_COUNT]; -static struct sbi_hartmask fdt_masks[FDT_DOMAIN_MAX_COUNT]; -static struct sbi_domain_memregion - fdt_regions[FDT_DOMAIN_MAX_COUNT][FDT_DOMAIN_REGION_MAX_COUNT + 1]; +struct parse_region_data { + struct sbi_domain *dom; + u32 region_count; + u32 max_regions; +}; static int __fdt_parse_region(void *fdt, int domain_offset, int region_offset, u32 region_access, @@ -236,13 +236,25 @@ static int __fdt_parse_region(void *fdt, int domain_offset, u32 val32; u64 val64; const u32 *val; - u32 *region_count = opaque; + struct parse_region_data *preg = opaque; struct sbi_domain_memregion *region; - /* Find next region of the domain */ - if (FDT_DOMAIN_REGION_MAX_COUNT <= *region_count) + /* + * Non-root domains cannot add a region with only M-mode + * access permissions. M-mode regions can only be part of + * root domain. + * + * SU permission bits can't be all zeroes when M-mode permission + * bits have at least one bit set. + */ + if (!(region_access & SBI_DOMAIN_MEMREGION_SU_ACCESS_MASK) + && (region_access & SBI_DOMAIN_MEMREGION_M_ACCESS_MASK)) return SBI_EINVAL; - region = &fdt_regions[fdt_domains_count][*region_count]; + + /* Find next region of the domain */ + if (preg->max_regions <= preg->region_count) + return SBI_ENOSPC; + region = &preg->dom->regions[preg->region_count]; /* Read "base" DT property */ val = fdt_getprop(fdt, region_offset, "base", &len); @@ -266,7 +278,7 @@ static int __fdt_parse_region(void *fdt, int domain_offset, if (fdt_get_property(fdt, region_offset, "mmio", NULL)) region->flags |= SBI_DOMAIN_MEMREGION_MMIO; - (*region_count)++; + preg->region_count++; return 0; } @@ -279,16 +291,30 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque) struct sbi_domain *dom; struct sbi_hartmask *mask; struct sbi_hartmask assign_mask; + struct parse_region_data preg; int *cold_domain_offset = opaque; - struct sbi_domain_memregion *reg, *regions; - int i, err, len, cpus_offset, cpu_offset, doffset; - - /* Sanity check on maximum domains we can handle */ - if (FDT_DOMAIN_MAX_COUNT <= fdt_domains_count) - return SBI_EINVAL; - dom = &fdt_domains[fdt_domains_count]; - mask = &fdt_masks[fdt_domains_count]; - regions = &fdt_regions[fdt_domains_count][0]; + struct sbi_domain_memregion *reg; + int i, err = 0, len, cpus_offset, cpu_offset, doffset; + + dom = sbi_zalloc(sizeof(*dom)); + if (!dom) + return SBI_ENOMEM; + + dom->regions = sbi_calloc(sizeof(*dom->regions), + FDT_DOMAIN_REGION_MAX_COUNT + 1); + if (!dom->regions) { + err = SBI_ENOMEM; + goto fail_free_domain; + } + preg.dom = dom; + preg.region_count = 0; + preg.max_regions = FDT_DOMAIN_REGION_MAX_COUNT; + + mask = sbi_zalloc(sizeof(*mask)); + if (!mask) { + err = SBI_ENOMEM; + goto fail_free_regions; + } /* Read DT node name */ strncpy(dom->name, fdt_get_name(fdt, domain_offset, NULL), @@ -304,12 +330,14 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque) for (i = 0; i < len; i++) { cpu_offset = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(val[i])); - if (cpu_offset < 0) - return cpu_offset; + if (cpu_offset < 0) { + err = cpu_offset; + goto fail_free_all; + } err = fdt_parse_hart_id(fdt, cpu_offset, &val32); if (err) - return err; + goto fail_free_all; if (!fdt_node_is_enabled(fdt, cpu_offset)) continue; @@ -319,14 +347,10 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque) } /* Setup memregions from DT */ - val32 = 0; - memset(regions, 0, - sizeof(*regions) * (FDT_DOMAIN_REGION_MAX_COUNT + 1)); - dom->regions = regions; - err = fdt_iterate_each_memregion(fdt, domain_offset, &val32, + err = fdt_iterate_each_memregion(fdt, domain_offset, &preg, __fdt_parse_region); if (err) - return err; + goto fail_free_all; /* * Copy over root domain memregions which don't allow @@ -338,14 +362,17 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque) * 2) mmio regions protecting M-mode only mmio devices */ sbi_domain_for_each_memregion(&root, reg) { - if ((reg->flags & SBI_DOMAIN_MEMREGION_READABLE) || - (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE) || - (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE)) + if ((reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) || + (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) || + (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)) continue; - if (FDT_DOMAIN_REGION_MAX_COUNT <= val32) - return SBI_EINVAL; - memcpy(®ions[val32++], reg, sizeof(*reg)); + if (preg.max_regions <= preg.region_count) { + err = SBI_EINVAL; + goto fail_free_all; + } + memcpy(&dom->regions[preg.region_count++], reg, sizeof(*reg)); } + dom->fw_region_inited = root.fw_region_inited; /* Read "boot-hart" DT property */ val32 = -1U; @@ -405,10 +432,19 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque) else dom->system_reset_allowed = false; + /* Read "system-suspend-allowed" DT property */ + if (fdt_get_property(fdt, domain_offset, + "system-suspend-allowed", NULL)) + dom->system_suspend_allowed = true; + else + dom->system_suspend_allowed = false; + /* Find /cpus DT node */ cpus_offset = fdt_path_offset(fdt, "/cpus"); - if (cpus_offset < 0) - return cpus_offset; + if (cpus_offset < 0) { + err = cpus_offset; + goto fail_free_all; + } /* HART to domain assignment mask based on CPU DT nodes */ sbi_hartmask_clear_all(&assign_mask); @@ -424,22 +460,35 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque) continue; val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len); - if (!val || len < 4) - return SBI_EINVAL; + if (!val || len < 4) { + err = SBI_EINVAL; + goto fail_free_all; + } doffset = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val)); - if (doffset < 0) - return doffset; + if (doffset < 0) { + err = doffset; + goto fail_free_all; + } if (doffset == domain_offset) sbi_hartmask_set_hart(val32, &assign_mask); } - /* Increment domains count */ - fdt_domains_count++; - /* Register the domain */ - return sbi_domain_register(dom, &assign_mask); + err = sbi_domain_register(dom, &assign_mask); + if (err) + goto fail_free_all; + + return 0; + +fail_free_all: + sbi_free(mask); +fail_free_regions: + sbi_free(dom->regions); +fail_free_domain: + sbi_free(dom); + return err; } int fdt_domains_populate(void *fdt) diff --git a/lib/utils/fdt/fdt_fixup.c b/lib/utils/fdt/fdt_fixup.c index 41f6cbb7023..e213dedba61 100644 --- a/lib/utils/fdt/fdt_fixup.c +++ b/lib/utils/fdt/fdt_fixup.c @@ -1,3 +1,4 @@ + // SPDX-License-Identifier: BSD-2-Clause /* * fdt_fixup.c - Flat Device Tree parsing helper routines @@ -14,10 +15,96 @@ #include #include #include +#include #include #include #include +int fdt_add_cpu_idle_states(void *fdt, const struct sbi_cpu_idle_state *state) +{ + int cpu_node, cpus_node, err, idle_states_node; + uint32_t count, phandle; + + err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 1024); + if (err < 0) + return err; + + err = fdt_find_max_phandle(fdt, &phandle); + phandle++; + if (err < 0) + return err; + + cpus_node = fdt_path_offset(fdt, "/cpus"); + if (cpus_node < 0) + return cpus_node; + + /* Do nothing if the idle-states node already exists. */ + idle_states_node = fdt_subnode_offset(fdt, cpus_node, "idle-states"); + if (idle_states_node >= 0) + return 0; + + /* Create the idle-states node and its child nodes. */ + idle_states_node = fdt_add_subnode(fdt, cpus_node, "idle-states"); + if (idle_states_node < 0) + return idle_states_node; + + for (count = 0; state->name; count++, phandle++, state++) { + int idle_state_node; + + idle_state_node = fdt_add_subnode(fdt, idle_states_node, + state->name); + if (idle_state_node < 0) + return idle_state_node; + + fdt_setprop_string(fdt, idle_state_node, "compatible", + "riscv,idle-state"); + fdt_setprop_u32(fdt, idle_state_node, + "riscv,sbi-suspend-param", + state->suspend_param); + if (state->local_timer_stop) + fdt_setprop_empty(fdt, idle_state_node, + "local-timer-stop"); + fdt_setprop_u32(fdt, idle_state_node, "entry-latency-us", + state->entry_latency_us); + fdt_setprop_u32(fdt, idle_state_node, "exit-latency-us", + state->exit_latency_us); + fdt_setprop_u32(fdt, idle_state_node, "min-residency-us", + state->min_residency_us); + if (state->wakeup_latency_us) + fdt_setprop_u32(fdt, idle_state_node, + "wakeup-latency-us", + state->wakeup_latency_us); + fdt_setprop_u32(fdt, idle_state_node, "phandle", phandle); + } + + if (count == 0) + return 0; + + /* Link each cpu node to the idle state nodes. */ + fdt_for_each_subnode(cpu_node, fdt, cpus_node) { + const char *device_type; + fdt32_t *value; + + /* Only process child nodes with device_type = "cpu". */ + device_type = fdt_getprop(fdt, cpu_node, "device_type", NULL); + if (!device_type || strcmp(device_type, "cpu")) + continue; + + /* Allocate space for the list of phandles. */ + err = fdt_setprop_placeholder(fdt, cpu_node, "cpu-idle-states", + count * sizeof(phandle), + (void **)&value); + if (err < 0) + return err; + + /* Fill in the phandles of the idle state nodes. */ + for (uint32_t i = 0; i < count; ++i) + value[i] = cpu_to_fdt32(phandle - count + i); + } + + return 0; +} + void fdt_cpu_fixup(void *fdt) { struct sbi_domain *dom = sbi_domain_thishart_ptr(); @@ -123,7 +210,7 @@ void fdt_plic_fixup(void *fdt) static int fdt_resv_memory_update_node(void *fdt, unsigned long addr, unsigned long size, int index, - int parent, bool no_map) + int parent) { int na = fdt_address_cells(fdt, 0); int ns = fdt_size_cells(fdt, 0); @@ -152,16 +239,14 @@ static int fdt_resv_memory_update_node(void *fdt, unsigned long addr, if (subnode < 0) return subnode; - if (no_map) { - /* - * Tell operating system not to create a virtual - * mapping of the region as part of its standard - * mapping of system memory. - */ - err = fdt_setprop_empty(fdt, subnode, "no-map"); - if (err < 0) - return err; - } + /* + * Tell operating system not to create a virtual + * mapping of the region as part of its standard + * mapping of system memory. + */ + err = fdt_setprop_empty(fdt, subnode, "no-map"); + if (err < 0) + return err; /* encode the property value */ val = reg; @@ -199,9 +284,10 @@ int fdt_reserved_memory_fixup(void *fdt) { struct sbi_domain_memregion *reg; struct sbi_domain *dom = sbi_domain_thishart_ptr(); - struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + unsigned long filtered_base[PMP_COUNT] = { 0 }; + unsigned char filtered_order[PMP_COUNT] = { 0 }; unsigned long addr, size; - int err, parent, i; + int err, parent, i, j; int na = fdt_address_cells(fdt, 0); int ns = fdt_size_cells(fdt, 0); @@ -259,42 +345,41 @@ int fdt_reserved_memory_fixup(void *fdt) /* Ignore MMIO or READABLE or WRITABLE or EXECUTABLE regions */ if (reg->flags & SBI_DOMAIN_MEMREGION_MMIO) continue; - if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE) + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) continue; - if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE) + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) continue; - if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE) + if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE) continue; + if (i >= PMP_COUNT) { + sbi_printf("%s: Too many memory regions to fixup.\n", + __func__); + return SBI_ENOSPC; + } + + bool overlap = false; addr = reg->base; - size = 1UL << reg->order; - fdt_resv_memory_update_node(fdt, addr, size, i, parent, - (sbi_hart_pmp_count(scratch)) ? false : true); - i++; + for (j = 0; j < i; j++) { + if (addr == filtered_base[j] + && filtered_order[j] < reg->order) { + overlap = true; + filtered_order[j] = reg->order; + break; + } + } + + if (!overlap) { + filtered_base[i] = reg->base; + filtered_order[i] = reg->order; + i++; + } } - return 0; -} - -int fdt_reserved_memory_nomap_fixup(void *fdt) -{ - int parent, subnode; - int err; - - /* Locate the reserved memory node */ - parent = fdt_path_offset(fdt, "/reserved-memory"); - if (parent < 0) - return parent; - - fdt_for_each_subnode(subnode, fdt, parent) { - /* - * Tell operating system not to create a virtual - * mapping of the region as part of its standard - * mapping of system memory. - */ - err = fdt_setprop_empty(fdt, subnode, "no-map"); - if (err < 0) - return err; + for (j = 0; j < i; j++) { + addr = filtered_base[j]; + size = 1UL << filtered_order[j]; + fdt_resv_memory_update_node(fdt, addr, size, j, parent); } return 0; diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c index 48bc2fe4c28..61a41003ae6 100644 --- a/lib/utils/fdt/fdt_helper.c +++ b/lib/utils/fdt/fdt_helper.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -313,6 +314,116 @@ int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq) return 0; } +static int fdt_get_isa_string(void *fdt, unsigned int hartid, + const char **isa_string) +{ + int err, cpu_offset, cpus_offset, len; + u32 c_hartid; + const fdt32_t *val; + + if (!fdt) + return SBI_EINVAL; + + cpus_offset = fdt_path_offset(fdt, "/cpus"); + if (cpus_offset < 0) + return cpus_offset; + + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { + err = fdt_parse_hart_id(fdt, cpu_offset, &c_hartid); + if (err) + continue; + + if (!fdt_node_is_enabled(fdt, cpu_offset)) + continue; + + if (c_hartid == hartid) { + val = fdt_getprop(fdt, cpu_offset, "riscv,isa", &len); + if (val && len > 0) { + *isa_string = (const char *)val; + return 0; + } + } + } + + return SBI_EINVAL; +} + +#define RISCV_ISA_EXT_NAME_LEN_MAX 32 + +int fdt_parse_isa_extensions(void *fdt, unsigned int hartid, + unsigned long *extensions) +{ + size_t i, j, isa_len; + char mstr[RISCV_ISA_EXT_NAME_LEN_MAX]; + const char *isa = NULL; + + if (fdt_get_isa_string(fdt, hartid, &isa)) + return SBI_EINVAL; + + if (!isa) + return SBI_EINVAL; + + i = 0; + isa_len = strlen(isa); + + if (isa[i] == 'r' || isa[i] == 'R') + i++; + else + return SBI_EINVAL; + + if (isa[i] == 'v' || isa[i] == 'V') + i++; + else + return SBI_EINVAL; + + if (isa[i] == '3' || isa[i+1] == '2') + i += 2; + else if (isa[i] == '6' || isa[i+1] == '4') + i += 2; + else + return SBI_EINVAL; + + /* Skip base ISA extensions */ + for (; i < isa_len; i++) { + if (isa[i] == '_') + break; + } + + while (i < isa_len) { + if (isa[i] != '_') { + i++; + continue; + } + + /* Skip the '_' character */ + i++; + + /* Extract the multi-letter extension name */ + j = 0; + while ((i < isa_len) && (isa[i] != '_') && + (j < (sizeof(mstr) - 1))) + mstr[j++] = isa[i++]; + mstr[j] = '\0'; + + /* Skip empty multi-letter extension name */ + if (!j) + continue; + +#define SET_ISA_EXT_MAP(name, bit) \ + do { \ + if (!strcmp(mstr, name)) { \ + __set_bit(bit, extensions); \ + continue; \ + } \ + } while (false) \ + + SET_ISA_EXT_MAP("smepmp", SBI_HART_EXT_SMEPMP); +#undef SET_ISA_EXT_MAP + } + + return 0; +} + static int fdt_parse_uart_node_common(void *fdt, int nodeoffset, struct platform_uart_data *uart, unsigned long default_freq, @@ -689,7 +800,7 @@ int fdt_parse_imsic_node(void *fdt, int nodeoff, struct imsic_data *imsic) break; regs->addr = reg_addr; regs->size = reg_size; - }; + } if (!imsic->regs[0].size) return SBI_EINVAL; diff --git a/lib/utils/fdt/fdt_pmu.c b/lib/utils/fdt/fdt_pmu.c index 4829f2127ac..83301bb5c1a 100644 --- a/lib/utils/fdt/fdt_pmu.c +++ b/lib/utils/fdt/fdt_pmu.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #define FDT_PMU_HW_EVENT_MAX (SBI_PMU_HW_EVENT_MAX * 2) diff --git a/lib/utils/gpio/Kconfig b/lib/utils/gpio/Kconfig index 38a9d75c829..1a437e67d97 100644 --- a/lib/utils/gpio/Kconfig +++ b/lib/utils/gpio/Kconfig @@ -10,10 +10,17 @@ config FDT_GPIO if FDT_GPIO +config FDT_GPIO_DESIGNWARE + bool "DesignWare GPIO driver" + default n + config FDT_GPIO_SIFIVE bool "SiFive GPIO FDT driver" default n +config FDT_GPIO_STARFIVE + bool "StarFive GPIO FDT driver" + default n endif config GPIO diff --git a/lib/utils/gpio/fdt_gpio_designware.c b/lib/utils/gpio/fdt_gpio_designware.c new file mode 100644 index 00000000000..018e2d54035 --- /dev/null +++ b/lib/utils/gpio/fdt_gpio_designware.c @@ -0,0 +1,140 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 SiFive + * + * GPIO driver for Synopsys DesignWare APB GPIO + * + * Authors: + * Ben Dooks + */ + +#include + +#include +#include + +#include +#include + +#define DW_GPIO_CHIP_MAX 4 /* need 1 per bank in use */ +#define DW_GPIO_PINS_MAX 32 + +#define DW_GPIO_DDR 0x4 +#define DW_GPIO_DR 0x0 +#define DW_GPIO_BIT(_b) (1UL << (_b)) + +struct dw_gpio_chip { + void *dr; + void *ext; + struct gpio_chip chip; +}; + +extern struct fdt_gpio fdt_gpio_designware; + +static unsigned int dw_gpio_chip_count; +static struct dw_gpio_chip dw_gpio_chip_array[DW_GPIO_CHIP_MAX]; + +#define pin_to_chip(__p) container_of((__p)->chip, struct dw_gpio_chip, chip); + +static int dw_gpio_direction_output(struct gpio_pin *gp, int value) +{ + struct dw_gpio_chip *chip = pin_to_chip(gp); + unsigned long v; + + v = readl(chip->dr + DW_GPIO_DR); + if (!value) + v &= ~DW_GPIO_BIT(gp->offset); + else + v |= DW_GPIO_BIT(gp->offset); + writel(v, chip->dr + DW_GPIO_DR); + + /* the DR is output only so we can set it then the DDR to set + * the data direction, to avoid glitches. + */ + v = readl(chip->dr + DW_GPIO_DDR); + v |= DW_GPIO_BIT(gp->offset); + writel(v, chip->dr + DW_GPIO_DDR); + + return 0; +} + +static void dw_gpio_set(struct gpio_pin *gp, int value) +{ + struct dw_gpio_chip *chip = pin_to_chip(gp); + unsigned long v; + + v = readl(chip->dr + DW_GPIO_DR); + if (!value) + v &= ~DW_GPIO_BIT(gp->offset); + else + v |= DW_GPIO_BIT(gp->offset); + writel(v, chip->dr + DW_GPIO_DR); +} + +/* notes: + * each sub node is a bank and has ngpios or snpns,nr-gpios and a reg property + * with the compatible `snps,dw-apb-gpio-port`. + * bank A is the only one with irq support but we're not using it here +*/ + +static int dw_gpio_init_bank(void *fdt, int nodeoff, u32 phandle, + const struct fdt_match *match) +{ + struct dw_gpio_chip *chip; + const fdt32_t *val; + uint64_t addr; + int rc, poff, nr_pins, bank, len; + + if (dw_gpio_chip_count >= DW_GPIO_CHIP_MAX) + return SBI_ENOSPC; + + /* need to get parent for the address property */ + poff = fdt_parent_offset(fdt, nodeoff); + if (poff < 0) + return SBI_EINVAL; + + rc = fdt_get_node_addr_size(fdt, poff, 0, &addr, NULL); + if (rc) + return rc; + + val = fdt_getprop(fdt, nodeoff, "reg", &len); + if (!val || len <= 0) + return SBI_EINVAL; + bank = fdt32_to_cpu(*val); + + val = fdt_getprop(fdt, nodeoff, "snps,nr-gpios", &len); + if (!val) + val = fdt_getprop(fdt, nodeoff, "ngpios", &len); + if (!val || len <= 0) + return SBI_EINVAL; + nr_pins = fdt32_to_cpu(*val); + + chip = &dw_gpio_chip_array[dw_gpio_chip_count]; + + chip->dr = (void *)(uintptr_t)addr + (bank * 0xc); + chip->ext = (void *)(uintptr_t)addr + (bank * 4) + 0x50; + chip->chip.driver = &fdt_gpio_designware; + chip->chip.id = phandle; + chip->chip.ngpio = nr_pins; + chip->chip.set = dw_gpio_set; + chip->chip.direction_output = dw_gpio_direction_output; + rc = gpio_chip_add(&chip->chip); + if (rc) + return rc; + + dw_gpio_chip_count++; + return 0; +} + +/* since we're only probed when used, match on port not main controller node */ +static const struct fdt_match dw_gpio_match[] = { + { .compatible = "snps,dw-apb-gpio-port" }, + { }, +}; + +struct fdt_gpio fdt_gpio_designware = { + .match_table = dw_gpio_match, + .xlate = fdt_gpio_simple_xlate, + .init = dw_gpio_init_bank, +}; diff --git a/lib/utils/gpio/fdt_gpio_sifive.c b/lib/utils/gpio/fdt_gpio_sifive.c index 677f0fa78b6..5e3f39d77b9 100644 --- a/lib/utils/gpio/fdt_gpio_sifive.c +++ b/lib/utils/gpio/fdt_gpio_sifive.c @@ -9,11 +9,10 @@ #include #include +#include #include #include -#define SIFIVE_GPIO_CHIP_MAX 2 - #define SIFIVE_GPIO_PINS_MIN 1 #define SIFIVE_GPIO_PINS_MAX 32 #define SIFIVE_GPIO_PINS_DEF 16 @@ -27,9 +26,6 @@ struct sifive_gpio_chip { struct gpio_chip chip; }; -static unsigned int sifive_gpio_chip_count; -static struct sifive_gpio_chip sifive_gpio_chip_array[SIFIVE_GPIO_CHIP_MAX]; - static int sifive_gpio_direction_output(struct gpio_pin *gp, int value) { unsigned int v; @@ -73,13 +69,15 @@ static int sifive_gpio_init(void *fdt, int nodeoff, u32 phandle, struct sifive_gpio_chip *chip; uint64_t addr; - if (SIFIVE_GPIO_CHIP_MAX <= sifive_gpio_chip_count) - return SBI_ENOSPC; - chip = &sifive_gpio_chip_array[sifive_gpio_chip_count]; + chip = sbi_zalloc(sizeof(*chip)); + if (!chip) + return SBI_ENOMEM; rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL); - if (rc) + if (rc) { + sbi_free(chip); return rc; + } chip->addr = addr; chip->chip.driver = &fdt_gpio_sifive; @@ -88,10 +86,11 @@ static int sifive_gpio_init(void *fdt, int nodeoff, u32 phandle, chip->chip.direction_output = sifive_gpio_direction_output; chip->chip.set = sifive_gpio_set; rc = gpio_chip_add(&chip->chip); - if (rc) + if (rc) { + sbi_free(chip); return rc; + } - sifive_gpio_chip_count++; return 0; } diff --git a/lib/utils/gpio/fdt_gpio_starfive.c b/lib/utils/gpio/fdt_gpio_starfive.c new file mode 100644 index 00000000000..f430b1352c9 --- /dev/null +++ b/lib/utils/gpio/fdt_gpio_starfive.c @@ -0,0 +1,117 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Starfive + * + * Authors: + * Minda.chen + */ + +#include +#include +#include +#include +#include +#include + +#define STARFIVE_GPIO_PINS_DEF 64 +#define STARFIVE_GPIO_OUTVAL 0x40 +#define STARFIVE_GPIO_MASK 0xff +#define STARFIVE_GPIO_REG_SHIFT_MASK 0x3 +#define STARFIVE_GPIO_SHIFT_BITS 0x3 + +struct starfive_gpio_chip { + unsigned long addr; + struct gpio_chip chip; +}; + +static int starfive_gpio_direction_output(struct gpio_pin *gp, int value) +{ + u32 val; + unsigned long reg_addr; + u32 bit_mask, shift_bits; + struct starfive_gpio_chip *chip = + container_of(gp->chip, struct starfive_gpio_chip, chip); + + /* set out en*/ + reg_addr = chip->addr + gp->offset; + reg_addr &= ~(STARFIVE_GPIO_REG_SHIFT_MASK); + + val = readl((void *)(reg_addr)); + shift_bits = (gp->offset & STARFIVE_GPIO_REG_SHIFT_MASK) + << STARFIVE_GPIO_SHIFT_BITS; + bit_mask = STARFIVE_GPIO_MASK << shift_bits; + + val = readl((void *)reg_addr); + val &= ~bit_mask; + writel(val, (void *)reg_addr); + + return 0; +} + +static void starfive_gpio_set(struct gpio_pin *gp, int value) +{ + u32 val; + unsigned long reg_addr; + u32 bit_mask, shift_bits; + struct starfive_gpio_chip *chip = + container_of(gp->chip, struct starfive_gpio_chip, chip); + + reg_addr = chip->addr + gp->offset; + reg_addr &= ~(STARFIVE_GPIO_REG_SHIFT_MASK); + + shift_bits = (gp->offset & STARFIVE_GPIO_REG_SHIFT_MASK) + << STARFIVE_GPIO_SHIFT_BITS; + bit_mask = STARFIVE_GPIO_MASK << shift_bits; + /* set output value */ + val = readl((void *)(reg_addr + STARFIVE_GPIO_OUTVAL)); + val &= ~bit_mask; + val |= value << shift_bits; + writel(val, (void *)(reg_addr + STARFIVE_GPIO_OUTVAL)); +} + +extern struct fdt_gpio fdt_gpio_starfive; + +static int starfive_gpio_init(void *fdt, int nodeoff, u32 phandle, + const struct fdt_match *match) +{ + int rc; + struct starfive_gpio_chip *chip; + u64 addr; + + chip = sbi_zalloc(sizeof(*chip)); + if (!chip) + return SBI_ENOMEM; + + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL); + if (rc) { + sbi_free(chip); + return rc; + } + + chip->addr = addr; + chip->chip.driver = &fdt_gpio_starfive; + chip->chip.id = phandle; + chip->chip.ngpio = STARFIVE_GPIO_PINS_DEF; + chip->chip.direction_output = starfive_gpio_direction_output; + chip->chip.set = starfive_gpio_set; + rc = gpio_chip_add(&chip->chip); + if (rc) { + sbi_free(chip); + return rc; + } + + return 0; +} + +static const struct fdt_match starfive_gpio_match[] = { + { .compatible = "starfive,jh7110-sys-pinctrl" }, + { .compatible = "starfive,iomux-pinctrl" }, + { }, +}; + +struct fdt_gpio fdt_gpio_starfive = { + .match_table = starfive_gpio_match, + .xlate = fdt_gpio_simple_xlate, + .init = starfive_gpio_init, +}; diff --git a/lib/utils/gpio/objects.mk b/lib/utils/gpio/objects.mk index eedd699d85f..76647a591d5 100644 --- a/lib/utils/gpio/objects.mk +++ b/lib/utils/gpio/objects.mk @@ -10,7 +10,13 @@ libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio.o libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio_drivers.o +carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_DESIGNWARE) += fdt_gpio_designware +libsbiutils-objs-$(CONFIG_FDT_GPIO_DESIGNWARE) += gpio/fdt_gpio_designware.o + carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_SIFIVE) += fdt_gpio_sifive libsbiutils-objs-$(CONFIG_FDT_GPIO_SIFIVE) += gpio/fdt_gpio_sifive.o +carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_STARFIVE) += fdt_gpio_starfive +libsbiutils-objs-$(CONFIG_FDT_GPIO_STARFIVE) += gpio/fdt_gpio_starfive.o + libsbiutils-objs-$(CONFIG_GPIO) += gpio/gpio.o diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig index 46a3454c034..7fa32fcf967 100644 --- a/lib/utils/i2c/Kconfig +++ b/lib/utils/i2c/Kconfig @@ -14,8 +14,16 @@ config FDT_I2C_SIFIVE bool "SiFive I2C FDT driver" default n +config FDT_I2C_DW + bool "Synopsys Designware I2C FDT driver" + select I2C_DW + default n endif +config I2C_DW + bool "Synopsys Designware I2C support" + default n + config I2C bool "I2C support" default n diff --git a/lib/utils/i2c/dw_i2c.c b/lib/utils/i2c/dw_i2c.c new file mode 100644 index 00000000000..e2ffc71259a --- /dev/null +++ b/lib/utils/i2c/dw_i2c.c @@ -0,0 +1,190 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 starfivetech.com + * + * Authors: + * Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DW_IC_CON 0x00 +#define DW_IC_TAR 0x04 +#define DW_IC_SAR 0x08 +#define DW_IC_DATA_CMD 0x10 +#define DW_IC_SS_SCL_HCNT 0x14 +#define DW_IC_SS_SCL_LCNT 0x18 +#define DW_IC_FS_SCL_HCNT 0x1c +#define DW_IC_FS_SCL_LCNT 0x20 +#define DW_IC_HS_SCL_HCNT 0x24 +#define DW_IC_HS_SCL_LCNT 0x28 +#define DW_IC_INTR_STAT 0x2c +#define DW_IC_INTR_MASK 0x30 +#define DW_IC_RAW_INTR_STAT 0x34 +#define DW_IC_RX_TL 0x38 +#define DW_IC_TX_TL 0x3c +#define DW_IC_CLR_INTR 0x40 +#define DW_IC_CLR_RX_UNDER 0x44 +#define DW_IC_CLR_RX_OVER 0x48 +#define DW_IC_CLR_TX_OVER 0x4c +#define DW_IC_CLR_RD_REQ 0x50 +#define DW_IC_CLR_TX_ABRT 0x54 +#define DW_IC_CLR_RX_DONE 0x58 +#define DW_IC_CLR_ACTIVITY 0x5c +#define DW_IC_CLR_STOP_DET 0x60 +#define DW_IC_CLR_START_DET 0x64 +#define DW_IC_CLR_GEN_CALL 0x68 +#define DW_IC_ENABLE 0x6c +#define DW_IC_STATUS 0x70 +#define DW_IC_TXFLR 0x74 +#define DW_IC_RXFLR 0x78 +#define DW_IC_SDA_HOLD 0x7c +#define DW_IC_TX_ABRT_SOURCE 0x80 +#define DW_IC_ENABLE_STATUS 0x9c +#define DW_IC_CLR_RESTART_DET 0xa8 +#define DW_IC_COMP_PARAM_1 0xf4 +#define DW_IC_COMP_VERSION 0xf8 + +#define DW_I2C_STATUS_TXFIFO_EMPTY BIT(2) +#define DW_I2C_STATUS_RXFIFO_NOT_EMPTY BIT(3) + +#define IC_DATA_CMD_READ BIT(8) +#define IC_DATA_CMD_STOP BIT(9) +#define IC_DATA_CMD_RESTART BIT(10) +#define IC_INT_STATUS_STOPDET BIT(9) + +static inline void dw_i2c_setreg(struct dw_i2c_adapter *adap, + u8 reg, u32 value) +{ + writel(value, (void *)adap->addr + reg); +} + +static inline u32 dw_i2c_getreg(struct dw_i2c_adapter *adap, + u32 reg) +{ + return readl((void *)adap->addr + reg); +} + +static int dw_i2c_adapter_poll(struct dw_i2c_adapter *adap, + u32 mask, u32 addr, + bool inverted) +{ + unsigned int timeout = 10; /* msec */ + int count = 0; + u32 val; + + do { + val = dw_i2c_getreg(adap, addr); + if (inverted) { + if (!(val & mask)) + return 0; + } else { + if (val & mask) + return 0; + } + sbi_timer_udelay(2); + count += 1; + if (count == (timeout * 1000)) + return SBI_ETIMEDOUT; + } while (1); +} + +#define dw_i2c_adapter_poll_rxrdy(adap) \ + dw_i2c_adapter_poll(adap, DW_I2C_STATUS_RXFIFO_NOT_EMPTY, DW_IC_STATUS, 0) +#define dw_i2c_adapter_poll_txfifo_ready(adap) \ + dw_i2c_adapter_poll(adap, DW_I2C_STATUS_TXFIFO_EMPTY, DW_IC_STATUS, 0) + +static int dw_i2c_write_addr(struct dw_i2c_adapter *adap, u8 addr) +{ + dw_i2c_setreg(adap, DW_IC_ENABLE, 0); + dw_i2c_setreg(adap, DW_IC_TAR, addr); + dw_i2c_setreg(adap, DW_IC_ENABLE, 1); + + return 0; +} + +static int dw_i2c_adapter_read(struct i2c_adapter *ia, u8 addr, + u8 reg, u8 *buffer, int len) +{ + struct dw_i2c_adapter *adap = + container_of(ia, struct dw_i2c_adapter, adapter); + int rc; + + dw_i2c_write_addr(adap, addr); + + rc = dw_i2c_adapter_poll_txfifo_ready(adap); + if (rc) + return rc; + + /* set register address */ + dw_i2c_setreg(adap, DW_IC_DATA_CMD, reg); + + /* set value */ + while (len) { + if (len == 1) + dw_i2c_setreg(adap, DW_IC_DATA_CMD, + IC_DATA_CMD_READ | IC_DATA_CMD_STOP); + else + dw_i2c_setreg(adap, DW_IC_DATA_CMD, IC_DATA_CMD_READ); + + rc = dw_i2c_adapter_poll_rxrdy(adap); + if (rc) + return rc; + + *buffer = dw_i2c_getreg(adap, DW_IC_DATA_CMD) & 0xff; + buffer++; + len--; + } + + return 0; +} + +static int dw_i2c_adapter_write(struct i2c_adapter *ia, u8 addr, + u8 reg, u8 *buffer, int len) +{ + struct dw_i2c_adapter *adap = + container_of(ia, struct dw_i2c_adapter, adapter); + int rc; + + dw_i2c_write_addr(adap, addr); + + rc = dw_i2c_adapter_poll_txfifo_ready(adap); + if (rc) + return rc; + + /* set register address */ + dw_i2c_setreg(adap, DW_IC_DATA_CMD, reg); + + while (len) { + rc = dw_i2c_adapter_poll_txfifo_ready(adap); + if (rc) + return rc; + + if (len == 1) + dw_i2c_setreg(adap, DW_IC_DATA_CMD, *buffer | IC_DATA_CMD_STOP); + else + dw_i2c_setreg(adap, DW_IC_DATA_CMD, *buffer); + + buffer++; + len--; + } + rc = dw_i2c_adapter_poll_txfifo_ready(adap); + + return rc; +} + +int dw_i2c_init(struct i2c_adapter *adapter, int nodeoff) +{ + adapter->id = nodeoff; + adapter->write = dw_i2c_adapter_write; + adapter->read = dw_i2c_adapter_read; + + return i2c_adapter_add(adapter); +} diff --git a/lib/utils/i2c/fdt_i2c_dw.c b/lib/utils/i2c/fdt_i2c_dw.c new file mode 100644 index 00000000000..99b2ddb8411 --- /dev/null +++ b/lib/utils/i2c/fdt_i2c_dw.c @@ -0,0 +1,58 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 starfivetech.com + * + * Authors: + * Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include + +extern struct fdt_i2c_adapter fdt_i2c_adapter_dw; + +static int fdt_dw_i2c_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + struct dw_i2c_adapter *adapter; + u64 addr; + + adapter = sbi_zalloc(sizeof(*adapter)); + if (!adapter) + return SBI_ENOMEM; + + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL); + if (rc) { + sbi_free(adapter); + return rc; + } + + adapter->addr = addr; + adapter->adapter.driver = &fdt_i2c_adapter_dw; + + rc = dw_i2c_init(&adapter->adapter, nodeoff); + if (rc) { + sbi_free(adapter); + return rc; + } + + return 0; +} + +static const struct fdt_match fdt_dw_i2c_match[] = { + { .compatible = "snps,designware-i2c" }, + { .compatible = "starfive,jh7110-i2c" }, + { }, +}; + +struct fdt_i2c_adapter fdt_i2c_adapter_dw = { + .match_table = fdt_dw_i2c_match, + .init = fdt_dw_i2c_init, +}; diff --git a/lib/utils/i2c/fdt_i2c_sifive.c b/lib/utils/i2c/fdt_i2c_sifive.c index 195541cf2f0..b85b245c75f 100644 --- a/lib/utils/i2c/fdt_i2c_sifive.c +++ b/lib/utils/i2c/fdt_i2c_sifive.c @@ -9,12 +9,11 @@ #include #include +#include #include #include #include -#define SIFIVE_I2C_ADAPTER_MAX 2 - #define SIFIVE_I2C_PRELO 0x00 #define SIFIVE_I2C_PREHI 0x04 #define SIFIVE_I2C_CTR 0x08 @@ -47,10 +46,6 @@ struct sifive_i2c_adapter { struct i2c_adapter adapter; }; -static unsigned int sifive_i2c_adapter_count; -static struct sifive_i2c_adapter - sifive_i2c_adapter_array[SIFIVE_I2C_ADAPTER_MAX]; - extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive; static inline void sifive_i2c_setreg(struct sifive_i2c_adapter *adap, @@ -244,14 +239,15 @@ static int sifive_i2c_init(void *fdt, int nodeoff, struct sifive_i2c_adapter *adapter; uint64_t addr; - if (sifive_i2c_adapter_count >= SIFIVE_I2C_ADAPTER_MAX) - return SBI_ENOSPC; - - adapter = &sifive_i2c_adapter_array[sifive_i2c_adapter_count]; + adapter = sbi_zalloc(sizeof(*adapter)); + if (!adapter) + return SBI_ENOMEM; rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL); - if (rc) + if (rc) { + sbi_free(adapter); return rc; + } adapter->addr = addr; adapter->adapter.driver = &fdt_i2c_adapter_sifive; @@ -259,10 +255,11 @@ static int sifive_i2c_init(void *fdt, int nodeoff, adapter->adapter.write = sifive_i2c_adapter_write; adapter->adapter.read = sifive_i2c_adapter_read; rc = i2c_adapter_add(&adapter->adapter); - if (rc) + if (rc) { + sbi_free(adapter); return rc; + } - sifive_i2c_adapter_count++; return 0; } diff --git a/lib/utils/i2c/objects.mk b/lib/utils/i2c/objects.mk index a0fbbb5de32..5f7a79ff3d3 100644 --- a/lib/utils/i2c/objects.mk +++ b/lib/utils/i2c/objects.mk @@ -14,3 +14,8 @@ libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c_adapter_drivers.o carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SIFIVE) += fdt_i2c_adapter_sifive libsbiutils-objs-$(CONFIG_FDT_I2C_SIFIVE) += i2c/fdt_i2c_sifive.o + +carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_DW) += fdt_i2c_adapter_dw +libsbiutils-objs-$(CONFIG_FDT_I2C_DW) += i2c/fdt_i2c_dw.o + +libsbiutils-objs-$(CONFIG_I2C_DW) += i2c/dw_i2c.o diff --git a/lib/utils/ipi/aclint_mswi.c b/lib/utils/ipi/aclint_mswi.c index 832e223f08d..140a49b5f52 100644 --- a/lib/utils/ipi/aclint_mswi.c +++ b/lib/utils/ipi/aclint_mswi.c @@ -12,21 +12,30 @@ #include #include #include -#include #include +#include #include #include -static struct aclint_mswi_data *mswi_hartid2data[SBI_HARTMASK_MAX_BITS]; +static unsigned long mswi_ptr_offset; + +#define mswi_get_hart_data_ptr(__scratch) \ + sbi_scratch_read_type((__scratch), void *, mswi_ptr_offset) + +#define mswi_set_hart_data_ptr(__scratch, __mswi) \ + sbi_scratch_write_type((__scratch), void *, mswi_ptr_offset, (__mswi)) static void mswi_ipi_send(u32 target_hart) { u32 *msip; + struct sbi_scratch *scratch; struct aclint_mswi_data *mswi; - if (SBI_HARTMASK_MAX_BITS <= target_hart) + scratch = sbi_hartid_to_scratch(target_hart); + if (!scratch) return; - mswi = mswi_hartid2data[target_hart]; + + mswi = mswi_get_hart_data_ptr(scratch); if (!mswi) return; @@ -38,11 +47,14 @@ static void mswi_ipi_send(u32 target_hart) static void mswi_ipi_clear(u32 target_hart) { u32 *msip; + struct sbi_scratch *scratch; struct aclint_mswi_data *mswi; - if (SBI_HARTMASK_MAX_BITS <= target_hart) + scratch = sbi_hartid_to_scratch(target_hart); + if (!scratch) return; - mswi = mswi_hartid2data[target_hart]; + + mswi = mswi_get_hart_data_ptr(scratch); if (!mswi) return; @@ -69,26 +81,45 @@ int aclint_mswi_cold_init(struct aclint_mswi_data *mswi) { u32 i; int rc; + struct sbi_scratch *scratch; unsigned long pos, region_size; struct sbi_domain_memregion reg; /* Sanity checks */ if (!mswi || (mswi->addr & (ACLINT_MSWI_ALIGN - 1)) || (mswi->size < (mswi->hart_count * sizeof(u32))) || - (mswi->first_hartid >= SBI_HARTMASK_MAX_BITS) || - (mswi->hart_count > ACLINT_MSWI_MAX_HARTS)) + (!mswi->hart_count || mswi->hart_count > ACLINT_MSWI_MAX_HARTS)) return SBI_EINVAL; - /* Update MSWI hartid table */ - for (i = 0; i < mswi->hart_count; i++) - mswi_hartid2data[mswi->first_hartid + i] = mswi; + /* Allocate scratch space pointer */ + if (!mswi_ptr_offset) { + mswi_ptr_offset = sbi_scratch_alloc_type_offset(void *); + if (!mswi_ptr_offset) + return SBI_ENOMEM; + } + + /* Update MSWI pointer in scratch space */ + for (i = 0; i < mswi->hart_count; i++) { + scratch = sbi_hartid_to_scratch(mswi->first_hartid + i); + /* + * We don't need to fail if scratch pointer is not available + * because we might be dealing with hartid of a HART disabled + * in the device tree. + */ + if (!scratch) + continue; + mswi_set_hart_data_ptr(scratch, mswi); + } /* Add MSWI regions to the root domain */ for (pos = 0; pos < mswi->size; pos += ACLINT_MSWI_ALIGN) { region_size = ((mswi->size - pos) < ACLINT_MSWI_ALIGN) ? (mswi->size - pos) : ACLINT_MSWI_ALIGN; sbi_domain_memregion_init(mswi->addr + pos, region_size, - SBI_DOMAIN_MEMREGION_MMIO, ®); + (SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_WRITABLE), + ®); rc = sbi_domain_root_add_memregion(®); if (rc) return rc; diff --git a/lib/utils/ipi/fdt_ipi.c b/lib/utils/ipi/fdt_ipi.c index 66dc51082ed..7762ababe59 100644 --- a/lib/utils/ipi/fdt_ipi.c +++ b/lib/utils/ipi/fdt_ipi.c @@ -16,24 +16,17 @@ extern struct fdt_ipi *fdt_ipi_drivers[]; extern unsigned long fdt_ipi_drivers_size; -static struct fdt_ipi dummy = { - .match_table = NULL, - .cold_init = NULL, - .warm_init = NULL, - .exit = NULL, -}; - -static struct fdt_ipi *current_driver = &dummy; +static struct fdt_ipi *current_driver = NULL; void fdt_ipi_exit(void) { - if (current_driver->exit) + if (current_driver && current_driver->exit) current_driver->exit(); } static int fdt_ipi_warm_init(void) { - if (current_driver->warm_init) + if (current_driver && current_driver->warm_init) return current_driver->warm_init(); return 0; } @@ -51,20 +44,28 @@ static int fdt_ipi_cold_init(void) noff = -1; while ((noff = fdt_find_match(fdt, noff, drv->match_table, &match)) >= 0) { - if (drv->cold_init) { - rc = drv->cold_init(fdt, noff, match); - if (rc == SBI_ENODEV) - continue; - if (rc) - return rc; - } + /* drv->cold_init must not be NULL */ + if (drv->cold_init == NULL) + return SBI_EFAIL; + + rc = drv->cold_init(fdt, noff, match); + if (rc == SBI_ENODEV) + continue; + if (rc) + return rc; current_driver = drv; - } - if (current_driver != &dummy) - break; + /* + * We will have multiple IPI devices on multi-die or + * multi-socket systems so we cannot break here. + */ + } } + /* + * On some single-hart system there is no need for ipi, + * so we cannot return a failure here + */ return 0; } diff --git a/lib/utils/ipi/fdt_ipi_mswi.c b/lib/utils/ipi/fdt_ipi_mswi.c index 4dc91f2cb96..e3a63782465 100644 --- a/lib/utils/ipi/fdt_ipi_mswi.c +++ b/lib/utils/ipi/fdt_ipi_mswi.c @@ -8,15 +8,11 @@ */ #include +#include #include #include #include -#define MSWI_MAX_NR 16 - -static unsigned long mswi_count = 0; -static struct aclint_mswi_data mswi[MSWI_MAX_NR]; - static int ipi_mswi_cold_init(void *fdt, int nodeoff, const struct fdt_match *match) { @@ -24,15 +20,17 @@ static int ipi_mswi_cold_init(void *fdt, int nodeoff, unsigned long offset; struct aclint_mswi_data *ms; - if (MSWI_MAX_NR <= mswi_count) - return SBI_ENOSPC; - ms = &mswi[mswi_count]; + ms = sbi_zalloc(sizeof(*ms)); + if (!ms) + return SBI_ENOMEM; rc = fdt_parse_aclint_node(fdt, nodeoff, false, &ms->addr, &ms->size, NULL, NULL, &ms->first_hartid, &ms->hart_count); - if (rc) + if (rc) { + sbi_free(ms); return rc; + } if (match->data) { /* Adjust MSWI address and size for CLINT device */ @@ -44,10 +42,11 @@ static int ipi_mswi_cold_init(void *fdt, int nodeoff, } rc = aclint_mswi_cold_init(ms); - if (rc) + if (rc) { + sbi_free(ms); return rc; + } - mswi_count++; return 0; } diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c index 0a8469b618b..d7fd9928044 100644 --- a/lib/utils/irqchip/aplic.c +++ b/lib/utils/irqchip/aplic.c @@ -269,7 +269,10 @@ int aplic_cold_irqchip_init(struct aplic_data *aplic) (last_deleg_irq == aplic->num_source) && (first_deleg_irq == 1))) { sbi_domain_memregion_init(aplic->addr, aplic->size, - SBI_DOMAIN_MEMREGION_MMIO, ®); + (SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_WRITABLE), + ®); rc = sbi_domain_root_add_memregion(®); if (rc) return rc; diff --git a/lib/utils/irqchip/fdt_irqchip_aplic.c b/lib/utils/irqchip/fdt_irqchip_aplic.c index 965f02394c4..583a3e30fa5 100644 --- a/lib/utils/irqchip/fdt_irqchip_aplic.c +++ b/lib/utils/irqchip/fdt_irqchip_aplic.c @@ -11,15 +11,11 @@ #include #include #include +#include #include #include #include -#define APLIC_MAX_NR 16 - -static unsigned long aplic_count = 0; -static struct aplic_data aplic[APLIC_MAX_NR]; - static int irqchip_aplic_warm_init(void) { /* Nothing to do here. */ @@ -32,15 +28,23 @@ static int irqchip_aplic_cold_init(void *fdt, int nodeoff, int rc; struct aplic_data *pd; - if (APLIC_MAX_NR <= aplic_count) - return SBI_ENOSPC; - pd = &aplic[aplic_count++]; + pd = sbi_zalloc(sizeof(*pd)); + if (!pd) + return SBI_ENOMEM; rc = fdt_parse_aplic_node(fdt, nodeoff, pd); if (rc) - return rc; + goto fail_free_data; + + rc = aplic_cold_irqchip_init(pd); + if (rc) + goto fail_free_data; + + return 0; - return aplic_cold_irqchip_init(pd); +fail_free_data: + sbi_free(pd); + return rc; } static const struct fdt_match irqchip_aplic_match[] = { diff --git a/lib/utils/irqchip/fdt_irqchip_imsic.c b/lib/utils/irqchip/fdt_irqchip_imsic.c index 6020ac0b992..4eb1854556b 100644 --- a/lib/utils/irqchip/fdt_irqchip_imsic.c +++ b/lib/utils/irqchip/fdt_irqchip_imsic.c @@ -11,16 +11,11 @@ #include #include #include -#include +#include #include #include #include -#define IMSIC_MAX_NR 16 - -static unsigned long imsic_count = 0; -static struct imsic_data imsic[IMSIC_MAX_NR]; - static int irqchip_imsic_update_hartid_table(void *fdt, int nodeoff, struct imsic_data *id) { @@ -48,8 +43,6 @@ static int irqchip_imsic_update_hartid_table(void *fdt, int nodeoff, err = fdt_parse_hart_id(fdt, cpu_offset, &hartid); if (err) return SBI_EINVAL; - if (SBI_HARTMASK_MAX_BITS <= hartid) - return SBI_EINVAL; switch (hwirq) { case IRQ_M_EXT: @@ -71,27 +64,27 @@ static int irqchip_imsic_cold_init(void *fdt, int nodeoff, int rc; struct imsic_data *id; - if (IMSIC_MAX_NR <= imsic_count) - return SBI_ENOSPC; - id = &imsic[imsic_count]; + id = sbi_zalloc(sizeof(*id)); + if (!id) + return SBI_ENOMEM; rc = fdt_parse_imsic_node(fdt, nodeoff, id); - if (rc) - return rc; - if (!id->targets_mmode) - return 0; - - rc = irqchip_imsic_update_hartid_table(fdt, nodeoff, id); - if (rc) - return rc; + if (rc || !id->targets_mmode) + goto fail_free_data; rc = imsic_cold_irqchip_init(id); if (rc) - return rc; + goto fail_free_data; - imsic_count++; + rc = irqchip_imsic_update_hartid_table(fdt, nodeoff, id); + if (rc) + goto fail_free_data; return 0; + +fail_free_data: + sbi_free(id); + return rc; } static const struct fdt_match irqchip_imsic_match[] = { diff --git a/lib/utils/irqchip/fdt_irqchip_plic.c b/lib/utils/irqchip/fdt_irqchip_plic.c index 1aadf9190ca..829c5ee2034 100644 --- a/lib/utils/irqchip/fdt_irqchip_plic.c +++ b/lib/utils/irqchip/fdt_irqchip_plic.c @@ -11,59 +11,78 @@ #include #include #include -#include +#include +#include #include #include #include -#define PLIC_MAX_NR 16 +static unsigned long plic_ptr_offset; -static unsigned long plic_count = 0; -static struct plic_data plic[PLIC_MAX_NR]; +#define plic_get_hart_data_ptr(__scratch) \ + sbi_scratch_read_type((__scratch), void *, plic_ptr_offset) -static struct plic_data *plic_hartid2data[SBI_HARTMASK_MAX_BITS]; -static int plic_hartid2context[SBI_HARTMASK_MAX_BITS][2]; +#define plic_set_hart_data_ptr(__scratch, __plic) \ + sbi_scratch_write_type((__scratch), void *, plic_ptr_offset, (__plic)) + +static unsigned long plic_mcontext_offset; + +#define plic_get_hart_mcontext(__scratch) \ + (sbi_scratch_read_type((__scratch), long, plic_mcontext_offset) - 1) + +#define plic_set_hart_mcontext(__scratch, __mctx) \ + sbi_scratch_write_type((__scratch), long, plic_mcontext_offset, (__mctx) + 1) + +static unsigned long plic_scontext_offset; + +#define plic_get_hart_scontext(__scratch) \ + (sbi_scratch_read_type((__scratch), long, plic_scontext_offset) - 1) + +#define plic_set_hart_scontext(__scratch, __sctx) \ + sbi_scratch_write_type((__scratch), long, plic_scontext_offset, (__sctx) + 1) void fdt_plic_priority_save(u8 *priority, u32 num) { - struct plic_data *plic = plic_hartid2data[current_hartid()]; + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); - plic_priority_save(plic, priority, num); + plic_priority_save(plic_get_hart_data_ptr(scratch), priority, num); } void fdt_plic_priority_restore(const u8 *priority, u32 num) { - struct plic_data *plic = plic_hartid2data[current_hartid()]; + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); - plic_priority_restore(plic, priority, num); + plic_priority_restore(plic_get_hart_data_ptr(scratch), priority, num); } void fdt_plic_context_save(bool smode, u32 *enable, u32 *threshold, u32 num) { - u32 hartid = current_hartid(); + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); - plic_context_save(plic_hartid2data[hartid], - plic_hartid2context[hartid][smode], + plic_context_save(plic_get_hart_data_ptr(scratch), + smode ? plic_get_hart_scontext(scratch) : + plic_get_hart_mcontext(scratch), enable, threshold, num); } void fdt_plic_context_restore(bool smode, const u32 *enable, u32 threshold, u32 num) { - u32 hartid = current_hartid(); + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); - plic_context_restore(plic_hartid2data[hartid], - plic_hartid2context[hartid][smode], + plic_context_restore(plic_get_hart_data_ptr(scratch), + smode ? plic_get_hart_scontext(scratch) : + plic_get_hart_mcontext(scratch), enable, threshold, num); } static int irqchip_plic_warm_init(void) { - u32 hartid = current_hartid(); + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); - return plic_warm_irqchip_init(plic_hartid2data[hartid], - plic_hartid2context[hartid][0], - plic_hartid2context[hartid][1]); + return plic_warm_irqchip_init(plic_get_hart_data_ptr(scratch), + plic_get_hart_mcontext(scratch), + plic_get_hart_scontext(scratch)); } static int irqchip_plic_update_hartid_table(void *fdt, int nodeoff, @@ -71,6 +90,7 @@ static int irqchip_plic_update_hartid_table(void *fdt, int nodeoff, { const fdt32_t *val; u32 phandle, hwirq, hartid; + struct sbi_scratch *scratch; int i, err, count, cpu_offset, cpu_intc_offset; val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &count); @@ -94,16 +114,17 @@ static int irqchip_plic_update_hartid_table(void *fdt, int nodeoff, if (err) continue; - if (SBI_HARTMASK_MAX_BITS <= hartid) + scratch = sbi_hartid_to_scratch(hartid); + if (!scratch) continue; - plic_hartid2data[hartid] = pd; + plic_set_hart_data_ptr(scratch, pd); switch (hwirq) { case IRQ_M_EXT: - plic_hartid2context[hartid][0] = i / 2; + plic_set_hart_mcontext(scratch, i / 2); break; case IRQ_S_EXT: - plic_hartid2context[hartid][1] = i / 2; + plic_set_hart_scontext(scratch, i / 2); break; } } @@ -114,16 +135,34 @@ static int irqchip_plic_update_hartid_table(void *fdt, int nodeoff, static int irqchip_plic_cold_init(void *fdt, int nodeoff, const struct fdt_match *match) { - int i, rc; + int rc; struct plic_data *pd; - if (PLIC_MAX_NR <= plic_count) - return SBI_ENOSPC; - pd = &plic[plic_count++]; + if (!plic_ptr_offset) { + plic_ptr_offset = sbi_scratch_alloc_type_offset(void *); + if (!plic_ptr_offset) + return SBI_ENOMEM; + } + + if (!plic_mcontext_offset) { + plic_mcontext_offset = sbi_scratch_alloc_type_offset(long); + if (!plic_mcontext_offset) + return SBI_ENOMEM; + } + + if (!plic_scontext_offset) { + plic_scontext_offset = sbi_scratch_alloc_type_offset(long); + if (!plic_scontext_offset) + return SBI_ENOMEM; + } + + pd = sbi_zalloc(sizeof(*pd)); + if (!pd) + return SBI_ENOMEM; rc = fdt_parse_plic_node(fdt, nodeoff, pd); if (rc) - return rc; + goto fail_free_data; if (match->data) { void (*plic_plat_init)(struct plic_data *) = match->data; @@ -132,17 +171,17 @@ static int irqchip_plic_cold_init(void *fdt, int nodeoff, rc = plic_cold_irqchip_init(pd); if (rc) - return rc; + goto fail_free_data; - if (plic_count == 1) { - for (i = 0; i < SBI_HARTMASK_MAX_BITS; i++) { - plic_hartid2data[i] = NULL; - plic_hartid2context[i][0] = -1; - plic_hartid2context[i][1] = -1; - } - } + rc = irqchip_plic_update_hartid_table(fdt, nodeoff, pd); + if (rc) + goto fail_free_data; + + return 0; - return irqchip_plic_update_hartid_table(fdt, nodeoff, pd); +fail_free_data: + sbi_free(pd); + return rc; } #define THEAD_PLIC_CTRL_REG 0x1ffffc @@ -154,7 +193,8 @@ static void thead_plic_plat_init(struct plic_data *pd) void thead_plic_restore(void) { - struct plic_data *plic = plic_hartid2data[current_hartid()]; + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + struct plic_data *plic = plic_get_hart_data_ptr(scratch); thead_plic_plat_init(plic); } diff --git a/lib/utils/irqchip/imsic.c b/lib/utils/irqchip/imsic.c index 98f2cb63eeb..7fc61d9e8a2 100644 --- a/lib/utils/irqchip/imsic.c +++ b/lib/utils/irqchip/imsic.c @@ -13,10 +13,10 @@ #include #include #include -#include #include #include #include +#include #include #define IMSIC_MMIO_PAGE_LE 0x00 @@ -79,33 +79,65 @@ do { \ csr_clear(CSR_MIREG, __v); \ } while (0) -static struct imsic_data *imsic_hartid2data[SBI_HARTMASK_MAX_BITS]; -static int imsic_hartid2file[SBI_HARTMASK_MAX_BITS]; +static unsigned long imsic_ptr_offset; + +#define imsic_get_hart_data_ptr(__scratch) \ + sbi_scratch_read_type((__scratch), void *, imsic_ptr_offset) + +#define imsic_set_hart_data_ptr(__scratch, __imsic) \ + sbi_scratch_write_type((__scratch), void *, imsic_ptr_offset, (__imsic)) + +static unsigned long imsic_file_offset; + +#define imsic_get_hart_file(__scratch) \ + sbi_scratch_read_type((__scratch), long, imsic_file_offset) + +#define imsic_set_hart_file(__scratch, __file) \ + sbi_scratch_write_type((__scratch), long, imsic_file_offset, (__file)) int imsic_map_hartid_to_data(u32 hartid, struct imsic_data *imsic, int file) { - if (!imsic || !imsic->targets_mmode || - (SBI_HARTMASK_MAX_BITS <= hartid)) + struct sbi_scratch *scratch; + + if (!imsic || !imsic->targets_mmode) return SBI_EINVAL; - imsic_hartid2data[hartid] = imsic; - imsic_hartid2file[hartid] = file; + /* + * We don't need to fail if scratch pointer is not available + * because we might be dealing with hartid of a HART disabled + * in device tree. For HARTs disabled in device tree, the + * imsic_get_data() and imsic_get_target_file() will anyway + * fail. + */ + scratch = sbi_hartid_to_scratch(hartid); + if (!scratch) + return 0; + + imsic_set_hart_data_ptr(scratch, imsic); + imsic_set_hart_file(scratch, file); return 0; } struct imsic_data *imsic_get_data(u32 hartid) { - if (SBI_HARTMASK_MAX_BITS <= hartid) + struct sbi_scratch *scratch; + + scratch = sbi_hartid_to_scratch(hartid); + if (!scratch) return NULL; - return imsic_hartid2data[hartid]; + + return imsic_get_hart_data_ptr(scratch); } int imsic_get_target_file(u32 hartid) { - if ((SBI_HARTMASK_MAX_BITS <= hartid) || - !imsic_hartid2data[hartid]) + struct sbi_scratch *scratch; + + scratch = sbi_hartid_to_scratch(hartid); + if (!scratch) return SBI_ENOENT; - return imsic_hartid2file[hartid]; + + return imsic_get_hart_file(scratch); } static int imsic_external_irqfn(struct sbi_trap_regs *regs) @@ -133,9 +165,16 @@ static void imsic_ipi_send(u32 target_hart) { unsigned long reloff; struct imsic_regs *regs; - struct imsic_data *data = imsic_hartid2data[target_hart]; - int file = imsic_hartid2file[target_hart]; + struct imsic_data *data; + struct sbi_scratch *scratch; + int file; + + scratch = sbi_hartid_to_scratch(target_hart); + if (!scratch) + return; + data = imsic_get_hart_data_ptr(scratch); + file = imsic_get_hart_file(scratch); if (!data || !data->targets_mmode) return; @@ -204,7 +243,7 @@ void imsic_local_irqchip_init(void) int imsic_warm_irqchip_init(void) { - struct imsic_data *imsic = imsic_hartid2data[current_hartid()]; + struct imsic_data *imsic = imsic_get_data(current_hartid()); /* Sanity checks */ if (!imsic || !imsic->targets_mmode) @@ -306,6 +345,20 @@ int imsic_cold_irqchip_init(struct imsic_data *imsic) if (!imsic->targets_mmode) return SBI_EINVAL; + /* Allocate scratch space pointer */ + if (!imsic_ptr_offset) { + imsic_ptr_offset = sbi_scratch_alloc_type_offset(void *); + if (!imsic_ptr_offset) + return SBI_ENOMEM; + } + + /* Allocate scratch space file */ + if (!imsic_file_offset) { + imsic_file_offset = sbi_scratch_alloc_type_offset(long); + if (!imsic_file_offset) + return SBI_ENOMEM; + } + /* Setup external interrupt function for IMSIC */ sbi_irqchip_set_irqfn(imsic_external_irqfn); @@ -313,7 +366,10 @@ int imsic_cold_irqchip_init(struct imsic_data *imsic) for (i = 0; i < IMSIC_MAX_REGS && imsic->regs[i].size; i++) { sbi_domain_memregion_init(imsic->regs[i].addr, imsic->regs[i].size, - SBI_DOMAIN_MEMREGION_MMIO, ®); + (SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_WRITABLE), + ®); rc = sbi_domain_root_add_memregion(®); if (rc) return rc; diff --git a/lib/utils/libfdt/fdt_overlay.c b/lib/utils/libfdt/fdt_overlay.c index d217e79b672..0871e260ff4 100644 --- a/lib/utils/libfdt/fdt_overlay.c +++ b/lib/utils/libfdt/fdt_overlay.c @@ -404,7 +404,7 @@ static int overlay_fixup_one_phandle(void *fdt, void *fdto, name, name_len, poffset, &phandle_prop, sizeof(phandle_prop)); -}; +} /** * overlay_fixup_phandle - Set an overlay phandle to the base one diff --git a/lib/utils/libfdt/libfdt_env.h b/lib/utils/libfdt/libfdt_env.h index f9d9c6783c5..6ebbae01b51 100644 --- a/lib/utils/libfdt/libfdt_env.h +++ b/lib/utils/libfdt/libfdt_env.h @@ -9,6 +9,7 @@ #include #include +#include #define INT_MAX ((int)(~0U >> 1)) #define UINT_MAX ((unsigned int)~0U) @@ -37,49 +38,39 @@ #define strlen sbi_strlen #define strnlen sbi_strnlen -typedef uint16_t FDT_BITWISE fdt16_t; -typedef uint32_t FDT_BITWISE fdt32_t; -typedef uint64_t FDT_BITWISE fdt64_t; - -#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) -#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) -#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ - (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) -#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ - (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ - (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ - (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) +typedef be16_t FDT_BITWISE fdt16_t; +typedef be32_t FDT_BITWISE fdt32_t; +typedef be64_t FDT_BITWISE fdt64_t; static inline uint16_t fdt16_to_cpu(fdt16_t x) { - return (FDT_FORCE uint16_t)CPU_TO_FDT16(x); + return (FDT_FORCE uint16_t)be16_to_cpu(x); } + static inline fdt16_t cpu_to_fdt16(uint16_t x) { - return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x); + return (FDT_FORCE fdt16_t)cpu_to_be16(x); } static inline uint32_t fdt32_to_cpu(fdt32_t x) { - return (FDT_FORCE uint32_t)CPU_TO_FDT32(x); + return (FDT_FORCE uint32_t)be32_to_cpu(x); } + static inline fdt32_t cpu_to_fdt32(uint32_t x) { - return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x); + return (FDT_FORCE fdt32_t)cpu_to_be32(x); } static inline uint64_t fdt64_to_cpu(fdt64_t x) { - return (FDT_FORCE uint64_t)CPU_TO_FDT64(x); + return (FDT_FORCE uint64_t)be64_to_cpu(x); } + static inline fdt64_t cpu_to_fdt64(uint64_t x) { - return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x); + return (FDT_FORCE fdt64_t)cpu_to_be64(x); } -#undef CPU_TO_FDT64 -#undef CPU_TO_FDT32 -#undef CPU_TO_FDT16 -#undef EXTRACT_BYTE #ifdef __APPLE__ #include diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig index 0e0c2c1d7c4..4748cc8504a 100644 --- a/lib/utils/reset/Kconfig +++ b/lib/utils/reset/Kconfig @@ -11,6 +11,7 @@ if FDT_RESET config FDT_RESET_ATCWDT200 bool "Andes WDT FDT reset driver" + depends on SYS_ATCSMU default n config FDT_RESET_GPIO diff --git a/lib/utils/reset/fdt_reset_atcwdt200.c b/lib/utils/reset/fdt_reset_atcwdt200.c index 91acc9f7f11..03d547b9dd7 100644 --- a/lib/utils/reset/fdt_reset_atcwdt200.c +++ b/lib/utils/reset/fdt_reset_atcwdt200.c @@ -16,6 +16,7 @@ #include #include #include +#include #define ATCWDT200_WP_NUM 0x5aa5 #define WREN_REG 0x18 @@ -41,14 +42,8 @@ #define CLK_PCLK (1 << 1) #define WDT_EN (1 << 0) -#define FLASH_BASE 0x80000000ULL -#define SMU_RESET_VEC_LO_OFF 0x50 -#define SMU_RESET_VEC_HI_OFF 0x60 -#define SMU_HARTn_RESET_VEC_LO(n) (SMU_RESET_VEC_LO_OFF + (n * 0x4)) -#define SMU_HARTn_RESET_VEC_HI(n) (SMU_RESET_VEC_HI_OFF + (n * 0x4)) - -static volatile char *wdt_addr; -static volatile char *smu_addr; +static volatile char *wdt_addr = NULL; +static struct smu_data smu = { 0 }; static int ae350_system_reset_check(u32 type, u32 reason) { @@ -66,16 +61,16 @@ static void ae350_system_reset(u32 type, u32 reason) { const struct sbi_platform *plat = sbi_platform_thishart_ptr(); - for (int i = 0; i < sbi_platform_hart_count(plat); i++) { - writel(FLASH_BASE, smu_addr + SMU_HARTn_RESET_VEC_LO(i)); - writel(FLASH_BASE >> 32, smu_addr + SMU_HARTn_RESET_VEC_HI(i)); - } + for (int i = 0; i < sbi_platform_hart_count(plat); i++) + if (smu_set_reset_vector(&smu, FLASH_BASE, i)) + goto fail; /* Program WDT control register */ writew(ATCWDT200_WP_NUM, wdt_addr + WREN_REG); writel(INT_CLK_32768 | INT_EN | RST_CLK_128 | RST_EN | WDT_EN, wdt_addr + CTRL_REG); +fail: sbi_hart_hang(); } @@ -104,7 +99,7 @@ static int atcwdt200_reset_init(void *fdt, int nodeoff, if (fdt_parse_compat_addr(fdt, ®_addr, "andestech,atcsmu")) return SBI_ENODEV; - smu_addr = (volatile char *)(unsigned long)reg_addr; + smu.addr = (unsigned long)reg_addr; sbi_system_reset_add_device(&atcwdt200_reset); diff --git a/lib/utils/serial/fdt_serial.c b/lib/utils/serial/fdt_serial.c index 1a4bf9e87b6..0baa7228117 100644 --- a/lib/utils/serial/fdt_serial.c +++ b/lib/utils/serial/fdt_serial.c @@ -17,13 +17,6 @@ extern struct fdt_serial *fdt_serial_drivers[]; extern unsigned long fdt_serial_drivers_size; -static struct fdt_serial dummy = { - .match_table = NULL, - .init = NULL, -}; - -static struct fdt_serial *current_driver = &dummy; - int fdt_serial_init(void) { const void *prop; @@ -57,20 +50,15 @@ int fdt_serial_init(void) if (!match) continue; - if (drv->init) { - rc = drv->init(fdt, noff, match); - if (rc == SBI_ENODEV) - continue; - if (rc) - return rc; - } - current_driver = drv; - break; - } + /* drv->init must not be NULL */ + if (drv->init == NULL) + return SBI_EFAIL; - /* Check if we found desired driver */ - if (current_driver != &dummy) - goto done; + rc = drv->init(fdt, noff, match); + if (rc == SBI_ENODEV) + continue; + return rc; + } /* Lastly check all DT nodes */ for (pos = 0; pos < fdt_serial_drivers_size; pos++) { @@ -80,17 +68,15 @@ int fdt_serial_init(void) if (noff < 0) continue; - if (drv->init) { - rc = drv->init(fdt, noff, match); - if (rc == SBI_ENODEV) - continue; - if (rc) - return rc; - } - current_driver = drv; - break; + /* drv->init must not be NULL */ + if (drv->init == NULL) + return SBI_EFAIL; + + rc = drv->init(fdt, noff, match); + if (rc == SBI_ENODEV) + continue; + return rc; } -done: - return 0; + return SBI_ENODEV; } diff --git a/lib/utils/serial/fdt_serial_cadence.c b/lib/utils/serial/fdt_serial_cadence.c index 946e5336547..ae59e4c1e9c 100644 --- a/lib/utils/serial/fdt_serial_cadence.c +++ b/lib/utils/serial/fdt_serial_cadence.c @@ -24,6 +24,7 @@ static int serial_cadence_init(void *fdt, int nodeoff, } static const struct fdt_match serial_cadence_match[] = { + { .compatible = "cdns,uart-r1p8", }, { .compatible = "cdns,uart-r1p12" }, { .compatible = "starfive,jh8100-uart" }, { }, diff --git a/lib/utils/serial/semihosting.c b/lib/utils/serial/semihosting.c index 86fa296ce17..ce65887dd72 100644 --- a/lib/utils/serial/semihosting.c +++ b/lib/utils/serial/semihosting.c @@ -15,6 +15,7 @@ #define SYSOPEN 0x01 #define SYSWRITEC 0x03 +#define SYSWRITE 0x05 #define SYSREAD 0x06 #define SYSREADC 0x07 #define SYSERRNO 0x13 @@ -93,6 +94,7 @@ static int semihosting_errno(void) } static int semihosting_infd = SBI_ENODEV; +static int semihosting_outfd = SBI_ENODEV; static long semihosting_open(const char *fname, enum semihosting_open_mode mode) { @@ -141,6 +143,21 @@ static long semihosting_read(long fd, void *memp, size_t len) return len - ret; } +static long semihosting_write(long fd, const void *memp, size_t len) +{ + long ret; + struct semihosting_rdwr_s write; + + write.fd = fd; + write.memp = (void *)memp; + write.len = len; + + ret = semihosting_trap(SYSWRITE, &write); + if (ret < 0) + return semihosting_errno(); + return len - ret; +} + /* clang-format on */ static void semihosting_putc(char ch) @@ -148,6 +165,24 @@ static void semihosting_putc(char ch) semihosting_trap(SYSWRITEC, &ch); } +static unsigned long semihosting_puts(const char *str, unsigned long len) +{ + char ch; + long ret; + unsigned long i; + + if (semihosting_outfd < 0) { + for (i = 0; i < len; i++) { + ch = str[i]; + semihosting_trap(SYSWRITEC, &ch); + } + ret = len; + } else + ret = semihosting_write(semihosting_outfd, str, len); + + return (ret < 0) ? 0 : ret; +} + static int semihosting_getc(void) { char ch = 0; @@ -165,12 +200,14 @@ static int semihosting_getc(void) static struct sbi_console_device semihosting_console = { .name = "semihosting", .console_putc = semihosting_putc, + .console_puts = semihosting_puts, .console_getc = semihosting_getc }; int semihosting_init(void) { semihosting_infd = semihosting_open(":tt", MODE_READ); + semihosting_outfd = semihosting_open(":tt", MODE_WRITE); sbi_console_set_device(&semihosting_console); diff --git a/lib/utils/sys/Kconfig b/lib/utils/sys/Kconfig index ee85b1a3106..76190f8c52a 100644 --- a/lib/utils/sys/Kconfig +++ b/lib/utils/sys/Kconfig @@ -2,6 +2,10 @@ menu "System Device Support" +config SYS_ATCSMU + bool "Andes System Management Unit (SMU) support" + default n + config SYS_HTIF bool "Host transfere interface (HTIF) support" default n diff --git a/lib/utils/sys/atcsmu.c b/lib/utils/sys/atcsmu.c new file mode 100644 index 00000000000..8ddd88d5f52 --- /dev/null +++ b/lib/utils/sys/atcsmu.c @@ -0,0 +1,92 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2023 Andes Technology Corporation + * + * Authors: + * Yu Chien Peter Lin + */ + +#include +#include +#include +#include +#include + +inline int smu_set_wakeup_events(struct smu_data *smu, u32 events, u32 hartid) +{ + if (smu) { + writel(events, (void *)(smu->addr + PCSm_WE_OFFSET(hartid))); + return 0; + } else + return SBI_EINVAL; +} + +inline bool smu_support_sleep_mode(struct smu_data *smu, u32 sleep_mode, + u32 hartid) +{ + u32 pcs_cfg; + + if (!smu) { + sbi_printf("%s(): Failed to access smu_data\n", __func__); + return false; + } + + pcs_cfg = readl((void *)(smu->addr + PCSm_CFG_OFFSET(hartid))); + + switch (sleep_mode) { + case LIGHTSLEEP_MODE: + if (EXTRACT_FIELD(pcs_cfg, PCS_CFG_LIGHT_SLEEP) == 0) { + sbi_printf( + "SMU: hart%d (PCS%d) does not support light sleep mode\n", + hartid, hartid + 3); + return false; + } + break; + case DEEPSLEEP_MODE: + if (EXTRACT_FIELD(pcs_cfg, PCS_CFG_DEEP_SLEEP) == 0) { + sbi_printf( + "SMU: hart%d (PCS%d) does not support deep sleep mode\n", + hartid, hartid + 3); + return false; + } + break; + } + + return true; +} + +inline int smu_set_command(struct smu_data *smu, u32 pcs_ctl, u32 hartid) +{ + if (smu) { + writel(pcs_ctl, (void *)(smu->addr + PCSm_CTL_OFFSET(hartid))); + return 0; + } else + return SBI_EINVAL; +} + +inline int smu_set_reset_vector(struct smu_data *smu, ulong wakeup_addr, + u32 hartid) +{ + u32 vec_lo, vec_hi; + u64 reset_vector; + + if (!smu) + return SBI_EINVAL; + + writel(wakeup_addr, (void *)(smu->addr + HARTn_RESET_VEC_LO(hartid))); + writel((u64)wakeup_addr >> 32, + (void *)(smu->addr + HARTn_RESET_VEC_HI(hartid))); + + vec_lo = readl((void *)(smu->addr + HARTn_RESET_VEC_LO(hartid))); + vec_hi = readl((void *)(smu->addr + HARTn_RESET_VEC_HI(hartid))); + reset_vector = ((u64)vec_hi << 32) | vec_lo; + + if (reset_vector != (u64)wakeup_addr) { + sbi_printf( + "hard%d (PCS%d): Failed to program the reset vector.\n", + hartid, hartid + 3); + return SBI_EFAIL; + } else + return 0; +} diff --git a/lib/utils/sys/objects.mk b/lib/utils/sys/objects.mk index 9f67aee2777..03d6740b142 100644 --- a/lib/utils/sys/objects.mk +++ b/lib/utils/sys/objects.mk @@ -9,3 +9,4 @@ libsbiutils-objs-$(CONFIG_SYS_HTIF) += sys/htif.o libsbiutils-objs-$(CONFIG_SYS_SIFIVE_TEST) += sys/sifive_test.o +libsbiutils-objs-$(CONFIG_SYS_ATCSMU) += sys/atcsmu.o diff --git a/lib/utils/timer/aclint_mtimer.c b/lib/utils/timer/aclint_mtimer.c index 1846a9a7cc9..271e6252fef 100644 --- a/lib/utils/timer/aclint_mtimer.c +++ b/lib/utils/timer/aclint_mtimer.c @@ -13,12 +13,17 @@ #include #include #include -#include -#include +#include #include #include -static struct aclint_mtimer_data *mtimer_hartid2data[SBI_HARTMASK_MAX_BITS]; +static unsigned long mtimer_ptr_offset; + +#define mtimer_get_hart_data_ptr(__scratch) \ + sbi_scratch_read_type((__scratch), void *, mtimer_ptr_offset) + +#define mtimer_set_hart_data_ptr(__scratch, __mtimer) \ + sbi_scratch_write_type((__scratch), void *, mtimer_ptr_offset, (__mtimer)) #if __riscv_xlen != 32 static u64 mtimer_time_rd64(volatile u64 *addr) @@ -53,30 +58,54 @@ static void mtimer_time_wr32(bool timecmp, u64 value, volatile u64 *addr) static u64 mtimer_value(void) { - struct aclint_mtimer_data *mt = mtimer_hartid2data[current_hartid()]; - u64 *time_val = (void *)mt->mtime_addr; + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + struct aclint_mtimer_data *mt; + + mt = mtimer_get_hart_data_ptr(scratch); + if (!mt) + return 0; /* Read MTIMER Time Value */ - return mt->time_rd(time_val); + return mt->time_rd((void *)mt->mtime_addr); } static void mtimer_event_stop(void) { u32 target_hart = current_hartid(); - struct aclint_mtimer_data *mt = mtimer_hartid2data[target_hart]; - u64 *time_cmp = (void *)mt->mtimecmp_addr; + struct sbi_scratch *scratch; + struct aclint_mtimer_data *mt; + u64 *time_cmp; + + scratch = sbi_hartid_to_scratch(target_hart); + if (!scratch) + return; + + mt = mtimer_get_hart_data_ptr(scratch); + if (!mt) + return; /* Clear MTIMER Time Compare */ + time_cmp = (void *)mt->mtimecmp_addr; mt->time_wr(true, -1ULL, &time_cmp[target_hart - mt->first_hartid]); } static void mtimer_event_start(u64 next_event) { u32 target_hart = current_hartid(); - struct aclint_mtimer_data *mt = mtimer_hartid2data[target_hart]; - u64 *time_cmp = (void *)mt->mtimecmp_addr; + struct sbi_scratch *scratch; + struct aclint_mtimer_data *mt; + u64 *time_cmp; + + scratch = sbi_hartid_to_scratch(target_hart); + if (!scratch) + return; + + mt = mtimer_get_hart_data_ptr(scratch); + if (!mt) + return; /* Program MTIMER Time Compare */ + time_cmp = (void *)mt->mtimecmp_addr; mt->time_wr(true, next_event, &time_cmp[target_hart - mt->first_hartid]); } @@ -126,8 +155,14 @@ int aclint_mtimer_warm_init(void) { u64 *mt_time_cmp; u32 target_hart = current_hartid(); - struct aclint_mtimer_data *mt = mtimer_hartid2data[target_hart]; + struct sbi_scratch *scratch; + struct aclint_mtimer_data *mt; + scratch = sbi_hartid_to_scratch(target_hart); + if (!scratch) + return SBI_ENOENT; + + mt = mtimer_get_hart_data_ptr(scratch); if (!mt) return SBI_ENODEV; @@ -147,6 +182,7 @@ int aclint_mtimer_cold_init(struct aclint_mtimer_data *mt, { u32 i; int rc; + struct sbi_scratch *scratch; /* Sanity checks */ if (!mt || @@ -155,12 +191,18 @@ int aclint_mtimer_cold_init(struct aclint_mtimer_data *mt, (mt->mtime_size && (mt->mtime_size & (ACLINT_MTIMER_ALIGN - 1))) || (mt->mtimecmp_addr & (ACLINT_MTIMER_ALIGN - 1)) || (mt->mtimecmp_size & (ACLINT_MTIMER_ALIGN - 1)) || - (mt->first_hartid >= SBI_HARTMASK_MAX_BITS) || (mt->hart_count > ACLINT_MTIMER_MAX_HARTS)) return SBI_EINVAL; if (reference && mt->mtime_freq != reference->mtime_freq) return SBI_EINVAL; + /* Allocate scratch space pointer */ + if (!mtimer_ptr_offset) { + mtimer_ptr_offset = sbi_scratch_alloc_type_offset(void *); + if (!mtimer_ptr_offset) + return SBI_ENOMEM; + } + /* Initialize private data */ aclint_mtimer_set_reference(mt, reference); mt->time_rd = mtimer_time_rd32; @@ -174,9 +216,18 @@ int aclint_mtimer_cold_init(struct aclint_mtimer_data *mt, } #endif - /* Update MTIMER hartid table */ - for (i = 0; i < mt->hart_count; i++) - mtimer_hartid2data[mt->first_hartid + i] = mt; + /* Update MTIMER pointer in scratch space */ + for (i = 0; i < mt->hart_count; i++) { + scratch = sbi_hartid_to_scratch(mt->first_hartid + i); + /* + * We don't need to fail if scratch pointer is not available + * because we might be dealing with hartid of a HART disabled + * in the device tree. + */ + if (!scratch) + continue; + mtimer_set_hart_data_ptr(scratch, mt); + } if (!mt->mtime_size) { /* Disable reading mtime when mtime is not available */ @@ -188,26 +239,34 @@ int aclint_mtimer_cold_init(struct aclint_mtimer_data *mt, rc = sbi_domain_root_add_memrange(mt->mtimecmp_addr, mt->mtime_size + mt->mtimecmp_size, MTIMER_REGION_ALIGN, - SBI_DOMAIN_MEMREGION_MMIO); + (SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_WRITABLE)); if (rc) return rc; } else if (mt->mtimecmp_addr == (mt->mtime_addr + mt->mtime_size)) { rc = sbi_domain_root_add_memrange(mt->mtime_addr, mt->mtime_size + mt->mtimecmp_size, MTIMER_REGION_ALIGN, - SBI_DOMAIN_MEMREGION_MMIO); + (SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_WRITABLE)); if (rc) return rc; } else { rc = sbi_domain_root_add_memrange(mt->mtime_addr, mt->mtime_size, MTIMER_REGION_ALIGN, - SBI_DOMAIN_MEMREGION_MMIO); + (SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_WRITABLE)); if (rc) return rc; rc = sbi_domain_root_add_memrange(mt->mtimecmp_addr, mt->mtimecmp_size, MTIMER_REGION_ALIGN, - SBI_DOMAIN_MEMREGION_MMIO); + (SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_WRITABLE)); if (rc) return rc; } diff --git a/lib/utils/timer/fdt_timer.c b/lib/utils/timer/fdt_timer.c index 4695c0f02aa..62426a70fb0 100644 --- a/lib/utils/timer/fdt_timer.c +++ b/lib/utils/timer/fdt_timer.c @@ -16,24 +16,17 @@ extern struct fdt_timer *fdt_timer_drivers[]; extern unsigned long fdt_timer_drivers_size; -static struct fdt_timer dummy = { - .match_table = NULL, - .cold_init = NULL, - .warm_init = NULL, - .exit = NULL, -}; - -static struct fdt_timer *current_driver = &dummy; +static struct fdt_timer *current_driver = NULL; void fdt_timer_exit(void) { - if (current_driver->exit) + if (current_driver && current_driver->exit) current_driver->exit(); } static int fdt_timer_warm_init(void) { - if (current_driver->warm_init) + if (current_driver && current_driver->warm_init) return current_driver->warm_init(); return 0; } @@ -51,20 +44,28 @@ static int fdt_timer_cold_init(void) noff = -1; while ((noff = fdt_find_match(fdt, noff, drv->match_table, &match)) >= 0) { - if (drv->cold_init) { - rc = drv->cold_init(fdt, noff, match); - if (rc == SBI_ENODEV) - continue; - if (rc) - return rc; - } + /* drv->cold_init must not be NULL */ + if (drv->cold_init == NULL) + return SBI_EFAIL; + + rc = drv->cold_init(fdt, noff, match); + if (rc == SBI_ENODEV) + continue; + if (rc) + return rc; current_driver = drv; - } - if (current_driver != &dummy) - break; + /* + * We will have multiple timer devices on multi-die or + * multi-socket systems so we cannot break here. + */ + } } + /* + * We can't fail here since systems with Sstc might not provide + * mtimer/clint DT node in the device tree. + */ return 0; } diff --git a/lib/utils/timer/fdt_timer_mtimer.c b/lib/utils/timer/fdt_timer_mtimer.c index 5244f98f5e7..9eaa11d855c 100644 --- a/lib/utils/timer/fdt_timer_mtimer.c +++ b/lib/utils/timer/fdt_timer_mtimer.c @@ -9,44 +9,54 @@ #include #include +#include +#include #include #include #include -#define MTIMER_MAX_NR 16 - struct timer_mtimer_quirks { unsigned int mtime_offset; bool has_64bit_mmio; bool without_mtime; }; -static unsigned long mtimer_count = 0; -static struct aclint_mtimer_data mtimer[MTIMER_MAX_NR]; +struct timer_mtimer_node { + struct sbi_dlist head; + struct aclint_mtimer_data data; +}; +static SBI_LIST_HEAD(mtn_list); + static struct aclint_mtimer_data *mt_reference = NULL; static int timer_mtimer_cold_init(void *fdt, int nodeoff, const struct fdt_match *match) { - int i, rc; + int rc; unsigned long addr[2], size[2]; + struct timer_mtimer_node *mtn, *n; struct aclint_mtimer_data *mt; - if (MTIMER_MAX_NR <= mtimer_count) - return SBI_ENOSPC; - mt = &mtimer[mtimer_count]; + mtn = sbi_zalloc(sizeof(*mtn)); + if (!mtn) + return SBI_ENOMEM; + mt = &mtn->data; rc = fdt_parse_aclint_node(fdt, nodeoff, true, &addr[0], &size[0], &addr[1], &size[1], &mt->first_hartid, &mt->hart_count); - if (rc) + if (rc) { + sbi_free(mtn); return rc; + } mt->has_64bit_mmio = true; mt->has_shared_mtime = false; rc = fdt_parse_timebase_frequency(fdt, &mt->mtime_freq); - if (rc) + if (rc) { + sbi_free(mtn); return rc; + } if (match->data) { /* SiFive CLINT */ const struct timer_mtimer_quirks *quirks = match->data; @@ -77,8 +87,8 @@ static int timer_mtimer_cold_init(void *fdt, int nodeoff, /* Check if MTIMER device has shared MTIME address */ if (mt->mtime_size) { mt->has_shared_mtime = false; - for (i = 0; i < mtimer_count; i++) { - if (mtimer[i].mtime_addr == mt->mtime_addr) { + sbi_list_for_each_entry(n, &mtn_list, head) { + if (n->data.mtime_addr == mt->mtime_addr) { mt->has_shared_mtime = true; break; } @@ -90,8 +100,10 @@ static int timer_mtimer_cold_init(void *fdt, int nodeoff, /* Initialize the MTIMER device */ rc = aclint_mtimer_cold_init(mt, mt_reference); - if (rc) + if (rc) { + sbi_free(mtn); return rc; + } /* * Select first MTIMER device with no associated HARTs as our @@ -106,16 +118,17 @@ static int timer_mtimer_cold_init(void *fdt, int nodeoff, * Set reference for already propbed MTIMER devices * with non-shared MTIME */ - for (i = 0; i < mtimer_count; i++) - if (!mtimer[i].has_shared_mtime) - aclint_mtimer_set_reference(&mtimer[i], mt); + sbi_list_for_each_entry(n, &mtn_list, head) { + if (!n->data.has_shared_mtime) + aclint_mtimer_set_reference(&n->data, mt); + } } /* Explicitly sync-up MTIMER devices not associated with any HARTs */ if (!mt->hart_count) aclint_mtimer_sync(mt); - mtimer_count++; + sbi_list_add_tail(&mtn->head, &mtn_list); return 0; } diff --git a/platform/fpga/ariane/platform.c b/platform/fpga/ariane/platform.c index 1e341c2049f..975528f1372 100644 --- a/platform/fpga/ariane/platform.c +++ b/platform/fpga/ariane/platform.c @@ -185,5 +185,6 @@ const struct sbi_platform platform = { .features = SBI_PLATFORM_DEFAULT_FEATURES, .hart_count = ARIANE_HART_COUNT, .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .heap_size = SBI_PLATFORM_DEFAULT_HEAP_SIZE(ARIANE_HART_COUNT), .platform_ops_addr = (unsigned long)&platform_ops }; diff --git a/platform/fpga/openpiton/platform.c b/platform/fpga/openpiton/platform.c index 57ae6980924..e59dc992fe3 100644 --- a/platform/fpga/openpiton/platform.c +++ b/platform/fpga/openpiton/platform.c @@ -220,5 +220,7 @@ const struct sbi_platform platform = { .features = SBI_PLATFORM_DEFAULT_FEATURES, .hart_count = OPENPITON_DEFAULT_HART_COUNT, .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .heap_size = + SBI_PLATFORM_DEFAULT_HEAP_SIZE(OPENPITON_DEFAULT_HART_COUNT), .platform_ops_addr = (unsigned long)&platform_ops }; diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig index 62c7a2d8051..72768edeb92 100644 --- a/platform/generic/Kconfig +++ b/platform/generic/Kconfig @@ -30,10 +30,13 @@ config PLATFORM_ALLWINNER_D1 config PLATFORM_ANDES_AE350 bool "Andes AE350 support" + select SYS_ATCSMU default n config PLATFORM_RENESAS_RZFIVE bool "Renesas RZ/Five support" + select ANDES45_PMA + select ANDES_SBI default n config PLATFORM_SIFIVE_FU540 @@ -45,4 +48,10 @@ config PLATFORM_SIFIVE_FU740 depends on FDT_RESET && FDT_I2C default n +config PLATFORM_STARFIVE_JH7110 + bool "StarFive JH7110 support" + default n + +source "$(OPENSBI_SRC_DIR)/platform/generic/andes/Kconfig" + endif diff --git a/platform/generic/allwinner/sun20i-d1.c b/platform/generic/allwinner/sun20i-d1.c index 1da9e5b5914..0f0a9f347a6 100644 --- a/platform/generic/allwinner/sun20i-d1.c +++ b/platform/generic/allwinner/sun20i-d1.c @@ -6,12 +6,15 @@ #include #include +#include #include #include #include #include #include #include +#include +#include #include #include @@ -69,10 +72,10 @@ static void sun20i_d1_csr_restore(void) * PLIC */ -#define PLIC_SOURCES 176 -#define PLIC_IE_WORDS ((PLIC_SOURCES + 31) / 32) +#define PLIC_SOURCES 175 +#define PLIC_IE_WORDS (PLIC_SOURCES / 32 + 1) -static u8 plic_priority[PLIC_SOURCES]; +static u8 plic_priority[1 + PLIC_SOURCES]; static u32 plic_sie[PLIC_IE_WORDS]; static u32 plic_threshold; @@ -202,6 +205,24 @@ static int sun20i_d1_final_init(bool cold_boot, const struct fdt_match *match) return 0; } +static const struct sbi_cpu_idle_state sun20i_d1_cpu_idle_states[] = { + { + .name = "cpu-nonretentive", + .suspend_param = SBI_HSM_SUSPEND_NON_RET_DEFAULT, + .local_timer_stop = true, + .entry_latency_us = 40, + .exit_latency_us = 67, + .min_residency_us = 1100, + .wakeup_latency_us = 67, + }, + { } +}; + +static int sun20i_d1_fdt_fixup(void *fdt, const struct fdt_match *match) +{ + return fdt_add_cpu_idle_states(fdt, sun20i_d1_cpu_idle_states); +} + static void thead_c9xx_pmu_ctr_enable_irq(uint32_t ctr_idx) { unsigned long mip_val; @@ -265,5 +286,6 @@ static const struct fdt_match sun20i_d1_match[] = { const struct platform_override sun20i_d1 = { .match_table = sun20i_d1_match, .final_init = sun20i_d1_final_init, + .fdt_fixup = sun20i_d1_fdt_fixup, .extensions_init = sun20i_d1_extensions_init, }; diff --git a/platform/generic/andes/Kconfig b/platform/generic/andes/Kconfig new file mode 100644 index 00000000000..a91fb9c95dc --- /dev/null +++ b/platform/generic/andes/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: BSD-2-Clause + +config ANDES45_PMA + bool "Andes PMA support" + default n + +config ANDES_SBI + bool "Andes SBI support" + default n diff --git a/platform/generic/andes/ae350.c b/platform/generic/andes/ae350.c index cf7f6f2a0f5..01bd02d5b96 100644 --- a/platform/generic/andes/ae350.c +++ b/platform/generic/andes/ae350.c @@ -10,6 +10,105 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct smu_data smu = { 0 }; +extern void __ae350_enable_coherency_warmboot(void); +extern void __ae350_disable_coherency(void); + +static __always_inline bool is_andes25(void) +{ + ulong marchid = csr_read(CSR_MARCHID); + return !!(EXTRACT_FIELD(marchid, CSR_MARCHID_MICROID) == 0xa25); +} + +static int ae350_hart_start(u32 hartid, ulong saddr) +{ + /* Don't send wakeup command at boot-time */ + if (!sbi_init_count(hartid) || (is_andes25() && hartid == 0)) + return sbi_ipi_raw_send(hartid); + + /* Write wakeup command to the sleep hart */ + smu_set_command(&smu, WAKEUP_CMD, hartid); + + return 0; +} + +static int ae350_hart_stop(void) +{ + int rc; + u32 hartid = current_hartid(); + + /** + * For Andes AX25MP, the hart0 shares power domain with + * L2-cache, instead of turning it off, it should fall + * through and jump to warmboot_addr. + */ + if (is_andes25() && hartid == 0) + return SBI_ENOTSUPP; + + if (!smu_support_sleep_mode(&smu, DEEPSLEEP_MODE, hartid)) + return SBI_ENOTSUPP; + + /** + * disable all events, the current hart will be + * woken up from reset vector when other hart + * writes its PCS (power control slot) control + * register + */ + smu_set_wakeup_events(&smu, 0x0, hartid); + smu_set_command(&smu, DEEP_SLEEP_CMD, hartid); + + rc = smu_set_reset_vector(&smu, (ulong)__ae350_enable_coherency_warmboot, + hartid); + if (rc) + goto fail; + + __ae350_disable_coherency(); + + wfi(); + +fail: + /* It should never reach here */ + sbi_hart_hang(); + return 0; +} + +static const struct sbi_hsm_device andes_smu = { + .name = "andes_smu", + .hart_start = ae350_hart_start, + .hart_stop = ae350_hart_stop, +}; + +static void ae350_hsm_device_init(void) +{ + int rc; + void *fdt; + + fdt = fdt_get_address(); + + rc = fdt_parse_compat_addr(fdt, (uint64_t *)&smu.addr, + "andestech,atcsmu"); + + if (!rc) { + sbi_hsm_set_device(&andes_smu); + } +} + +static int ae350_final_init(bool cold_boot, const struct fdt_match *match) +{ + if (cold_boot) + ae350_hsm_device_init(); + + return 0; +} static const struct fdt_match andes_ae350_match[] = { { .compatible = "andestech,ae350" }, @@ -18,4 +117,5 @@ static const struct fdt_match andes_ae350_match[] = { const struct platform_override andes_ae350 = { .match_table = andes_ae350_match, + .final_init = ae350_final_init, }; diff --git a/platform/generic/andes/andes45-pma.c b/platform/generic/andes/andes45-pma.c new file mode 100644 index 00000000000..2745bc3a998 --- /dev/null +++ b/platform/generic/andes/andes45-pma.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Renesas Electronics Corp. + * + * Copyright (c) 2020 Andes Technology Corporation + * + * Authors: + * Nick Hu + * Nylon Chen + * Lad Prabhakar + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Configuration Registers */ +#define ANDES45_CSR_MMSC_CFG 0xFC2 +#define ANDES45_CSR_MMSC_PPMA_OFFSET (1 << 30) + +#define ANDES45_PMAADDR_0 0xBD0 + +#define ANDES45_PMACFG_0 0xBC0 + +static inline unsigned long andes45_pma_read_cfg(unsigned int pma_cfg_off) +{ +#define switchcase_pma_cfg_read(__pma_cfg_off, __val) \ + case __pma_cfg_off: \ + __val = csr_read(__pma_cfg_off); \ + break; +#define switchcase_pma_cfg_read_2(__pma_cfg_off, __val) \ + switchcase_pma_cfg_read(__pma_cfg_off + 0, __val) \ + switchcase_pma_cfg_read(__pma_cfg_off + 2, __val) + + unsigned long ret = 0; + + switch (pma_cfg_off) { + switchcase_pma_cfg_read_2(ANDES45_PMACFG_0, ret) + + default: + sbi_panic("%s: Unknown PMA CFG offset %#x", __func__, pma_cfg_off); + break; + } + + return ret; + +#undef switchcase_pma_cfg_read_2 +#undef switchcase_pma_cfg_read +} + +static inline void andes45_pma_write_cfg(unsigned int pma_cfg_off, unsigned long val) +{ +#define switchcase_pma_cfg_write(__pma_cfg_off, __val) \ + case __pma_cfg_off: \ + csr_write(__pma_cfg_off, __val); \ + break; +#define switchcase_pma_cfg_write_2(__pma_cfg_off, __val) \ + switchcase_pma_cfg_write(__pma_cfg_off + 0, __val) \ + switchcase_pma_cfg_write(__pma_cfg_off + 2, __val) + + switch (pma_cfg_off) { + switchcase_pma_cfg_write_2(ANDES45_PMACFG_0, val) + + default: + sbi_panic("%s: Unknown PMA CFG offset %#x", __func__, pma_cfg_off); + break; + } + +#undef switchcase_pma_cfg_write_2 +#undef switchcase_pma_cfg_write +} + +static inline void andes45_pma_write_addr(unsigned int pma_addr_off, unsigned long val) +{ +#define switchcase_pma_write(__pma_addr_off, __val) \ + case __pma_addr_off: \ + csr_write(__pma_addr_off, __val); \ + break; +#define switchcase_pma_write_2(__pma_addr_off, __val) \ + switchcase_pma_write(__pma_addr_off + 0, __val) \ + switchcase_pma_write(__pma_addr_off + 1, __val) +#define switchcase_pma_write_4(__pma_addr_off, __val) \ + switchcase_pma_write_2(__pma_addr_off + 0, __val) \ + switchcase_pma_write_2(__pma_addr_off + 2, __val) +#define switchcase_pma_write_8(__pma_addr_off, __val) \ + switchcase_pma_write_4(__pma_addr_off + 0, __val) \ + switchcase_pma_write_4(__pma_addr_off + 4, __val) +#define switchcase_pma_write_16(__pma_addr_off, __val) \ + switchcase_pma_write_8(__pma_addr_off + 0, __val) \ + switchcase_pma_write_8(__pma_addr_off + 8, __val) + + switch (pma_addr_off) { + switchcase_pma_write_16(ANDES45_PMAADDR_0, val) + + default: + sbi_panic("%s: Unknown PMA ADDR offset %#x", __func__, pma_addr_off); + break; + } + +#undef switchcase_pma_write_16 +#undef switchcase_pma_write_8 +#undef switchcase_pma_write_4 +#undef switchcase_pma_write_2 +#undef switchcase_pma_write +} + +static inline unsigned long andes45_pma_read_addr(unsigned int pma_addr_off) +{ +#define switchcase_pma_read(__pma_addr_off, __val) \ + case __pma_addr_off: \ + __val = csr_read(__pma_addr_off); \ + break; +#define switchcase_pma_read_2(__pma_addr_off, __val) \ + switchcase_pma_read(__pma_addr_off + 0, __val) \ + switchcase_pma_read(__pma_addr_off + 1, __val) +#define switchcase_pma_read_4(__pma_addr_off, __val) \ + switchcase_pma_read_2(__pma_addr_off + 0, __val) \ + switchcase_pma_read_2(__pma_addr_off + 2, __val) +#define switchcase_pma_read_8(__pma_addr_off, __val) \ + switchcase_pma_read_4(__pma_addr_off + 0, __val) \ + switchcase_pma_read_4(__pma_addr_off + 4, __val) +#define switchcase_pma_read_16(__pma_addr_off, __val) \ + switchcase_pma_read_8(__pma_addr_off + 0, __val) \ + switchcase_pma_read_8(__pma_addr_off + 8, __val) + + unsigned long ret = 0; + + switch (pma_addr_off) { + switchcase_pma_read_16(ANDES45_PMAADDR_0, ret) + + default: + sbi_panic("%s: Unknown PMA ADDR offset %#x", __func__, pma_addr_off); + break; + } + + return ret; + +#undef switchcase_pma_read_16 +#undef switchcase_pma_read_8 +#undef switchcase_pma_read_4 +#undef switchcase_pma_read_2 +#undef switchcase_pma_read +} + +static unsigned long +andes45_pma_setup(const struct andes45_pma_region *pma_region, + unsigned int entry_id) +{ + unsigned long size = pma_region->size; + unsigned long addr = pma_region->pa; + unsigned int pma_cfg_addr; + unsigned long pmacfg_val; + unsigned long pmaaddr; + char *pmaxcfg; + + /* Check for 4KiB granularity */ + if (size < (1 << 12)) + return SBI_EINVAL; + + /* Check size is power of 2 */ + if (size & (size - 1)) + return SBI_EINVAL; + + if (entry_id > 15) + return SBI_EINVAL; + + if (!(pma_region->flags & ANDES45_PMACFG_ETYP_NAPOT)) + return SBI_EINVAL; + + if ((addr & (size - 1)) != 0) + return SBI_EINVAL; + + pma_cfg_addr = entry_id / 8 ? ANDES45_PMACFG_0 + 2 : ANDES45_PMACFG_0; + pmacfg_val = andes45_pma_read_cfg(pma_cfg_addr); + pmaxcfg = (char *)&pmacfg_val + (entry_id % 8); + *pmaxcfg = 0; + *pmaxcfg = pma_region->flags; + + andes45_pma_write_cfg(pma_cfg_addr, pmacfg_val); + + pmaaddr = (addr >> 2) + (size >> 3) - 1; + + andes45_pma_write_addr(ANDES45_PMAADDR_0 + entry_id, pmaaddr); + + return andes45_pma_read_addr(ANDES45_PMAADDR_0 + entry_id) == pmaaddr ? + pmaaddr : SBI_EINVAL; +} + +static int andes45_fdt_pma_resv(void *fdt, const struct andes45_pma_region *pma, + unsigned int index, int parent) +{ + int na = fdt_address_cells(fdt, 0); + int ns = fdt_size_cells(fdt, 0); + static bool dma_default = false; + fdt32_t addr_high, addr_low; + fdt32_t size_high, size_low; + int subnode, err; + fdt32_t reg[4]; + fdt32_t *val; + char name[32]; + + addr_high = (u64)pma->pa >> 32; + addr_low = pma->pa; + size_high = (u64)pma->size >> 32; + size_low = pma->size; + + if (na > 1 && addr_high) + sbi_snprintf(name, sizeof(name), + "pma_resv%d@%x,%x", index, + addr_high, addr_low); + else + sbi_snprintf(name, sizeof(name), + "pma_resv%d@%x", index, + addr_low); + + subnode = fdt_add_subnode(fdt, parent, name); + if (subnode < 0) + return subnode; + + if (pma->shared_dma) { + err = fdt_setprop_string(fdt, subnode, "compatible", "shared-dma-pool"); + if (err < 0) + return err; + } + + if (pma->no_map) { + err = fdt_setprop_empty(fdt, subnode, "no-map"); + if (err < 0) + return err; + } + + /* Linux allows single linux,dma-default region. */ + if (pma->dma_default) { + if (dma_default) + return SBI_EINVAL; + + err = fdt_setprop_empty(fdt, subnode, "linux,dma-default"); + if (err < 0) + return err; + dma_default = true; + } + + /* encode the property value */ + val = reg; + if (na > 1) + *val++ = cpu_to_fdt32(addr_high); + *val++ = cpu_to_fdt32(addr_low); + if (ns > 1) + *val++ = cpu_to_fdt32(size_high); + *val++ = cpu_to_fdt32(size_low); + + err = fdt_setprop(fdt, subnode, "reg", reg, + (na + ns) * sizeof(fdt32_t)); + if (err < 0) + return err; + + return 0; +} + +static int andes45_fdt_reserved_memory_fixup(void *fdt, + const struct andes45_pma_region *pma, + unsigned int entry) +{ + int parent; + + /* try to locate the reserved memory node */ + parent = fdt_path_offset(fdt, "/reserved-memory"); + if (parent < 0) { + int na = fdt_address_cells(fdt, 0); + int ns = fdt_size_cells(fdt, 0); + int err; + + /* if such node does not exist, create one */ + parent = fdt_add_subnode(fdt, 0, "reserved-memory"); + if (parent < 0) + return parent; + + err = fdt_setprop_empty(fdt, parent, "ranges"); + if (err < 0) + return err; + + err = fdt_setprop_u32(fdt, parent, "#size-cells", ns); + if (err < 0) + return err; + + err = fdt_setprop_u32(fdt, parent, "#address-cells", na); + if (err < 0) + return err; + } + + return andes45_fdt_pma_resv(fdt, pma, entry, parent); +} + +int andes45_pma_setup_regions(const struct andes45_pma_region *pma_regions, + unsigned int pma_regions_count) +{ + unsigned long mmsc = csr_read(ANDES45_CSR_MMSC_CFG); + unsigned int dt_populate_cnt; + unsigned int i, j; + unsigned long pa; + void *fdt; + int ret; + + if (!pma_regions || !pma_regions_count) + return 0; + + if (pma_regions_count > ANDES45_MAX_PMA_REGIONS) + return SBI_EINVAL; + + if ((mmsc & ANDES45_CSR_MMSC_PPMA_OFFSET) == 0) + return SBI_ENOTSUPP; + + /* Configure the PMA regions */ + for (i = 0; i < pma_regions_count; i++) { + pa = andes45_pma_setup(&pma_regions[i], i); + if (pa == SBI_EINVAL) + return SBI_EINVAL; + } + + dt_populate_cnt = 0; + for (i = 0; i < pma_regions_count; i++) { + if (!pma_regions[i].dt_populate) + continue; + dt_populate_cnt++; + } + + if (!dt_populate_cnt) + return 0; + + fdt = fdt_get_address(); + + ret = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + (64 * dt_populate_cnt)); + if (ret < 0) + return ret; + + for (i = 0, j = 0; i < pma_regions_count; i++) { + if (!pma_regions[i].dt_populate) + continue; + + ret = andes45_fdt_reserved_memory_fixup(fdt, &pma_regions[i], j++); + if (ret) + return ret; + } + + return 0; +} diff --git a/platform/generic/andes/andes_sbi.c b/platform/generic/andes/andes_sbi.c new file mode 100644 index 00000000000..3e89fb9c696 --- /dev/null +++ b/platform/generic/andes/andes_sbi.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2023 Renesas Electronics Corp. + * + */ +#include +#include +#include +#include + +enum sbi_ext_andes_fid { + SBI_EXT_ANDES_FID0 = 0, /* Reserved for future use */ + SBI_EXT_ANDES_IOCP_SW_WORKAROUND, +}; + +static bool andes45_cache_controllable(void) +{ + return (((csr_read(CSR_MICM_CFG) & MICM_CFG_ISZ_MASK) || + (csr_read(CSR_MDCM_CFG) & MDCM_CFG_DSZ_MASK)) && + (csr_read(CSR_MMSC_CFG) & MMSC_CFG_CCTLCSR_MASK) && + (csr_read(CSR_MCACHE_CTL) & MCACHE_CTL_CCTL_SUEN_MASK) && + misa_extension('U')); +} + +static bool andes45_iocp_disabled(void) +{ + return (csr_read(CSR_MMSC_CFG) & MMSC_IOCP_MASK) ? false : true; +} + +static bool andes45_apply_iocp_sw_workaround(void) +{ + return andes45_cache_controllable() & andes45_iocp_disabled(); +} + +int andes_sbi_vendor_ext_provider(long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_value, + struct sbi_trap_info *out_trap, + const struct fdt_match *match) +{ + switch (funcid) { + case SBI_EXT_ANDES_IOCP_SW_WORKAROUND: + *out_value = andes45_apply_iocp_sw_workaround(); + break; + + default: + return SBI_EINVAL; + } + + return 0; +} diff --git a/platform/generic/andes/objects.mk b/platform/generic/andes/objects.mk index dd6408de01f..e8f86ea0815 100644 --- a/platform/generic/andes/objects.mk +++ b/platform/generic/andes/objects.mk @@ -3,4 +3,7 @@ # carray-platform_override_modules-$(CONFIG_PLATFORM_ANDES_AE350) += andes_ae350 -platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o +platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o andes/sleep.o + +platform-objs-$(CONFIG_ANDES45_PMA) += andes/andes45-pma.o +platform-objs-$(CONFIG_ANDES_SBI) += andes/andes_sbi.o diff --git a/platform/generic/andes/sleep.S b/platform/generic/andes/sleep.S new file mode 100644 index 00000000000..59a5597d6d8 --- /dev/null +++ b/platform/generic/andes/sleep.S @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Andes Technology Corporation + * + * Authors: + * Yu Chien Peter Lin + */ + +#include +#include +#include + + .section .text, "ax", %progbits + .align 3 + .global __ae350_disable_coherency +__ae350_disable_coherency: + /* flush d-cache */ + csrw CSR_MCCTLCOMMAND, 0x6 + /* disable i/d-cache */ + csrc CSR_MCACHE_CTL, 0x3 + /* disable d-cache coherency */ + lui t1, 0x80 + csrc CSR_MCACHE_CTL, t1 + /* + * wait for mcache_ctl.DC_COHSTA to be cleared, + * the bit is hard-wired 0 on platforms w/o CM + * (Coherence Manager) + */ +check_cm_disabled: + csrr t1, CSR_MCACHE_CTL + srli t1, t1, 20 + andi t1, t1, 0x1 + bnez t1, check_cm_disabled + + ret + + .section .text, "ax", %progbits + .align 3 + .global __ae350_enable_coherency +__ae350_enable_coherency: + /* enable d-cache coherency */ + lui t1, 0x80 + csrs CSR_MCACHE_CTL, t1 + /* + * mcache_ctl.DC_COHEN is hard-wired 0 on platforms + * w/o CM support + */ + csrr t1, CSR_MCACHE_CTL + srli t1, t1, 19 + andi t1, t1, 0x1 + beqz t1, enable_L1_cache + /* wait for mcache_ctl.DC_COHSTA to be set */ +check_cm_enabled: + csrr t1, CSR_MCACHE_CTL + srli t1, t1, 20 + andi t1, t1, 0x1 + beqz t1, check_cm_enabled +enable_L1_cache: + /* enable i/d-cache */ + csrs CSR_MCACHE_CTL, 0x3 + + ret + + .section .text, "ax", %progbits + .align 3 + .global __ae350_enable_coherency_warmboot +__ae350_enable_coherency_warmboot: + call ra, __ae350_enable_coherency + j _start_warm diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig index 47fca950c24..671281bb6cb 100644 --- a/platform/generic/configs/defconfig +++ b/platform/generic/configs/defconfig @@ -3,10 +3,14 @@ CONFIG_PLATFORM_ANDES_AE350=y CONFIG_PLATFORM_RENESAS_RZFIVE=y CONFIG_PLATFORM_SIFIVE_FU540=y CONFIG_PLATFORM_SIFIVE_FU740=y +CONFIG_PLATFORM_STARFIVE_JH7110=y CONFIG_FDT_GPIO=y +CONFIG_FDT_GPIO_DESIGNWARE=y CONFIG_FDT_GPIO_SIFIVE=y +CONFIG_FDT_GPIO_STARFIVE=y CONFIG_FDT_I2C=y CONFIG_FDT_I2C_SIFIVE=y +CONFIG_FDT_I2C_DW=y CONFIG_FDT_IPI=y CONFIG_FDT_IPI_MSWI=y CONFIG_FDT_IPI_PLICSW=y diff --git a/platform/generic/include/andes/andes45.h b/platform/generic/include/andes/andes45.h new file mode 100644 index 00000000000..f5709943538 --- /dev/null +++ b/platform/generic/include/andes/andes45.h @@ -0,0 +1,29 @@ +#ifndef _RISCV_ANDES45_H +#define _RISCV_ANDES45_H + +#define CSR_MARCHID_MICROID 0xfff + +/* Memory and Miscellaneous Registers */ +#define CSR_MCACHE_CTL 0x7ca +#define CSR_MCCTLCOMMAND 0x7cc + +/* Configuration Control & Status Registers */ +#define CSR_MICM_CFG 0xfc0 +#define CSR_MDCM_CFG 0xfc1 +#define CSR_MMSC_CFG 0xfc2 + +#define MICM_CFG_ISZ_OFFSET 6 +#define MICM_CFG_ISZ_MASK (0x7 << MICM_CFG_ISZ_OFFSET) + +#define MDCM_CFG_DSZ_OFFSET 6 +#define MDCM_CFG_DSZ_MASK (0x7 << MDCM_CFG_DSZ_OFFSET) + +#define MMSC_CFG_CCTLCSR_OFFSET 16 +#define MMSC_CFG_CCTLCSR_MASK (0x1 << MMSC_CFG_CCTLCSR_OFFSET) +#define MMSC_IOCP_OFFSET 47 +#define MMSC_IOCP_MASK (0x1ULL << MMSC_IOCP_OFFSET) + +#define MCACHE_CTL_CCTL_SUEN_OFFSET 8 +#define MCACHE_CTL_CCTL_SUEN_MASK (0x1 << MCACHE_CTL_CCTL_SUEN_OFFSET) + +#endif /* _RISCV_ANDES45_H */ diff --git a/platform/generic/include/andes/andes45_pma.h b/platform/generic/include/andes/andes45_pma.h new file mode 100644 index 00000000000..37ec77cf670 --- /dev/null +++ b/platform/generic/include/andes/andes45_pma.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Renesas Electronics Corp. + */ + +#ifndef _ANDES45_PMA_H_ +#define _ANDES45_PMA_H_ + +#include + +#define ANDES45_MAX_PMA_REGIONS 16 + +/* Naturally aligned power of 2 region */ +#define ANDES45_PMACFG_ETYP_NAPOT 3 + +/* Memory, Non-cacheable, Bufferable */ +#define ANDES45_PMACFG_MTYP_MEM_NON_CACHE_BUF (3 << 2) + +/** + * struct andes45_pma_region - Describes PMA regions + * + * @pa: Address to be configured in the PMA + * @size: Size of the region + * @flags: Flags to be set for the PMA region + * @dt_populate: Boolean flag indicating if the DT entry should be + * populated for the given PMA region + * @shared_dma: Boolean flag if set "shared-dma-pool" property will + * be set in the DT node + * @no_map: Boolean flag if set "no-map" property will be set in the + * DT node + * @dma_default: Boolean flag if set "linux,dma-default" property will + * be set in the DT node. Note Linux expects single node + * with this property set. + */ +struct andes45_pma_region { + unsigned long pa; + unsigned long size; + u8 flags:7; + bool dt_populate; + bool shared_dma; + bool no_map; + bool dma_default; +}; + +int andes45_pma_setup_regions(const struct andes45_pma_region *pma_regions, + unsigned int pma_regions_count); + +#endif /* _ANDES45_PMA_H_ */ diff --git a/platform/generic/include/andes/andes_sbi.h b/platform/generic/include/andes/andes_sbi.h new file mode 100644 index 00000000000..e5dc250707b --- /dev/null +++ b/platform/generic/include/andes/andes_sbi.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BSD-2-Clause + +#ifndef _RISCV_ANDES_SBI_H +#define _RISCV_ANDES_SBI_H + +#include +#include + +int andes_sbi_vendor_ext_provider(long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_value, + struct sbi_trap_info *out_trap, + const struct fdt_match *match); + +#endif /* _RISCV_ANDES_SBI_H */ diff --git a/platform/generic/include/platform_override.h b/platform/generic/include/platform_override.h index 7f1558dde1a..0d9e5ee6b40 100644 --- a/platform/generic/include/platform_override.h +++ b/platform/generic/include/platform_override.h @@ -18,6 +18,7 @@ struct platform_override { const struct fdt_match *match_table; u64 (*features)(const struct fdt_match *match); u64 (*tlbr_flush_limit)(const struct fdt_match *match); + bool (*cold_boot_allowed)(u32 hartid, const struct fdt_match *match); int (*early_init)(bool cold_boot, const struct fdt_match *match); int (*final_init)(bool cold_boot, const struct fdt_match *match); void (*early_exit)(const struct fdt_match *match); @@ -25,8 +26,8 @@ struct platform_override { int (*fdt_fixup)(void *fdt, const struct fdt_match *match); int (*extensions_init)(const struct fdt_match *match, struct sbi_hart_features *hfeatures); - int (*vendor_ext_check)(long extid, const struct fdt_match *match); - int (*vendor_ext_provider)(long extid, long funcid, + void (*fw_init)(void *fdt, const struct fdt_match *match); + int (*vendor_ext_provider)(long funcid, const struct sbi_trap_regs *regs, unsigned long *out_value, struct sbi_trap_info *out_trap, diff --git a/platform/generic/platform.c b/platform/generic/platform.c index bfe15f0d6b8..34b87f74d37 100644 --- a/platform/generic/platform.c +++ b/platform/generic/platform.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -85,6 +86,9 @@ unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1, fw_platform_lookup_special(fdt, root_offset); + if (generic_plat && generic_plat->fw_init) + generic_plat->fw_init(fdt, generic_plat_match); + model = fdt_getprop(fdt, root_offset, "model", &len); if (model) sbi_strncpy(platform.name, model, sizeof(platform.name) - 1); @@ -111,7 +115,7 @@ unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1, } platform.hart_count = hart_count; - + platform.heap_size = SBI_PLATFORM_DEFAULT_HEAP_SIZE(hart_count); platform_has_mlevel_imsic = fdt_check_imsic_mlevel(fdt); /* Return original FDT pointer */ @@ -122,6 +126,14 @@ unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1, wfi(); } +static bool generic_cold_boot_allowed(u32 hartid) +{ + if (generic_plat && generic_plat->cold_boot_allowed) + return generic_plat->cold_boot_allowed( + hartid, generic_plat_match); + return true; +} + static int generic_nascent_init(void) { if (platform_has_mlevel_imsic) @@ -131,6 +143,9 @@ static int generic_nascent_init(void) static int generic_early_init(bool cold_boot) { + if (cold_boot) + fdt_reset_init(); + if (!generic_plat || !generic_plat->early_init) return 0; @@ -142,9 +157,6 @@ static int generic_final_init(bool cold_boot) void *fdt; int rc; - if (cold_boot) - fdt_reset_init(); - if (generic_plat && generic_plat->final_init) { rc = generic_plat->final_init(cold_boot, generic_plat_match); if (rc) @@ -169,27 +181,20 @@ static int generic_final_init(bool cold_boot) return 0; } -static int generic_vendor_ext_check(long extid) +static bool generic_vendor_ext_check(void) { - if (generic_plat && generic_plat->vendor_ext_check) - return generic_plat->vendor_ext_check(extid, - generic_plat_match); - - return 0; + return (generic_plat && generic_plat->vendor_ext_provider) ? + true : false; } -static int generic_vendor_ext_provider(long extid, long funcid, +static int generic_vendor_ext_provider(long funcid, const struct sbi_trap_regs *regs, unsigned long *out_value, struct sbi_trap_info *out_trap) { - if (generic_plat && generic_plat->vendor_ext_provider) { - return generic_plat->vendor_ext_provider(extid, funcid, regs, - out_value, out_trap, - generic_plat_match); - } - - return SBI_ENOTSUPP; + return generic_plat->vendor_ext_provider(funcid, regs, + out_value, out_trap, + generic_plat_match); } static void generic_early_exit(void) @@ -206,6 +211,15 @@ static void generic_final_exit(void) static int generic_extensions_init(struct sbi_hart_features *hfeatures) { + int rc; + + /* Parse the ISA string from FDT and enable the listed extensions */ + rc = fdt_parse_isa_extensions(fdt_get_address(), current_hartid(), + &hfeatures->extensions); + + if (rc) + return rc; + if (generic_plat && generic_plat->extensions_init) return generic_plat->extensions_init(generic_plat_match, hfeatures); @@ -215,7 +229,24 @@ static int generic_extensions_init(struct sbi_hart_features *hfeatures) static int generic_domains_init(void) { - return fdt_domains_populate(fdt_get_address()); + void *fdt = fdt_get_address(); + int offset, ret; + + ret = fdt_domains_populate(fdt); + if (ret < 0) + return ret; + + offset = fdt_path_offset(fdt, "/chosen"); + + if (offset >= 0) { + offset = fdt_node_offset_by_compatible(fdt, offset, + "opensbi,domain,config"); + if (offset >= 0 && + fdt_get_property(fdt, offset, "system-suspend-test", NULL)) + sbi_system_suspend_test_enable(); + } + + return 0; } static u64 generic_tlbr_flush_limit(void) @@ -261,6 +292,7 @@ static int generic_console_init(void) } const struct sbi_platform_operations platform_ops = { + .cold_boot_allowed = generic_cold_boot_allowed, .nascent_init = generic_nascent_init, .early_init = generic_early_init, .final_init = generic_final_init, @@ -292,5 +324,6 @@ struct sbi_platform platform = { .hart_count = SBI_HARTMASK_MAX_BITS, .hart_index2id = generic_hart_index2id, .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .heap_size = SBI_PLATFORM_DEFAULT_HEAP_SIZE(0), .platform_ops_addr = (unsigned long)&platform_ops }; diff --git a/platform/generic/renesas/rzfive/rzfive.c b/platform/generic/renesas/rzfive/rzfive.c index ca182e3136f..a69797b18df 100644 --- a/platform/generic/renesas/rzfive/rzfive.c +++ b/platform/generic/renesas/rzfive/rzfive.c @@ -4,9 +4,49 @@ * */ +#include +#include #include +#include #include +static const struct andes45_pma_region renesas_rzfive_pma_regions[] = { + { + .pa = 0x58000000, + .size = 0x8000000, + .flags = ANDES45_PMACFG_ETYP_NAPOT | + ANDES45_PMACFG_MTYP_MEM_NON_CACHE_BUF, + .dt_populate = true, + .shared_dma = true, + .no_map = true, + .dma_default = true, + }, +}; + +static int renesas_rzfive_final_init(bool cold_boot, const struct fdt_match *match) +{ + return andes45_pma_setup_regions(renesas_rzfive_pma_regions, + array_size(renesas_rzfive_pma_regions)); +} + +int renesas_rzfive_early_init(bool cold_boot, const struct fdt_match *match) +{ + /* + * Renesas RZ/Five RISC-V SoC has Instruction local memory and + * Data local memory (ILM & DLM) mapped between region 0x30000 + * to 0x4FFFF. When a virtual address falls within this range, + * the MMU doesn't trigger a page fault; it assumes the virtual + * address is a physical address which can cause undesired + * behaviours for statically linked applications/libraries. To + * avoid this, add the ILM/DLM memory regions to the root domain + * region of the PMPU with permissions set to 0x0 for S/U modes + * so that any access to these regions gets blocked and for M-mode + * we grant full access. + */ + return sbi_domain_root_add_memrange(0x30000, 0x20000, 0x1000, + SBI_DOMAIN_MEMREGION_M_RWX); +} + static const struct fdt_match renesas_rzfive_match[] = { { .compatible = "renesas,r9a07g043f01" }, { /* sentinel */ } @@ -14,4 +54,7 @@ static const struct fdt_match renesas_rzfive_match[] = { const struct platform_override renesas_rzfive = { .match_table = renesas_rzfive_match, + .early_init = renesas_rzfive_early_init, + .final_init = renesas_rzfive_final_init, + .vendor_ext_provider = andes_sbi_vendor_ext_provider, }; diff --git a/platform/generic/sifive/fu540.c b/platform/generic/sifive/fu540.c index 08f7bfc401e..b980f44371b 100644 --- a/platform/generic/sifive/fu540.c +++ b/platform/generic/sifive/fu540.c @@ -20,18 +20,6 @@ static u64 sifive_fu540_tlbr_flush_limit(const struct fdt_match *match) return 0; } -static int sifive_fu540_fdt_fixup(void *fdt, const struct fdt_match *match) -{ - /* - * SiFive Freedom U540 has an erratum that prevents S-mode software - * to access a PMP protected region using 1GB page table mapping, so - * always add the no-map attribute on this platform. - */ - fdt_reserved_memory_nomap_fixup(fdt); - - return 0; -} - static const struct fdt_match sifive_fu540_match[] = { { .compatible = "sifive,fu540" }, { .compatible = "sifive,fu540g" }, @@ -43,5 +31,4 @@ static const struct fdt_match sifive_fu540_match[] = { const struct platform_override sifive_fu540 = { .match_table = sifive_fu540_match, .tlbr_flush_limit = sifive_fu540_tlbr_flush_limit, - .fdt_fixup = sifive_fu540_fdt_fixup, }; diff --git a/platform/generic/starfive/jh7110.c b/platform/generic/starfive/jh7110.c new file mode 100644 index 00000000000..dcd6306be5f --- /dev/null +++ b/platform/generic/starfive/jh7110.c @@ -0,0 +1,319 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 StarFive + * + * Authors: + * Wei Liang Lim + * Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pmic { + struct i2c_adapter *adapter; + u32 dev_addr; + const char *compatible; +}; + +struct jh7110 { + u64 pmu_reg_base; + u64 clk_reg_base; + u32 i2c_index; +}; + +static struct pmic pmic_inst; +static struct jh7110 jh7110_inst; +static u32 selected_hartid = -1; + +/* PMU register define */ +#define HW_EVENT_TURN_ON_MASK 0x04 +#define HW_EVENT_TURN_OFF_MASK 0x08 +#define SW_TURN_ON_POWER_MODE 0x0C +#define SW_TURN_OFF_POWER_MODE 0x10 +#define SW_ENCOURAGE 0x44 +#define PMU_INT_MASK 0x48 +#define PCH_BYPASS 0x4C +#define PCH_PSTATE 0x50 +#define PCH_TIMEOUT 0x54 +#define LP_TIMEOUT 0x58 +#define HW_TURN_ON_MODE 0x5C +#define CURR_POWER_MODE 0x80 +#define PMU_EVENT_STATUS 0x88 +#define PMU_INT_STATUS 0x8C + +/* sw encourage cfg */ +#define SW_MODE_ENCOURAGE_EN_LO 0x05 +#define SW_MODE_ENCOURAGE_EN_HI 0x50 +#define SW_MODE_ENCOURAGE_DIS_LO 0x0A +#define SW_MODE_ENCOURAGE_DIS_HI 0xA0 +#define SW_MODE_ENCOURAGE_ON 0xFF + +#define DEVICE_PD_MASK 0xfc +#define SYSTOP_CPU_PD_MASK 0x3 + +#define TIMEOUT_COUNT 100000 +#define AXP15060_POWER_REG 0x32 +#define AXP15060_POWER_OFF_BIT BIT(7) +#define AXP15060_RESET_BIT BIT(6) + +#define I2C_APB_CLK_OFFSET 0x228 +#define I2C_APB_CLK_ENABLE_BIT BIT(31) + +static int pm_system_reset_check(u32 type, u32 reason) +{ + switch (type) { + case SBI_SRST_RESET_TYPE_SHUTDOWN: + return 1; + case SBI_SRST_RESET_TYPE_COLD_REBOOT: + return 255; + default: + break; + } + + return 0; +} + +static int wait_pmu_pd_state(u32 mask) +{ + int count = 0; + unsigned long addr = jh7110_inst.pmu_reg_base; + u32 val; + + do { + val = readl((void *)(addr + CURR_POWER_MODE)); + if (val == mask) + return 0; + + sbi_timer_udelay(2); + count += 1; + if (count == TIMEOUT_COUNT) + return SBI_ETIMEDOUT; + } while (1); +} + +static int shutdown_device_power_domain(void) +{ + unsigned long addr = jh7110_inst.pmu_reg_base; + u32 curr_mode; + int ret = 0; + + curr_mode = readl((void *)(addr + CURR_POWER_MODE)); + curr_mode &= DEVICE_PD_MASK; + + if (curr_mode) { + writel(curr_mode, (void *)(addr + SW_TURN_OFF_POWER_MODE)); + writel(SW_MODE_ENCOURAGE_ON, (void *)(addr + SW_ENCOURAGE)); + writel(SW_MODE_ENCOURAGE_DIS_LO, (void *)(addr + SW_ENCOURAGE)); + writel(SW_MODE_ENCOURAGE_DIS_HI, (void *)(addr + SW_ENCOURAGE)); + ret = wait_pmu_pd_state(SYSTOP_CPU_PD_MASK); + if (ret) + sbi_printf("%s shutdown device power %x error\n", + __func__, curr_mode); + } + return ret; +} + +static void pmic_ops(struct pmic *pmic, int type) +{ + int ret = 0; + u8 val; + + ret = shutdown_device_power_domain(); + + if (ret) + return; + + if (!sbi_strcmp("stf,axp15060-regulator", pmic->compatible)) { + ret = i2c_adapter_reg_read(pmic->adapter, pmic->dev_addr, + AXP15060_POWER_REG, &val); + + if (ret) { + sbi_printf("%s: cannot read pmic power register\n", + __func__); + return; + } + + val |= AXP15060_POWER_OFF_BIT; + if (type == SBI_SRST_RESET_TYPE_SHUTDOWN) + val |= AXP15060_POWER_OFF_BIT; + else + val |= AXP15060_RESET_BIT; + + ret = i2c_adapter_reg_write(pmic->adapter, pmic->dev_addr, + AXP15060_POWER_REG, val); + if (ret) + sbi_printf("%s: cannot write pmic power register\n", + __func__); + } +} + +static void pmic_i2c_clk_enable(void) +{ + unsigned long clock_base; + unsigned int val; + + clock_base = jh7110_inst.clk_reg_base + + I2C_APB_CLK_OFFSET + + (jh7110_inst.i2c_index << 2); + + val = readl((void *)clock_base); + + if (!val) + writel(I2C_APB_CLK_ENABLE_BIT, (void *)clock_base); +} + +static void pm_system_reset(u32 type, u32 reason) +{ + if (pmic_inst.adapter) { + switch (type) { + case SBI_SRST_RESET_TYPE_SHUTDOWN: + case SBI_SRST_RESET_TYPE_COLD_REBOOT: + /* i2c clk may be disabled by kernel driver */ + pmic_i2c_clk_enable(); + pmic_ops(&pmic_inst, type); + break; + default: + break; + } + } + + sbi_hart_hang(); +} + +static struct sbi_system_reset_device pm_reset = { + .name = "pm-reset", + .system_reset_check = pm_system_reset_check, + .system_reset = pm_system_reset +}; + +static int pm_reset_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + int i2c_bus; + struct i2c_adapter *adapter; + u64 addr; + + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL); + if (rc) + return rc; + + pmic_inst.dev_addr = addr; + pmic_inst.compatible = match->compatible; + + i2c_bus = fdt_parent_offset(fdt, nodeoff); + if (i2c_bus < 0) + return i2c_bus; + + /* i2c adapter get */ + rc = fdt_i2c_adapter_get(fdt, i2c_bus, &adapter); + if (rc) + return rc; + + pmic_inst.adapter = adapter; + + sbi_system_reset_add_device(&pm_reset); + + return 0; +} + +static const struct fdt_match pm_reset_match[] = { + { .compatible = "stf,axp15060-regulator", .data = (void *)true }, + { }, +}; + +static struct fdt_reset fdt_reset_pmic = { + .match_table = pm_reset_match, + .init = pm_reset_init, +}; + +static int starfive_jh7110_inst_init(void *fdt) +{ + int noff, rc = 0; + const char *name; + u64 addr; + + noff = fdt_node_offset_by_compatible(fdt, -1, "starfive,jh7110-pmu"); + if (-1 < noff) { + rc = fdt_get_node_addr_size(fdt, noff, 0, &addr, NULL); + if (rc) + goto err; + jh7110_inst.pmu_reg_base = addr; + } + + noff = fdt_node_offset_by_compatible(fdt, -1, "starfive,jh7110-clkgen"); + if (-1 < noff) { + rc = fdt_get_node_addr_size(fdt, noff, 0, &addr, NULL); + if (rc) + goto err; + jh7110_inst.clk_reg_base = addr; + } + + if (pmic_inst.adapter) { + name = fdt_get_name(fdt, pmic_inst.adapter->id, NULL); + if (!sbi_strncmp(name, "i2c", 3)) + jh7110_inst.i2c_index = name[3] - '0'; + else + rc = SBI_EINVAL; + } +err: + return rc; +} + +static int starfive_jh7110_final_init(bool cold_boot, + const struct fdt_match *match) +{ + void *fdt = fdt_get_address(); + + if (cold_boot) { + fdt_reset_driver_init(fdt, &fdt_reset_pmic); + return starfive_jh7110_inst_init(fdt); + } + + return 0; +} + +static bool starfive_jh7110_cold_boot_allowed(u32 hartid, + const struct fdt_match *match) +{ + if (selected_hartid != -1) + return (selected_hartid == hartid); + + return true; +} + +static void starfive_jh7110_fw_init(void *fdt, const struct fdt_match *match) +{ + const fdt32_t *val; + int len, coff; + + coff = fdt_path_offset(fdt, "/chosen"); + if (-1 < coff) { + val = fdt_getprop(fdt, coff, "starfive,boot-hart-id", &len); + if (val && len >= sizeof(fdt32_t)) + selected_hartid = (u32) fdt32_to_cpu(*val); + } +} + +static const struct fdt_match starfive_jh7110_match[] = { + { .compatible = "starfive,jh7110" }, + { }, +}; + +const struct platform_override starfive_jh7110 = { + .match_table = starfive_jh7110_match, + .cold_boot_allowed = starfive_jh7110_cold_boot_allowed, + .fw_init = starfive_jh7110_fw_init, + .final_init = starfive_jh7110_final_init, +}; diff --git a/platform/generic/starfive/objects.mk b/platform/generic/starfive/objects.mk new file mode 100644 index 00000000000..0b900fb6fe6 --- /dev/null +++ b/platform/generic/starfive/objects.mk @@ -0,0 +1,6 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# + +carray-platform_override_modules-$(CONFIG_PLATFORM_STARFIVE_JH7110) += starfive_jh7110 +platform-objs-$(CONFIG_PLATFORM_STARFIVE_JH7110) += starfive/jh7110.o diff --git a/platform/kendryte/k210/platform.c b/platform/kendryte/k210/platform.c index 7eb9015bb85..637a217fcd7 100644 --- a/platform/kendryte/k210/platform.c +++ b/platform/kendryte/k210/platform.c @@ -196,5 +196,7 @@ const struct sbi_platform platform = { .features = 0, .hart_count = K210_HART_COUNT, .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .heap_size = + SBI_PLATFORM_DEFAULT_HEAP_SIZE(K210_HART_COUNT), .platform_ops_addr = (unsigned long)&platform_ops }; diff --git a/platform/nuclei/ux600/platform.c b/platform/nuclei/ux600/platform.c index 4eccff15ee2..6fd6cd7082f 100644 --- a/platform/nuclei/ux600/platform.c +++ b/platform/nuclei/ux600/platform.c @@ -244,5 +244,7 @@ const struct sbi_platform platform = { .features = SBI_PLATFORM_DEFAULT_FEATURES, .hart_count = UX600_HART_COUNT, .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .heap_size = + SBI_PLATFORM_DEFAULT_HEAP_SIZE(UX600_HART_COUNT), .platform_ops_addr = (unsigned long)&platform_ops }; diff --git a/platform/template/platform.c b/platform/template/platform.c index 8adc431f1ce..86381ca2b70 100644 --- a/platform/template/platform.c +++ b/platform/template/platform.c @@ -152,5 +152,6 @@ const struct sbi_platform platform = { .features = SBI_PLATFORM_DEFAULT_FEATURES, .hart_count = 1, .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .heap_size = SBI_PLATFORM_DEFAULT_HEAP_SIZE(1), .platform_ops_addr = (unsigned long)&platform_ops };