From 9e8ff05cb61f157fb0bcb6b0071d7b6dc0763faa Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Tue, 11 Dec 2018 19:24:06 +0530 Subject: [PATCH] Initial commit. Signed-off-by: Anup Patel --- LICENSE | 37 +++ Makefile | 296 ++++++++++++++++++ README.md | 37 +++ blob/fw_common.S | 305 +++++++++++++++++++ blob/fw_common.ldS | 89 ++++++ blob/fw_jump.S | 44 +++ blob/fw_jump.elf.ldS | 16 + blob/fw_payload.S | 46 +++ blob/fw_payload.elf.ldS | 26 ++ blob/objects.mk | 29 ++ include/sbi/riscv_asm.h | 244 +++++++++++++++ include/sbi/riscv_atomic.h | 38 +++ include/sbi/riscv_barrier.h | 53 ++++ include/sbi/riscv_encoding.h | 423 ++++++++++++++++++++++++++ include/sbi/riscv_io.h | 109 +++++++ include/sbi/riscv_locks.h | 33 ++ include/sbi/sbi_bits.h | 44 +++ include/sbi/sbi_console.h | 37 +++ include/sbi/sbi_const.h | 43 +++ include/sbi/sbi_ecall.h | 26 ++ include/sbi/sbi_emulate_csr.h | 27 ++ include/sbi/sbi_error.h | 25 ++ include/sbi/sbi_hart.h | 41 +++ include/sbi/sbi_illegal_insn.h | 22 ++ include/sbi/sbi_init.h | 19 ++ include/sbi/sbi_ipi.h | 33 ++ include/sbi/sbi_platform.h | 258 ++++++++++++++++ include/sbi/sbi_scratch.h | 35 +++ include/sbi/sbi_system.h | 31 ++ include/sbi/sbi_timer.h | 30 ++ include/sbi/sbi_trap.h | 57 ++++ include/sbi/sbi_types.h | 57 ++++ include/sbi/sbi_unpriv.h | 111 +++++++ lib/objects.mk | 23 ++ lib/riscv_asm.c | 275 +++++++++++++++++ lib/riscv_atomic.c | 152 +++++++++ lib/riscv_locks.c | 46 +++ lib/sbi_console.c | 367 ++++++++++++++++++++++ lib/sbi_ecall.c | 98 ++++++ lib/sbi_emulate_csr.c | 157 ++++++++++ lib/sbi_hart.c | 294 ++++++++++++++++++ lib/sbi_illegal_insn.c | 190 ++++++++++++ lib/sbi_init.c | 162 ++++++++++ lib/sbi_ipi.c | 82 +++++ lib/sbi_system.c | 47 +++ lib/sbi_timer.c | 78 +++++ lib/sbi_trap.c | 110 +++++++ plat/common/fdt.c | 314 +++++++++++++++++++ plat/common/include/plat/fdt.h | 73 +++++ plat/common/include/plat/irqchip/plic.h | 22 ++ plat/common/include/plat/serial/sifive-uart.h | 22 ++ plat/common/include/plat/serial/uart8250.h | 23 ++ plat/common/include/plat/sys/clint.h | 35 +++ plat/common/irqchip/objects.mk | 10 + plat/common/irqchip/plic.c | 118 +++++++ plat/common/objects.mk | 10 + plat/common/serial/objects.mk | 11 + plat/common/serial/sifive-uart.c | 73 +++++ plat/common/serial/uart8250.c | 117 +++++++ plat/common/sys/clint.c | 136 +++++++++ plat/common/sys/objects.mk | 10 + plat/qemu/virt/config.mk | 31 ++ plat/qemu/virt/objects.mk | 10 + plat/qemu/virt/platform.c | 112 +++++++ plat/sifive/hifive_u540/config.mk | 31 ++ plat/sifive/hifive_u540/objects.mk | 10 + plat/sifive/hifive_u540/platform.c | 114 +++++++ 67 files changed, 6084 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 blob/fw_common.S create mode 100644 blob/fw_common.ldS create mode 100644 blob/fw_jump.S create mode 100644 blob/fw_jump.elf.ldS create mode 100644 blob/fw_payload.S create mode 100644 blob/fw_payload.elf.ldS create mode 100644 blob/objects.mk create mode 100644 include/sbi/riscv_asm.h create mode 100644 include/sbi/riscv_atomic.h create mode 100644 include/sbi/riscv_barrier.h create mode 100644 include/sbi/riscv_encoding.h create mode 100644 include/sbi/riscv_io.h create mode 100644 include/sbi/riscv_locks.h create mode 100644 include/sbi/sbi_bits.h create mode 100644 include/sbi/sbi_console.h create mode 100644 include/sbi/sbi_const.h create mode 100644 include/sbi/sbi_ecall.h create mode 100644 include/sbi/sbi_emulate_csr.h create mode 100644 include/sbi/sbi_error.h create mode 100644 include/sbi/sbi_hart.h create mode 100644 include/sbi/sbi_illegal_insn.h create mode 100644 include/sbi/sbi_init.h create mode 100644 include/sbi/sbi_ipi.h create mode 100644 include/sbi/sbi_platform.h create mode 100644 include/sbi/sbi_scratch.h create mode 100644 include/sbi/sbi_system.h create mode 100644 include/sbi/sbi_timer.h create mode 100644 include/sbi/sbi_trap.h create mode 100644 include/sbi/sbi_types.h create mode 100644 include/sbi/sbi_unpriv.h create mode 100644 lib/objects.mk create mode 100644 lib/riscv_asm.c create mode 100644 lib/riscv_atomic.c create mode 100644 lib/riscv_locks.c create mode 100644 lib/sbi_console.c create mode 100644 lib/sbi_ecall.c create mode 100644 lib/sbi_emulate_csr.c create mode 100644 lib/sbi_hart.c create mode 100644 lib/sbi_illegal_insn.c create mode 100644 lib/sbi_init.c create mode 100644 lib/sbi_ipi.c create mode 100644 lib/sbi_system.c create mode 100644 lib/sbi_timer.c create mode 100644 lib/sbi_trap.c create mode 100644 plat/common/fdt.c create mode 100644 plat/common/include/plat/fdt.h create mode 100644 plat/common/include/plat/irqchip/plic.h create mode 100644 plat/common/include/plat/serial/sifive-uart.h create mode 100644 plat/common/include/plat/serial/uart8250.h create mode 100644 plat/common/include/plat/sys/clint.h create mode 100644 plat/common/irqchip/objects.mk create mode 100644 plat/common/irqchip/plic.c create mode 100644 plat/common/objects.mk create mode 100644 plat/common/serial/objects.mk create mode 100644 plat/common/serial/sifive-uart.c create mode 100644 plat/common/serial/uart8250.c create mode 100644 plat/common/sys/clint.c create mode 100644 plat/common/sys/objects.mk create mode 100644 plat/qemu/virt/config.mk create mode 100644 plat/qemu/virt/objects.mk create mode 100644 plat/qemu/virt/platform.c create mode 100644 plat/sifive/hifive_u540/config.mk create mode 100644 plat/sifive/hifive_u540/objects.mk create mode 100644 plat/sifive/hifive_u540/platform.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1cdc70e --- /dev/null +++ b/LICENSE @@ -0,0 +1,37 @@ +Copyright (c) 2018 Western Digital Corporation or its affiliates. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the project. + +-------------- + +Note: +Individual files contain the following tag instead of the full license text. + +:: + + SPDX-License-Identifier: BSD-2-Clause + +This enables machine processing of license information based on the SPDX +License Identifiers that are here available: http://spdx.org/licenses/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ff41674 --- /dev/null +++ b/Makefile @@ -0,0 +1,296 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +# Current Version +MAJOR = 0 +MINOR = 1 + +# Select Make Options: +# o Do not use make's built-in rules and variables +# o Do not print "Entering directory ..."; +MAKEFLAGS += -rR --no-print-directory + +# Find out source, build, and install directories +src_dir=$(CURDIR) +ifdef O + build_dir=$(shell readlink -f $(O)) +else + build_dir=$(CURDIR)/build +endif +ifeq ($(build_dir),$(CURDIR)) +$(error Build directory is same as source directory.) +endif +ifdef I + install_dir=$(shell readlink -f $(I)) +else + install_dir=$(CURDIR)/install +endif +ifeq ($(install_dir),$(CURDIR)) +$(error Install directory is same as source directory.) +endif +ifeq ($(install_dir),$(build_dir)) +$(error Install directory is same as build directory.) +endif + +# Check if verbosity is ON for build process +VERBOSE_DEFAULT := 0 +CMD_PREFIX_DEFAULT := @ +ifdef VERBOSE + ifeq ("$(origin VERBOSE)", "command line") + VB := $(VERBOSE) + else + VB := $(VERBOSE_DEFAULT) + endif +else + VB := $(VERBOSE_DEFAULT) +endif +ifeq ($(VB), 1) + override V := +else + override V := $(CMD_PREFIX_DEFAULT) +endif + +# Setup path of directories +export plat_subdir=plat/$(PLAT) +export plat_dir=$(CURDIR)/$(plat_subdir) +export plat_common_dir=$(CURDIR)/plat/common +export include_dir=$(CURDIR)/include +export lib_dir=$(CURDIR)/lib +export blob_dir=$(CURDIR)/blob + +# Setup list of objects.mk files +ifdef PLAT +plat-object-mks=$(shell if [ -d $(plat_dir) ]; then find $(plat_dir) -iname "objects.mk" | sort -r; fi) +plat-common-object-mks=$(shell if [ -d $(plat_common_dir) ]; then find $(plat_common_dir) -iname "objects.mk" | sort -r; fi) +endif +lib-object-mks=$(shell if [ -d $(lib_dir) ]; then find $(lib_dir) -iname "objects.mk" | sort -r; fi) +blob-object-mks=$(shell if [ -d $(blob_dir) ]; then find $(blob_dir) -iname "objects.mk" | sort -r; fi) + +# Include platform specifig config.mk +ifdef PLAT +include $(plat_dir)/config.mk +endif + +# Include all object.mk files +ifdef PLAT +include $(plat-object-mks) +include $(plat-common-object-mks) +endif +include $(lib-object-mks) +include $(blob-object-mks) + +# Setup list of objects +lib-objs-path-y=$(foreach obj,$(lib-objs-y),$(build_dir)/lib/$(obj)) +ifdef PLAT +plat-objs-path-y=$(foreach obj,$(plat-objs-y),$(build_dir)/$(plat_subdir)/$(obj)) +plat-common-objs-path-y=$(foreach obj,$(plat-common-objs-y),$(build_dir)/plat/common/$(obj)) +blob-bins-path-y=$(foreach bin,$(blob-bins-y),$(build_dir)/$(plat_subdir)/blob/$(bin)) +endif +blob-elfs-path-y=$(blob-bins-path-y:.bin=.elf) +blob-objs-path-y=$(blob-bins-path-y:.bin=.o) + +# Setup list of deps files for objects +deps-y=$(plat-objs-path-y:.o=.dep) +deps-y+=$(plat-common-objs-path-y:.o=.dep) +deps-y+=$(lib-objs-path-y:.o=.dep) +deps-y+=$(blob-objs-path-y:.o=.dep) + +# Setup compilation environment +cpp=$(CROSS_COMPILE)cpp +cppflags+=-DOPENSBI_MAJOR=$(MAJOR) +cppflags+=-DOPENSBI_MINOR=$(MINOR) +cppflags+=-I$(plat_dir)/include +cppflags+=-I$(plat_common_dir)/include +cppflags+=-I$(include_dir) +cppflags+=$(plat-cppflags-y) +cppflags+=$(blob-cppflags-y) +cc=$(CROSS_COMPILE)gcc +cflags=-g -Wall -Werror -nostdlib -fno-strict-aliasing -O2 +cflags+=-fno-omit-frame-pointer -fno-optimize-sibling-calls +cflags+=-mno-save-restore -mstrict-align +cflags+=$(cppflags) +cflags+=$(plat-cflags-y) +cflags+=$(blob-cflags-y) +cflags+=$(EXTRA_CFLAGS) +as=$(CROSS_COMPILE)gcc +asflags=-g -Wall -nostdlib -D__ASSEMBLY__ +asflags+=-fno-omit-frame-pointer -fno-optimize-sibling-calls +asflags+=-mno-save-restore -mstrict-align +asflags+=$(cppflags) +asflags+=$(plat-asflags-y) +asflags+=$(blob-asflags-y) +asflags+=$(EXTRA_ASFLAGS) +ar=$(CROSS_COMPILE)ar +arflags=rcs +ld=$(CROSS_COMPILE)gcc +ldflags=-g -Wall -nostdlib -Wl,--build-id=none +ldflags+=$(plat-ldflags-y) +ldflags+=$(blob-ldflags-y) +merge=$(CROSS_COMPILE)ld +mergeflags=-r +objcopy=$(CROSS_COMPILE)objcopy + +# Setup functions for compilation +define dynamic_flags +-I$(shell dirname $(2)) -D__OBJNAME__=$(subst -,_,$(shell basename $(1) .o)) +endef +merge_objs = $(V)mkdir -p `dirname $(1)`; \ + echo " MERGE $(subst $(build_dir)/,,$(1))"; \ + $(merge) $(mergeflags) $(2) -o $(1) +merge_deps = $(V)mkdir -p `dirname $(1)`; \ + echo " MERGE-DEP $(subst $(build_dir)/,,$(1))"; \ + cat $(2) > $(1) +copy_file = $(V)mkdir -p `dirname $(1)`; \ + echo " COPY $(subst $(build_dir)/,,$(1))"; \ + cp -f $(2) $(1) +inst_file = $(V)mkdir -p `dirname $(1)`; \ + echo " INSTALL $(subst $(install_dir)/,,$(1))"; \ + cp -f $(2) $(1) +inst_file_list = $(V)if [ ! -z "$(3)" ]; then \ + mkdir -p $(1); \ + for f in $(3) ; do \ + echo " INSTALL "$(2)"/"`basename $$f`; \ + cp -f $$f $(1); \ + done \ + fi +inst_header_dir = $(V)mkdir -p $(1); \ + echo " INSTALL $(subst $(install_dir)/,,$(1))"; \ + cp -rf $(2) $(1) +compile_cpp = $(V)mkdir -p `dirname $(1)`; \ + echo " CPP $(subst $(build_dir)/,,$(1))"; \ + $(cpp) $(cppflags) $(2) | grep -v "\#" > $(1) +compile_cc_dep = $(V)mkdir -p `dirname $(1)`; \ + echo " CC-DEP $(subst $(build_dir)/,,$(1))"; \ + echo -n `dirname $(1)`/ > $(1) && \ + $(cc) $(cflags) $(call dynamic_flags,$(1),$(2)) \ + -MM $(2) >> $(1) || rm -f $(1) +compile_cc = $(V)mkdir -p `dirname $(1)`; \ + echo " CC $(subst $(build_dir)/,,$(1))"; \ + $(cc) $(cflags) $(call dynamic_flags,$(1),$(2)) -c $(2) -o $(1) +compile_as_dep = $(V)mkdir -p `dirname $(1)`; \ + echo " AS-DEP $(subst $(build_dir)/,,$(1))"; \ + echo -n `dirname $(1)`/ > $(1) && \ + $(as) $(asflags) $(call dynamic_flags,$(1),$(2)) \ + -MM $(2) >> $(1) || rm -f $(1) +compile_as = $(V)mkdir -p `dirname $(1)`; \ + echo " AS $(subst $(build_dir)/,,$(1))"; \ + $(as) $(asflags) $(call dynamic_flags,$(1),$(2)) -c $(2) -o $(1) +compile_ld = $(V)mkdir -p `dirname $(1)`; \ + echo " LD $(subst $(build_dir)/,,$(1))"; \ + $(ld) $(3) $(ldflags) -Wl,-T$(2) -o $(1) +compile_ar = $(V)mkdir -p `dirname $(1)`; \ + echo " AR $(subst $(build_dir)/,,$(1))"; \ + $(ar) $(arflags) $(1) $(2) +compile_objcopy = $(V)mkdir -p `dirname $(1)`; \ + echo " OBJCOPY $(subst $(build_dir)/,,$(1))"; \ + $(objcopy) -S -O binary $(2) $(1) + +targets-y = $(build_dir)/lib/libsbi.a +ifdef PLAT +targets-y += $(build_dir)/$(plat_subdir)/lib/libplatsbi.a +endif +targets-y += $(blob-bins-path-y) + +# Default rule "make" should always be first rule +.PHONY: all +all: $(targets-y) + +# Preserve all intermediate files +.SECONDARY: + +$(build_dir)/%.bin: $(build_dir)/%.elf + $(call compile_objcopy,$@,$<) + +$(build_dir)/%.elf: $(build_dir)/%.o $(build_dir)/%.elf.ld $(build_dir)/$(plat_subdir)/lib/libplatsbi.a + $(call compile_ld,$@,$@.ld,$< $(build_dir)/$(plat_subdir)/lib/libplatsbi.a) + +$(build_dir)/$(plat_subdir)/%.ld: $(src_dir)/%.ldS + $(call compile_cpp,$@,$<) + +$(build_dir)/lib/libsbi.a: $(lib-objs-path-y) + $(call compile_ar,$@,$^) + +$(build_dir)/$(plat_subdir)/lib/libplatsbi.a: $(lib-objs-path-y) $(plat-common-objs-path-y) $(plat-objs-path-y) + $(call compile_ar,$@,$^) + +$(build_dir)/%.dep: $(src_dir)/%.c + $(call compile_cc_dep,$@,$<) + +$(build_dir)/%.o: $(src_dir)/%.c + $(call compile_cc,$@,$<) + +$(build_dir)/%.dep: $(src_dir)/%.S + $(call compile_as_dep,$@,$<) + +$(build_dir)/%.o: $(src_dir)/%.S + $(call compile_as,$@,$<) + +$(build_dir)/$(plat_subdir)/%.dep: $(src_dir)/%.c + $(call compile_cc_dep,$@,$<) + +$(build_dir)/$(plat_subdir)/%.o: $(src_dir)/%.c + $(call compile_cc,$@,$<) + +$(build_dir)/$(plat_subdir)/%.dep: $(src_dir)/%.S + $(call compile_as_dep,$@,$<) + +$(build_dir)/$(plat_subdir)/%.o: $(src_dir)/%.S + $(call compile_as,$@,$<) + +# Dependency files should only be included after default Makefile rule +# They should not be included for any "xxxconfig" or "xxxclean" rule +all-deps-1 = $(if $(findstring config,$(MAKECMDGOALS)),,$(deps-y)) +all-deps-2 = $(if $(findstring clean,$(MAKECMDGOALS)),,$(all-deps-1)) +-include $(all-deps-2) + +install_targets-y = install_libsbi +ifdef PLAT +install_targets-y += install_libplatsbi +install_targets-y += install_blobs +endif + +# Rule for "make install" +.PHONY: install +install: $(install_targets-y) + +.PHONY: install_libsbi +install_libsbi: $(build_dir)/lib/libsbi.a + $(call inst_header_dir,$(install_dir)/include,$(include_dir)/sbi) + $(call inst_file,$(install_dir)/lib/libsbi.a,$(build_dir)/lib/libsbi.a) + +.PHONY: install_libplatsbi +install_libplatsbi: $(build_dir)/$(plat_subdir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a + $(call inst_header_dir,$(install_dir)/$(plat_subdir)/include,$(include_dir)/sbi) + $(call inst_file,$(install_dir)/$(plat_subdir)/lib/libplatsbi.a,$(build_dir)/$(plat_subdir)/lib/libplatsbi.a) + +.PHONY: install_blobs +install_blobs: $(build_dir)/$(plat_subdir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(blob-bins-path-y) + $(call inst_file_list,$(install_dir)/$(plat_subdir)/blob,$(plat_subdir)/blob,$(blob-elfs-path-y)) + $(call inst_file_list,$(install_dir)/$(plat_subdir)/blob,$(plat_subdir)/blob,$(blob-bins-path-y)) + +# Rule for "make clean" +.PHONY: clean +clean: +ifeq ($(build_dir),$(CURDIR)/build) + $(V)mkdir -p $(build_dir) + $(if $(V), @echo " CLEAN $(build_dir)") + $(V)find $(build_dir) -maxdepth 1 -type f -exec rm -rf {} + +endif + +# Rule for "make distclean" +.PHONY: distclean +distclean: +ifeq ($(build_dir),$(CURDIR)/build) + $(if $(V), @echo " RM $(build_dir)") + $(V)rm -rf $(build_dir) +endif +ifeq ($(install_dir),$(CURDIR)/install) + $(if $(V), @echo " RM $(install_dir)") + $(V)rm -rf $(install_dir) +endif diff --git a/README.md b/README.md new file mode 100644 index 0000000..3892273 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +RISC-V Open Source Supervisor Binary Interface (OpenSBI) +======================================================== + +The RISC-V Supervisor Binary Interface (SBI) is a recommended +interface between: +1. platform specific firmware running in M-mode and bootloader + running in S-mode +2. platform specific firmware running in M-mode and general + purpose operating system running in S-mode +3. hypervisor runnng in HS-mode and general purpose operating + system running in VS-mode. + +The RISC-V SBI spec is maintained as independent project by +RISC-V Foundation at https://github.com/riscv/riscv-sbi-doc + +The RISC-V OpenSBI project aims to provides an open-source and +extensible implementation of the SBI spec. This project can be +easily extended by RISC-V platform or RISC-V System-on-Chip vendors. + + +How to Build? +------------- + +Below are the steps to cross-compile and install RISC-V OpenSBI: + +1. Setup build environment +$ CROSS_COMPILE=riscv64-unknown-linux-gnu- + +2. Build sources +$ make PLAT= +OR +$ make PLAT= O= + +3. Install blobs +$ make PLAT= install +OR +$ make PLAT= I= install diff --git a/blob/fw_common.S b/blob/fw_common.S new file mode 100644 index 0000000..9cc2b09 --- /dev/null +++ b/blob/fw_common.S @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + + .align 3 + .section .entry, "ax", %progbits + .globl _start + .globl _start_warm +_start: + /* Jump to warm-boot for mhartid != 0 */ + csrr a6, mhartid + blt zero, a6, _wait_for_boot_hart + + /* Zero-out BSS */ + la a4, _bss_start + la a5, _bss_end +_bss_zero: + REG_S zero, (a4) + add a4, a4, __SIZEOF_POINTER__ + blt a4, a5, _bss_zero + + /* + * Relocate FDT + * Note: We will preserve a0 and a1 passed by + * previous booting stage. + */ + /* Mask values in a3 and a4 */ + li a3, ~0xf + li a4, 0xff + /* t1 = destinetion FDT start address */ + add s0, a0, zero + add s1, a1, zero + call fw_next_arg1 + add t1, a0, zero + add a0, s0, zero + add a1, s1, zero + beqz t1, _fdt_reloc_done + and t1, t1, a3 + /* t0 = source FDT start address */ + add t0, a1, zero + and t0, t0, a3 + /* t2 = source FDT size in big-endian */ + lwu t2, 4(t0) + /* t3 = bit[15:8] of FDT size */ + add t3, t2, zero + srli t3, t3, 16 + and t3, t3, a4 + slli t3, t3, 8 + /* t4 = bit[23:16] of FDT size */ + add t4, t2, zero + srli t4, t4, 8 + and t4, t4, a4 + slli t4, t4, 16 + /* t5 = bit[31:24] of FDT size */ + add t5, t2, zero + and t5, t5, a4 + slli t5, t5, 24 + /* t2 = bit[7:0] of FDT size */ + srli t2, t2, 24 + and t2, t2, a4 + /* t2 = FDT size in little-endian */ + or t2, t2, t3 + or t2, t2, t4 + or t2, t2, t5 + /* t2 = destinetion FDT end address */ + add t2, t1, t2 + /* FDT copy loop */ + ble t2, t1, _fdt_reloc_done +_fdt_reloc_again: + REG_L t3, 0(t0) + REG_S t3, 0(t1) + add t0, t0, __SIZEOF_POINTER__ + add t1, t1, __SIZEOF_POINTER__ + blt t1, t2, _fdt_reloc_again +_fdt_reloc_done: + + /* Update boot hart flag */ + la a4, _boot_hart_done + li a5, 1 + REG_S a5, (a4) + j _wait_for_boot_hart + + .align 3 +_boot_hart_done: + RISCV_PTR 0 + .align 3 + + /* Wait for boot hart */ +_wait_for_boot_hart: + la a4, _boot_hart_done + REG_L a5, (a4) + beqz a5, _wait_for_boot_hart + +_start_warm: + /* Disable and clear all interrupts */ + csrw mie, zero + csrw mip, zero + + /* HART ID should be withing expected limit */ + csrr a6, mhartid + li a5, PLAT_HART_COUNT + bge a6, a5, _start_hang + + /* Setup scratch space */ + li a5, PLAT_HART_STACK_SIZE + la tp, _stack_end + mul a5, a5, a6 + sub tp, tp, a5 + li a5, RISCV_SCRATCH_SIZE + sub tp, tp, a5 + csrw mscratch, tp + + /* Initialize scratch space */ + REG_S zero, RISCV_SCRATCH_TMP0_OFFSET(tp) + la a4, _fw_start + la a5, _fw_end + sub a5, a5, a4 + REG_S a4, RISCV_SCRATCH_FW_START_OFFSET(tp) + REG_S a5, RISCV_SCRATCH_FW_SIZE_OFFSET(tp) + /* Note: fw_next_arg1() uses a0, a1, and ra */ + call fw_next_arg1 + REG_S a0, RISCV_SCRATCH_NEXT_ARG1_OFFSET(tp) + /* Note: fw_next_addr() uses a0, a1, and ra */ + call fw_next_addr + REG_S a0, RISCV_SCRATCH_NEXT_ADDR_OFFSET(tp) + li a4, PRV_S + REG_S a4, RISCV_SCRATCH_NEXT_MODE_OFFSET(tp) + la a4, _start_warm + REG_S a4, RISCV_SCRATCH_WARMBOOT_ADDR_OFFSET(tp) + la a4, platform + REG_S a4, RISCV_SCRATCH_PLATFORM_ADDR_OFFSET(tp) + la a4, _hartid_to_scratch + REG_S a4, RISCV_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) + REG_S zero, RISCV_SCRATCH_IPI_TYPE_OFFSET(tp) + + /* Setup stack */ + add sp, tp, zero + + /* Setup trap handler */ + la a4, _trap_handler + csrw mtvec, a4 + + /* Initialize SBI runtime */ + csrr a0, mscratch + call sbi_init + + /* We don't expect to reach here hence just hang */ + j _start_hang + + .align 3 + .section .entry, "ax", %progbits + .globl _hartid_to_scratch +_hartid_to_scratch: + add sp, sp, -(2 * __SIZEOF_POINTER__) + REG_S a1, (sp) + REG_S a2, (__SIZEOF_POINTER__)(sp) + li a1, PLAT_HART_STACK_SIZE + la a2, _stack_end + mul a1, a1, a0 + sub a2, a2, a1 + li a1, RISCV_SCRATCH_SIZE + sub a0, a2, a1 + REG_L a1, (sp) + REG_L a2, (__SIZEOF_POINTER__)(sp) + add sp, sp, (2 * __SIZEOF_POINTER__) + ret + + .align 3 + .section .entry, "ax", %progbits + .globl _start_hang +_start_hang: + wfi + j _start_hang + + .align 3 + .section .entry, "ax", %progbits + .globl _trap_handler +_trap_handler: + /* Swap SP and MSCRATCH */ + csrrw sp, mscratch, sp + + /* Setup exception stack */ + add sp, sp, -(RISCV_TRAP_REGS_SIZE) + + /* Save RA, T0, T1, and T2 */ + REG_S ra, RISCV_TRAP_REGS_OFFSET(ra)(sp) + REG_S t0, RISCV_TRAP_REGS_OFFSET(t0)(sp) + REG_S t1, RISCV_TRAP_REGS_OFFSET(t1)(sp) + REG_S t2, RISCV_TRAP_REGS_OFFSET(t2)(sp) + + /* Save original SP and restore MSCRATCH */ + add t0, sp, RISCV_TRAP_REGS_SIZE + csrrw t0, mscratch, t0 + REG_S t0, RISCV_TRAP_REGS_OFFSET(sp)(sp) + + /* Save MEPC and MSTATUS CSRs */ + csrr t0, mepc + csrr t1, mstatus + + /* + * Note: Fast path trap handling can be done here + * using SP, RA, T0, T1, and T2 registers where + * T0 <- MEPC + * T1 <- MSTATUS + */ + + /* Save MEPC and MSTATUS CSRs */ + REG_S t0, RISCV_TRAP_REGS_OFFSET(mepc)(sp) + REG_S t1, RISCV_TRAP_REGS_OFFSET(mstatus)(sp) + + /* Save all general regisers except SP, RA, T0, T1, and T2 */ + REG_S zero, RISCV_TRAP_REGS_OFFSET(zero)(sp) + REG_S gp, RISCV_TRAP_REGS_OFFSET(gp)(sp) + REG_S tp, RISCV_TRAP_REGS_OFFSET(tp)(sp) + REG_S s0, RISCV_TRAP_REGS_OFFSET(s0)(sp) + REG_S s1, RISCV_TRAP_REGS_OFFSET(s1)(sp) + REG_S a0, RISCV_TRAP_REGS_OFFSET(a0)(sp) + REG_S a1, RISCV_TRAP_REGS_OFFSET(a1)(sp) + REG_S a2, RISCV_TRAP_REGS_OFFSET(a2)(sp) + REG_S a3, RISCV_TRAP_REGS_OFFSET(a3)(sp) + REG_S a4, RISCV_TRAP_REGS_OFFSET(a4)(sp) + REG_S a5, RISCV_TRAP_REGS_OFFSET(a5)(sp) + REG_S a6, RISCV_TRAP_REGS_OFFSET(a6)(sp) + REG_S a7, RISCV_TRAP_REGS_OFFSET(a7)(sp) + REG_S s2, RISCV_TRAP_REGS_OFFSET(s2)(sp) + REG_S s3, RISCV_TRAP_REGS_OFFSET(s3)(sp) + REG_S s4, RISCV_TRAP_REGS_OFFSET(s4)(sp) + REG_S s5, RISCV_TRAP_REGS_OFFSET(s5)(sp) + REG_S s6, RISCV_TRAP_REGS_OFFSET(s6)(sp) + REG_S s7, RISCV_TRAP_REGS_OFFSET(s7)(sp) + REG_S s8, RISCV_TRAP_REGS_OFFSET(s8)(sp) + REG_S s9, RISCV_TRAP_REGS_OFFSET(s9)(sp) + REG_S s10, RISCV_TRAP_REGS_OFFSET(s10)(sp) + REG_S s11, RISCV_TRAP_REGS_OFFSET(s11)(sp) + REG_S t3, RISCV_TRAP_REGS_OFFSET(t3)(sp) + REG_S t4, RISCV_TRAP_REGS_OFFSET(t4)(sp) + REG_S t5, RISCV_TRAP_REGS_OFFSET(t5)(sp) + REG_S t6, RISCV_TRAP_REGS_OFFSET(t6)(sp) + + /* Call C routine */ + add a0, sp, zero + csrr a1, mscratch + call sbi_trap_handler + + /* Restore all general regisers except SP, RA, T0, T1, T2, and T3 */ + REG_L gp, RISCV_TRAP_REGS_OFFSET(gp)(sp) + REG_L tp, RISCV_TRAP_REGS_OFFSET(tp)(sp) + REG_L s0, RISCV_TRAP_REGS_OFFSET(s0)(sp) + REG_L s1, RISCV_TRAP_REGS_OFFSET(s1)(sp) + REG_L a0, RISCV_TRAP_REGS_OFFSET(a0)(sp) + REG_L a1, RISCV_TRAP_REGS_OFFSET(a1)(sp) + REG_L a2, RISCV_TRAP_REGS_OFFSET(a2)(sp) + REG_L a3, RISCV_TRAP_REGS_OFFSET(a3)(sp) + REG_L a4, RISCV_TRAP_REGS_OFFSET(a4)(sp) + REG_L a5, RISCV_TRAP_REGS_OFFSET(a5)(sp) + REG_L a6, RISCV_TRAP_REGS_OFFSET(a6)(sp) + REG_L a7, RISCV_TRAP_REGS_OFFSET(a7)(sp) + REG_L s2, RISCV_TRAP_REGS_OFFSET(s2)(sp) + REG_L s3, RISCV_TRAP_REGS_OFFSET(s3)(sp) + REG_L s4, RISCV_TRAP_REGS_OFFSET(s4)(sp) + REG_L s5, RISCV_TRAP_REGS_OFFSET(s5)(sp) + REG_L s6, RISCV_TRAP_REGS_OFFSET(s6)(sp) + REG_L s7, RISCV_TRAP_REGS_OFFSET(s7)(sp) + REG_L s8, RISCV_TRAP_REGS_OFFSET(s8)(sp) + REG_L s9, RISCV_TRAP_REGS_OFFSET(s9)(sp) + REG_L s10, RISCV_TRAP_REGS_OFFSET(s10)(sp) + REG_L s11, RISCV_TRAP_REGS_OFFSET(s11)(sp) + REG_L t3, RISCV_TRAP_REGS_OFFSET(t3)(sp) + REG_L t4, RISCV_TRAP_REGS_OFFSET(t4)(sp) + REG_L t5, RISCV_TRAP_REGS_OFFSET(t5)(sp) + REG_L t6, RISCV_TRAP_REGS_OFFSET(t6)(sp) + + /* Load T0 and T1 with MEPC and MSTATUS */ + REG_L t0, RISCV_TRAP_REGS_OFFSET(mepc)(sp) + REG_L t1, RISCV_TRAP_REGS_OFFSET(mstatus)(sp) + + /* + * Note: Jump here after fast trap handling + * using SP, RA, T0, T1, and T2 + * T0 <- MEPC + * T1 <- MSTATUS + */ + + /* Restore MEPC and MSTATUS CSRs */ + csrw mepc, t0 + csrw mstatus, t1 + + /* Restore RA, T0, T1, and T2 */ + REG_L ra, RISCV_TRAP_REGS_OFFSET(ra)(sp) + REG_L t0, RISCV_TRAP_REGS_OFFSET(t0)(sp) + REG_L t1, RISCV_TRAP_REGS_OFFSET(t1)(sp) + REG_L t2, RISCV_TRAP_REGS_OFFSET(t2)(sp) + + /* Restore SP */ + REG_L sp, RISCV_TRAP_REGS_OFFSET(sp)(sp) + + mret diff --git a/blob/fw_common.ldS b/blob/fw_common.ldS new file mode 100644 index 0000000..eac7ede --- /dev/null +++ b/blob/fw_common.ldS @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + + . = PLAT_TEXT_START; + + PROVIDE(_fw_start = .); + + . = ALIGN(0x1000); /* Need this to create proper sections */ + + /* Beginning of the code section */ + + .text : + { + PROVIDE(_text_start = .); + *(.entry) + *(.text) + . = ALIGN(8); + PROVIDE(_text_end = .); + } + + . = ALIGN(0x1000); /* Ensure next section is page aligned */ + + /* End of the code sections */ + + /* Beginning of the read-only data sections */ + + . = ALIGN(0x1000); /* Ensure next section is page aligned */ + + .rodata : + { + PROVIDE(_rodata_start = .); + *(.rodata .rodata.*) + . = ALIGN(8); + PROVIDE(_rodata_end = .); + } + + /* End of the read-only data sections */ + + /* Beginning of the read-write data sections */ + + . = ALIGN(0x1000); /* Ensure next section is page aligned */ + + .data : + { + PROVIDE(_data_start = .); + + *(.data) + *(.data.*) + *(.readmostly.data) + *(*.data) + . = ALIGN(8); + + PROVIDE(_data_end = .); + } + + . = ALIGN(0x1000); /* Ensure next section is page aligned */ + + .stack : + { + PROVIDE(_stack_start = .); + *(.stack) + *(.stack.*) + . = . + (PLAT_HART_STACK_SIZE * PLAT_HART_COUNT); + . = ALIGN(8); + PROVIDE(_stack_end = .); + } + + . = ALIGN(0x1000); /* Ensure next section is page aligned */ + + .bss : + { + PROVIDE(_bss_start = .); + *(.bss) + *(.bss.*) + . = ALIGN(8); + PROVIDE(_bss_end = .); + } + + /* End of the read-write data sections */ + + . = ALIGN(0x1000); /* Need this to create proper sections */ + + PROVIDE(_fw_end = .); diff --git a/blob/fw_jump.S b/blob/fw_jump.S new file mode 100644 index 0000000..960e594 --- /dev/null +++ b/blob/fw_jump.S @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "fw_common.S" + + .align 3 + .section .entry, "ax", %progbits + .global fw_next_arg1 +fw_next_arg1: + /* We return FDT destinetion address in 'a0' */ +#ifdef FW_JUMP_FDT_OFFSET + /* a0 = destinetion FDT start address */ + la a0, _jump_addr + REG_L a0, (a0) + li a1, FW_JUMP_FDT_OFFSET + add a0, a0, a1 +#else + add a0, zero, zero +#endif + ret + + .align 3 + .section .entry, "ax", %progbits + .global fw_next_addr +fw_next_addr: + /* We return next address in 'a0' */ + la a0, _jump_addr + REG_L a0, (a0) + ret + +#ifndef FW_JUMP_ADDR +#error "Must define FW_JUMP_ADDR" +#endif + + .align 3 + .section .entry, "ax", %progbits +_jump_addr: + RISCV_PTR FW_JUMP_ADDR diff --git a/blob/fw_jump.elf.ldS b/blob/fw_jump.elf.ldS new file mode 100644 index 0000000..dfffbf6 --- /dev/null +++ b/blob/fw_jump.elf.ldS @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +SECTIONS +{ + #include "fw_common.ldS" +} diff --git a/blob/fw_payload.S b/blob/fw_payload.S new file mode 100644 index 0000000..abc2495 --- /dev/null +++ b/blob/fw_payload.S @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "fw_common.S" + + .align 3 + .section .entry, "ax", %progbits + .global fw_next_arg1 +fw_next_arg1: + /* We return FDT destinetion address in 'a0' */ +#ifdef FW_PAYLOAD_FDT_OFFSET + /* a0 = destinetion FDT start address */ + la a0, payload_bin + li a1, FW_PAYLOAD_FDT_OFFSET + add a0, a0, a1 +#else + add a0, zero, zero +#endif + ret + + .align 3 + .section .entry, "ax", %progbits + .global fw_next_addr +fw_next_addr: + /* We return next address in 'a0' */ + la a0, payload_bin + ret + +#define str(s) #s +#define stringify(s) str(s) + + .section .payload, "ax", %progbits + .globl payload_bin +payload_bin: +#ifndef FW_PAYLOAD_PATH + wfi + j payload_bin +#else + .incbin stringify(FW_PAYLOAD_PATH) +#endif diff --git a/blob/fw_payload.elf.ldS b/blob/fw_payload.elf.ldS new file mode 100644 index 0000000..2196e9c --- /dev/null +++ b/blob/fw_payload.elf.ldS @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +SECTIONS +{ + #include "fw_common.ldS" + + . = ALIGN(0x200000); + + .payload : + { + PROVIDE(_payload_start = .); + *(.payload) + . = ALIGN(8); + PROVIDE(_payload_end = .); + } +} diff --git a/blob/objects.mk b/blob/objects.mk new file mode 100644 index 0000000..7376213 --- /dev/null +++ b/blob/objects.mk @@ -0,0 +1,29 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +blob-cppflags-y = +blob-cflags-y = +blob-asflags-y = +blob-ldflags-y = + +blob-bins-$(FW_JUMP) += fw_jump.bin +ifdef FW_JUMP_ADDR +blob-cppflags-$(FW_JUMP) += -DFW_JUMP_ADDR=$(FW_JUMP_ADDR) +endif +ifdef FW_JUMP_FDT_OFFSET +blob-cppflags-$(FW_JUMP) += -DFW_JUMP_FDT_OFFSET=$(FW_JUMP_FDT_OFFSET) +endif + +blob-bins-$(FW_PAYLOAD) += fw_payload.bin +ifdef FW_PAYLOAD_PATH +blob-cppflags-$(FW_PAYLOAD) += -DFW_PAYLOAD_PATH=$(FW_PAYLOAD_PATH) +endif +ifdef FW_PAYLOAD_FDT_OFFSET +blob-cppflags-$(FW_PAYLOAD) += -DFW_PAYLOAD_FDT_OFFSET=$(FW_PAYLOAD_FDT_OFFSET) +endif diff --git a/include/sbi/riscv_asm.h b/include/sbi/riscv_asm.h new file mode 100644 index 0000000..516bf6f --- /dev/null +++ b/include/sbi/riscv_asm.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __RISCV_ASM_H__ +#define __RISCV_ASM_H__ + +#ifdef __ASSEMBLY__ +#define __ASM_STR(x) x +#else +#define __ASM_STR(x) #x +#endif + +#if __riscv_xlen == 64 +#define __REG_SEL(a, b) __ASM_STR(a) +#elif __riscv_xlen == 32 +#define __REG_SEL(a, b) __ASM_STR(b) +#else +#error "Unexpected __riscv_xlen" +#endif + +#define REG_L __REG_SEL(ld, lw) +#define REG_S __REG_SEL(sd, sw) +#define SZREG __REG_SEL(8, 4) +#define LGREG __REG_SEL(3, 2) + +#if __SIZEOF_POINTER__ == 8 +#ifdef __ASSEMBLY__ +#define RISCV_PTR .dword +#define RISCV_SZPTR 8 +#define RISCV_LGPTR 3 +#else +#define RISCV_PTR ".dword" +#define RISCV_SZPTR "8" +#define RISCV_LGPTR "3" +#endif +#elif __SIZEOF_POINTER__ == 4 +#ifdef __ASSEMBLY__ +#define RISCV_PTR .word +#define RISCV_SZPTR 4 +#define RISCV_LGPTR 2 +#else +#define RISCV_PTR ".word" +#define RISCV_SZPTR "4" +#define RISCV_LGPTR "2" +#endif +#else +#error "Unexpected __SIZEOF_POINTER__" +#endif + +#if (__SIZEOF_INT__ == 4) +#define RISCV_INT __ASM_STR(.word) +#define RISCV_SZINT __ASM_STR(4) +#define RISCV_LGINT __ASM_STR(2) +#else +#error "Unexpected __SIZEOF_INT__" +#endif + +#if (__SIZEOF_SHORT__ == 2) +#define RISCV_SHORT __ASM_STR(.half) +#define RISCV_SZSHORT __ASM_STR(2) +#define RISCV_LGSHORT __ASM_STR(1) +#else +#error "Unexpected __SIZEOF_SHORT__" +#endif + +#define RISCV_SCRATCH_TMP0_OFFSET (0 * __SIZEOF_POINTER__) +#define RISCV_SCRATCH_FW_START_OFFSET (1 * __SIZEOF_POINTER__) +#define RISCV_SCRATCH_FW_SIZE_OFFSET (2 * __SIZEOF_POINTER__) +#define RISCV_SCRATCH_NEXT_ARG1_OFFSET (3 * __SIZEOF_POINTER__) +#define RISCV_SCRATCH_NEXT_ADDR_OFFSET (4 * __SIZEOF_POINTER__) +#define RISCV_SCRATCH_NEXT_MODE_OFFSET (5 * __SIZEOF_POINTER__) +#define RISCV_SCRATCH_WARMBOOT_ADDR_OFFSET (6 * __SIZEOF_POINTER__) +#define RISCV_SCRATCH_PLATFORM_ADDR_OFFSET (7 * __SIZEOF_POINTER__) +#define RISCV_SCRATCH_HARTID_TO_SCRATCH_OFFSET (8 * __SIZEOF_POINTER__) +#define RISCV_SCRATCH_IPI_TYPE_OFFSET (9 * __SIZEOF_POINTER__) +#define RISCV_SCRATCH_SIZE 256 + +#define RISCV_TRAP_REGS_zero 0 +#define RISCV_TRAP_REGS_ra 1 +#define RISCV_TRAP_REGS_sp 2 +#define RISCV_TRAP_REGS_gp 3 +#define RISCV_TRAP_REGS_tp 4 +#define RISCV_TRAP_REGS_t0 5 +#define RISCV_TRAP_REGS_t1 6 +#define RISCV_TRAP_REGS_t2 7 +#define RISCV_TRAP_REGS_s0 8 +#define RISCV_TRAP_REGS_s1 9 +#define RISCV_TRAP_REGS_a0 10 +#define RISCV_TRAP_REGS_a1 11 +#define RISCV_TRAP_REGS_a2 12 +#define RISCV_TRAP_REGS_a3 13 +#define RISCV_TRAP_REGS_a4 14 +#define RISCV_TRAP_REGS_a5 15 +#define RISCV_TRAP_REGS_a6 16 +#define RISCV_TRAP_REGS_a7 17 +#define RISCV_TRAP_REGS_s2 18 +#define RISCV_TRAP_REGS_s3 19 +#define RISCV_TRAP_REGS_s4 20 +#define RISCV_TRAP_REGS_s5 21 +#define RISCV_TRAP_REGS_s6 22 +#define RISCV_TRAP_REGS_s7 23 +#define RISCV_TRAP_REGS_s8 24 +#define RISCV_TRAP_REGS_s9 25 +#define RISCV_TRAP_REGS_s10 26 +#define RISCV_TRAP_REGS_s11 27 +#define RISCV_TRAP_REGS_t3 28 +#define RISCV_TRAP_REGS_t4 29 +#define RISCV_TRAP_REGS_t5 30 +#define RISCV_TRAP_REGS_t6 31 +#define RISCV_TRAP_REGS_mepc 32 +#define RISCV_TRAP_REGS_mstatus 33 +#define RISCV_TRAP_REGS_last 34 + +#define RISCV_TRAP_REGS_OFFSET(x) \ + ((RISCV_TRAP_REGS_##x) * __SIZEOF_POINTER__) +#define RISCV_TRAP_REGS_SIZE RISCV_TRAP_REGS_OFFSET(last) + +#ifndef __ASSEMBLY__ + +#define csr_swap(csr, val) \ +({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__ ("csrrw %0, " #csr ", %1" \ + : "=r" (__v) : "rK" (__v) \ + : "memory"); \ + __v; \ +}) + +#define csr_read(csr) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__ ("csrr %0, " #csr \ + : "=r" (__v) : \ + : "memory"); \ + __v; \ +}) + +#define csr_read_n(csr_num) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__ ("csrr %0, " __ASM_STR(csr_num) \ + : "=r" (__v) : \ + : "memory"); \ + __v; \ +}) + +#define csr_write(csr, val) \ +({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__ ("csrw " #csr ", %0" \ + : : "rK" (__v) \ + : "memory"); \ +}) + +#define csr_write_n(csr_num, val) \ +({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__ ("csrw " __ASM_STR(csr_num) ", %0" \ + : : "rK" (__v) \ + : "memory"); \ +}) + +#define csr_read_set(csr, val) \ +({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__ ("csrrs %0, " #csr ", %1" \ + : "=r" (__v) : "rK" (__v) \ + : "memory"); \ + __v; \ +}) + +#define csr_set(csr, val) \ +({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__ ("csrs " #csr ", %0" \ + : : "rK" (__v) \ + : "memory"); \ +}) + +#define csr_read_clear(csr, val) \ +({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__ ("csrrc %0, " #csr ", %1" \ + : "=r" (__v) : "rK" (__v) \ + : "memory"); \ + __v; \ +}) + +#define csr_clear(csr, val) \ +({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__ ("csrc " #csr ", %0" \ + : : "rK" (__v) \ + : "memory"); \ +}) + +unsigned long csr_read_num(int csr_num); + +void csr_write_num(int csr_num, unsigned long val); + +#define wfi() \ +do { \ + __asm__ __volatile__ ("wfi" ::: "memory"); \ +} while (0) + +static inline int misa_extension(char ext) +{ + return csr_read(misa) & (1 << (ext - 'A')); +} + +static inline int misa_xlen(void) +{ + return ((long)csr_read(misa) < 0) ? 64 : 32; +} + +static inline void misa_string(char *out, unsigned int out_sz) +{ + unsigned long i, val = csr_read(misa); + + for (i = 0; i < 26; i++) { + if (val & (1 << i)) { + *out = 'A' + i; + out++; + } + } + *out = '\0'; + out++; +} + +int pmp_set(unsigned int n, unsigned long prot, + unsigned long addr, unsigned long log2len); + +int pmp_get(unsigned int n, unsigned long *prot_out, + unsigned long *addr_out, unsigned long *log2len_out); + +#endif /* !__ASSEMBLY__ */ + +#endif diff --git a/include/sbi/riscv_atomic.h b/include/sbi/riscv_atomic.h new file mode 100644 index 0000000..775cd6f --- /dev/null +++ b/include/sbi/riscv_atomic.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __RISCV_ATOMIC_H__ +#define __RISCV_ATOMIC_H__ + +typedef struct { + volatile long counter; +} atomic_t; + +#define ATOMIC_INIT(_lptr, val) \ + (_lptr)->counter = (val) + +#define ATOMIC_INITIALIZER(val) \ + { .counter = (val), } + +long atomic_read(atomic_t *atom); + +void atomic_write(atomic_t *atom, long value); + +long atomic_add_return(atomic_t *atom, long value); + +long atomic_sub_return(atomic_t *atom, long value); + +long arch_atomic_cmpxchg(atomic_t *atom, long oldval, long newval); + +long arch_atomic_xchg(atomic_t *atom, long newval); + +unsigned int atomic_raw_xchg_uint(volatile unsigned int *ptr, + unsigned int newval); + +#endif diff --git a/include/sbi/riscv_barrier.h b/include/sbi/riscv_barrier.h new file mode 100644 index 0000000..993cbda --- /dev/null +++ b/include/sbi/riscv_barrier.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __RISCV_BARRIER_H__ +#define __RISCV_BARRIER_H__ + +#define RISCV_ACQUIRE_BARRIER "\tfence r , rw\n" +#define RISCV_RELEASE_BARRIER "\tfence rw, w\n" + +#define RISCV_FENCE(p, s) \ + __asm__ __volatile__ ("fence " #p "," #s : : : "memory") + +/* Read & Write Memory barrier */ +#define mb() RISCV_FENCE(iorw,iorw) + +/* Read Memory barrier */ +#define rmb() RISCV_FENCE(ir,ir) + +/* Write Memory barrier */ +#define wmb() RISCV_FENCE(ow,ow) + +/* SMP Read & Write Memory barrier */ +#define smp_mb() RISCV_FENCE(rw,rw) + +/* SMP Read Memory barrier */ +#define smp_rmb() RISCV_FENCE(r,r) + +/* SMP Write Memory barrier */ +#define smp_wmb() RISCV_FENCE(w,w) + +/* CPU relax for busy loop */ +#define cpu_relax() asm volatile ("" : : : "memory") + +#define __smp_store_release(p, v) \ +do { \ + RISCV_FENCE(rw,w); \ + *(p) = (v); \ +} while (0) + +#define __smp_load_acquire(p) \ +({ \ + typeof(*p) ___p1 = *(p); \ + RISCV_FENCE(r,rw); \ + ___p1; \ +}) + +#endif diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h new file mode 100644 index 0000000..a4407d2 --- /dev/null +++ b/include/sbi/riscv_encoding.h @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __RISCV_ENCODING_H__ +#define __RISCV_ENCODING_H__ + +#include + +/* TODO: Make constants usable in assembly with _AC() macro */ + +#define MSTATUS_UIE 0x00000001 +#define MSTATUS_SIE 0x00000002 +#define MSTATUS_HIE 0x00000004 +#define MSTATUS_MIE 0x00000008 +#define MSTATUS_UPIE 0x00000010 +#define MSTATUS_SPIE 0x00000020 +#define MSTATUS_HPIE 0x00000040 +#define MSTATUS_MPIE 0x00000080 +#define MSTATUS_SPP 0x00000100 +#define MSTATUS_HPP 0x00000600 +#define MSTATUS_MPP 0x00001800 +#define MSTATUS_FS 0x00006000 +#define MSTATUS_XS 0x00018000 +#define MSTATUS_MPRV 0x00020000 +#define MSTATUS_SUM 0x00040000 +#define MSTATUS_MXR 0x00080000 +#define MSTATUS_TVM 0x00100000 +#define MSTATUS_TW 0x00200000 +#define MSTATUS_TSR 0x00400000 +#define MSTATUS32_SD 0x80000000 +#define MSTATUS_UXL 0x0000000300000000 +#define MSTATUS_SXL 0x0000000C00000000 +#define MSTATUS64_SD 0x8000000000000000 + +#define SSTATUS_UIE 0x00000001 +#define SSTATUS_SIE 0x00000002 +#define SSTATUS_UPIE 0x00000010 +#define SSTATUS_SPIE 0x00000020 +#define SSTATUS_SPP 0x00000100 +#define SSTATUS_FS 0x00006000 +#define SSTATUS_XS 0x00018000 +#define SSTATUS_SUM 0x00040000 +#define SSTATUS_MXR 0x00080000 +#define SSTATUS32_SD 0x80000000 +#define SSTATUS_UXL 0x0000000300000000 +#define SSTATUS64_SD 0x8000000000000000 + +#define DCSR_XDEBUGVER (3U<<30) +#define DCSR_NDRESET (1<<29) +#define DCSR_FULLRESET (1<<28) +#define DCSR_EBREAKM (1<<15) +#define DCSR_EBREAKH (1<<14) +#define DCSR_EBREAKS (1<<13) +#define DCSR_EBREAKU (1<<12) +#define DCSR_STOPCYCLE (1<<10) +#define DCSR_STOPTIME (1<<9) +#define DCSR_CAUSE (7<<6) +#define DCSR_DEBUGINT (1<<5) +#define DCSR_HALT (1<<3) +#define DCSR_STEP (1<<2) +#define DCSR_PRV (3<<0) + +#define DCSR_CAUSE_NONE 0 +#define DCSR_CAUSE_SWBP 1 +#define DCSR_CAUSE_HWBP 2 +#define DCSR_CAUSE_DEBUGINT 3 +#define DCSR_CAUSE_STEP 4 +#define DCSR_CAUSE_HALT 5 + +#define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4)) +#define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5)) +#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11)) + +#define MCONTROL_SELECT (1<<19) +#define MCONTROL_TIMING (1<<18) +#define MCONTROL_ACTION (0x3f<<12) +#define MCONTROL_CHAIN (1<<11) +#define MCONTROL_MATCH (0xf<<7) +#define MCONTROL_M (1<<6) +#define MCONTROL_H (1<<5) +#define MCONTROL_S (1<<4) +#define MCONTROL_U (1<<3) +#define MCONTROL_EXECUTE (1<<2) +#define MCONTROL_STORE (1<<1) +#define MCONTROL_LOAD (1<<0) + +#define MCONTROL_TYPE_NONE 0 +#define MCONTROL_TYPE_MATCH 2 + +#define MCONTROL_ACTION_DEBUG_EXCEPTION 0 +#define MCONTROL_ACTION_DEBUG_MODE 1 +#define MCONTROL_ACTION_TRACE_START 2 +#define MCONTROL_ACTION_TRACE_STOP 3 +#define MCONTROL_ACTION_TRACE_EMIT 4 + +#define MCONTROL_MATCH_EQUAL 0 +#define MCONTROL_MATCH_NAPOT 1 +#define MCONTROL_MATCH_GE 2 +#define MCONTROL_MATCH_LT 3 +#define MCONTROL_MATCH_MASK_LOW 4 +#define MCONTROL_MATCH_MASK_HIGH 5 + +#define IRQ_S_SOFT 1 +#define IRQ_H_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_S_TIMER 5 +#define IRQ_H_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_S_EXT 9 +#define IRQ_H_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_COP 12 +#define IRQ_HOST 13 + +#define MIP_SSIP (1 << IRQ_S_SOFT) +#define MIP_HSIP (1 << IRQ_H_SOFT) +#define MIP_MSIP (1 << IRQ_M_SOFT) +#define MIP_STIP (1 << IRQ_S_TIMER) +#define MIP_HTIP (1 << IRQ_H_TIMER) +#define MIP_MTIP (1 << IRQ_M_TIMER) +#define MIP_SEIP (1 << IRQ_S_EXT) +#define MIP_HEIP (1 << IRQ_H_EXT) +#define MIP_MEIP (1 << IRQ_M_EXT) + +#define SIP_SSIP MIP_SSIP +#define SIP_STIP MIP_STIP + +#define PRV_U 0 +#define PRV_S 1 +#define PRV_H 2 +#define PRV_M 3 + +#define SATP32_MODE 0x80000000 +#define SATP32_ASID 0x7FC00000 +#define SATP32_PPN 0x003FFFFF +#define SATP64_MODE 0xF000000000000000 +#define SATP64_ASID 0x0FFFF00000000000 +#define SATP64_PPN 0x00000FFFFFFFFFFF + +#define SATP_MODE_OFF 0 +#define SATP_MODE_SV32 1 +#define SATP_MODE_SV39 8 +#define SATP_MODE_SV48 9 +#define SATP_MODE_SV57 10 +#define SATP_MODE_SV64 11 + +#define PMP_R 0x01 +#define PMP_W 0x02 +#define PMP_X 0x04 +#define PMP_A 0x18 +#define PMP_A_TOR 0x08 +#define PMP_A_NA4 0x10 +#define PMP_A_NAPOT 0x18 +#define PMP_L 0x80 + +#define PMP_SHIFT 2 +#define PMP_COUNT 16 + +/* page table entry (PTE) fields */ +#define PTE_V 0x001 /* Valid */ +#define PTE_R 0x002 /* Read */ +#define PTE_W 0x004 /* Write */ +#define PTE_X 0x008 /* Execute */ +#define PTE_U 0x010 /* User */ +#define PTE_G 0x020 /* Global */ +#define PTE_A 0x040 /* Accessed */ +#define PTE_D 0x080 /* Dirty */ +#define PTE_SOFT 0x300 /* Reserved for Software */ + +#define PTE_PPN_SHIFT 10 + +#define PTE_TABLE(PTE) \ + (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V) + +#if __riscv_xlen == 64 +#define MSTATUS_SD MSTATUS64_SD +#define SSTATUS_SD SSTATUS64_SD +#define RISCV_PGLEVEL_BITS 9 +#define SATP_MODE SATP64_MODE +#else +#define MSTATUS_SD MSTATUS32_SD +#define SSTATUS_SD SSTATUS32_SD +#define RISCV_PGLEVEL_BITS 10 +#define SATP_MODE SATP32_MODE +#endif +#define RISCV_PGSHIFT 12 +#define RISCV_PGSIZE (1 << RISCV_PGSHIFT) + +#define CSR_FFLAGS 0x1 +#define CSR_FRM 0x2 +#define CSR_FCSR 0x3 +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 +#define CSR_INSTRET 0xc02 +#define CSR_HPMCOUNTER3 0xc03 +#define CSR_HPMCOUNTER4 0xc04 +#define CSR_HPMCOUNTER5 0xc05 +#define CSR_HPMCOUNTER6 0xc06 +#define CSR_HPMCOUNTER7 0xc07 +#define CSR_HPMCOUNTER8 0xc08 +#define CSR_HPMCOUNTER9 0xc09 +#define CSR_HPMCOUNTER10 0xc0a +#define CSR_HPMCOUNTER11 0xc0b +#define CSR_HPMCOUNTER12 0xc0c +#define CSR_HPMCOUNTER13 0xc0d +#define CSR_HPMCOUNTER14 0xc0e +#define CSR_HPMCOUNTER15 0xc0f +#define CSR_HPMCOUNTER16 0xc10 +#define CSR_HPMCOUNTER17 0xc11 +#define CSR_HPMCOUNTER18 0xc12 +#define CSR_HPMCOUNTER19 0xc13 +#define CSR_HPMCOUNTER20 0xc14 +#define CSR_HPMCOUNTER21 0xc15 +#define CSR_HPMCOUNTER22 0xc16 +#define CSR_HPMCOUNTER23 0xc17 +#define CSR_HPMCOUNTER24 0xc18 +#define CSR_HPMCOUNTER25 0xc19 +#define CSR_HPMCOUNTER26 0xc1a +#define CSR_HPMCOUNTER27 0xc1b +#define CSR_HPMCOUNTER28 0xc1c +#define CSR_HPMCOUNTER29 0xc1d +#define CSR_HPMCOUNTER30 0xc1e +#define CSR_HPMCOUNTER31 0xc1f +#define CSR_SSTATUS 0x100 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_STVAL 0x143 +#define CSR_SIP 0x144 +#define CSR_SATP 0x180 +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MEDELEG 0x302 +#define CSR_MIDELEG 0x303 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MCOUNTEREN 0x306 +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MTVAL 0x343 +#define CSR_MIP 0x344 +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPCFG1 0x3a1 +#define CSR_PMPCFG2 0x3a2 +#define CSR_PMPCFG3 0x3a3 +#define CSR_PMPADDR0 0x3b0 +#define CSR_PMPADDR1 0x3b1 +#define CSR_PMPADDR2 0x3b2 +#define CSR_PMPADDR3 0x3b3 +#define CSR_PMPADDR4 0x3b4 +#define CSR_PMPADDR5 0x3b5 +#define CSR_PMPADDR6 0x3b6 +#define CSR_PMPADDR7 0x3b7 +#define CSR_PMPADDR8 0x3b8 +#define CSR_PMPADDR9 0x3b9 +#define CSR_PMPADDR10 0x3ba +#define CSR_PMPADDR11 0x3bb +#define CSR_PMPADDR12 0x3bc +#define CSR_PMPADDR13 0x3bd +#define CSR_PMPADDR14 0x3be +#define CSR_PMPADDR15 0x3bf +#define CSR_TSELECT 0x7a0 +#define CSR_TDATA1 0x7a1 +#define CSR_TDATA2 0x7a2 +#define CSR_TDATA3 0x7a3 +#define CSR_DCSR 0x7b0 +#define CSR_DPC 0x7b1 +#define CSR_DSCRATCH 0x7b2 +#define CSR_MCYCLE 0xb00 +#define CSR_MINSTRET 0xb02 +#define CSR_MHPMCOUNTER3 0xb03 +#define CSR_MHPMCOUNTER4 0xb04 +#define CSR_MHPMCOUNTER5 0xb05 +#define CSR_MHPMCOUNTER6 0xb06 +#define CSR_MHPMCOUNTER7 0xb07 +#define CSR_MHPMCOUNTER8 0xb08 +#define CSR_MHPMCOUNTER9 0xb09 +#define CSR_MHPMCOUNTER10 0xb0a +#define CSR_MHPMCOUNTER11 0xb0b +#define CSR_MHPMCOUNTER12 0xb0c +#define CSR_MHPMCOUNTER13 0xb0d +#define CSR_MHPMCOUNTER14 0xb0e +#define CSR_MHPMCOUNTER15 0xb0f +#define CSR_MHPMCOUNTER16 0xb10 +#define CSR_MHPMCOUNTER17 0xb11 +#define CSR_MHPMCOUNTER18 0xb12 +#define CSR_MHPMCOUNTER19 0xb13 +#define CSR_MHPMCOUNTER20 0xb14 +#define CSR_MHPMCOUNTER21 0xb15 +#define CSR_MHPMCOUNTER22 0xb16 +#define CSR_MHPMCOUNTER23 0xb17 +#define CSR_MHPMCOUNTER24 0xb18 +#define CSR_MHPMCOUNTER25 0xb19 +#define CSR_MHPMCOUNTER26 0xb1a +#define CSR_MHPMCOUNTER27 0xb1b +#define CSR_MHPMCOUNTER28 0xb1c +#define CSR_MHPMCOUNTER29 0xb1d +#define CSR_MHPMCOUNTER30 0xb1e +#define CSR_MHPMCOUNTER31 0xb1f +#define CSR_MHPMEVENT3 0x323 +#define CSR_MHPMEVENT4 0x324 +#define CSR_MHPMEVENT5 0x325 +#define CSR_MHPMEVENT6 0x326 +#define CSR_MHPMEVENT7 0x327 +#define CSR_MHPMEVENT8 0x328 +#define CSR_MHPMEVENT9 0x329 +#define CSR_MHPMEVENT10 0x32a +#define CSR_MHPMEVENT11 0x32b +#define CSR_MHPMEVENT12 0x32c +#define CSR_MHPMEVENT13 0x32d +#define CSR_MHPMEVENT14 0x32e +#define CSR_MHPMEVENT15 0x32f +#define CSR_MHPMEVENT16 0x330 +#define CSR_MHPMEVENT17 0x331 +#define CSR_MHPMEVENT18 0x332 +#define CSR_MHPMEVENT19 0x333 +#define CSR_MHPMEVENT20 0x334 +#define CSR_MHPMEVENT21 0x335 +#define CSR_MHPMEVENT22 0x336 +#define CSR_MHPMEVENT23 0x337 +#define CSR_MHPMEVENT24 0x338 +#define CSR_MHPMEVENT25 0x339 +#define CSR_MHPMEVENT26 0x33a +#define CSR_MHPMEVENT27 0x33b +#define CSR_MHPMEVENT28 0x33c +#define CSR_MHPMEVENT29 0x33d +#define CSR_MHPMEVENT30 0x33e +#define CSR_MHPMEVENT31 0x33f +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 +#define CSR_CYCLEH 0xc80 +#define CSR_TIMEH 0xc81 +#define CSR_INSTRETH 0xc82 +#define CSR_HPMCOUNTER3H 0xc83 +#define CSR_HPMCOUNTER4H 0xc84 +#define CSR_HPMCOUNTER5H 0xc85 +#define CSR_HPMCOUNTER6H 0xc86 +#define CSR_HPMCOUNTER7H 0xc87 +#define CSR_HPMCOUNTER8H 0xc88 +#define CSR_HPMCOUNTER9H 0xc89 +#define CSR_HPMCOUNTER10H 0xc8a +#define CSR_HPMCOUNTER11H 0xc8b +#define CSR_HPMCOUNTER12H 0xc8c +#define CSR_HPMCOUNTER13H 0xc8d +#define CSR_HPMCOUNTER14H 0xc8e +#define CSR_HPMCOUNTER15H 0xc8f +#define CSR_HPMCOUNTER16H 0xc90 +#define CSR_HPMCOUNTER17H 0xc91 +#define CSR_HPMCOUNTER18H 0xc92 +#define CSR_HPMCOUNTER19H 0xc93 +#define CSR_HPMCOUNTER20H 0xc94 +#define CSR_HPMCOUNTER21H 0xc95 +#define CSR_HPMCOUNTER22H 0xc96 +#define CSR_HPMCOUNTER23H 0xc97 +#define CSR_HPMCOUNTER24H 0xc98 +#define CSR_HPMCOUNTER25H 0xc99 +#define CSR_HPMCOUNTER26H 0xc9a +#define CSR_HPMCOUNTER27H 0xc9b +#define CSR_HPMCOUNTER28H 0xc9c +#define CSR_HPMCOUNTER29H 0xc9d +#define CSR_HPMCOUNTER30H 0xc9e +#define CSR_HPMCOUNTER31H 0xc9f +#define CSR_MCYCLEH 0xb80 +#define CSR_MINSTRETH 0xb82 +#define CSR_MHPMCOUNTER3H 0xb83 +#define CSR_MHPMCOUNTER4H 0xb84 +#define CSR_MHPMCOUNTER5H 0xb85 +#define CSR_MHPMCOUNTER6H 0xb86 +#define CSR_MHPMCOUNTER7H 0xb87 +#define CSR_MHPMCOUNTER8H 0xb88 +#define CSR_MHPMCOUNTER9H 0xb89 +#define CSR_MHPMCOUNTER10H 0xb8a +#define CSR_MHPMCOUNTER11H 0xb8b +#define CSR_MHPMCOUNTER12H 0xb8c +#define CSR_MHPMCOUNTER13H 0xb8d +#define CSR_MHPMCOUNTER14H 0xb8e +#define CSR_MHPMCOUNTER15H 0xb8f +#define CSR_MHPMCOUNTER16H 0xb90 +#define CSR_MHPMCOUNTER17H 0xb91 +#define CSR_MHPMCOUNTER18H 0xb92 +#define CSR_MHPMCOUNTER19H 0xb93 +#define CSR_MHPMCOUNTER20H 0xb94 +#define CSR_MHPMCOUNTER21H 0xb95 +#define CSR_MHPMCOUNTER22H 0xb96 +#define CSR_MHPMCOUNTER23H 0xb97 +#define CSR_MHPMCOUNTER24H 0xb98 +#define CSR_MHPMCOUNTER25H 0xb99 +#define CSR_MHPMCOUNTER26H 0xb9a +#define CSR_MHPMCOUNTER27H 0xb9b +#define CSR_MHPMCOUNTER28H 0xb9c +#define CSR_MHPMCOUNTER29H 0xb9d +#define CSR_MHPMCOUNTER30H 0xb9e +#define CSR_MHPMCOUNTER31H 0xb9f + +#define CAUSE_MISALIGNED_FETCH 0x0 +#define CAUSE_FETCH_ACCESS 0x1 +#define CAUSE_ILLEGAL_INSTRUCTION 0x2 +#define CAUSE_BREAKPOINT 0x3 +#define CAUSE_MISALIGNED_LOAD 0x4 +#define CAUSE_LOAD_ACCESS 0x5 +#define CAUSE_MISALIGNED_STORE 0x6 +#define CAUSE_STORE_ACCESS 0x7 +#define CAUSE_USER_ECALL 0x8 +#define CAUSE_SUPERVISOR_ECALL 0x9 +#define CAUSE_HYPERVISOR_ECALL 0xa +#define CAUSE_MACHINE_ECALL 0xb +#define CAUSE_FETCH_PAGE_FAULT 0xc +#define CAUSE_LOAD_PAGE_FAULT 0xd +#define CAUSE_STORE_PAGE_FAULT 0xf + +#endif diff --git a/include/sbi/riscv_io.h b/include/sbi/riscv_io.h new file mode 100644 index 0000000..08f1a2f --- /dev/null +++ b/include/sbi/riscv_io.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __RISCV_IO_H__ +#define __RISCV_IO_H__ + +#include +#include + +static inline void __raw_writeb(u8 val, volatile void *addr) +{ + asm volatile("sb %0, 0(%1)" : : "r" (val), "r" (addr)); +} + +static inline void __raw_writew(u16 val, volatile void *addr) +{ + asm volatile("sh %0, 0(%1)" : : "r" (val), "r" (addr)); +} + +static inline void __raw_writel(u32 val, volatile void *addr) +{ + asm volatile("sw %0, 0(%1)" : : "r" (val), "r" (addr)); +} + +#if __riscv_xlen != 32 +static inline void __raw_writeq(u64 val, volatile void *addr) +{ + asm volatile("sd %0, 0(%1)" : : "r" (val), "r" (addr)); +} +#endif + +static inline u8 __raw_readb(const volatile void *addr) +{ + u8 val; + + asm volatile("lb %0, 0(%1)" : "=r" (val) : "r" (addr)); + return val; +} + +static inline u16 __raw_readw(const volatile void *addr) +{ + u16 val; + + asm volatile("lh %0, 0(%1)" : "=r" (val) : "r" (addr)); + return val; +} + +static inline u32 __raw_readl(const volatile void *addr) +{ + u32 val; + + asm volatile("lw %0, 0(%1)" : "=r" (val) : "r" (addr)); + return val; +} + +#if __riscv_xlen != 32 +static inline u64 __raw_readq(const volatile void *addr) +{ + u64 val; + + asm volatile("ld %0, 0(%1)" : "=r" (val) : "r" (addr)); + return val; +} +#endif + +/* FIXME: These are now the same as asm-generic */ +#define __io_rbr() do {} while (0) +#define __io_rar() do {} while (0) +#define __io_rbw() do {} while (0) +#define __io_raw() do {} while (0) + +#define readb_relaxed(c) ({ u8 __v; __io_rbr(); __v = __raw_readb(c); __io_rar(); __v; }) +#define readw_relaxed(c) ({ u16 __v; __io_rbr(); __v = __raw_readw(c); __io_rar(); __v; }) +#define readl_relaxed(c) ({ u32 __v; __io_rbr(); __v = __raw_readl(c); __io_rar(); __v; }) + +#define writeb_relaxed(v,c) ({ __io_rbw(); __raw_writeb((v),(c)); __io_raw(); }) +#define writew_relaxed(v,c) ({ __io_rbw(); __raw_writew((v),(c)); __io_raw(); }) +#define writel_relaxed(v,c) ({ __io_rbw(); __raw_writel((v),(c)); __io_raw(); }) + +#if __riscv_xlen != 32 +#define readq_relaxed(c) ({ u64 __v; __io_rbr(); __v = __raw_readq(c); __io_rar(); __v; }) +#define writeq_relaxed(v,c) ({ __io_rbw(); __raw_writeq((v),(c)); __io_raw(); }) +#endif + +#define __io_br() do {} while (0) +#define __io_ar() __asm__ __volatile__ ("fence i,r" : : : "memory"); +#define __io_bw() __asm__ __volatile__ ("fence w,o" : : : "memory"); +#define __io_aw() do {} while (0) + +#define readb(c) ({ u8 __v; __io_br(); __v = __raw_readb(c); __io_ar(); __v; }) +#define readw(c) ({ u16 __v; __io_br(); __v = __raw_readw(c); __io_ar(); __v; }) +#define readl(c) ({ u32 __v; __io_br(); __v = __raw_readl(c); __io_ar(); __v; }) + +#define writeb(v,c) ({ __io_bw(); __raw_writeb((v),(c)); __io_aw(); }) +#define writew(v,c) ({ __io_bw(); __raw_writew((v),(c)); __io_aw(); }) +#define writel(v,c) ({ __io_bw(); __raw_writel((v),(c)); __io_aw(); }) + +#if __riscv_xlen != 32 +#define readq(c) ({ u64 __v; __io_br(); __v = __raw_readq(c); __io_ar(); __v; }) +#define writeq(v,c) ({ __io_bw(); __raw_writeq((v),(c)); __io_aw(); }) +#endif + +#endif diff --git a/include/sbi/riscv_locks.h b/include/sbi/riscv_locks.h new file mode 100644 index 0000000..4a683d9 --- /dev/null +++ b/include/sbi/riscv_locks.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __RISCV_LOCKS_H__ +#define __RISCV_LOCKS_H__ + +typedef struct { + volatile long lock; +} spinlock_t; + +#define __RISCV_SPIN_UNLOCKED 0 + +#define SPIN_LOCK_INIT(_lptr) \ + (_lptr)->lock = __RISCV_SPIN_UNLOCKED + +#define SPIN_LOCK_INITIALIZER \ + { .lock = __RISCV_SPIN_UNLOCKED, } + +int spin_lock_check(spinlock_t *lock); + +int spin_trylock(spinlock_t *lock); + +void spin_lock(spinlock_t *lock); + +void spin_unlock(spinlock_t *lock); + +#endif diff --git a/include/sbi/sbi_bits.h b/include/sbi/sbi_bits.h new file mode 100644 index 0000000..118fab2 --- /dev/null +++ b/include/sbi/sbi_bits.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_BITS_H__ +#define __SBI_BITS_H__ + +#define likely(x) __builtin_expect((x), 1) +#define unlikely(x) __builtin_expect((x), 0) + +#define ROUNDUP(a, b) ((((a)-1)/(b)+1)*(b)) +#define ROUNDDOWN(a, b) ((a)/(b)*(b)) + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define CLAMP(a, lo, hi) MIN(MAX(a, lo), hi) + +#define EXTRACT_FIELD(val, which) (((val) & (which)) / ((which) & ~((which)-1))) +#define INSERT_FIELD(val, which, fieldval) (((val) & ~(which)) | ((fieldval) * ((which) & ~((which)-1)))) + +#define STR(x) XSTR(x) +#define XSTR(x) #x + +#if __riscv_xlen == 64 +#define SLL32 sllw +#define STORE sd +#define LOAD ld +#define LWU lwu +#define LOG_REGBYTES 3 +#else +#define SLL32 sll +#define STORE sw +#define LOAD lw +#define LWU lw +#define LOG_REGBYTES 2 +#endif +#define REGBYTES (1 << LOG_REGBYTES) + +#endif diff --git a/include/sbi/sbi_console.h b/include/sbi/sbi_console.h new file mode 100644 index 0000000..df258ba --- /dev/null +++ b/include/sbi/sbi_console.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_CONSOLE_H__ +#define __SBI_CONSOLE_H__ + +#include + +#define __printf(a, b) __attribute__((format(printf, a, b))) + +bool sbi_isprintable(char ch); + +char sbi_getc(void); + +void sbi_putc(char ch); + +void sbi_puts(const char *str); + +void sbi_gets(char *s, int maxwidth, char endchar); + +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, ...); + +int __printf(1, 2) sbi_printf(const char *format, ...); + +struct sbi_scratch; +int sbi_console_init(struct sbi_scratch *scratch); + +#endif diff --git a/include/sbi/sbi_const.h b/include/sbi/sbi_const.h new file mode 100644 index 0000000..3071094 --- /dev/null +++ b/include/sbi/sbi_const.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_CONST_H__ +#define __SBI_CONST_H__ + +/* Some constant macros are used in both assembler and + * C code. Therefore we cannot annotate them always with + * 'UL' and other type specifiers unilaterally. We + * use the following macros to deal with this. + * + * Similarly, _AT() will cast an expression with a type in C, but + * leave it unchanged in asm. + */ + +#ifdef __ASSEMBLY__ +#define _AC(X,Y) X +#define _AT(T,X) X +#else +#define __AC(X,Y) (X##Y) +#define _AC(X,Y) __AC(X,Y) +#define _AT(T,X) ((T)(X)) +#endif + +#define _UL(x) (_AC(x, UL)) +#define _ULL(x) (_AC(x, ULL)) + +#define _BITUL(x) (_UL(1) << (x)) +#define _BITULL(x) (_ULL(1) << (x)) + +#define UL(x) (_UL(x)) +#define ULL(x) (_ULL(x)) + +#define __STR(s) #s +#define STRINGIFY(s) __STR(s) + +#endif diff --git a/include/sbi/sbi_ecall.h b/include/sbi/sbi_ecall.h new file mode 100644 index 0000000..4f39329 --- /dev/null +++ b/include/sbi/sbi_ecall.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_ECALL_H__ +#define __SBI_ECALL_H__ + +#include + +struct sbi_trap_regs; +struct sbi_scratch; + +u16 sbi_ecall_version_major(void); + +u16 sbi_ecall_version_minor(void); + +int sbi_ecall_handler(u32 hartid, ulong mcause, + struct sbi_trap_regs *regs, + struct sbi_scratch *scratch); + +#endif diff --git a/include/sbi/sbi_emulate_csr.h b/include/sbi/sbi_emulate_csr.h new file mode 100644 index 0000000..c6fe419 --- /dev/null +++ b/include/sbi/sbi_emulate_csr.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_EMULATE_CSR_H__ +#define __SBI_EMULATE_CSR_H__ + +#include + +struct sbi_scratch; + +int sbi_emulate_csr_read(int csr_num, + u32 hartid, ulong mstatus, + struct sbi_scratch *scratch, + ulong *csr_val); + +int sbi_emulate_csr_write(int csr_num, + u32 hartid, ulong mstatus, + struct sbi_scratch *scratch, + ulong csr_val); + +#endif diff --git a/include/sbi/sbi_error.h b/include/sbi/sbi_error.h new file mode 100644 index 0000000..955674e --- /dev/null +++ b/include/sbi/sbi_error.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_ERROR_H__ +#define __SBI_ERROR_H__ + +#define SBI_OK 0 +#define SBI_EUNKNOWN -1 +#define SBI_EFAIL -2 +#define SBI_EINVAL -3 +#define SBI_ENOENT -4 +#define SBI_ENOTSUPP -5 +#define SBI_ENODEV -6 +#define SBI_ENOSYS -7 +#define SBI_ETIMEDOUT -8 +#define SBI_EIO -9 +#define SBI_EILL -10 + +#endif diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h new file mode 100644 index 0000000..38032ed --- /dev/null +++ b/include/sbi/sbi_hart.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_HART_H__ +#define __SBI_HART_H__ + +#include + +struct sbi_scratch; + +int sbi_hart_init(struct sbi_scratch *scratch, u32 hartid); + +void sbi_hart_pmp_dump(void); + +void __attribute__((noreturn)) sbi_hart_hang(void); + +void __attribute__((noreturn)) sbi_hart_boot_next(unsigned long arg0, + unsigned long arg1, + unsigned long next_addr, + unsigned long next_mode); + +void sbi_hart_mark_available(u32 hartid); + +ulong sbi_hart_available_mask(void); + +void sbi_hart_unmark_available(u32 hartid); + +struct sbi_scratch *sbi_hart_id_to_scratch(struct sbi_scratch *scratch, + u32 hartid); + +void sbi_hart_wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid); + +void sbi_hart_wake_coldboot_harts(struct sbi_scratch *scratch); + +#endif diff --git a/include/sbi/sbi_illegal_insn.h b/include/sbi/sbi_illegal_insn.h new file mode 100644 index 0000000..1b8837f --- /dev/null +++ b/include/sbi/sbi_illegal_insn.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_ILLEGAl_INSN_H__ +#define __SBI_ILLEGAl_INSN_H__ + +#include + +struct sbi_trap_regs; +struct sbi_scratch; + +int sbi_illegal_insn_handler(u32 hartid, ulong mcause, + struct sbi_trap_regs *regs, + struct sbi_scratch *scratch); + +#endif diff --git a/include/sbi/sbi_init.h b/include/sbi/sbi_init.h new file mode 100644 index 0000000..b3aafdc --- /dev/null +++ b/include/sbi/sbi_init.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_INIT_H__ +#define __SBI_INIT_H__ + +#include + +struct sbi_scratch; + +void __attribute__((noreturn)) sbi_init(struct sbi_scratch *scratch); + +#endif diff --git a/include/sbi/sbi_ipi.h b/include/sbi/sbi_ipi.h new file mode 100644 index 0000000..4058194 --- /dev/null +++ b/include/sbi/sbi_ipi.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_IPI_H__ +#define __SBI_IPI_H__ + +#include + +#define SBI_IPI_EVENT_SOFT 0x1 +#define SBI_IPI_EVENT_FENCE_I 0x2 +#define SBI_IPI_EVENT_SFENCE_VMA 0x4 +#define SBI_IPI_EVENT_HALT 0x8 + +struct sbi_scratch; + +int sbi_ipi_send_many(struct sbi_scratch *scratch, + u32 hartid, ulong *pmask, u32 event); + +void sbi_ipi_clear_smode(struct sbi_scratch *scratch, u32 hartid); + +void sbi_ipi_process(struct sbi_scratch *scratch, u32 hartid); + +int sbi_ipi_warm_init(struct sbi_scratch *scratch, u32 hartid); + +int sbi_ipi_cold_init(struct sbi_scratch *scratch); + +#endif diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h new file mode 100644 index 0000000..57b8e6b --- /dev/null +++ b/include/sbi/sbi_platform.h @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_PLATFORM_H__ +#define __SBI_PLATFORM_H__ + +#include + +enum sbi_platform_features { + SBI_PLATFORM_HAS_MMIO_TIMER_VALUE = (1 << 0), + SBI_PLATFORM_HAS_HART_HOTPLUG = (1 << 1), +}; + +struct sbi_platform { + char name[64]; + u64 features; + u32 hart_count; + u32 hart_stack_size; + int (*cold_early_init)(void); + int (*cold_final_init)(void); + int (*warm_early_init)(u32 target_hart); + int (*warm_final_init)(u32 target_hart); + u32 (*pmp_region_count)(u32 target_hart); + int (*pmp_region_info)(u32 target_hart, u32 index, + ulong *prot, ulong *addr, ulong *log2size); + void (*console_putc)(char ch); + char (*console_getc)(void); + int (*console_init)(void); + int (*cold_irqchip_init)(void); + int (*warm_irqchip_init)(u32 target_hart); + void (*ipi_inject)(u32 target_hart, u32 source_hart); + void (*ipi_sync)(u32 target_hart, u32 source_hart); + void (*ipi_clear)(u32 target_hart); + int (*cold_ipi_init)(void); + int (*warm_ipi_init)(u32 target_hart); + u64 (*timer_value)(void); + void (*timer_event_stop)(u32 target_hart); + void (*timer_event_start)(u32 target_hart, u64 next_event); + int (*cold_timer_init)(void); + int (*warm_timer_init)(u32 target_hart); + int (*system_reboot)(u32 type); + int (*system_shutdown)(u32 type); +} __attribute__((packed)); + +#define sbi_platform_ptr(__s) \ +((struct sbi_platform *)((__s)->platform_addr)) + +#define sbi_platform_thishart_ptr() \ +((struct sbi_platform *)(sbi_scratch_thishart_ptr()->platform_addr)) + +#define sbi_platform_has_mmio_timer_value(__p) \ +((__p)->features & SBI_PLATFORM_HAS_MMIO_TIMER_VALUE) + +#define sbi_platform_has_hart_hotplug(__p) \ +((__p)->features & SBI_PLATFORM_HAS_HART_HOTPLUG) + +static inline const char *sbi_platform_name(struct sbi_platform *plat) +{ + if (plat) + return plat->name; + return NULL; +} + +static inline u32 sbi_platform_hart_count(struct sbi_platform *plat) +{ + if (plat) + return plat->hart_count; + return 0; +} + +static inline u32 sbi_platform_hart_stack_size(struct sbi_platform *plat) +{ + if (plat) + return plat->hart_stack_size; + return 0; +} + +static inline int sbi_platform_cold_early_init(struct sbi_platform *plat) +{ + if (plat && plat->cold_early_init) + return plat->cold_early_init(); + return 0; +} + +static inline int sbi_platform_cold_final_init(struct sbi_platform *plat) +{ + if (plat && plat->cold_final_init) + return plat->cold_final_init(); + return 0; +} + +static inline int sbi_platform_warm_early_init(struct sbi_platform *plat, + u32 target_hart) +{ + if (plat && plat->warm_early_init) + return plat->warm_early_init(target_hart); + return 0; +} + +static inline int sbi_platform_warm_final_init(struct sbi_platform *plat, + u32 target_hart) +{ + if (plat && plat->warm_final_init) + return plat->warm_final_init(target_hart); + return 0; +} + +static inline u32 sbi_platform_pmp_region_count(struct sbi_platform *plat, + u32 target_hart) +{ + if (plat && plat->pmp_region_count) + return plat->pmp_region_count(target_hart); + return 0; +} + +static inline int sbi_platform_pmp_region_info(struct sbi_platform *plat, + u32 target_hart, u32 index, + ulong *prot, ulong *addr, + ulong *log2size) +{ + if (plat && plat->pmp_region_info) + return plat->pmp_region_info(target_hart, index, + prot, addr, log2size); + return 0; +} + +static inline void sbi_platform_console_putc(struct sbi_platform *plat, + char ch) +{ + if (plat && plat->console_putc) + plat->console_putc(ch); +} + +static inline char sbi_platform_console_getc(struct sbi_platform *plat) +{ + if (plat && plat->console_getc) + return plat->console_getc(); + return 0; +} + +static inline int sbi_platform_console_init(struct sbi_platform *plat) +{ + if (plat && plat->console_init) + return plat->console_init(); + return 0; +} + +static inline int sbi_platform_warm_irqchip_init(struct sbi_platform *plat, + u32 target_hart) +{ + if (plat && plat->warm_irqchip_init) + return plat->warm_irqchip_init(target_hart); + return 0; +} + +static inline int sbi_platform_cold_irqchip_init(struct sbi_platform *plat) +{ + if (plat && plat->cold_irqchip_init) + return plat->cold_irqchip_init(); + return 0; +} + +static inline void sbi_platform_ipi_inject(struct sbi_platform *plat, + u32 target_hart, u32 source_hart) +{ + if (plat && plat->ipi_inject) + plat->ipi_inject(target_hart, source_hart); +} + +static inline void sbi_platform_ipi_sync(struct sbi_platform *plat, + u32 target_hart, u32 source_hart) +{ + if (plat && plat->ipi_sync) + plat->ipi_sync(target_hart, source_hart); +} + +static inline void sbi_platform_ipi_clear(struct sbi_platform *plat, + u32 target_hart) +{ + if (plat && plat->ipi_clear) + plat->ipi_clear(target_hart); +} + +static inline int sbi_platform_warm_ipi_init(struct sbi_platform *plat, + u32 target_hart) +{ + if (plat && plat->warm_ipi_init) + return plat->warm_ipi_init(target_hart); + return 0; +} + +static inline int sbi_platform_cold_ipi_init(struct sbi_platform *plat) +{ + if (plat && plat->cold_ipi_init) + return plat->cold_ipi_init(); + return 0; +} + +static inline u64 sbi_platform_timer_value(struct sbi_platform *plat) +{ + if (plat && plat->timer_value) + return plat->timer_value(); + return 0; +} + +static inline void sbi_platform_timer_event_stop(struct sbi_platform *plat, + u32 target_hart) +{ + if (plat && plat->timer_event_stop) + plat->timer_event_stop(target_hart); +} + +static inline void sbi_platform_timer_event_start(struct sbi_platform *plat, + u32 target_hart, + u64 next_event) +{ + if (plat && plat->timer_event_start) + plat->timer_event_start(target_hart, next_event); +} + +static inline int sbi_platform_warm_timer_init(struct sbi_platform *plat, + u32 target_hart) +{ + if (plat && plat->warm_timer_init) + return plat->warm_timer_init(target_hart); + return 0; +} + +static inline int sbi_platform_cold_timer_init(struct sbi_platform *plat) +{ + if (plat && plat->cold_timer_init) + return plat->cold_timer_init(); + return 0; +} + +static inline int sbi_platform_system_reboot(struct sbi_platform *plat, + u32 type) +{ + if (plat && plat->system_reboot) + return plat->system_reboot(type); + return 0; +} + +static inline int sbi_platform_system_shutdown(struct sbi_platform *plat, + u32 type) +{ + if (plat && plat->system_shutdown) + return plat->system_shutdown(type); + return 0; +} + +#endif diff --git a/include/sbi/sbi_scratch.h b/include/sbi/sbi_scratch.h new file mode 100644 index 0000000..d5c6f35 --- /dev/null +++ b/include/sbi/sbi_scratch.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_SCRATCH_H__ +#define __SBI_SCRATCH_H__ + +#include +#include + +struct sbi_scratch { + unsigned long tmp0; + unsigned long fw_start; + unsigned long fw_size; + unsigned long next_arg1; + unsigned long next_addr; + unsigned long next_mode; + unsigned long warmboot_addr; + unsigned long platform_addr; + unsigned long hartid_to_scratch; + unsigned long ipi_type; +} __attribute__((packed)); + +#define sbi_scratch_thishart_ptr() \ +((struct sbi_scratch *)csr_read(mscratch)) + +#define sbi_scratch_thishart_arg1_ptr() \ +((void *)(sbi_scratch_thishart_ptr()->next_arg1)) + +#endif diff --git a/include/sbi/sbi_system.h b/include/sbi/sbi_system.h new file mode 100644 index 0000000..ac1fb36 --- /dev/null +++ b/include/sbi/sbi_system.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_SYSTEM_H__ +#define __SBI_SYSTEM_H__ + +#include + +struct sbi_scratch; + +int sbi_system_warm_early_init(struct sbi_scratch *scratch, u32 hartid); + +int sbi_system_warm_final_init(struct sbi_scratch *scratch, u32 hartid); + +int sbi_system_cold_early_init(struct sbi_scratch *scratch); + +int sbi_system_cold_final_init(struct sbi_scratch *scratch); + +void __attribute__((noreturn)) sbi_system_reboot(struct sbi_scratch *scratch, + u32 type); + +void __attribute__((noreturn)) sbi_system_shutdown(struct sbi_scratch *scratch, + u32 type); + +#endif diff --git a/include/sbi/sbi_timer.h b/include/sbi/sbi_timer.h new file mode 100644 index 0000000..914e9c0 --- /dev/null +++ b/include/sbi/sbi_timer.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_TIMER_H__ +#define __SBI_TIMER_H__ + +#include + +struct sbi_scratch; + +u64 sbi_timer_value(struct sbi_scratch *scratch); + +void sbi_timer_event_stop(struct sbi_scratch *scratch, u32 hartid); + +void sbi_timer_event_start(struct sbi_scratch *scratch, u32 hartid, + u64 next_event); + +void sbi_timer_process(struct sbi_scratch *scratch, u32 hartid); + +int sbi_timer_warm_init(struct sbi_scratch *scratch, u32 hartid); + +int sbi_timer_cold_init(struct sbi_scratch *scratch); + +#endif diff --git a/include/sbi/sbi_trap.h b/include/sbi/sbi_trap.h new file mode 100644 index 0000000..a6b22e4 --- /dev/null +++ b/include/sbi/sbi_trap.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_TRAP_H__ +#define __SBI_TRAP_H__ + +#include + +struct sbi_trap_regs { + unsigned long zero; + unsigned long ra; + unsigned long sp; + unsigned long gp; + unsigned long tp; + unsigned long t0; + unsigned long t1; + unsigned long t2; + unsigned long s0; + unsigned long s1; + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; + unsigned long a4; + unsigned long a5; + unsigned long a6; + unsigned long a7; + unsigned long s2; + unsigned long s3; + unsigned long s4; + unsigned long s5; + unsigned long s6; + unsigned long s7; + unsigned long s8; + unsigned long s9; + unsigned long s10; + unsigned long s11; + unsigned long t3; + unsigned long t4; + unsigned long t5; + unsigned long t6; + unsigned long mepc; + unsigned long mstatus; +} __attribute__((packed)); + +struct sbi_scratch; + +void sbi_trap_handler(struct sbi_trap_regs *regs, + struct sbi_scratch *scratch); + +#endif diff --git a/include/sbi/sbi_types.h b/include/sbi/sbi_types.h new file mode 100644 index 0000000..ddcdea2 --- /dev/null +++ b/include/sbi/sbi_types.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_TYPES_H__ +#define __SBI_TYPES_H__ + +typedef char s8; +typedef unsigned char u8; +typedef char int8_t; +typedef unsigned char uint8_t; + +typedef short s16; +typedef unsigned short u16; +typedef short int16_t; +typedef unsigned short uint16_t; + +typedef int s32; +typedef unsigned int u32; +typedef int int32_t; +typedef unsigned int uint32_t; + +#if __riscv_xlen == 64 +typedef long s64; +typedef unsigned long u64; +typedef long int64_t; +typedef unsigned long uint64_t; +#elif __riscv_xlen == 32 +typedef long long s64; +typedef unsigned long long u64; +typedef long long int64_t; +typedef unsigned long long uint64_t; +#else +#error "Unexpected __riscv_xlen" +#endif + +typedef int bool; +typedef unsigned long ulong; +typedef unsigned long uintptr_t; +typedef unsigned long size_t; +typedef long ssize_t; +typedef unsigned long virtual_addr_t; +typedef unsigned long virtual_size_t; +typedef unsigned long physical_addr_t; +typedef unsigned long physical_size_t; + +#define TRUE 1 +#define FALSE 0 + +#define NULL ((void *)0) + +#endif diff --git a/include/sbi/sbi_unpriv.h b/include/sbi/sbi_unpriv.h new file mode 100644 index 0000000..2b925c5 --- /dev/null +++ b/include/sbi/sbi_unpriv.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SBI_UNPRIV_H__ +#define __SBI_UNPRIV_H__ + +#include +#include +#include + +#define DECLARE_UNPRIVILEGED_LOAD_FUNCTION(type, insn) \ +static inline type load_##type(const type *addr, ulong mepc) \ +{ \ + register ulong __mepc asm ("a2") = mepc; \ + register ulong __mstatus asm ("a3"); \ + type val; \ + asm ("csrrs %0, mstatus, %3\n" \ + #insn " %1, %2\n" \ + "csrw mstatus, %0" \ + : "+&r" (__mstatus), "=&r" (val) \ + : "m" (*addr), "r" (MSTATUS_MPRV), "r" (__mepc)); \ + return val; \ +} + +#define DECLARE_UNPRIVILEGED_STORE_FUNCTION(type, insn) \ +static inline void store_##type(type *addr, type val, ulong mepc) \ +{ \ + register ulong __mepc asm ("a2") = mepc; \ + register ulong __mstatus asm ("a3"); \ + asm volatile ("csrrs %0, mstatus, %3\n" \ + #insn " %1, %2\n" \ + "csrw mstatus, %0" \ + : "+&r" (__mstatus) \ + : "r" (val), "m" (*addr), "r" (MSTATUS_MPRV), "r" (__mepc)); \ +} + +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u8, lbu) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u16, lhu) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(s8, lb) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(s16, lh) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(s32, lw) +DECLARE_UNPRIVILEGED_STORE_FUNCTION(u8, sb) +DECLARE_UNPRIVILEGED_STORE_FUNCTION(u16, sh) +DECLARE_UNPRIVILEGED_STORE_FUNCTION(u32, sw) +#if __riscv_xlen == 64 +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u32, lwu) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u64, ld) +DECLARE_UNPRIVILEGED_STORE_FUNCTION(u64, sd) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(ulong, ld) +#else +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u32, lw) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(ulong, lw) + +static inline u64 load_u64(const u64 *addr, ulong mepc) +{ + return load_u32((u32 *)addr, mepc) + + ((u64)load_u32((u32 *)addr + 1, mepc) << 32); +} + +static inline void store_u64(u64 *addr, u64 val, ulong mepc) +{ + store_u32((u32 *)addr, val, mepc); + store_u32((u32 *)addr + 1, val >> 32, mepc); +} +#endif + +static inline ulong get_insn(ulong mepc, ulong *mstatus) +{ + register ulong __mepc asm ("a2") = mepc; + register ulong __mstatus asm ("a3"); + ulong val; +#ifndef __riscv_compressed + asm ("csrrs %[mstatus], mstatus, %[mprv]\n" + STR(LWU) " %[insn], (%[addr])\n" + "csrw mstatus, %[mstatus]" + : [mstatus] "+&r" (__mstatus), [insn] "=&r" (val) + : [mprv] "r" (MSTATUS_MPRV | MSTATUS_MXR), [addr] "r" (__mepc)); +#else + ulong rvc_mask = 3, tmp; + asm ("csrrs %[mstatus], mstatus, %[mprv]\n" + "and %[tmp], %[addr], 2\n" + "bnez %[tmp], 1f\n" + STR(LWU) " %[insn], (%[addr])\n" + "and %[tmp], %[insn], %[rvc_mask]\n" + "beq %[tmp], %[rvc_mask], 2f\n" + "sll %[insn], %[insn], %[xlen_minus_16]\n" + "srl %[insn], %[insn], %[xlen_minus_16]\n" + "j 2f\n" + "1:\n" + "lhu %[insn], (%[addr])\n" + "and %[tmp], %[insn], %[rvc_mask]\n" + "bne %[tmp], %[rvc_mask], 2f\n" + "lhu %[tmp], 2(%[addr])\n" + "sll %[tmp], %[tmp], 16\n" + "add %[insn], %[insn], %[tmp]\n" + "2: csrw mstatus, %[mstatus]" + : [mstatus] "+&r" (__mstatus), [insn] "=&r" (val), [tmp] "=&r" (tmp) + : [mprv] "r" (MSTATUS_MPRV | MSTATUS_MXR), [addr] "r" (__mepc), + [rvc_mask] "r" (rvc_mask), [xlen_minus_16] "i" (__riscv_xlen - 16)); +#endif + *mstatus = __mstatus; + return val; +} + +#endif diff --git a/lib/objects.mk b/lib/objects.mk new file mode 100644 index 0000000..ed14677 --- /dev/null +++ b/lib/objects.mk @@ -0,0 +1,23 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +lib-objs-y += riscv_asm.o +lib-objs-y += riscv_atomic.o +lib-objs-y += riscv_locks.o + +lib-objs-y += sbi_console.o +lib-objs-y += sbi_ecall.o +lib-objs-y += sbi_emulate_csr.o +lib-objs-y += sbi_hart.o +lib-objs-y += sbi_illegal_insn.o +lib-objs-y += sbi_init.o +lib-objs-y += sbi_ipi.o +lib-objs-y += sbi_system.o +lib-objs-y += sbi_timer.o +lib-objs-y += sbi_trap.o diff --git a/lib/riscv_asm.c b/lib/riscv_asm.c new file mode 100644 index 0000000..0fbdb31 --- /dev/null +++ b/lib/riscv_asm.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +unsigned long csr_read_num(int csr_num) +{ + unsigned long ret = 0; + + switch (csr_num) { + case CSR_PMPCFG0: + ret = csr_read_n(CSR_PMPCFG0); + break; + case CSR_PMPCFG1: + ret = csr_read_n(CSR_PMPCFG1); + break; + case CSR_PMPCFG2: + ret = csr_read_n(CSR_PMPCFG2); + break; + case CSR_PMPCFG3: + ret = csr_read_n(CSR_PMPCFG3); + break; + case CSR_PMPADDR0: + ret = csr_read_n(CSR_PMPADDR0); + break; + case CSR_PMPADDR1: + ret = csr_read_n(CSR_PMPADDR1); + break; + case CSR_PMPADDR2: + ret = csr_read_n(CSR_PMPADDR2); + break; + case CSR_PMPADDR3: + ret = csr_read_n(CSR_PMPADDR3); + break; + case CSR_PMPADDR4: + ret = csr_read_n(CSR_PMPADDR4); + break; + case CSR_PMPADDR5: + ret = csr_read_n(CSR_PMPADDR5); + break; + case CSR_PMPADDR6: + ret = csr_read_n(CSR_PMPADDR6); + break; + case CSR_PMPADDR7: + ret = csr_read_n(CSR_PMPADDR7); + break; + case CSR_PMPADDR8: + ret = csr_read_n(CSR_PMPADDR8); + break; + case CSR_PMPADDR9: + ret = csr_read_n(CSR_PMPADDR9); + break; + case CSR_PMPADDR10: + ret = csr_read_n(CSR_PMPADDR10); + break; + case CSR_PMPADDR11: + ret = csr_read_n(CSR_PMPADDR11); + break; + case CSR_PMPADDR12: + ret = csr_read_n(CSR_PMPADDR12); + break; + case CSR_PMPADDR13: + ret = csr_read_n(CSR_PMPADDR13); + break; + case CSR_PMPADDR14: + ret = csr_read_n(CSR_PMPADDR14); + break; + case CSR_PMPADDR15: + ret = csr_read_n(CSR_PMPADDR15); + break; + default: + break; + }; + + return ret; +} + +void csr_write_num(int csr_num, unsigned long val) +{ + switch (csr_num) { + case CSR_PMPCFG0: + csr_write_n(CSR_PMPCFG0, val); + break; + case CSR_PMPCFG1: + csr_write_n(CSR_PMPCFG1, val); + break; + case CSR_PMPCFG2: + csr_write_n(CSR_PMPCFG2, val); + break; + case CSR_PMPCFG3: + csr_write_n(CSR_PMPCFG3, val); + break; + case CSR_PMPADDR0: + csr_write_n(CSR_PMPADDR0, val); + break; + case CSR_PMPADDR1: + csr_write_n(CSR_PMPADDR1, val); + break; + case CSR_PMPADDR2: + csr_write_n(CSR_PMPADDR2, val); + break; + case CSR_PMPADDR3: + csr_write_n(CSR_PMPADDR3, val); + break; + case CSR_PMPADDR4: + csr_write_n(CSR_PMPADDR4, val); + break; + case CSR_PMPADDR5: + csr_write_n(CSR_PMPADDR5, val); + break; + case CSR_PMPADDR6: + csr_write_n(CSR_PMPADDR6, val); + break; + case CSR_PMPADDR7: + csr_write_n(CSR_PMPADDR7, val); + break; + case CSR_PMPADDR8: + csr_write_n(CSR_PMPADDR8, val); + break; + case CSR_PMPADDR9: + csr_write_n(CSR_PMPADDR9, val); + break; + case CSR_PMPADDR10: + csr_write_n(CSR_PMPADDR10, val); + break; + case CSR_PMPADDR11: + csr_write_n(CSR_PMPADDR11, val); + break; + case CSR_PMPADDR12: + csr_write_n(CSR_PMPADDR12, val); + break; + case CSR_PMPADDR13: + csr_write_n(CSR_PMPADDR13, val); + break; + case CSR_PMPADDR14: + csr_write_n(CSR_PMPADDR14, val); + break; + case CSR_PMPADDR15: + csr_write_n(CSR_PMPADDR15, val); + break; + default: + break; + }; +} + +static unsigned long ctz(unsigned long x) +{ + unsigned long ret = 0; + + while (!(x & 1UL)) { + ret++; + x = x >> 1; + } + + return ret; +} + +int pmp_set(unsigned int n, unsigned long prot, + unsigned long addr, unsigned long log2len) +{ + int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr; + unsigned long cfgmask, pmpcfg; + unsigned long addrmask, pmpaddr; + + /* check parameters */ + if (n >= PMP_COUNT || + log2len > __riscv_xlen || + log2len < PMP_SHIFT) + return SBI_EINVAL; + + /* calculate PMP register and offset */ +#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 + pmpcfg_csr = -1; + pmpcfg_shift = -1; +#endif + pmpaddr_csr = CSR_PMPADDR0 + n; + if (pmpcfg_csr < 0 || pmpcfg_shift < 0) + return SBI_ENOTSUPP; + + /* encode PMP config */ + prot |= (log2len == PMP_SHIFT) ? PMP_A_NA4 : PMP_A_NAPOT; + cfgmask = ~(0xff << pmpcfg_shift); + pmpcfg = (csr_read_num(pmpcfg_csr) & cfgmask); + pmpcfg |= ((prot << pmpcfg_shift) & ~cfgmask); + + /* encode PMP address */ + if (log2len == PMP_SHIFT) { + pmpaddr = (addr >> PMP_SHIFT); + } else { + if (log2len == __riscv_xlen) { + pmpaddr = -1UL; + } else { + addrmask = (1UL << (log2len - PMP_SHIFT)) - 1; + pmpaddr = ((addr >> PMP_SHIFT) & ~addrmask); + pmpaddr |= (addrmask >> 1); + } + } + + /* write csrs */ + csr_write_num(pmpaddr_csr, pmpaddr); + csr_write_num(pmpcfg_csr, pmpcfg); + + return 0; +} + +int pmp_get(unsigned int n, unsigned long *prot_out, + unsigned long *addr_out, unsigned long *log2len_out) +{ + int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr; + unsigned long cfgmask, pmpcfg, prot; + unsigned long t1, addr, log2len; + + /* check parameters */ + if (n >= PMP_COUNT || !prot_out || + !addr_out || !log2len_out) + return SBI_EINVAL; + *prot_out = *addr_out = *log2len_out = 0; + + /* calculate PMP register and offset */ +#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 + pmpcfg_csr = -1; + pmpcfg_shift = -1; +#endif + pmpaddr_csr = CSR_PMPADDR0 + n; + if (pmpcfg_csr < 0 || pmpcfg_shift < 0) + return SBI_ENOTSUPP; + + /* decode PMP config */ + cfgmask = (0xff << pmpcfg_shift); + pmpcfg = csr_read_num(pmpcfg_csr) & cfgmask; + prot = pmpcfg >> pmpcfg_shift; + + /* decode PMP address */ + if ((prot & PMP_A) == PMP_A_NAPOT) { + addr = csr_read_num(pmpaddr_csr); + if (addr == -1UL) { + addr = 0; + log2len = __riscv_xlen; + } else { + t1 = ctz(~addr); + addr = (addr & ~((1UL << t1) - 1)) << PMP_SHIFT; + log2len = (t1 + PMP_SHIFT + 1); + } + } else { + addr = csr_read_num(pmpaddr_csr) << PMP_SHIFT; + log2len = PMP_SHIFT; + } + + /* return details */ + *prot_out = prot; + *addr_out = addr; + *log2len_out = log2len; + + return 0; +} diff --git a/lib/riscv_atomic.c b/lib/riscv_atomic.c new file mode 100644 index 0000000..3a599f5 --- /dev/null +++ b/lib/riscv_atomic.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +long atomic_read(atomic_t *atom) +{ + long ret = atom->counter; + rmb(); + return ret; +} + +void atomic_write(atomic_t *atom, long value) +{ + atom->counter = value; + wmb(); +} + +long atomic_add_return(atomic_t *atom, long value) +{ + long ret; + + __asm__ __volatile__ ( + " amoadd.w.aqrl %1, %2, %0" + : "+A" (atom->counter), "=r" (ret) + : "r" (value) + : "memory"); + + return ret + value; +} + +long atomic_sub_return(atomic_t *atom, long value) +{ + long ret; + + __asm__ __volatile__ ( + " amoadd.w.aqrl %1, %2, %0" + : "+A" (atom->counter), "=r" (ret) + : "r" (-value) + : "memory"); + + return ret - value; +} + +#define __xchg(ptr, new, size) \ +({ \ + __typeof__(ptr) __ptr = (ptr); \ + __typeof__(*(ptr)) __new = (new); \ + __typeof__(*(ptr)) __ret; \ + register unsigned int __rc; \ + switch (size) { \ + case 4: \ + __asm__ __volatile__ ( \ + "0: lr.w %0, %2\n" \ + " sc.w.rl %1, %z3, %2\n" \ + " bnez %1, 0b\n" \ + " fence rw, rw\n" \ + : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \ + : "rJ" (__new) \ + : "memory"); \ + break; \ + case 8: \ + __asm__ __volatile__ ( \ + "0: lr.d %0, %2\n" \ + " sc.d.rl %1, %z3, %2\n" \ + " bnez %1, 0b\n" \ + " fence rw, rw\n" \ + : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \ + : "rJ" (__new) \ + : "memory"); \ + break; \ + default: \ + break; \ + } \ + __ret; \ +}) + +#define xchg(ptr, n) \ +({ \ + __typeof__(*(ptr)) _n_ = (n); \ + (__typeof__(*(ptr))) __xchg((ptr), _n_, sizeof(*(ptr))); \ +}) + +#define __cmpxchg(ptr, old, new, size) \ +({ \ + __typeof__(ptr) __ptr = (ptr); \ + __typeof__(*(ptr)) __old = (old); \ + __typeof__(*(ptr)) __new = (new); \ + __typeof__(*(ptr)) __ret; \ + register unsigned int __rc; \ + switch (size) { \ + case 4: \ + __asm__ __volatile__ ( \ + "0: lr.w %0, %2\n" \ + " bne %0, %z3, 1f\n" \ + " sc.w.rl %1, %z4, %2\n" \ + " bnez %1, 0b\n" \ + " fence rw, rw\n" \ + "1:\n" \ + : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \ + : "rJ" (__old), "rJ" (__new) \ + : "memory"); \ + break; \ + case 8: \ + __asm__ __volatile__ ( \ + "0: lr.d %0, %2\n" \ + " bne %0, %z3, 1f\n" \ + " sc.d.rl %1, %z4, %2\n" \ + " bnez %1, 0b\n" \ + " fence rw, rw\n" \ + "1:\n" \ + : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \ + : "rJ" (__old), "rJ" (__new) \ + : "memory"); \ + break; \ + default: \ + break; \ + } \ + __ret; \ +}) + +#define cmpxchg(ptr, o, n) \ +({ \ + __typeof__(*(ptr)) _o_ = (o); \ + __typeof__(*(ptr)) _n_ = (n); \ + (__typeof__(*(ptr))) __cmpxchg((ptr), \ + _o_, _n_, sizeof(*(ptr))); \ +}) + +long arch_atomic_cmpxchg(atomic_t *atom, long oldval, long newval) +{ + return cmpxchg(&atom->counter, oldval, newval); +} + +long arch_atomic_xchg(atomic_t *atom, long newval) +{ + return xchg(&atom->counter, newval); +} + +unsigned int atomic_raw_xchg_uint(volatile unsigned int *ptr, + unsigned int newval) +{ + return xchg(ptr, newval); +} diff --git a/lib/riscv_locks.c b/lib/riscv_locks.c new file mode 100644 index 0000000..0f94986 --- /dev/null +++ b/lib/riscv_locks.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +int spin_lock_check(spinlock_t *lock) +{ + return (lock->lock == __RISCV_SPIN_UNLOCKED) ? 0 : 1; +} + +int spin_trylock(spinlock_t *lock) +{ + int tmp = 1, busy; + + __asm__ __volatile__ ( + " amoswap.w %0, %2, %1\n" + RISCV_ACQUIRE_BARRIER + : "=r" (busy), "+A" (lock->lock) + : "r" (tmp) + : "memory"); + + return !busy; +} + +void spin_lock(spinlock_t *lock) +{ + while (1) { + if (spin_lock_check(lock)) + continue; + + if (spin_trylock(lock)) + break; + } +} + +void spin_unlock(spinlock_t *lock) +{ + __smp_store_release(&lock->lock, __RISCV_SPIN_UNLOCKED); +} diff --git a/lib/sbi_console.c b/lib/sbi_console.c new file mode 100644 index 0000000..24b9d3c --- /dev/null +++ b/lib/sbi_console.c @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +static struct sbi_platform *console_plat = NULL; + +bool sbi_isprintable(char c) +{ + if (((31 < c) && (c < 127)) || + (c == '\f') || + (c == '\r') || + (c == '\n') || + (c == '\t')) { + return TRUE; + } + return FALSE; +} + +char sbi_getc(void) +{ + return sbi_platform_console_getc(console_plat); +} + +void sbi_putc(char ch) +{ + sbi_platform_console_putc(console_plat, ch); +} + +void sbi_puts(const char *str) +{ + while (*str) { + sbi_putc(*str); + str++; + } +} + +void sbi_gets(char *s, int maxwidth, char endchar) +{ + char *retval; + char ch; + retval = s; + ch = sbi_getc(); + while (ch != endchar && maxwidth > 0) { + *retval = ch; + retval++; + maxwidth--; + if (maxwidth == 0) + break; + ch = sbi_getc(); + } + *retval = '\0'; + return; +} + +#define PAD_RIGHT 1 +#define PAD_ZERO 2 +#define PAD_ALTERNATE 4 +#define PRINT_BUF_LEN 64 + +#define va_start(v,l) __builtin_va_start((v),l) +#define va_end __builtin_va_end +#define va_arg __builtin_va_arg +typedef __builtin_va_list va_list; + +static void printc(char **out, u32 *out_len, char ch) +{ + if (out) { + if (*out) { + if (out_len && (0 < *out_len)) { + **out = ch; + ++(*out); + (*out_len)--; + } else { + **out = ch; + ++(*out); + } + } + } else { + sbi_putc(ch); + } +} + +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'; + } + if (!(flags & PAD_RIGHT)) { + for (; width > 0; --width) { + printc(out, out_len, padchar); + ++pc; + } + } + for (; *string; ++string) { + printc(out, out_len, *string); + ++pc; + } + for (; width > 0; --width) { + printc(out, out_len, padchar); + ++pc; + } + + return pc; +} + +static int printi(char **out, u32 *out_len, long long i, int b, int sg, + int width, int flags, int letbase) +{ + 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; + } + + s = print_buf + PRINT_BUF_LEN - 1; + *s = '\0'; + + if (!u) { + *--s = '0'; + } else { + while (u) { + t = u % b; + u = u / b; + if (t >= 10) + t += letbase - '0' - 10; + *--s = t + '0'; + } + } + + 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, '-'); + ++pc; + --width; + } else { + *--s = '-'; + } + } + + return pc + prints(out, out_len, s, width, flags); +} + +static int print(char **out, u32 *out_len, const char *format, va_list args) +{ + int width, flags, acnt = 0; + int pc = 0; + char scr[2]; + unsigned long long tmp; + + for (; *format != 0; ++format) { + if (*format == '%') { + ++format; + width = flags = 0; + if (*format == '\0') + break; + if (*format == '%') + goto out; + /* Get flags */ + if (*format == '-') { + ++format; + flags = PAD_RIGHT; + } + if (*format == '#') { + ++format; + flags |= PAD_ALTERNATE; + } + while (*format == '0') { + ++format; + flags |= PAD_ZERO; + } + /* Get width */ + for (; *format >= '0' && *format <= '9'; ++format) { + width *= 10; + width += *format - '0'; + } + if (*format == 's') { + char *s = va_arg(args, char *); + acnt += sizeof(char *); + pc += prints(out, out_len, + s ? s : "(null)", width, flags); + continue; + } + if ((*format == 'd') || (*format == 'i')) { + pc += printi(out, out_len, + va_arg(args, int), + 10, 1, width, flags, '0'); + acnt += sizeof(int); + continue; + } + if (*format == 'x') { + pc += printi(out, out_len, + va_arg(args, unsigned int), + 16, 0, width, flags, 'a'); + acnt += sizeof(unsigned int); + continue; + } + if (*format == 'X') { + pc += printi(out, out_len, + va_arg(args, unsigned int), + 16, 0, width, flags, 'A'); + acnt += sizeof(unsigned int); + continue; + } + if (*format == 'u') { + pc += printi(out, out_len, + va_arg(args, unsigned int), + 10, 0, width, flags, 'a'); + acnt += sizeof(unsigned int); + continue; + } + if (*format == 'p') { + pc += printi(out, out_len, + va_arg(args, unsigned long), + 16, 0, width, flags, 'a'); + acnt += sizeof(unsigned long); + continue; + } + if (*format == 'P') { + pc += printi(out, out_len, + va_arg(args, unsigned long), + 16, 0, width, flags, 'A'); + acnt += sizeof(unsigned long); + continue; + } + if (*format == 'l' && *(format + 1) == 'l') { + while (acnt & (sizeof(unsigned long long)-1)) { + va_arg(args, int); + acnt += sizeof(int); + } + if (sizeof(unsigned long long) == + sizeof(unsigned long)) { + tmp = va_arg(args, unsigned long long); + acnt += sizeof(unsigned long long); + } else { + ((unsigned long *)&tmp)[0] = + va_arg(args, unsigned long); + ((unsigned long *)&tmp)[1] = + va_arg(args, unsigned long); + acnt += 2*sizeof(unsigned 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'); + } + continue; + } else if (*format == 'l') { + if (*(format + 1) == 'x') { + format += 1; + pc += printi(out, out_len, + va_arg(args, unsigned long), + 16, 0, width, flags, 'a'); + acnt += sizeof(unsigned long); + } else if (*(format + 1) == 'X') { + format += 1; + pc += printi(out, out_len, + va_arg(args, unsigned long), + 16, 0, width, flags, 'A'); + acnt += sizeof(unsigned long); + } else { + pc += printi(out, out_len, + va_arg(args, long), + 10, 1, width, flags, '0'); + acnt += sizeof(long); + } + } + if (*format == 'c') { + /* char are converted to int then pushed on the stack */ + scr[0] = va_arg(args, int); + scr[1] = '\0'; + pc += prints(out, out_len, scr, width, flags); + acnt += sizeof(int); + continue; + } + } else { +out: + printc(out, out_len, *format); + ++pc; + } + } + if (out) + **out = '\0'; + return pc; +} + +int sbi_sprintf(char *out, const char *format, ...) +{ + va_list args; + int retval; + va_start(args, format); + retval = print(&out, NULL, format, args); + va_end(args); + return retval; +} + +int sbi_snprintf(char *out, u32 out_sz, const char *format, ...) +{ + va_list args; + int retval; + va_start(args, format); + retval = print(&out, &out_sz, format, args); + va_end(args); + return retval; +} + +int sbi_printf(const char *format, ...) +{ + va_list args; + int retval; + va_start(args, format); + retval = print(NULL, NULL, format, args); + va_end(args); + return retval; +} + +int sbi_console_init(struct sbi_scratch *scratch) +{ + console_plat = sbi_platform_ptr(scratch); + + return sbi_platform_console_init(console_plat); +} diff --git a/lib/sbi_ecall.c b/lib/sbi_ecall.c new file mode 100644 index 0000000..928a8b7 --- /dev/null +++ b/lib/sbi_ecall.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SBI_ECALL_VERSION_MAJOR 0 +#define SBI_ECALL_VERSION_MINOR 1 + +#define SBI_ECALL_SET_TIMER 0 +#define SBI_ECALL_CONSOLE_PUTCHAR 1 +#define SBI_ECALL_CONSOLE_GETCHAR 2 +#define SBI_ECALL_CLEAR_IPI 3 +#define SBI_ECALL_SEND_IPI 4 +#define SBI_ECALL_REMOTE_FENCE_I 5 +#define SBI_ECALL_REMOTE_SFENCE_VMA 6 +#define SBI_ECALL_REMOTE_SFENCE_VMA_ASID 7 +#define SBI_ECALL_SHUTDOWN 8 + +u16 sbi_ecall_version_major(void) +{ + return SBI_ECALL_VERSION_MAJOR; +} + +u16 sbi_ecall_version_minor(void) +{ + return SBI_ECALL_VERSION_MINOR; +} + +int sbi_ecall_handler(u32 hartid, ulong mcause, + struct sbi_trap_regs *regs, + struct sbi_scratch *scratch) +{ + int ret = SBI_ENOTSUPP; + + switch (regs->a7) { + case SBI_ECALL_SET_TIMER: +#if __riscv_xlen == 32 + sbi_timer_event_start(scratch, hartid, + (((u64)regs->a1 << 32) || (u64)regs->a0)); +#else + sbi_timer_event_start(scratch, hartid, (u64)regs->a0); +#endif + ret = 0; + break; + case SBI_ECALL_CONSOLE_PUTCHAR: + sbi_putc(regs->a0); + ret = 0; + break; + case SBI_ECALL_CONSOLE_GETCHAR: + regs->a0 = sbi_getc(); + ret = 0; + break; + case SBI_ECALL_CLEAR_IPI: + sbi_ipi_clear_smode(scratch, hartid); + ret = 0; + break; + case SBI_ECALL_SEND_IPI: + ret = sbi_ipi_send_many(scratch, hartid, + (ulong *)regs->a0, + SBI_IPI_EVENT_SOFT); + break; + case SBI_ECALL_REMOTE_FENCE_I: + ret = sbi_ipi_send_many(scratch, hartid, + (ulong *)regs->a0, + SBI_IPI_EVENT_FENCE_I); + break; + case SBI_ECALL_REMOTE_SFENCE_VMA: + case SBI_ECALL_REMOTE_SFENCE_VMA_ASID: + ret = sbi_ipi_send_many(scratch, hartid, + (ulong *)regs->a0, + SBI_IPI_EVENT_SFENCE_VMA); + break; + case SBI_ECALL_SHUTDOWN: + sbi_system_shutdown(scratch, 0); + ret = 0; + break; + default: + break; + }; + + if (!ret) { + regs->mepc += 4; + } + + return ret; +} diff --git a/lib/sbi_emulate_csr.c b/lib/sbi_emulate_csr.c new file mode 100644 index 0000000..cc74f96 --- /dev/null +++ b/lib/sbi_emulate_csr.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +int sbi_emulate_csr_read(int csr_num, + u32 hartid, ulong mstatus, + struct sbi_scratch *scratch, + ulong *csr_val) +{ + ulong cen = -1UL; + + if (EXTRACT_FIELD(mstatus, MSTATUS_MPP) == PRV_U) + cen = csr_read(scounteren); + + switch (csr_num) { + case CSR_MISA: + *csr_val = csr_read(misa); + break; + case CSR_MVENDORID: + *csr_val = csr_read(mvendorid); + break; + case CSR_MARCHID: + *csr_val = csr_read(marchid); + break; + case CSR_MIMPID: + *csr_val = csr_read(mimpid); + break; + case CSR_MHARTID: + *csr_val = csr_read(mhartid); + break; + case CSR_CYCLE: + if (!((cen >> (CSR_CYCLE - CSR_CYCLE)) & 1)) + return -1; + *csr_val = csr_read(mcycle); + break; + case CSR_TIME: + if (!((cen >> (CSR_TIME - CSR_CYCLE)) & 1)) + return -1; + *csr_val = sbi_timer_value(scratch); + break; + case CSR_INSTRET: + if (!((cen >> (CSR_INSTRET - CSR_CYCLE)) & 1)) + return -1; + *csr_val = csr_read(minstret); + break; + case CSR_MHPMCOUNTER3: + if (!((cen >> (3 + CSR_MHPMCOUNTER3 - CSR_MHPMCOUNTER3)) & 1)) + return -1; + *csr_val = csr_read(mhpmcounter3); + break; + case CSR_MHPMCOUNTER4: + if (!((cen >> (3 + CSR_MHPMCOUNTER4 - CSR_MHPMCOUNTER3)) & 1)) + return -1; + *csr_val = csr_read(mhpmcounter4); + break; +#if __riscv_xlen == 32 + case CSR_CYCLEH: + if (!((cen >> (CSR_CYCLE - CSR_CYCLE)) & 1)) + return -1; + *csr_val = csr_read(mcycleh); + break; + case CSR_TIMEH: + if (!((cen >> (CSR_TIME - CSR_CYCLE)) & 1)) + return -1; + *csr_val = sbi_timer_value(scratch); + *csr_val = *csr_val >> 32; + break; + case CSR_INSTRETH: + if (!((cen >> (CSR_INSTRET - CSR_CYCLE)) & 1)) + return -1; + *csr_val = csr_read(minstreth); + break; + case CSR_MHPMCOUNTER3H: + if (!((cen >> (3 + CSR_MHPMCOUNTER3 - CSR_MHPMCOUNTER3)) & 1)) + return -1; + *csr_val = csr_read(mhpmcounter3h); + break; + case CSR_MHPMCOUNTER4H: + if (!((cen >> (3 + CSR_MHPMCOUNTER4 - CSR_MHPMCOUNTER3)) & 1)) + return -1; + *csr_val = csr_read(mhpmcounter4h); + break; +#endif + case CSR_MHPMEVENT3: + *csr_val = csr_read(mhpmevent3); + break; + case CSR_MHPMEVENT4: + *csr_val = csr_read(mhpmevent4); + break; + default: + sbi_printf("%s: hartid%d: invalid csr_num=0x%x\n", + __func__, hartid, csr_num); + return SBI_ENOTSUPP; + }; + + return 0; +} + +int sbi_emulate_csr_write(int csr_num, + u32 hartid, ulong mstatus, + struct sbi_scratch *scratch, + ulong csr_val) +{ + switch (csr_num) { + case CSR_CYCLE: + csr_write(mcycle, csr_val); + break; + case CSR_INSTRET: + csr_write(minstret, csr_val); + break; + case CSR_MHPMCOUNTER3: + csr_write(mhpmcounter3, csr_val); + break; + case CSR_MHPMCOUNTER4: + csr_write(mhpmcounter4, csr_val); + break; +#if __riscv_xlen == 32 + case CSR_CYCLEH: + csr_write(mcycleh, csr_val); + break; + case CSR_INSTRETH: + csr_write(minstreth, csr_val); + break; + case CSR_MHPMCOUNTER3H: + csr_write(mhpmcounter3h, csr_val); + break; + case CSR_MHPMCOUNTER4H: + csr_write(mhpmcounter4h, csr_val); + break; +#endif + case CSR_MHPMEVENT3: + csr_write(mhpmevent3, csr_val); + break; + case CSR_MHPMEVENT4: + csr_write(mhpmevent4, csr_val); + break; + default: + sbi_printf("%s: hartid%d: invalid csr_num=0x%x\n", + __func__, hartid, csr_num); + return SBI_ENOTSUPP; + }; + + return 0; +} diff --git a/lib/sbi_hart.c b/lib/sbi_hart.c new file mode 100644 index 0000000..db833c7 --- /dev/null +++ b/lib/sbi_hart.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int mstatus_init(u32 hartid) +{ + /* Enable FPU */ + if (misa_extension('D') || misa_extension('F')) + csr_write(mstatus, MSTATUS_FS); + + /* Enable user/supervisor use of perf counters */ + if (misa_extension('S')) + csr_write(scounteren, -1); + csr_write(mcounteren, -1); + + /* Disable all interrupts */ + csr_write(mie, 0); + + /* Disable S-mode paging */ + if (misa_extension('S')) + csr_write(sptbr, 0); + + return 0; +} + +#ifdef __riscv_flen +static void init_fp_reg(int i) +{ + /* TODO: */ +} +#endif + +static int fp_init(u32 hartid) +{ +#ifdef __riscv_flen + int i; +#else + unsigned long fd_mask; +#endif + + if (!misa_extension('D') && !misa_extension('F')) + return 0; + + if (!(csr_read(mstatus) & MSTATUS_FS)) + return SBI_EINVAL; + +#ifdef __riscv_flen + for (i = 0; i < 32; i++) + init_fp_reg(i); + csr_write(fcsr, 0); +#else + fd_mask = (1 << ('F' - 'A')) | (1 << ('D' - 'A')); + csr_clear(misa, fd_mask); + if (csr_read(misa) & fd_mask) + return SBI_ENOTSUPP; +#endif + + return 0; +} + +static int delegate_traps(u32 hartid) +{ + /* send S-mode interrupts and most exceptions straight to S-mode */ + unsigned long interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP; + unsigned long exceptions = (1U << CAUSE_MISALIGNED_FETCH) | + (1U << CAUSE_FETCH_PAGE_FAULT) | + (1U << CAUSE_BREAKPOINT) | + (1U << CAUSE_LOAD_PAGE_FAULT) | + (1U << CAUSE_STORE_PAGE_FAULT) | + (1U << CAUSE_USER_ECALL); + + if (!misa_extension('S')) + return 0; + + csr_write(mideleg, interrupts); + csr_write(medeleg, exceptions); + + if (csr_read(mideleg) != interrupts) + return SBI_EFAIL; + if (csr_read(medeleg) != exceptions) + return SBI_EFAIL; + + return 0; +} + +unsigned long log2roundup(unsigned long x) +{ + unsigned long ret = 0; + + while (ret < __riscv_xlen) { + if (x <= (1UL << ret)) + break; + ret++; + } + + return ret; +} + +void sbi_hart_pmp_dump(void) +{ + unsigned int i; + unsigned long prot, addr, size, l2l; + + for (i = 0; i < PMP_COUNT; i++) { + pmp_get(i, &prot, &addr, &l2l); + if (!(prot & PMP_A)) + continue; + if (l2l < __riscv_xlen) + size = (1UL << l2l); + else + size = 0; +#if __riscv_xlen == 32 + sbi_printf("PMP%d: 0x%08lx-0x%08lx (A", +#else + sbi_printf("PMP%d: 0x%016lx-0x%016lx (A", +#endif + i, addr, addr + size - 1); + if (prot & PMP_L) + sbi_printf(",L"); + if (prot & PMP_R) + sbi_printf(",R"); + if (prot & PMP_W) + sbi_printf(",W"); + if (prot & PMP_X) + sbi_printf(",X"); + sbi_printf(")\n"); + } +} + +static int pmp_init(struct sbi_scratch *scratch, u32 hartid) +{ + u32 i, count; + unsigned long fw_start, fw_size_log2; + ulong prot, addr, log2size; + struct sbi_platform *plat = sbi_platform_ptr(scratch); + + fw_size_log2 = log2roundup(scratch->fw_size); + fw_start = scratch->fw_start & ~((1UL << fw_size_log2) - 1UL); + + pmp_set(0, 0, fw_start, fw_size_log2); + + count = sbi_platform_pmp_region_count(plat, hartid); + if ((PMP_COUNT - 1) < count) + count = (PMP_COUNT - 1); + + for (i = 0; i < count; i++) { + if (sbi_platform_pmp_region_info(plat, hartid, i, + &prot, &addr, &log2size)) + continue; + pmp_set(i + 1, prot, addr, log2size); + } + + return 0; +} + +int sbi_hart_init(struct sbi_scratch *scratch, u32 hartid) +{ + int rc; + + rc = mstatus_init(hartid); + if (rc) + return rc; + + rc = fp_init(hartid); + if (rc) + return rc; + + rc = delegate_traps(hartid); + if (rc) + return rc; + + return pmp_init(scratch, hartid); +} + +void __attribute__((noreturn)) sbi_hart_hang(void) +{ + while (1) + wfi(); + __builtin_unreachable(); +} + +void __attribute__((noreturn)) sbi_hart_boot_next(unsigned long arg0, + unsigned long arg1, + unsigned long next_addr, + unsigned long next_mode) +{ + unsigned long val; + + if (next_mode != PRV_S && next_mode != PRV_M && next_mode != PRV_U) + sbi_hart_hang(); + + val = csr_read(mstatus); + val = INSERT_FIELD(val, MSTATUS_MPP, next_mode); + val = INSERT_FIELD(val, MSTATUS_MPIE, 0); + csr_write(mstatus, val); + csr_write(mepc, next_addr); + + if (next_mode == PRV_S) { + csr_write(stvec, next_addr); + csr_write(sscratch, 0); + csr_write(sie, 0); + csr_write(satp, 0); + } else if (next_mode == PRV_U) { + csr_write(utvec, next_addr); + csr_write(uscratch, 0); + csr_write(uie, 0); + } + + register unsigned long a0 asm ("a0") = arg0; + register unsigned long a1 asm ("a1") = arg1; + __asm__ __volatile__ ("mret" : : "r" (a0), "r" (a1)); + __builtin_unreachable(); +} + +static spinlock_t avail_hart_mask_lock = SPIN_LOCK_INITIALIZER; +static volatile unsigned long avail_hart_mask = 0; + +void sbi_hart_mark_available(u32 hartid) +{ + spin_lock(&avail_hart_mask_lock); + avail_hart_mask |= (1UL << hartid); + spin_unlock(&avail_hart_mask_lock); +} + +void sbi_hart_unmark_available(u32 hartid) +{ + spin_lock(&avail_hart_mask_lock); + avail_hart_mask &= ~(1UL << hartid); + spin_unlock(&avail_hart_mask_lock); +} + +ulong sbi_hart_available_mask(void) +{ + ulong ret; + + spin_lock(&avail_hart_mask_lock); + ret = avail_hart_mask; + spin_unlock(&avail_hart_mask_lock); + + return ret; +} + +typedef struct sbi_scratch *(*h2s)(ulong hartid); + +struct sbi_scratch *sbi_hart_id_to_scratch(struct sbi_scratch *scratch, + u32 hartid) +{ + return ((h2s)scratch->hartid_to_scratch)(hartid); +} + +#define NO_HOTPLUG_BITMAP_SIZE __riscv_xlen +static spinlock_t coldboot_holding_pen_lock = SPIN_LOCK_INITIALIZER; +static volatile unsigned long coldboot_holding_pen = 0; + +void sbi_hart_wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid) +{ + unsigned long done; + struct sbi_platform *plat = sbi_platform_ptr(scratch); + + if ((sbi_platform_hart_count(plat) <= hartid) || + (NO_HOTPLUG_BITMAP_SIZE <= hartid)) + sbi_hart_hang(); + + while (1) { + spin_lock(&coldboot_holding_pen_lock); + done = coldboot_holding_pen; + spin_unlock(&coldboot_holding_pen_lock); + if (done) + break; + cpu_relax(); + } +} + +void sbi_hart_wake_coldboot_harts(struct sbi_scratch *scratch) +{ + spin_lock(&coldboot_holding_pen_lock); + coldboot_holding_pen = 1; + spin_unlock(&coldboot_holding_pen_lock); +} diff --git a/lib/sbi_illegal_insn.c b/lib/sbi_illegal_insn.c new file mode 100644 index 0000000..e8edd0e --- /dev/null +++ b/lib/sbi_illegal_insn.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SH_RD 7 +#define SH_RS1 15 +#define SH_RS2 20 +#define SH_RS2C 2 + +#define RV_X(x, s, n) (((x) >> (s)) & ((1 << (n)) - 1)) +#define RVC_LW_IMM(x) ((RV_X(x, 6, 1) << 2) | \ + (RV_X(x, 10, 3) << 3) | \ + (RV_X(x, 5, 1) << 6)) +#define RVC_LD_IMM(x) ((RV_X(x, 10, 3) << 3) | \ + (RV_X(x, 5, 2) << 6)) +#define RVC_LWSP_IMM(x) ((RV_X(x, 4, 3) << 2) | \ + (RV_X(x, 12, 1) << 5) | \ + (RV_X(x, 2, 2) << 6)) +#define RVC_LDSP_IMM(x) ((RV_X(x, 5, 2) << 3) | \ + (RV_X(x, 12, 1) << 5) | \ + (RV_X(x, 2, 3) << 6)) +#define RVC_SWSP_IMM(x) ((RV_X(x, 9, 4) << 2) | \ + (RV_X(x, 7, 2) << 6)) +#define RVC_SDSP_IMM(x) ((RV_X(x, 10, 3) << 3) | \ + (RV_X(x, 7, 3) << 6)) +#define RVC_RS1S(insn) (8 + RV_X(insn, SH_RD, 3)) +#define RVC_RS2S(insn) (8 + RV_X(insn, SH_RS2C, 3)) +#define RVC_RS2(insn) RV_X(insn, SH_RS2C, 5) + +#define SHIFT_RIGHT(x, y) ((y) < 0 ? ((x) << -(y)) : ((x) >> (y))) + +#define REG_MASK \ +((1 << (5 + LOG_REGBYTES)) - (1 << LOG_REGBYTES)) + +#define REG_OFFSET(insn, pos) \ +(SHIFT_RIGHT((insn), (pos) - LOG_REGBYTES) & REG_MASK) + +#define REG_PTR(insn, pos, regs)\ +(ulong *)((ulong)(regs) + REG_OFFSET(insn, pos)) + +#define GET_RM(insn) (((insn) >> 12) & 7) + +#define GET_RS1(insn, regs) (*REG_PTR(insn, SH_RS1, regs)) +#define GET_RS2(insn, regs) (*REG_PTR(insn, SH_RS2, regs)) +#define GET_RS1S(insn, regs) (*REG_PTR(RVC_RS1S(insn), 0, regs)) +#define GET_RS2S(insn, regs) (*REG_PTR(RVC_RS2S(insn), 0, regs)) +#define GET_RS2C(insn, regs) (*REG_PTR(insn, SH_RS2C, regs)) +#define GET_SP(regs) (*REG_PTR(2, 0, regs)) +#define SET_RD(insn, regs, val) (*REG_PTR(insn, SH_RD, regs) = (val)) +#define IMM_I(insn) ((s32)(insn) >> 20) +#define IMM_S(insn) (((s32)(insn) >> 25 << 5) | \ + (s32)(((insn) >> 7) & 0x1f)) +#define MASK_FUNCT3 0x7000 + +typedef int (*illegal_insn_func)(ulong insn, + u32 hartid, ulong mcause, + struct sbi_trap_regs *regs, + struct sbi_scratch *scratch); + +static int truly_illegal_insn(ulong insn, + u32 hartid, ulong mcause, + struct sbi_trap_regs *regs, + struct sbi_scratch *scratch) +{ + /* For now, always fails */ + return SBI_ENOTSUPP; +} + +static int system_opcode_insn(ulong insn, + u32 hartid, ulong mcause, + struct sbi_trap_regs *regs, + struct sbi_scratch *scratch) +{ + int do_write, rs1_num = (insn >> 15) & 0x1f; + ulong rs1_val = GET_RS1(insn, regs); + int csr_num = (u32)insn >> 20; + ulong csr_val, new_csr_val; + + if (sbi_emulate_csr_read(csr_num, hartid, regs->mstatus, + scratch, &csr_val)) + return truly_illegal_insn(insn, hartid, mcause, + regs, scratch); + + do_write = rs1_num; + switch (GET_RM(insn)) { + case 1: + new_csr_val = rs1_val; + do_write = 1; + break; + case 2: + new_csr_val = csr_val | rs1_val; + break; + case 3: new_csr_val = csr_val & ~rs1_val; + break; + case 5: + new_csr_val = rs1_num; + do_write = 1; + break; + case 6: + new_csr_val = csr_val | rs1_num; + break; + case 7: + new_csr_val = csr_val & ~rs1_num; + break; + default: + return truly_illegal_insn(insn, hartid, mcause, + regs, scratch); + }; + + if (do_write && + sbi_emulate_csr_write(csr_num, hartid, regs->mstatus, + scratch, new_csr_val)) + return truly_illegal_insn(insn, hartid, mcause, + regs, scratch); + + SET_RD(insn, regs, csr_val); + + regs->mepc += 4; + + return 0; +} + +static illegal_insn_func illegal_insn_table[32] = { + truly_illegal_insn, /* 0 */ + truly_illegal_insn, /* 1 */ + truly_illegal_insn, /* 2 */ + truly_illegal_insn, /* 3 */ + truly_illegal_insn, /* 4 */ + truly_illegal_insn, /* 5 */ + truly_illegal_insn, /* 6 */ + truly_illegal_insn, /* 7 */ + truly_illegal_insn, /* 8 */ + truly_illegal_insn, /* 9 */ + truly_illegal_insn, /* 10 */ + truly_illegal_insn, /* 11 */ + truly_illegal_insn, /* 12 */ + truly_illegal_insn, /* 13 */ + truly_illegal_insn, /* 14 */ + truly_illegal_insn, /* 15 */ + truly_illegal_insn, /* 16 */ + truly_illegal_insn, /* 17 */ + truly_illegal_insn, /* 18 */ + truly_illegal_insn, /* 19 */ + truly_illegal_insn, /* 20 */ + truly_illegal_insn, /* 21 */ + truly_illegal_insn, /* 22 */ + truly_illegal_insn, /* 23 */ + truly_illegal_insn, /* 24 */ + truly_illegal_insn, /* 25 */ + truly_illegal_insn, /* 26 */ + truly_illegal_insn, /* 27 */ + system_opcode_insn, /* 28 */ + truly_illegal_insn, /* 29 */ + truly_illegal_insn, /* 30 */ + truly_illegal_insn /* 31 */ +}; + +int sbi_illegal_insn_handler(u32 hartid, ulong mcause, + struct sbi_trap_regs *regs, + struct sbi_scratch *scratch) +{ + ulong mstatus; + ulong insn = csr_read(mbadaddr); + + if (unlikely((insn & 3) != 3)) { + if (insn == 0) { + mstatus = csr_read(mstatus); + insn = get_insn(regs->mepc, &mstatus); + } + if ((insn & 3) != 3) + return SBI_ENOTSUPP; + } + + return illegal_insn_table[(insn & 0x7c) >> 2](insn, hartid, mcause, + regs, scratch); +} diff --git a/lib/sbi_init.c b/lib/sbi_init.c new file mode 100644 index 0000000..52c63fa --- /dev/null +++ b/lib/sbi_init.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __attribute__((noreturn)) init_coldboot(struct sbi_scratch *scratch, + u32 hartid) +{ + int rc; + char str[64]; + struct sbi_platform *plat = sbi_platform_ptr(scratch); + + rc = sbi_system_cold_early_init(scratch); + if (rc) + sbi_hart_hang(); + + rc = sbi_system_warm_early_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + rc = sbi_hart_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + rc = sbi_console_init(scratch); + if (rc) + sbi_hart_hang(); + + rc = sbi_platform_cold_irqchip_init(plat); + if (rc) + sbi_hart_hang(); + + rc = sbi_platform_warm_irqchip_init(plat, hartid); + if (rc) + sbi_hart_hang(); + + rc = sbi_ipi_cold_init(scratch); + if (rc) + sbi_hart_hang(); + + rc = sbi_ipi_warm_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + rc = sbi_timer_cold_init(scratch); + if (rc) + sbi_hart_hang(); + + rc = sbi_timer_warm_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + rc = sbi_system_cold_final_init(scratch); + if (rc) + sbi_hart_hang(); + + rc = sbi_system_warm_final_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + misa_string(str, sizeof(str)); + sbi_printf("OpenSBI v%d.%d (%s %s)\n", + OPENSBI_MAJOR, OPENSBI_MINOR, + __DATE__, __TIME__); + sbi_printf("\n"); + /* Platform details */ + sbi_printf("Platform Name : %s\n", sbi_platform_name(plat)); + sbi_printf("Platform HART Features : RV%d%s\n", misa_xlen(), str); + sbi_printf("Platform Max HARTs : %d\n", + sbi_platform_hart_count(plat)); + /* Firmware details */ + sbi_printf("Firmware Base : 0x%lx\n", scratch->fw_start); + sbi_printf("Firmware Size : %d KB\n", + (u32)(scratch->fw_size / 1024)); + /* Generic details */ + sbi_printf("Runtime SBI Version : %d.%d\n", + sbi_ecall_version_major(), sbi_ecall_version_minor()); + sbi_printf("\n"); + + sbi_hart_pmp_dump(); + + sbi_hart_mark_available(hartid); + + if (!sbi_platform_has_hart_hotplug(plat)) + sbi_hart_wake_coldboot_harts(scratch); + + sbi_hart_boot_next(hartid, scratch->next_arg1, + scratch->next_addr, scratch->next_mode); +} + +static void __attribute__((noreturn)) init_warmboot(struct sbi_scratch *scratch, + u32 hartid) +{ + int rc; + struct sbi_platform *plat = sbi_platform_ptr(scratch); + + if (!sbi_platform_has_hart_hotplug(plat)) + sbi_hart_wait_for_coldboot(scratch, hartid); + + rc = sbi_system_warm_early_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + rc = sbi_hart_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + rc = sbi_platform_warm_irqchip_init(plat, hartid); + if (rc) + sbi_hart_hang(); + + rc = sbi_ipi_warm_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + rc = sbi_timer_warm_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + rc = sbi_system_warm_final_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + sbi_hart_mark_available(hartid); + + if (sbi_platform_has_hart_hotplug(plat)) + /* TODO: To be implemented in-future. */ + sbi_hart_hang(); + else + sbi_hart_boot_next(hartid, scratch->next_arg1, + scratch->next_addr, scratch->next_mode); +} + +static atomic_t coldboot_lottery = ATOMIC_INITIALIZER(0); + +void __attribute__((noreturn)) sbi_init(struct sbi_scratch *scratch) +{ + bool coldboot = FALSE; + u32 hartid = csr_read(mhartid); + + if (atomic_add_return(&coldboot_lottery, 1) == 1) + coldboot = TRUE; + + if (coldboot) + init_coldboot(scratch, hartid); + else + init_warmboot(scratch, hartid); +} diff --git a/lib/sbi_ipi.c b/lib/sbi_ipi.c new file mode 100644 index 0000000..f3e68de --- /dev/null +++ b/lib/sbi_ipi.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +int sbi_ipi_send_many(struct sbi_scratch *scratch, + u32 hartid, ulong *pmask, u32 event) +{ + ulong i, m; + struct sbi_scratch *oth; + ulong mask = sbi_hart_available_mask(); + struct sbi_platform *plat = sbi_platform_ptr(scratch); + + if (pmask) + mask &= load_ulong(pmask, csr_read(mepc)); + + /* send IPIs to everyone */ + for (i = 0, m = mask; m; i++, m >>= 1) { + if ((m & 1) && (i != hartid)) { + oth = sbi_hart_id_to_scratch(scratch, i); + oth->ipi_type = event; + mb(); + sbi_platform_ipi_inject(plat, i, hartid); + if (event != SBI_IPI_EVENT_SOFT) + sbi_platform_ipi_sync(plat, i, hartid); + } + } + + return 0; +} + +void sbi_ipi_clear_smode(struct sbi_scratch *scratch, u32 hartid) +{ + csr_clear(mip, MIP_SSIP); +} + +void sbi_ipi_process(struct sbi_scratch *scratch, u32 hartid) +{ + struct sbi_platform *plat = sbi_platform_ptr(scratch); + + sbi_platform_ipi_clear(plat, hartid); + switch (scratch->ipi_type) { + case SBI_IPI_EVENT_SOFT: + csr_set(mip, MIP_SSIP); + break; + case SBI_IPI_EVENT_FENCE_I: + __asm__ __volatile("fence.i"); + break; + case SBI_IPI_EVENT_SFENCE_VMA: + __asm__ __volatile("sfence.vma"); + break; + case SBI_IPI_EVENT_HALT: + sbi_hart_hang(); + break; + }; + scratch->ipi_type = 0; +} + +int sbi_ipi_warm_init(struct sbi_scratch *scratch, u32 hartid) +{ + /* Enable software interrupts */ + csr_set(mie, MIP_MSIP); + + return sbi_platform_warm_ipi_init(sbi_platform_ptr(scratch), hartid); +} + +int sbi_ipi_cold_init(struct sbi_scratch *scratch) +{ + return sbi_platform_cold_ipi_init(sbi_platform_ptr(scratch)); +} diff --git a/lib/sbi_system.c b/lib/sbi_system.c new file mode 100644 index 0000000..cd250f5 --- /dev/null +++ b/lib/sbi_system.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +int sbi_system_warm_early_init(struct sbi_scratch *scratch, u32 hartid) +{ + return sbi_platform_warm_early_init(sbi_platform_ptr(scratch), hartid); +} + +int sbi_system_warm_final_init(struct sbi_scratch *scratch, u32 hartid) +{ + return sbi_platform_warm_final_init(sbi_platform_ptr(scratch), hartid); +} + +int sbi_system_cold_early_init(struct sbi_scratch *scratch) +{ + return sbi_platform_cold_early_init(sbi_platform_ptr(scratch)); +} + +int sbi_system_cold_final_init(struct sbi_scratch *scratch) +{ + return sbi_platform_cold_final_init(sbi_platform_ptr(scratch)); +} + +void __attribute__((noreturn)) sbi_system_reboot(struct sbi_scratch *scratch, + u32 type) + +{ + sbi_platform_system_reboot(sbi_platform_ptr(scratch), type); + sbi_hart_hang(); +} + +void __attribute__((noreturn)) sbi_system_shutdown(struct sbi_scratch *scratch, + u32 type) +{ + sbi_platform_system_shutdown(sbi_platform_ptr(scratch), type); + sbi_hart_hang(); +} diff --git a/lib/sbi_timer.c b/lib/sbi_timer.c new file mode 100644 index 0000000..355bc64 --- /dev/null +++ b/lib/sbi_timer.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +#if __riscv_xlen == 32 +u64 get_ticks(void) +{ + u32 lo, hi, tmp; + __asm__ __volatile__ ( + "1:\n" + "rdtimeh %0\n" + "rdtime %1\n" + "rdtimeh %2\n" + "bne %0, %2, 1b" + : "=&r" (hi), "=&r" (lo), "=&r" (tmp)); + return ((u64)hi << 32) | lo; +} +#else +u64 get_ticks(void) +{ + unsigned long n; + + __asm__ __volatile__ ( + "rdtime %0" + : "=r" (n)); + return n; +} +#endif + +u64 sbi_timer_value(struct sbi_scratch *scratch) +{ + struct sbi_platform *plat = sbi_platform_ptr(scratch); + + if (sbi_platform_has_mmio_timer_value(plat)) + return sbi_platform_timer_value(plat); + else + return get_ticks(); +} + +void sbi_timer_event_stop(struct sbi_scratch *scratch, u32 hartid) +{ + sbi_platform_timer_event_stop(sbi_platform_ptr(scratch), hartid); +} + +void sbi_timer_event_start(struct sbi_scratch *scratch, u32 hartid, + u64 next_event) +{ + sbi_platform_timer_event_start(sbi_platform_ptr(scratch), + hartid, next_event); + csr_clear(mip, MIP_STIP); + csr_set(mie, MIP_MTIP); +} + +void sbi_timer_process(struct sbi_scratch *scratch, u32 hartid) +{ + csr_clear(mie, MIP_MTIP); + csr_set(mip, MIP_STIP); +} + +int sbi_timer_warm_init(struct sbi_scratch *scratch, u32 hartid) +{ + return sbi_platform_warm_timer_init(sbi_platform_ptr(scratch), hartid); +} + +int sbi_timer_cold_init(struct sbi_scratch *scratch) +{ + return sbi_platform_cold_timer_init(sbi_platform_ptr(scratch)); +} diff --git a/lib/sbi_trap.c b/lib/sbi_trap.c new file mode 100644 index 0000000..f9c70a6 --- /dev/null +++ b/lib/sbi_trap.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __attribute__((noreturn)) sbi_trap_error(const char *msg, + int rc, u32 hartid, + ulong mcause, + struct sbi_trap_regs *regs) +{ + sbi_printf("%s: hart%d: %s (error %d)\n", + __func__, hartid, msg, rc); + sbi_printf("%s: hart%d: mcause=0x%lx mepc=0x%lx mstatus=0x%lx\n", + __func__, hartid, mcause, regs->mepc, regs->mstatus); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "ra", regs->ra, "sp", regs->sp); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "gp", regs->gp, "tp", regs->tp); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "s0", regs->s0, "s1", regs->s1); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "a0", regs->a0, "a1", regs->a1); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "a2", regs->a2, "a3", regs->a3); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "a4", regs->a4, "a5", regs->a5); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "a6", regs->a6, "a7", regs->a7); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "s2", regs->s2, "s3", regs->s3); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "s4", regs->s4, "s5", regs->s5); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "s6", regs->s6, "s7", regs->s7); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "s8", regs->s8, "s9", regs->s9); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "s10", regs->s10, "s11", regs->s11); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "t0", regs->t0, "t1", regs->t1); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "t2", regs->t2, "t3", regs->t3); + sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", + __func__, hartid, "t4", regs->t4, "t5", regs->t5); + sbi_printf("%s: hart%d: %s=0x%lx\n", + __func__, hartid, "t6", regs->t6); + + sbi_hart_hang(); +} + +void sbi_trap_handler(struct sbi_trap_regs *regs, + struct sbi_scratch *scratch) +{ + int rc; + const char *msg; + u32 hartid = csr_read(mhartid); + ulong mcause = csr_read(mcause); + + if (mcause & (1UL << (__riscv_xlen - 1))) { + mcause &= ~(1UL << (__riscv_xlen - 1)); + switch (mcause) { + case IRQ_M_TIMER: + sbi_timer_process(scratch, hartid); + break; + case IRQ_M_SOFT: + sbi_ipi_process(scratch, hartid); + break; + default: + sbi_trap_error("unhandled external interrupt", + SBI_ENOTSUPP, hartid, mcause, regs); + break; + }; + return; + } + + rc = SBI_ENOTSUPP; + msg = "trap handler failed"; + switch (mcause) { + case CAUSE_ILLEGAL_INSTRUCTION: + rc = sbi_illegal_insn_handler(hartid, mcause, regs, scratch); + msg = "illegal instruction handler failed"; + break; + case CAUSE_SUPERVISOR_ECALL: + case CAUSE_HYPERVISOR_ECALL: + rc = sbi_ecall_handler(hartid, mcause, regs, scratch); + msg = "ecall handler failed"; + break; + default: + break; + }; + + if (rc) { + sbi_trap_error(msg, rc, hartid, mcause, regs); + } +} diff --git a/plat/common/fdt.c b/plat/common/fdt.c new file mode 100644 index 0000000..4f6017c --- /dev/null +++ b/plat/common/fdt.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#define FDT_MAGIC 0xd00dfeed +#define FDT_VERSION 17 + +struct fdt_header { + u32 magic; + u32 totalsize; + u32 off_dt_struct; + u32 off_dt_strings; + u32 off_mem_rsvmap; + u32 version; + u32 last_comp_version; /* <= 17 */ + u32 boot_cpuid_phys; + u32 size_dt_strings; + u32 size_dt_struct; +} __attribute__((packed)); + +#define FDT_BEGIN_NODE 1 +#define FDT_END_NODE 2 +#define FDT_PROP 3 +#define FDT_NOP 4 +#define FDT_END 9 + +u32 fdt_rev32(u32 v) +{ + return ((v & 0x000000FF) << 24) | + ((v & 0x0000FF00) << 8) | + ((v & 0x00FF0000) >> 8) | + ((v & 0xFF000000) >> 24); +} + +ulong fdt_strlen(const char *str) +{ + ulong ret = 0; + + while (*str != '\0') { + ret++; + str++; + } + + return ret; +} + +int fdt_strcmp(const char *a, const char *b) +{ + /* search first diff or end of string */ + for (; *a == *b && *a != '\0'; a++, b++); + return *a - *b; +} + +int fdt_prop_string_index(const struct fdt_prop *prop, + const char *str) +{ + int i; + ulong l = 0; + const char *p, *end; + + p = prop->value; + end = p + prop->len; + + for (i = 0; p < end; i++, p += l) { + l = fdt_strlen(p) + 1; + if (p + l > end) + return -1; + if (fdt_strcmp(str, p) == 0) + return i; /* Found it; return index */ + } + + return -1; +} + +struct recursive_iter_info { + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv); + void *fn_priv; + const char *str; +}; + +#define DATA32(ptr) fdt_rev32(*((u32*)ptr)) + +static void recursive_iter(char **data, struct recursive_iter_info *info, + const struct fdt_node *parent) +{ + struct fdt_node node; + struct fdt_prop prop; + + if (DATA32(*data) != FDT_BEGIN_NODE) + return; + + node.data = *data; + + (*data) += sizeof(u32); + + node.parent = parent; + node.name = *data; + + *data += fdt_strlen(*data) + 1; + while ((ulong)(*data) % sizeof(u32) != 0) + (*data)++; + + node.depth = (parent) ? (parent->depth + 1) : 1; + + /* Default cell counts, as per the FDT spec */ + node.address_cells = 2; + node.size_cells = 1; + + info->fn(&node, NULL, info->fn_priv); + + while (DATA32(*data) != FDT_END_NODE) { + switch (DATA32(*data)) { + case FDT_PROP: + prop.node = &node; + *data += sizeof(u32); + prop.len = DATA32(*data); + *data += sizeof(u32); + prop.name = &info->str[DATA32(*data)]; + *data += sizeof(u32); + prop.value = *data; + *data += prop.len; + while ((ulong)(*data) % sizeof(u32) != 0) + (*data)++; + info->fn(&node, &prop, info->fn_priv); + break; + case FDT_NOP: + *data += sizeof(u32); + break; + case FDT_BEGIN_NODE: + recursive_iter(data, info, &node); + break; + default: + return; + }; + } + + *data += sizeof(u32); +} + +struct match_iter_info { + int (*match)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv); + void *match_priv; + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv); + void *fn_priv; + const char *str; +}; + +static void match_iter(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv) +{ + char *data; + struct match_iter_info *minfo = priv; + struct fdt_prop nprop; + + /* Do nothing if node+prop dont match */ + if (!minfo->match(node, prop, minfo->match_priv)) + return; + + /* Call function for node */ + if (minfo->fn) + minfo->fn(node, NULL, minfo->fn_priv); + + /* Convert node to character stream */ + data = node->data; + data += sizeof(u32); + + /* Skip node name */ + data += fdt_strlen(data) + 1; + while ((ulong)(data) % sizeof(u32) != 0) + data++; + + /* Find node property and its value */ + while (DATA32(data) == FDT_PROP) { + nprop.node = node; + data += sizeof(u32); + nprop.len = DATA32(data); + data += sizeof(u32); + nprop.name = &minfo->str[DATA32(data)]; + data += sizeof(u32); + nprop.value = data; + data += nprop.len; + while ((ulong)(data) % sizeof(u32) != 0) + (data)++; + /* Call function for every property */ + if (minfo->fn) + minfo->fn(node, &nprop, minfo->fn_priv); + } +} + +int fdt_match_node_prop(void *fdt, + int (*match)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *match_priv, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv) +{ + char *data; + u32 string_offset, data_offset; + struct fdt_header *header; + struct match_iter_info minfo; + struct recursive_iter_info rinfo; + + if (!fdt || !match) + return -1; + + header = fdt; + if (fdt_rev32(header->magic) != FDT_MAGIC || + fdt_rev32(header->last_comp_version) > FDT_VERSION) + return -1; + string_offset = fdt_rev32(header->off_dt_strings); + data_offset = fdt_rev32(header->off_dt_struct); + + minfo.match = match; + minfo.match_priv = match_priv; + minfo.fn = fn; + minfo.fn_priv = fn_priv; + minfo.str = (const char *)(fdt + string_offset); + + rinfo.fn = match_iter; + rinfo.fn_priv = &minfo; + rinfo.str = minfo.str; + + data = (char *)(fdt + data_offset); + recursive_iter(&data, &rinfo, NULL); + + return 0; +} + +struct match_compat_info { + const char *compat; +}; + +static int match_compat(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv) +{ + struct match_compat_info *cinfo = priv; + + if (!prop) + return 0; + + if (fdt_strcmp(prop->name, "compatible")) + return 0; + + if (fdt_prop_string_index(prop, cinfo->compat) < 0) + return 0; + + return 1; +} + +int fdt_compat_node_prop(void *fdt, + const char *compat, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv) +{ + struct match_compat_info cinfo = { .compat = compat }; + + return fdt_match_node_prop(fdt, match_compat, &cinfo, + fn, fn_priv); +} + +static int match_walk(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv) +{ + if (!prop) + return 1; + + return 0; +} + +int fdt_walk(void *fdt, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv) +{ + return fdt_match_node_prop(fdt, match_walk, NULL, + fn, fn_priv); +} + +u32 fdt_size(void *fdt) +{ + struct fdt_header *header; + + if (!fdt) + return 0; + + header = fdt; + if (fdt_rev32(header->magic) != FDT_MAGIC || + fdt_rev32(header->last_comp_version) > FDT_VERSION) + return 0; + + return fdt_rev32(header->totalsize); +} diff --git a/plat/common/include/plat/fdt.h b/plat/common/include/plat/fdt.h new file mode 100644 index 0000000..246243f --- /dev/null +++ b/plat/common/include/plat/fdt.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __FDT_H__ +#define __FDT_H__ + +#include + +struct fdt_node { + char *data; + const struct fdt_node *parent; + const char *name; + int depth; + int address_cells; + int size_cells; +}; + +struct fdt_prop { + const struct fdt_node *node; + const char *name; + void *value; + u32 len; +}; + +/* Reverse byte-order of 32bit number */ +u32 fdt_rev32(u32 v); + +/* Length of a string */ +ulong fdt_strlen(const char *str); + +/* Compate two strings */ +int fdt_strcmp(const char *a, const char *b); + +/* Find index of matching string from a list of strings */ +int fdt_prop_string_index(const struct fdt_prop *prop, + const char *str); + +/* Iterate over each property of matching node */ +int fdt_match_node_prop(void *fdt, + int (*match)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *match_priv, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv); + +/* Iterate over each property of compatible node */ +int fdt_compat_node_prop(void *fdt, + const char *compat, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv); + +/* Iterate over each node and property */ +int fdt_walk(void *fdt, + void (*fn)(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv), + void *fn_priv); + +/* Get size of FDT */ +u32 fdt_size(void *fdt); + +#endif diff --git a/plat/common/include/plat/irqchip/plic.h b/plat/common/include/plat/irqchip/plic.h new file mode 100644 index 0000000..7c062aa --- /dev/null +++ b/plat/common/include/plat/irqchip/plic.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __IRQCHIP_PLIC_H__ +#define __IRQCHIP_PLIC_H__ + +#include + +int plic_fdt_fixup(void *fdt, const char *compat); + +int plic_warm_irqchip_init(u32 target_hart); + +int plic_cold_irqchip_init(unsigned long base, + u32 num_sources, u32 hart_count); + +#endif diff --git a/plat/common/include/plat/serial/sifive-uart.h b/plat/common/include/plat/serial/sifive-uart.h new file mode 100644 index 0000000..b932b16 --- /dev/null +++ b/plat/common/include/plat/serial/sifive-uart.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SERIAL_SIFIVE_UART_H__ +#define __SERIAL_SIFIVE_UART_H__ + +#include + +void sifive_uart_putc(char ch); + +char sifive_uart_getc(void); + +int sifive_uart_init(unsigned long base, + u32 in_freq, u32 baudrate); + +#endif diff --git a/plat/common/include/plat/serial/uart8250.h b/plat/common/include/plat/serial/uart8250.h new file mode 100644 index 0000000..ca19a9f --- /dev/null +++ b/plat/common/include/plat/serial/uart8250.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SERIAL_UART8250_H__ +#define __SERIAL_UART8250_H__ + +#include + +void uart8250_putc(char ch); + +char uart8250_getc(void); + +int uart8250_init(unsigned long base, + u32 in_freq, u32 baudrate, + u32 reg_shift, u32 reg_width); + +#endif diff --git a/plat/common/include/plat/sys/clint.h b/plat/common/include/plat/sys/clint.h new file mode 100644 index 0000000..642d83a --- /dev/null +++ b/plat/common/include/plat/sys/clint.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SYS_CLINT_H__ +#define __SYS_CLINT_H__ + +#include + +void clint_ipi_inject(u32 target_hart, u32 source_hart); + +void clint_ipi_sync(u32 target_hart, u32 source_hart); + +void clint_ipi_clear(u32 target_hart); + +int clint_warm_ipi_init(u32 target_hart); + +int clint_cold_ipi_init(unsigned long base, u32 hart_count); + +u64 clint_timer_value(void); + +void clint_timer_event_stop(u32 target_hart); + +void clint_timer_event_start(u32 target_hart, u64 next_event); + +int clint_warm_timer_init(u32 target_hart); + +int clint_cold_timer_init(unsigned long base, u32 hart_count); + +#endif diff --git a/plat/common/irqchip/objects.mk b/plat/common/irqchip/objects.mk new file mode 100644 index 0000000..3950734 --- /dev/null +++ b/plat/common/irqchip/objects.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +plat-common-objs-$(PLAT_IRQCHIP_PLIC) += irqchip/plic.o diff --git a/plat/common/irqchip/plic.c b/plat/common/irqchip/plic.c new file mode 100644 index 0000000..404d1b7 --- /dev/null +++ b/plat/common/irqchip/plic.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +#define PLIC_PRIORITY_BASE 0x0 +#define PLIC_PENDING_BASE 0x1000 +#define PLIC_ENABLE_BASE 0x2000 +#define PLIC_ENABLE_STRIDE 0x80 +#define PLIC_CONTEXT_BASE 0x200000 +#define PLIC_CONTEXT_STRIDE 0x1000 + +static u32 plic_hart_count; +static u32 plic_num_sources; +static volatile void *plic_base; + +static void plic_set_priority(u32 source, u32 val) +{ + writel(val, plic_base); +} + +static void plic_set_m_thresh(u32 hartid, u32 val) +{ + volatile void *plic_m_thresh = plic_base + + PLIC_CONTEXT_BASE + + PLIC_CONTEXT_STRIDE * (2 * hartid); + writel(val, plic_m_thresh); +} + +static void plic_set_s_thresh(u32 hartid, u32 val) +{ + volatile void *plic_s_thresh = plic_base + + PLIC_CONTEXT_BASE + + PLIC_CONTEXT_STRIDE * (2 * hartid + 1); + writel(val, plic_s_thresh); +} + +static void plic_set_s_ie(u32 hartid, u32 word_index, u32 val) +{ + volatile void *plic_s_ie = plic_base + + PLIC_ENABLE_BASE + + PLIC_ENABLE_STRIDE * (2 * hartid + 1); + writel(val, plic_s_ie + word_index * 4); +} + +static void plic_fdt_fixup_prop(const struct fdt_node *node, + const struct fdt_prop *prop, + void *priv) +{ + u32 *cells; + u32 i, cells_count; + + if (!prop) + return; + if (fdt_strcmp(prop->name, "interrupts-extended")) + return; + + cells = prop->value; + cells_count = prop->len / sizeof(u32); + + if (!cells_count) + return; + + for (i = 0; i < cells_count; i++) { + if (i % 4 == 1) + cells[i] = fdt_rev32(0xffffffff); + } +} + +int plic_fdt_fixup(void *fdt, const char *compat) +{ + fdt_compat_node_prop(fdt, compat, plic_fdt_fixup_prop, NULL); + return 0; +} + +int plic_warm_irqchip_init(u32 target_hart) +{ + size_t i, ie_words = plic_num_sources / 32 + 1; + + if (plic_hart_count <= target_hart) + return -1; + + /* By default, enable all IRQs for S-mode of target HART */ + for (i = 0; i < ie_words; i++) + plic_set_s_ie(target_hart, i, -1); + + /* By default, enable M-mode threshold */ + plic_set_m_thresh(target_hart, 1); + + /* By default, disable S-mode threshold */ + plic_set_s_thresh(target_hart, 0); + + return 0; +} + +int plic_cold_irqchip_init(unsigned long base, + u32 num_sources, u32 hart_count) +{ + int i; + + plic_hart_count = hart_count; + plic_num_sources = num_sources; + plic_base = (void *)base; + + /* Configure default priorities of all IRQs */ + for (i = 0; i < plic_num_sources; i++) + plic_set_priority(i, 1); + + return 0; +} diff --git a/plat/common/objects.mk b/plat/common/objects.mk new file mode 100644 index 0000000..18fcd0c --- /dev/null +++ b/plat/common/objects.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +plat-common-objs-y += fdt.o diff --git a/plat/common/serial/objects.mk b/plat/common/serial/objects.mk new file mode 100644 index 0000000..61d5d37 --- /dev/null +++ b/plat/common/serial/objects.mk @@ -0,0 +1,11 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +plat-common-objs-$(PLAT_SERIAL_UART8250) += serial/uart8250.o +plat-common-objs-$(PLAT_SERIAL_SIFIVE_UART) += serial/sifive-uart.o diff --git a/plat/common/serial/sifive-uart.c b/plat/common/serial/sifive-uart.c new file mode 100644 index 0000000..50c7cad --- /dev/null +++ b/plat/common/serial/sifive-uart.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +#define UART_REG_TXFIFO 0 +#define UART_REG_RXFIFO 1 +#define UART_REG_TXCTRL 2 +#define UART_REG_RXCTRL 3 +#define UART_REG_IE 4 +#define UART_REG_IP 5 +#define UART_REG_DIV 6 + +#define UART_TXFIFO_FULL 0x80000000 +#define UART_RXFIFO_EMPTY 0x80000000 +#define UART_RXFIFO_DATA 0x000000ff +#define UART_TXCTRL_TXEN 0x1 +#define UART_RXCTRL_RXEN 0x1 + +static volatile void *uart_base; +static u32 uart_in_freq; +static u32 uart_baudrate; + +static u32 get_reg(u32 num) +{ + return readl(uart_base + (num * 0x4)); +} + +static void set_reg(u32 num, u32 val) +{ + writel(val, uart_base + (num * 0x4)); +} + +void sifive_uart_putc(char ch) +{ + while (get_reg(UART_REG_TXFIFO) & UART_TXFIFO_FULL); + + set_reg(UART_REG_TXFIFO, ch); +} + +char sifive_uart_getc(void) +{ + u32 ret = get_reg(UART_REG_RXFIFO); + if (!(ret & UART_RXFIFO_EMPTY)) + return ret & UART_RXFIFO_DATA; + return 0; +} + +int sifive_uart_init(unsigned long base, + u32 in_freq, u32 baudrate) +{ + uart_base = (volatile void *)base; + uart_in_freq = in_freq; + uart_baudrate = baudrate; + + /* Configure baudrate */ + set_reg(UART_REG_DIV, (in_freq / baudrate) - 1); + /* Disable interrupts */ + set_reg(UART_REG_IE, 0); + /* Enable TX */ + set_reg(UART_REG_TXCTRL, UART_TXCTRL_TXEN); + /* Enable Rx */ + set_reg(UART_REG_RXCTRL, UART_RXCTRL_RXEN); + + return 0; +} diff --git a/plat/common/serial/uart8250.c b/plat/common/serial/uart8250.c new file mode 100644 index 0000000..7a99045 --- /dev/null +++ b/plat/common/serial/uart8250.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +#define UART_RBR_OFFSET 0 /* In: Recieve Buffer Register */ +#define UART_THR_OFFSET 0 /* Out: Transmitter Holding Register */ +#define UART_DLL_OFFSET 0 /* Out: Divisor Latch Low */ +#define UART_IER_OFFSET 1 /* I/O: Interrupt Enable Register */ +#define UART_DLM_OFFSET 1 /* Out: Divisor Latch High */ +#define UART_FCR_OFFSET 2 /* Out: FIFO Control Register */ +#define UART_IIR_OFFSET 2 /* I/O: Interrupt Identification Register */ +#define UART_LCR_OFFSET 3 /* Out: Line Control Register */ +#define UART_MCR_OFFSET 4 /* Out: Modem Control Register */ +#define UART_LSR_OFFSET 5 /* In: Line Status Register */ +#define UART_MSR_OFFSET 6 /* In: Modem Status Register */ +#define UART_SCR_OFFSET 7 /* I/O: Scratch Register */ +#define UART_MDR1_OFFSET 8 /* I/O: Mode Register */ + +#define UART_LSR_FIFOE 0x80 /* Fifo error */ +#define UART_LSR_TEMT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ +#define UART_LSR_BRK_ERROR_BITS 0x1E /* BI, FE, PE, OE bits */ + +static volatile void *uart8250_base; +static u32 uart8250_in_freq; +static u32 uart8250_baudrate; +static u32 uart8250_reg_width; +static u32 uart8250_reg_shift; + +static u32 get_reg(u32 num) +{ + u32 offset = num << uart8250_reg_shift; + + if (uart8250_reg_width == 1) + return readb(uart8250_base + offset); + else if (uart8250_reg_width == 2) + return readw(uart8250_base + offset); + else + return readl(uart8250_base + offset); +} + +static void set_reg(u32 num, u32 val) +{ + u32 offset = num << uart8250_reg_shift; + + if (uart8250_reg_width == 1) + writeb(val, uart8250_base + offset); + else if (uart8250_reg_width == 2) + writew(val, uart8250_base + offset); + else + writel(val, uart8250_base + offset); +} + +void uart8250_putc(char ch) +{ + while ((get_reg(UART_LSR_OFFSET) & UART_LSR_THRE) == 0); + + set_reg(UART_THR_OFFSET, ch); +} + +char uart8250_getc(void) +{ + if (get_reg(UART_LSR_OFFSET) & UART_LSR_DR) + return get_reg(UART_RBR_OFFSET); + return 0; +} + +int uart8250_init(unsigned long base, + u32 in_freq, u32 baudrate, + u32 reg_shift, u32 reg_width) +{ + u16 bdiv; + + uart8250_base = (volatile void *)base; + uart8250_reg_shift = reg_shift; + uart8250_reg_width = reg_width; + uart8250_in_freq = in_freq; + uart8250_baudrate = baudrate; + + bdiv = uart8250_in_freq / (16 * uart8250_baudrate); + + /* Disable all interrupts */ + set_reg(UART_IER_OFFSET, 0x00); + /* Enable DLAB */ + set_reg(UART_LCR_OFFSET, 0x80); + /* Set divisor low byte */ + set_reg(UART_DLL_OFFSET, bdiv & 0xff); + /* Set divisor high byte */ + set_reg(UART_DLM_OFFSET, (bdiv >> 8) & 0xff); + /* 8 bits, no parity, one stop bit */ + set_reg(UART_LCR_OFFSET, 0x03); + /* Enable FIFO */ + set_reg(UART_FCR_OFFSET, 0x01); + /* No modem control DTR RTS */ + set_reg(UART_MCR_OFFSET, 0x00); + /* Clear line status */ + get_reg(UART_LSR_OFFSET); + /* Read receive buffer */ + get_reg(UART_RBR_OFFSET); + /* Set scratchpad */ + set_reg(UART_SCR_OFFSET, 0x00); + + return 0; +} diff --git a/plat/common/sys/clint.c b/plat/common/sys/clint.c new file mode 100644 index 0000000..b6c6e9b --- /dev/null +++ b/plat/common/sys/clint.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +static u32 clint_ipi_hart_count; +static volatile void *clint_ipi_base; +static volatile u32 *clint_ipi; + +void clint_ipi_inject(u32 target_hart, u32 source_hart) +{ + if ((clint_ipi_hart_count <= target_hart) || + (clint_ipi_hart_count <= source_hart)) + return; + + /* Set CLINT IPI */ + __io_bw(); + atomic_raw_xchg_uint(&clint_ipi[target_hart], 1); +} + +void clint_ipi_sync(u32 target_hart, u32 source_hart) +{ + u32 target_ipi, incoming_ipi; + + if ((clint_ipi_hart_count <= target_hart) || + (clint_ipi_hart_count <= source_hart)) + return; + + /* Wait until target HART has handled IPI */ + incoming_ipi = 0; + while (1) { + target_ipi = readl(&clint_ipi[target_hart]); + if (!target_ipi) + break; + + __io_bw(); + incoming_ipi |= + atomic_raw_xchg_uint(&clint_ipi[source_hart], 0); + } + + if (incoming_ipi) { + __io_bw(); + atomic_raw_xchg_uint(&clint_ipi[source_hart], incoming_ipi); + } +} + +void clint_ipi_clear(u32 target_hart) +{ + if (clint_ipi_hart_count <= target_hart) + return; + + /* Clear CLINT IPI */ + __io_bw(); + atomic_raw_xchg_uint(&clint_ipi[target_hart], 0); +} + +int clint_warm_ipi_init(u32 target_hart) +{ + if (clint_ipi_hart_count <= target_hart || + !clint_ipi_base) + return -1; + + /* Clear CLINT IPI */ + clint_ipi_clear(target_hart); + + return 0; +} + +int clint_cold_ipi_init(unsigned long base, u32 hart_count) +{ + /* Figure-out CLINT IPI register address */ + clint_ipi_hart_count = hart_count; + clint_ipi_base = (void *)base; + clint_ipi = (u32 *)clint_ipi_base; + + return 0; +} + +static u32 clint_time_hart_count; +static volatile void *clint_time_base; +static volatile u64 *clint_time_val; +static volatile u64 *clint_time_cmp; + +u64 clint_timer_value(void) +{ + return readq_relaxed(clint_time_val); +} + +void clint_timer_event_stop(u32 target_hart) +{ + if (clint_time_hart_count <= target_hart) + return; + + /* Clear CLINT Time Compare */ + writeq_relaxed(-1ULL, &clint_time_cmp[target_hart]); +} + +void clint_timer_event_start(u32 target_hart, u64 next_event) +{ + if (clint_time_hart_count <= target_hart) + return; + + /* Program CLINT Time Compare */ + writeq_relaxed(next_event, &clint_time_cmp[target_hart]); +} + +int clint_warm_timer_init(u32 target_hart) +{ + if (clint_time_hart_count <= target_hart || + !clint_time_base) + return -1; + + /* Clear CLINT Time Compare */ + writeq_relaxed(-1ULL, &clint_time_cmp[target_hart]); + + return 0; +} + +int clint_cold_timer_init(unsigned long base, u32 hart_count) +{ + /* Figure-out CLINT Time register address */ + clint_time_hart_count = hart_count; + clint_time_base = (void *)base; + clint_time_val = (u64 *)(clint_time_base + 0xbff8); + clint_time_cmp = (u64 *)(clint_time_base + 0x4000); + + return 0; +} diff --git a/plat/common/sys/objects.mk b/plat/common/sys/objects.mk new file mode 100644 index 0000000..451adbb --- /dev/null +++ b/plat/common/sys/objects.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +plat-common-objs-$(PLAT_SYS_CLINT) += sys/clint.o diff --git a/plat/qemu/virt/config.mk b/plat/qemu/virt/config.mk new file mode 100644 index 0000000..3e33157 --- /dev/null +++ b/plat/qemu/virt/config.mk @@ -0,0 +1,31 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +# Essential defines required by SBI platform +plat-cppflags-y = -DPLAT_NAME="QEMU Virt Machine" +plat-cppflags-y+= -DPLAT_HART_COUNT=8 +plat-cppflags-y+= -DPLAT_HART_STACK_SIZE=8192 +plat-cppflags-y+= -DPLAT_TEXT_START=0x80000000 + +# Compiler flags +plat-cflags-y =-mabi=lp64 -march=rv64imafdc -mcmodel=medany +plat-asflags-y =-mabi=lp64 -march=rv64imafdc -mcmodel=medany +plat-ldflags-y = + +# Common drivers to enable +PLAT_IRQCHIP_PLIC=y +PLAT_SERIAL_UART8250=y +PLAT_SYS_CLINT=y + +# Blobs to build +FW_JUMP=y +FW_JUMP_ADDR=0x80200000 +FW_JUMP_FDT_OFFSET=0x2000000 +FW_PAYLOAD=y +FW_PAYLOAD_FDT_OFFSET=0x2000000 diff --git a/plat/qemu/virt/objects.mk b/plat/qemu/virt/objects.mk new file mode 100644 index 0000000..03ee2fe --- /dev/null +++ b/plat/qemu/virt/objects.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +plat-objs-y += platform.o diff --git a/plat/qemu/virt/platform.c b/plat/qemu/virt/platform.c new file mode 100644 index 0000000..e59cdfd --- /dev/null +++ b/plat/qemu/virt/platform.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +#define VIRT_TEST_ADDR 0x100000 + +#define VIRT_CLINT_ADDR 0x2000000 + +#define VIRT_PLIC_ADDR 0xc000000 +#define VIRT_PLIC_NUM_SOURCES 127 +#define VIRT_PLIC_NUM_PRIORITIES 7 + +#define VIRT_UART16550_ADDR 0x10000000 + +static int virt_cold_final_init(void) +{ + return plic_fdt_fixup(sbi_scratch_thishart_arg1_ptr(), "riscv,plic0"); +} + +static u32 virt_pmp_region_count(u32 target_hart) +{ + return 1; +} + +static int virt_pmp_region_info(u32 target_hart, u32 index, + ulong *prot, ulong *addr, ulong *log2size) +{ + int ret = 0; + + switch (index) { + case 0: + *prot = PMP_R | PMP_W | PMP_X; + *addr = 0; + *log2size = __riscv_xlen; + break; + default: + ret = -1; + break; + }; + + return ret; +} + +static int virt_console_init(void) +{ + return uart8250_init(VIRT_UART16550_ADDR, + 1843200, 115200, 0, 1); +} + +static int virt_cold_irqchip_init(void) +{ + return plic_cold_irqchip_init(VIRT_PLIC_ADDR, + VIRT_PLIC_NUM_SOURCES, + PLAT_HART_COUNT); +} + +static int virt_cold_ipi_init(void) +{ + return clint_cold_ipi_init(VIRT_CLINT_ADDR, + PLAT_HART_COUNT); +} + +static int virt_cold_timer_init(void) +{ + return clint_cold_timer_init(VIRT_CLINT_ADDR, + PLAT_HART_COUNT); +} + +static int virt_system_down(u32 type) +{ + /* For now nothing to do. */ + return 0; +} + +struct sbi_platform platform = { + .name = STRINGIFY(PLAT_NAME), + .features = SBI_PLATFORM_HAS_MMIO_TIMER_VALUE, + .hart_count = PLAT_HART_COUNT, + .hart_stack_size = PLAT_HART_STACK_SIZE, + .pmp_region_count = virt_pmp_region_count, + .pmp_region_info = virt_pmp_region_info, + .cold_final_init = virt_cold_final_init, + .console_putc = uart8250_putc, + .console_getc = uart8250_getc, + .console_init = virt_console_init, + .cold_irqchip_init = virt_cold_irqchip_init, + .warm_irqchip_init = plic_warm_irqchip_init, + .ipi_inject = clint_ipi_inject, + .ipi_sync = clint_ipi_sync, + .ipi_clear = clint_ipi_clear, + .warm_ipi_init = clint_warm_ipi_init, + .cold_ipi_init = virt_cold_ipi_init, + .timer_value = clint_timer_value, + .timer_event_stop = clint_timer_event_stop, + .timer_event_start = clint_timer_event_start, + .warm_timer_init = clint_warm_timer_init, + .cold_timer_init = virt_cold_timer_init, + .system_reboot = virt_system_down, + .system_shutdown = virt_system_down +}; diff --git a/plat/sifive/hifive_u540/config.mk b/plat/sifive/hifive_u540/config.mk new file mode 100644 index 0000000..fe5e040 --- /dev/null +++ b/plat/sifive/hifive_u540/config.mk @@ -0,0 +1,31 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +# Essential defines required by SBI platform +plat-cppflags-y = -DPLAT_NAME="SiFive HiFive U540" +plat-cppflags-y+= -DPLAT_HART_COUNT=1 +plat-cppflags-y+= -DPLAT_HART_STACK_SIZE=8192 +plat-cppflags-y+= -DPLAT_TEXT_START=0x80000000 + +# Compiler flags +plat-cflags-y =-mabi=lp64 -march=rv64imafdc -mcmodel=medany +plat-asflags-y =-mabi=lp64 -march=rv64imafdc -mcmodel=medany +plat-ldflags-y = + +# Common drivers to enable +PLAT_IRQCHIP_PLIC=y +PLAT_SERIAL_SIFIVE_UART=y +PLAT_SYS_CLINT=y + +# Blobs to build +FW_JUMP=y +FW_JUMP_ADDR=0x80200000 +FW_JUMP_FDT_OFFSET=0x2000000 +FW_PAYLOAD=y +FW_PAYLOAD_FDT_OFFSET=0x2000000 diff --git a/plat/sifive/hifive_u540/objects.mk b/plat/sifive/hifive_u540/objects.mk new file mode 100644 index 0000000..03ee2fe --- /dev/null +++ b/plat/sifive/hifive_u540/objects.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2018 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel +# +# SPDX-License-Identifier: BSD-2-Clause +# + +plat-objs-y += platform.o diff --git a/plat/sifive/hifive_u540/platform.c b/plat/sifive/hifive_u540/platform.c new file mode 100644 index 0000000..a4a401e --- /dev/null +++ b/plat/sifive/hifive_u540/platform.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +#define SIFIVE_U_SYS_CLK 1000000000 +#define SIFIVE_U_PERIPH_CLK (SIFIVE_U_SYS_CLK / 2) + +#define SIFIVE_U_CLINT_ADDR 0x2000000 + +#define SIFIVE_U_PLIC_ADDR 0xc000000 +#define SIFIVE_U_PLIC_NUM_SOURCES 0x35 +#define SIFIVE_U_PLIC_NUM_PRIORITIES 7 + +#define SIFIVE_U_UART0_ADDR 0x10013000 +#define SIFIVE_U_UART1_ADDR 0x10023000 + +static int sifive_u_cold_final_init(void) +{ + return plic_fdt_fixup(sbi_scratch_thishart_arg1_ptr(), "riscv,plic0"); +} + +static u32 sifive_u_pmp_region_count(u32 target_hart) +{ + return 1; +} + +static int sifive_u_pmp_region_info(u32 target_hart, u32 index, + ulong *prot, ulong *addr, ulong *log2size) +{ + int ret = 0; + + switch (index) { + case 0: + *prot = PMP_R | PMP_W | PMP_X; + *addr = 0; + *log2size = __riscv_xlen; + break; + default: + ret = -1; + break; + }; + + return ret; +} + +static int sifive_u_console_init(void) +{ + return sifive_uart_init(SIFIVE_U_UART0_ADDR, + SIFIVE_U_PERIPH_CLK, 115200); +} + +static int sifive_u_cold_irqchip_init(void) +{ + return plic_cold_irqchip_init(SIFIVE_U_PLIC_ADDR, + SIFIVE_U_PLIC_NUM_SOURCES, + PLAT_HART_COUNT); +} + +static int sifive_u_cold_ipi_init(void) +{ + return clint_cold_ipi_init(SIFIVE_U_CLINT_ADDR, + PLAT_HART_COUNT); +} + +static int sifive_u_cold_timer_init(void) +{ + return clint_cold_timer_init(SIFIVE_U_CLINT_ADDR, + PLAT_HART_COUNT); +} + +static int sifive_u_system_down(u32 type) +{ + /* For now nothing to do. */ + return 0; +} + +struct sbi_platform platform = { + .name = STRINGIFY(PLAT_NAME), + .features = SBI_PLATFORM_HAS_MMIO_TIMER_VALUE, + .hart_count = PLAT_HART_COUNT, + .hart_stack_size = PLAT_HART_STACK_SIZE, + .pmp_region_count = sifive_u_pmp_region_count, + .pmp_region_info = sifive_u_pmp_region_info, + .cold_final_init = sifive_u_cold_final_init, + .console_putc = sifive_uart_putc, + .console_getc = sifive_uart_getc, + .console_init = sifive_u_console_init, + .cold_irqchip_init = sifive_u_cold_irqchip_init, + .warm_irqchip_init = plic_warm_irqchip_init, + .ipi_inject = clint_ipi_inject, + .ipi_sync = clint_ipi_sync, + .ipi_clear = clint_ipi_clear, + .warm_ipi_init = clint_warm_ipi_init, + .cold_ipi_init = sifive_u_cold_ipi_init, + .timer_value = clint_timer_value, + .timer_event_stop = clint_timer_event_stop, + .timer_event_start = clint_timer_event_start, + .warm_timer_init = clint_warm_timer_init, + .cold_timer_init = sifive_u_cold_timer_init, + .system_reboot = sifive_u_system_down, + .system_shutdown = sifive_u_system_down +}; -- 2.7.4