diff options
Diffstat (limited to 'roms/opensbi')
224 files changed, 30074 insertions, 0 deletions
diff --git a/roms/opensbi/.clang-format b/roms/opensbi/.clang-format new file mode 100644 index 000000000..c80c0aa98 --- /dev/null +++ b/roms/opensbi/.clang-format @@ -0,0 +1,16 @@ +AlignConsecutiveAssignments: true +AlignEscapedNewlines: Left +AlignTrailingComments: true +AllowShortFunctionsOnASingleLine: None +BraceWrapping: + AfterFunction: true +BreakBeforeBraces: Custom +BreakStringLiterals: false +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +IndentWidth: 8 +ReflowComments: false +SortIncludes: false +SpacesInContainerLiterals: false +TabWidth: 8 +UseTab: Always diff --git a/roms/opensbi/.gitignore b/roms/opensbi/.gitignore new file mode 100644 index 000000000..534ad6477 --- /dev/null +++ b/roms/opensbi/.gitignore @@ -0,0 +1,11 @@ +# Object files +*.o +*.a +*.dep + +#Build & install directories +build/ +install/ + +# Development friendly files +tags diff --git a/roms/opensbi/CONTRIBUTORS.md b/roms/opensbi/CONTRIBUTORS.md new file mode 100644 index 000000000..afae1251d --- /dev/null +++ b/roms/opensbi/CONTRIBUTORS.md @@ -0,0 +1,27 @@ + +List of OpenSBI Contributors (Alphabetically sorted) +==================================================== + +* **[Western Digital Corporation](https://www.wdc.com/)** + * Project initiator and maintainer + * Copyright (c) 2019 Western Digital Corporation or its affiliates + +* Alistair Francis <alistair@alistair23.me> + +* Andreas Schwab <schwab@suse.de> + +* Anup Patel <anup.patel@wdc.com> + +* Atish Patra <atish.patra@wdc.com> + +* Bin Meng <bmeng.cn@gmail.com> + +* Damien Le Moal <damien.lemoal@wdc.com> + +* Karsten Merker <merker@debian.org> + +* Nick Kossifidis <mickflemm@gmail.com> + +* Shawn Chang <citypw@gmail.com> + +* Xiang Wang <wxjstz@126.com> diff --git a/roms/opensbi/COPYING.BSD b/roms/opensbi/COPYING.BSD new file mode 100644 index 000000000..26972c8a4 --- /dev/null +++ b/roms/opensbi/COPYING.BSD @@ -0,0 +1,25 @@ +The 2-Clause BSD License +SPDX short identifier: BSD-2-Clause + +Copyright (c) 2019 Western Digital Corporation or its affiliates and other +contributors. + +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 HOLDER 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. diff --git a/roms/opensbi/Makefile b/roms/opensbi/Makefile new file mode 100644 index 000000000..d6f097d30 --- /dev/null +++ b/roms/opensbi/Makefile @@ -0,0 +1,493 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +# Select Make Options: +# o Do not use make's built-in rules +# o Do not print "Entering directory ..."; +MAKEFLAGS += -r --no-print-directory + +# Readlink -f requires GNU readlink +ifeq ($(shell uname -s),Darwin) +READLINK ?= greadlink +else +READLINK ?= readlink +endif + +# 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 +install_root_dir_default=$(CURDIR)/install +ifdef I + install_root_dir=$(shell $(READLINK) -f $(I)) +else + install_root_dir=$(install_root_dir_default)/usr +endif +ifeq ($(install_root_dir),$(CURDIR)) +$(error Install root directory is same as source directory.) +endif +ifeq ($(install_root_dir),$(build_dir)) +$(error Install root directory is same as build directory.) +endif +ifdef PLATFORM_DIR + platform_dir_path=$(shell $(READLINK) -f $(PLATFORM_DIR)) + ifdef PLATFORM + platform_parent_dir=$(platform_dir_path) + else + PLATFORM=$(shell basename $(platform_dir_path)) + platform_parent_dir=$(subst $(PLATFORM),,$(platform_dir_path)) + endif +else + platform_parent_dir=$(src_dir)/platform +endif + +# Check if verbosity is ON for build process +CMD_PREFIX_DEFAULT := @ +ifeq ($(V), 1) + CMD_PREFIX := +else + CMD_PREFIX := $(CMD_PREFIX_DEFAULT) +endif + +# Setup path of directories +export platform_subdir=$(PLATFORM) +export platform_src_dir=$(platform_parent_dir)/$(platform_subdir) +export platform_build_dir=$(build_dir)/platform/$(platform_subdir) +export include_dir=$(CURDIR)/include +export libsbi_dir=$(CURDIR)/lib/sbi +export libsbiutils_dir=$(CURDIR)/lib/utils +export firmware_dir=$(CURDIR)/firmware + +# Find library version +OPENSBI_VERSION_MAJOR=`grep "define OPENSBI_VERSION_MAJOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MAJOR.*\([0-9][0-9]*\)/\1/'` +OPENSBI_VERSION_MINOR=`grep "define OPENSBI_VERSION_MINOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MINOR.*\([0-9][0-9]*\)/\1/'` +OPENSBI_VERSION_GIT=$(shell if [ -d $(src_dir)/.git ]; then git describe 2> /dev/null; fi) + +# Setup compilation commands +ifdef CROSS_COMPILE +CC = $(CROSS_COMPILE)gcc +CPP = $(CROSS_COMPILE)cpp +AR = $(CROSS_COMPILE)ar +LD = $(CROSS_COMPILE)ld +OBJCOPY = $(CROSS_COMPILE)objcopy +else +CC ?= gcc +CPP ?= cpp +AR ?= ar +LD ?= ld +OBJCOPY ?= objcopy +endif +AS = $(CC) +DTC = dtc + +# Guess the compillers xlen +OPENSBI_CC_XLEN := $(shell TMP=`$(CC) -dumpmachine | sed 's/riscv\([0-9][0-9]\).*/\1/'`; echo $${TMP}) +OPENSBI_CC_ABI := $(shell TMP=`$(CC) -v 2>&1 | sed -n 's/.*\(with\-abi=\([a-zA-Z0-9]*\)\).*/\2/p'`; echo $${TMP}) +OPENSBI_CC_ISA := $(shell TMP=`$(CC) -v 2>&1 | sed -n 's/.*\(with\-arch=\([a-zA-Z0-9]*\)\).*/\2/p'`; echo $${TMP}) + +# Setup platform XLEN +ifndef PLATFORM_RISCV_XLEN + ifeq ($(OPENSBI_CC_XLEN), 32) + PLATFORM_RISCV_XLEN = 32 + else + PLATFORM_RISCV_XLEN = 64 + endif +endif + +# Setup list of objects.mk files +ifdef PLATFORM +platform-object-mks=$(shell if [ -d $(platform_src_dir)/ ]; then find $(platform_src_dir) -iname "objects.mk" | sort -r; fi) +endif +libsbi-object-mks=$(shell if [ -d $(libsbi_dir) ]; then find $(libsbi_dir) -iname "objects.mk" | sort -r; fi) +libsbiutils-object-mks=$(shell if [ -d $(libsbiutils_dir) ]; then find $(libsbiutils_dir) -iname "objects.mk" | sort -r; fi) +firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir) -iname "objects.mk" | sort -r; fi) + +# Include platform specifig config.mk +ifdef PLATFORM +include $(platform_src_dir)/config.mk +endif + +# Include all object.mk files +ifdef PLATFORM +include $(platform-object-mks) +endif +include $(libsbi-object-mks) +include $(libsbiutils-object-mks) +include $(firmware-object-mks) + +# Setup list of objects +libsbi-objs-path-y=$(foreach obj,$(libsbi-objs-y),$(build_dir)/lib/sbi/$(obj)) +libsbiutils-objs-path-y=$(foreach obj,$(libsbiutils-objs-y),$(build_dir)/lib/utils/$(obj)) +ifdef PLATFORM +platform-objs-path-y=$(foreach obj,$(platform-objs-y),$(platform_build_dir)/$(obj)) +firmware-bins-path-y=$(foreach bin,$(firmware-bins-y),$(platform_build_dir)/firmware/$(bin)) +endif +firmware-elfs-path-y=$(firmware-bins-path-y:.bin=.elf) +firmware-objs-path-y=$(firmware-bins-path-y:.bin=.o) + +# Setup list of deps files for objects +deps-y=$(platform-objs-path-y:.o=.dep) +deps-y+=$(libsbi-objs-path-y:.o=.dep) +deps-y+=$(libsbiutils-objs-path-y:.o=.dep) +deps-y+=$(firmware-objs-path-y:.o=.dep) + +# Setup platform ABI, ISA and Code Model +ifndef PLATFORM_RISCV_ABI + ifneq ($(PLATFORM_RISCV_TOOLCHAIN_DEFAULT), 1) + ifeq ($(PLATFORM_RISCV_XLEN), 32) + PLATFORM_RISCV_ABI = ilp$(PLATFORM_RISCV_XLEN) + else + PLATFORM_RISCV_ABI = lp$(PLATFORM_RISCV_XLEN) + endif + else + PLATFORM_RISCV_ABI = $(OPENSBI_CC_ABI) + endif +endif +ifndef PLATFORM_RISCV_ISA + ifneq ($(PLATFORM_RISCV_TOOLCHAIN_DEFAULT), 1) + PLATFORM_RISCV_ISA = rv$(PLATFORM_RISCV_XLEN)imafdc + else + PLATFORM_RISCV_ISA = $(OPENSBI_CC_ISA) + endif +endif +ifndef PLATFORM_RISCV_CODE_MODEL + PLATFORM_RISCV_CODE_MODEL = medany +endif + +# Setup install directories +ifdef INSTALL_INCLUDE_PATH + install_include_path=$(INSTALL_INCLUDE_PATH) +else + install_include_path=include +endif +ifdef INSTALL_LIB_PATH + install_lib_path=$(INSTALL_LIB_PATH) +else + ifneq ($(origin INSTALL_LIB_SUBDIR), undefined) + install_lib_subdir=$(INSTALL_LIB_SUBDIR) + else + install_lib_subdir=$(PLATFORM_RISCV_ABI) + endif + install_lib_path=lib$(subst 32,,$(PLATFORM_RISCV_XLEN))/$(install_lib_subdir) +endif +ifdef INSTALL_FIRMWARE_PATH + install_firmware_path=$(INSTALL_FIRMWARE_PATH) +else + install_firmware_path=share/opensbi/$(PLATFORM_RISCV_ABI) +endif +ifdef INSTALL_DOCS_PATH + install_docs_path=$(INSTALL_DOCS_PATH) +else + install_docs_path=share/opensbi/docs +endif + +# Setup compilation commands flags +GENFLAGS = -I$(platform_src_dir)/include +GENFLAGS += -I$(include_dir) +ifneq ($(OPENSBI_VERSION_GIT),) +GENFLAGS += -DOPENSBI_VERSION_GIT="\"$(OPENSBI_VERSION_GIT)\"" +endif +GENFLAGS += $(libsbiutils-genflags-y) +GENFLAGS += $(platform-genflags-y) +GENFLAGS += $(firmware-genflags-y) + +CFLAGS = -g -Wall -Werror -ffreestanding -nostdlib -fno-strict-aliasing -O2 +CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls +CFLAGS += -mno-save-restore -mstrict-align +CFLAGS += -mabi=$(PLATFORM_RISCV_ABI) -march=$(PLATFORM_RISCV_ISA) +CFLAGS += -mcmodel=$(PLATFORM_RISCV_CODE_MODEL) +CFLAGS += $(GENFLAGS) +CFLAGS += $(platform-cflags-y) +CFLAGS += $(firmware-cflags-y) +CFLAGS += -fno-pie -no-pie + +CPPFLAGS += $(GENFLAGS) +CPPFLAGS += $(platform-cppflags-y) +CPPFLAGS += $(firmware-cppflags-y) + +ASFLAGS = -g -Wall -nostdlib -D__ASSEMBLY__ +ASFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls +ASFLAGS += -mno-save-restore -mstrict-align +ASFLAGS += -mabi=$(PLATFORM_RISCV_ABI) -march=$(PLATFORM_RISCV_ISA) +ASFLAGS += -mcmodel=$(PLATFORM_RISCV_CODE_MODEL) +ASFLAGS += $(GENFLAGS) +ASFLAGS += $(platform-asflags-y) +ASFLAGS += $(firmware-asflags-y) + +ARFLAGS = rcs + +ELFFLAGS += -Wl,--build-id=none -N -static-libgcc -lgcc +ELFFLAGS += $(platform-ldflags-y) +ELFFLAGS += $(firmware-ldflags-y) + +MERGEFLAGS += -r +MERGEFLAGS += -b elf$(PLATFORM_RISCV_XLEN)-littleriscv +MERGEFLAGS += -m elf$(PLATFORM_RISCV_XLEN)lriscv + +DTSCPPFLAGS = $(CPPFLAGS) -nostdinc -nostdlib -fno-builtin -D__DTS__ -x assembler-with-cpp + +# Setup functions for compilation +define dynamic_flags +-I$(shell dirname $(2)) -D__OBJNAME__=$(subst -,_,$(shell basename $(1) .o)) +endef +merge_objs = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " MERGE $(subst $(build_dir)/,,$(1))"; \ + $(LD) $(MERGEFLAGS) $(2) -o $(1) +merge_deps = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " MERGE-DEP $(subst $(build_dir)/,,$(1))"; \ + cat $(2) > $(1) +copy_file = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " COPY $(subst $(build_dir)/,,$(1))"; \ + cp -f $(2) $(1) +inst_file = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " INSTALL $(subst $(install_root_dir)/,,$(1))"; \ + cp -f $(2) $(1) +inst_file_list = $(CMD_PREFIX)if [ ! -z "$(4)" ]; then \ + mkdir -p $(1)/$(3); \ + for file in $(4) ; do \ + rel_file=`echo $$file | sed -e 's@$(2)/$(subst $(install_firmware_path),platform,$(3))@@'`; \ + dest_file=$(1)"/"$(3)"/"`echo $$rel_file`; \ + dest_dir=`dirname $$dest_file`; \ + echo " INSTALL "$(3)"/"`echo $$rel_file`; \ + mkdir -p $$dest_dir; \ + cp -f $$file $$dest_file; \ + done \ + fi +inst_header_dir = $(CMD_PREFIX)mkdir -p $(1); \ + echo " INSTALL $(subst $(install_root_dir)/,,$(1))"; \ + cp -rf $(2) $(1) +compile_cpp = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " CPP $(subst $(build_dir)/,,$(1))"; \ + $(CPP) $(CPPFLAGS) -x c $(2) | grep -v "\#" > $(1) +compile_cc_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " CC-DEP $(subst $(build_dir)/,,$(1))"; \ + printf %s `dirname $(1)`/ > $(1) && \ + $(CC) $(CFLAGS) $(call dynamic_flags,$(1),$(2)) \ + -MM $(2) >> $(1) || rm -f $(1) +compile_cc = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " CC $(subst $(build_dir)/,,$(1))"; \ + $(CC) $(CFLAGS) $(call dynamic_flags,$(1),$(2)) -c $(2) -o $(1) +compile_as_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " AS-DEP $(subst $(build_dir)/,,$(1))"; \ + printf %s `dirname $(1)`/ > $(1) && \ + $(AS) $(ASFLAGS) $(call dynamic_flags,$(1),$(2)) \ + -MM $(2) >> $(1) || rm -f $(1) +compile_as = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " AS $(subst $(build_dir)/,,$(1))"; \ + $(AS) $(ASFLAGS) $(call dynamic_flags,$(1),$(2)) -c $(2) -o $(1) +compile_elf = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " ELF $(subst $(build_dir)/,,$(1))"; \ + $(CC) $(CFLAGS) $(3) $(ELFFLAGS) -Wl,-T$(2) -o $(1) +compile_ar = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " AR $(subst $(build_dir)/,,$(1))"; \ + $(AR) $(ARFLAGS) $(1) $(2) +compile_objcopy = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " OBJCOPY $(subst $(build_dir)/,,$(1))"; \ + $(OBJCOPY) -S -O binary $(2) $(1) +compile_dts = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " DTC $(subst $(build_dir)/,,$(1))"; \ + $(CPP) $(DTSCPPFLAGS) $(2) | $(DTC) -O dtb -i `dirname $(2)` -o $(1) +compile_d2c = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " D2C $(subst $(build_dir)/,,$(1))"; \ + $(if $($(2)-varalign-$(3)),$(eval D2C_ALIGN_BYTES := $($(2)-varalign-$(3))),$(eval D2C_ALIGN_BYTES := $(4))) \ + $(if $($(2)-varprefix-$(3)),$(eval D2C_NAME_PREFIX := $($(2)-varprefix-$(3))),$(eval D2C_NAME_PREFIX := $(5))) \ + $(if $($(2)-padding-$(3)),$(eval D2C_PADDING_BYTES := $($(2)-padding-$(3))),$(eval D2C_PADDING_BYTES := 0)) \ + $(src_dir)/scripts/d2c.sh -i $(6) -a $(D2C_ALIGN_BYTES) -p $(D2C_NAME_PREFIX) -t $(D2C_PADDING_BYTES) > $(1) +compile_gen_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \ + echo " GEN-DEP $(subst $(build_dir)/,,$(1))"; \ + echo "$(1:.dep=$(2)): $(3)" >> $(1) + +targets-y = $(build_dir)/lib/libsbi.a +targets-y += $(build_dir)/lib/libsbiutils.a +ifdef PLATFORM +targets-y += $(platform_build_dir)/lib/libplatsbi.a +endif +targets-y += $(firmware-bins-path-y) + +# Default rule "make" should always be first rule +.PHONY: all +all: $(targets-y) + +# Preserve all intermediate files +.SECONDARY: + +$(build_dir)/lib/libsbi.a: $(libsbi-objs-path-y) + $(call compile_ar,$@,$^) + +$(build_dir)/lib/libsbiutils.a: $(libsbi-objs-path-y) $(libsbiutils-objs-path-y) + $(call compile_ar,$@,$^) + +$(platform_build_dir)/lib/libplatsbi.a: $(libsbi-objs-path-y) $(libsbiutils-objs-path-y) $(platform-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,$@,$<) + +$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf + $(call compile_objcopy,$@,$<) + +$(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a + $(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a) + +$(platform_build_dir)/%.ld: $(src_dir)/%.ldS + $(call compile_cpp,$@,$<) + +$(platform_build_dir)/%.dep: $(platform_src_dir)/%.c + $(call compile_cc_dep,$@,$<) + +$(platform_build_dir)/%.o: $(platform_src_dir)/%.c + $(call compile_cc,$@,$<) + +$(platform_build_dir)/%.o: $(platform_build_dir)/%.c + $(call compile_cc,$@,$<) + +$(platform_build_dir)/%.dep: $(platform_src_dir)/%.S + $(call compile_as_dep,$@,$<) + +$(platform_build_dir)/%.o: $(platform_src_dir)/%.S + $(call compile_as,$@,$<) + +$(platform_build_dir)/%.dep: $(platform_src_dir)/%.dts + $(call compile_gen_dep,$@,.dtb,$<) + $(call compile_gen_dep,$@,.c,$(@:.dep=.dtb)) + $(call compile_gen_dep,$@,.o,$(@:.dep=.c)) + +$(platform_build_dir)/%.c: $(platform_build_dir)/%.dtb + $(call compile_d2c,$@,platform,$(subst .dtb,.o,$(subst /,-,$(subst $(platform_build_dir)/,,$<))),16,dt,$<) + +$(platform_build_dir)/%.dtb: $(platform_src_dir)/%.dts + $(call compile_dts,$@,$<) + +$(platform_build_dir)/%.dep: $(src_dir)/%.c + $(call compile_cc_dep,$@,$<) + +$(platform_build_dir)/%.o: $(src_dir)/%.c + $(call compile_cc,$@,$<) + +$(platform_build_dir)/%.dep: $(src_dir)/%.S + $(call compile_as_dep,$@,$<) + +$(platform_build_dir)/%.o: $(src_dir)/%.S + $(call compile_as,$@,$<) + +# Rule for "make docs" +$(build_dir)/docs/latex/refman.pdf: $(build_dir)/docs/latex/refman.tex + $(CMD_PREFIX)mkdir -p $(build_dir)/docs + $(CMD_PREFIX)$(MAKE) -C $(build_dir)/docs/latex +$(build_dir)/docs/latex/refman.tex: $(build_dir)/docs/doxygen.cfg + $(CMD_PREFIX)mkdir -p $(build_dir)/docs + $(CMD_PREFIX)doxygen $(build_dir)/docs/doxygen.cfg +$(build_dir)/docs/doxygen.cfg: $(src_dir)/docs/doxygen.cfg + $(CMD_PREFIX)mkdir -p $(build_dir)/docs + $(CMD_PREFIX)cat docs/doxygen.cfg | sed -e "s#@@SRC_DIR@@#$(src_dir)#" -e "s#@@BUILD_DIR@@#$(build_dir)#" -e "s#@@OPENSBI_MAJOR@@#$(OPENSBI_VERSION_MAJOR)#" -e "s#@@OPENSBI_MINOR@@#$(OPENSBI_VERSION_MINOR)#" > $(build_dir)/docs/doxygen.cfg +.PHONY: docs +docs: $(build_dir)/docs/latex/refman.pdf + +# Dependency files should only be included after default Makefile rules +# 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) + +# Include external dependency of firmwares after default Makefile rules +include $(src_dir)/firmware/external_deps.mk + +# Convenient "make run" command for emulated platforms +.PHONY: run +run: all +ifneq ($(platform-runcmd),) + $(platform-runcmd) $(RUN_ARGS) +else +ifdef PLATFORM + @echo "Platform $(PLATFORM) doesn't specify a run command" + @false +else + @echo Run command only available when targeting a platform + @false +endif +endif + +install_targets-y = install_libsbi +install_targets-y += install_libsbiutils +ifdef PLATFORM +install_targets-y += install_libplatsbi +install_targets-y += install_firmwares +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_root_dir)/$(install_include_path),$(include_dir)/sbi) + $(call inst_file,$(install_root_dir)/$(install_lib_path)/libsbi.a,$(build_dir)/lib/libsbi.a) + +.PHONY: install_libsbiutils +install_libsbiutils: $(build_dir)/lib/libsbiutils.a + $(call inst_header_dir,$(install_root_dir)/$(install_include_path),$(include_dir)/sbi_utils) + $(call inst_file,$(install_root_dir)/$(install_lib_path)/libsbiutils.a,$(build_dir)/lib/libsbiutils.a) + +.PHONY: install_libplatsbi +install_libplatsbi: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a + $(call inst_file,$(install_root_dir)/$(install_lib_path)/opensbi/$(platform_subdir)/lib/libplatsbi.a,$(platform_build_dir)/lib/libplatsbi.a) + +.PHONY: install_firmwares +install_firmwares: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a $(firmware-bins-path-y) + $(call inst_file_list,$(install_root_dir),$(build_dir),$(install_firmware_path)/$(platform_subdir)/firmware,$(firmware-elfs-path-y)) + $(call inst_file_list,$(install_root_dir),$(build_dir),$(install_firmware_path)/$(platform_subdir)/firmware,$(firmware-bins-path-y)) + +.PHONY: install_docs +install_docs: $(build_dir)/docs/latex/refman.pdf + $(call inst_file,$(install_root_dir)/$(install_docs_path)/refman.pdf,$(build_dir)/docs/latex/refman.pdf) + +# Rule for "make clean" +.PHONY: clean +clean: + $(CMD_PREFIX)mkdir -p $(build_dir) + $(if $(V), @echo " RM $(build_dir)/*.o") + $(CMD_PREFIX)find $(build_dir) -type f -name "*.o" -exec rm -rf {} + + $(if $(V), @echo " RM $(build_dir)/*.a") + $(CMD_PREFIX)find $(build_dir) -type f -name "*.a" -exec rm -rf {} + + $(if $(V), @echo " RM $(build_dir)/*.elf") + $(CMD_PREFIX)find $(build_dir) -type f -name "*.elf" -exec rm -rf {} + + $(if $(V), @echo " RM $(build_dir)/*.bin") + $(CMD_PREFIX)find $(build_dir) -type f -name "*.bin" -exec rm -rf {} + + $(if $(V), @echo " RM $(build_dir)/*.dtb") + $(CMD_PREFIX)find $(build_dir) -type f -name "*.dtb" -exec rm -rf {} + + +# Rule for "make distclean" +.PHONY: distclean +distclean: clean + $(CMD_PREFIX)mkdir -p $(build_dir) + $(if $(V), @echo " RM $(build_dir)/*.dep") + $(CMD_PREFIX)find $(build_dir) -type f -name "*.dep" -exec rm -rf {} + +ifeq ($(build_dir),$(CURDIR)/build) + $(if $(V), @echo " RM $(build_dir)") + $(CMD_PREFIX)rm -rf $(build_dir) +endif +ifeq ($(install_root_dir),$(install_root_dir_default)/usr) + $(if $(V), @echo " RM $(install_root_dir_default)") + $(CMD_PREFIX)rm -rf $(install_root_dir_default) +endif diff --git a/roms/opensbi/README.md b/roms/opensbi/README.md new file mode 100644 index 000000000..03c02fb1c --- /dev/null +++ b/roms/opensbi/README.md @@ -0,0 +1,286 @@ +RISC-V Open Source Supervisor Binary Interface (OpenSBI) +======================================================== + +Copyright and License +--------------------- + +The OpenSBI project is copyright (c) 2019 Western Digital Corporation +or its affiliates and other contributors. + +It is distributed under the terms of the BSD 2-clause license +("Simplified BSD License" or "FreeBSD License", SPDX: *BSD-2-Clause*). +A copy of this license with OpenSBI copyright can be found in the file +[COPYING.BSD]. + +All source files in OpenSBI contain the 2-Clause BSD license SPDX short +identifier in place 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 available on the [SPDX] web site. + +OpenSBI source code also contains code reused from other projects as listed +below. The original license text of these projects is included in the source +files where the reused code is present. + +* The libfdt source code is disjunctively dual licensed + (GPL-2.0+ OR BSD-2-Clause). Some of this project code is used in OpenSBI + under the terms of the BSD 2-Clause license. Any contributions to this + code must be made under the terms of both licenses. + +See also the [third party notices] file for more information. + +Introduction +------------ + +The **RISC-V Supervisor Binary Interface (SBI)** is the recommended interface +between: + +1. A platform-specific firmware running in M-mode and a bootloader, a + hypervisor or a general-purpose OS executing in S-mode or HS-mode. +2. A hypervisor running in HS-mode and a bootloader or a general-purpose OS + executing in VS-mode. + +The *RISC-V SBI specification* is maintained as an independent project by the +RISC-V Foundation on [Github]. + +The goal of the OpenSBI project is to provide an open-source reference +implementation of the RISC-V SBI specifications for platform-specific firmwares +executing in M-mode (case 1 mentioned above). An OpenSBI implementation can be +easily extended by RISC-V platform and system-on-chip vendors to fit a +particular hardware configuration. + +The main component of OpenSBI is provided in the form of a platform-independent +static library **libsbi.a** implementing the SBI interface. A firmware or +bootloader implementation can link against this library to ensure conformance +with the SBI interface specifications. *libsbi.a* also defines an interface for +integrating with platform-specific operations provided by the platform firmware +implementation (e.g. console access functions, inter-processor interrupt +control, etc). + +To illustrate the use of the *libsbi.a* library, OpenSBI also provides a set of +platform-specific support examples. For each example, a platform-specific +static library *libplatsbi.a* can be compiled. This library implements +SBI call processing by integrating *libsbi.a* with the necessary +platform-dependent hardware manipulation functions. For all supported platforms, +OpenSBI also provides several runtime firmware examples built using the platform +*libplatsbi.a*. These example firmwares can be used to replace the legacy +*riscv-pk* bootloader (aka BBL) and enable the use of well-known bootloaders +such as [U-Boot]. + +Supported SBI version +--------------------- +Currently, OpenSBI fully supports SBI specification *v0.2*. OpenSBI also +supports Hart State Management (HSM) SBI extension starting from OpenSBI v0.7. +HSM extension allows S-mode software to boot all the harts a defined order +rather than legacy method of random booting of harts. As a result, many +required features such as CPU hotplug, kexec/kdump can also be supported easily +in S-mode. HSM extension in OpenSBI is implemented in a non-backward compatible +manner to reduce the maintenance burden and avoid confusion. That's why, any +S-mode software using OpenSBI will not be able to boot more than 1 hart if HSM +extension is not supported in S-mode. + +Linux kernel already supports SBI v0.2 and HSM SBI extension starting from +**v5.7-rc1**. If you are using an Linux kernel older than **5.7-rc1** or any +other S-mode software without HSM SBI extension, you should stick to OpenSBI +v0.6 to boot all the harts. For a UMP systems, it doesn't matter. + +N.B. Any S-mode boot loader (i.e. U-Boot) doesn't need to support HSM extension, +as it doesn't need to boot all the harts. The operating system should be +capable enough to bring up all other non-booting harts using HSM extension. + +Required Toolchain +------------------ + +OpenSBI can be compiled natively or cross-compiled on a x86 host. For +cross-compilation, you can build your own toolchain or just download +a prebuilt one from the [Bootlin toolchain repository]. + +Please note that only a 64-bit version of the toolchain is available in +the Bootlin toolchain repository for now. + +Building and Installing the OpenSBI Platform-Independent Library +---------------------------------------------------------------- + +The OpenSBI platform-independent static library *libsbi.a* can be compiled +natively or it can be cross-compiled on a host with a different base +architecture than RISC-V. + +For cross-compiling, the environment variable *CROSS_COMPILE* must be defined +to specify the name prefix of the RISC-V compiler toolchain executables, e.g. +*riscv64-unknown-elf-* if the gcc executable used is *riscv64-unknown-elf-gcc*. + +To build *libsbi.a* simply execute: +``` +make +``` + +All compiled binaries as well as the resulting *libsbi.a* static library file +will be placed in the *build/lib* directory. To specify an alternate build root +directory path, run: +``` +make O=<build_directory> +``` + +To generate files to be installed for using *libsbi.a* in other projects, run: +``` +make install +``` + +This will create the *install* directory with all necessary include files +copied under the *install/include* directory and the library file copied into +the *install/lib* directory. To specify an alternate installation root +directory path, run: +``` +make I=<install_directory> install +``` + +Building and Installing a Reference Platform Static Library and Firmware +------------------------------------------------------------------------ + +When the *PLATFORM=<platform_subdir>* argument is specified on the make command +line, the platform-specific static library *libplatsbi.a* and firmware examples +are built for the platform *<platform_subdir>* present in the directory +*platform* in the OpenSBI top directory. For example, to compile the platform +library and the firmware examples for the QEMU RISC-V *virt* machine, +*<platform_subdir>* should be *generic*. + +To build *libsbi.a*, *libplatsbi.a* and the firmware for one of the supported +platforms, run: +``` +make PLATFORM=<platform_subdir> +``` + +An alternate build directory path can also be specified: +``` +make PLATFORM=<platform_subdir> O=<build_directory> +``` + +The platform-specific library *libplatsbi.a* will be generated in the +*build/platform/<platform_subdir>/lib* directory. The platform firmware files +will be under the *build/platform/<platform_subdir>/firmware* directory. +The compiled firmwares will be available in two different formats: an ELF file +and an expanded image file. + +To install *libsbi.a*, *libplatsbi.a*, and the compiled firmwares, run: +``` +make PLATFORM=<platform_subdir> install +``` + +This will copy the compiled platform-specific libraries and firmware files +under the *install/platform/<platform_subdir>/* directory. An alternate +install root directory path can be specified as follows: +``` +make PLATFORM=<platform_subdir> I=<install_directory> install +``` + +In addition, platform-specific configuration options can be specified with the +top-level make command line. These options, such as *PLATFORM_<xyz>* or +*FW_<abc>*, are platform-specific and described in more details in the +*docs/platform/<platform_name>.md* files and +*docs/firmware/<firmware_name>.md* files. + +Building 32-bit / 64-bit OpenSBI Images +--------------------------------------- +By default, building OpenSBI generates 32-bit or 64-bit images based on the +supplied RISC-V cross-compile toolchain. For example if *CROSS_COMPILE* is set +to *riscv64-unknown-elf-*, 64-bit OpenSBI images will be generated. If building +32-bit OpenSBI images, *CROSS_COMPILE* should be set to a toolchain that is +pre-configured to generate 32-bit RISC-V codes, like *riscv32-unknown-elf-*. + +However it's possible to explicitly specify the image bits we want to build with +a given RISC-V toolchain. This can be done by setting the environment variable +*PLATFORM_RISCV_XLEN* to the desired width, for example: + +``` +export CROSS_COMPILE=riscv64-unknown-elf- +export PLATFORM_RISCV_XLEN=32 +``` + +will generate 32-bit OpenSBI images. And vice vesa. + +Contributing to OpenSBI +----------------------- + +The OpenSBI project encourages and welcomes contributions. Contributions should +follow the rules described in the OpenSBI [Contribution Guideline] document. +In particular, all patches sent should contain a Signed-off-by tag. + +The [Contributors List] document provides a list of individuals and +organizations actively contributing to the OpenSBI project. + +Documentation +------------- + +Detailed documentation of various aspects of OpenSBI can be found under the +*docs* directory. The documentation covers the following topics. + +* [Contribution Guideline]: Guideline for contributing code to OpenSBI project +* [Library Usage]: API documentation of OpenSBI static library *libsbi.a* +* [Platform Requirements]: Requirements for using OpenSBI on a platform +* [Platform Support Guide]: Guideline for implementing support for new platforms +* [Platform Documentation]: Documentation of the platforms currently supported. +* [Firmware Documentation]: Documentation for the different types of firmware + examples build supported by OpenSBI. +* [Domain Support]: Documentation for the OpenSBI domain support which helps + users achieve system-level partitioning using OpenSBI. + +OpenSBI source code is also well documented. For source level documentation, +doxygen style is used. Please refer to the [Doxygen manual] for details on this +format. + +Doxygen can be installed on Linux distributions using *.deb* packages using +the following command. +``` +sudo apt-get install doxygen doxygen-latex doxygen-doc doxygen-gui graphviz +``` + +For *.rpm* based Linux distributions, the following commands can be used. +``` +sudo yum install doxygen doxygen-latex doxywizard graphviz +``` +or +``` +sudo yum install doxygen doxygen-latex doxywizard graphviz +``` + +To build a consolidated *refman.pdf* of all documentation, run: +``` +make docs +``` +or +``` +make O=<build_directory> docs +``` + +the resulting *refman.pdf* will be available under the directory +*<build_directory>/docs/latex*. To install this file, run: +``` +make install_docs +``` +or +``` +make I=<install_directory> install_docs +``` + +*refman.pdf* will be installed under *<install_directory>/docs*. + +[Github]: https://github.com/riscv/riscv-sbi-doc +[U-Boot]: https://www.denx.de/wiki/U-Boot/SourceCode +[Bootlin toolchain repository]: https://toolchains.bootlin.com/ +[COPYING.BSD]: COPYING.BSD +[SPDX]: http://spdx.org/licenses/ +[Contribution Guideline]: docs/contributing.md +[Contributors List]: CONTRIBUTORS.md +[Library Usage]: docs/library_usage.md +[Platform Requirements]: docs/platform_requirements.md +[Platform Support Guide]: docs/platform_guide.md +[Platform Documentation]: docs/platform/platform.md +[Firmware Documentation]: docs/firmware/fw.md +[Domain Support]: docs/domain_support.md +[Doxygen manual]: http://www.doxygen.nl/manual/index.html +[Kendryte standalone SDK]: https://github.com/kendryte/kendryte-standalone-sdk +[third party notices]: ThirdPartyNotices.md diff --git a/roms/opensbi/ThirdPartyNotices.md b/roms/opensbi/ThirdPartyNotices.md new file mode 100644 index 000000000..1162d4361 --- /dev/null +++ b/roms/opensbi/ThirdPartyNotices.md @@ -0,0 +1,18 @@ +Copyright (c) 2019 Western Digital Corporation or its affiliates. + +Third Party Notices +=================== + +This project includes or partly uses code from the following open source +software subject to the following open source licenses. + +libfdt +------ + +Copyright (C) 2016 Free Electrons +Copyright (C) 2016 NextThing Co. + +The libfdt source code is disjunctively dual licensed (GPL-2.0+ or +BSD-2-Clause). Some of this project code is used in OpenSBI under the terms of +the BSD 2-Clause license. The full text of this license can be found in the +file [COPYING.BSD](COPYING.BSD). diff --git a/roms/opensbi/docs/contributing.md b/roms/opensbi/docs/contributing.md new file mode 100644 index 000000000..9c2b3ba2c --- /dev/null +++ b/roms/opensbi/docs/contributing.md @@ -0,0 +1,84 @@ +OpenSBI Contribution Guideline +============================== + +All contributions to OpenSBI can be sent in the following ways: +1. Email patches to the OpenSBI mailing list at `opensbi@lists.infradead.org` +2. GitHub Pull Requests (PRs) to the [OpenSBI main repository] + +To join the OpenSBI mailing list, please visit the [OpenSBI infradead page]. + +The OpenSBI maintainers prefer patches via the OpenSBI mailing list +(option 1 above) so that they are visible to a wider audience. All +accepted patches on the OpenSBI mailing list will be taken by any of +the OpenSBI maintainers and merged into the [OpenSBI main repository] +using GitHub PRs. + +All contributed work must follow the following rules: +1. OpenSBI code should be written in accordance to the [Linux coding style]. +2. This project embraces the [Developer Certificate of Origin (DCO)] for +contributions. This means that you must agree to the following prior to +submitting patches: if you agree with this developer certificate you +acknowledge this by adding a Signed-off-by tag to your patch commit log. +Every submitted patch must have this tag. +3. A commit message must have a subject line, followed by a blank line, +followed by a description of the patch content. A blank line and the author +Signed-off-by tag must follow this description. +4. A commit subject line must start with a prefix followed by a ":". Common +prefixes are for example "lib:", "platform:", "firmware:", "docs:", "utils:" +and "top:". +5. Maintainers should use "Rebase and Merge" when using GitHub to merge pull +requests to avoid creating unnecessary merge commits. +6. Maintainers should avoid creating branches directly in the main +riscv/opensbi repository. Instead prefer using a fork of the riscv/opensbi main +repository and branches within that fork to create pull requests. +7. A maintainer cannot merge his own pull requests in the riscv/opensbi main +repository. +8. A pull request must get at least one review from a maintainer. +9. A pull request must spend at least 24 hours in review to allow for other +developers to review. + +----------------------------------------------------------------------- + +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. + +----------------------------------------------------------------------- + +[OpenSBI main repository]: https://github.com/riscv/opensbi +[OpenSBI infradead page]: http://lists.infradead.org/mailman/listinfo/opensbi +[Linux coding style]: https://www.kernel.org/doc/html/v4.10/process/coding-style.html +[Developer Certificate of Origin (DCO)]: http://developercertificate.org/ diff --git a/roms/opensbi/docs/domain_support.md b/roms/opensbi/docs/domain_support.md new file mode 100644 index 000000000..73931f1da --- /dev/null +++ b/roms/opensbi/docs/domain_support.md @@ -0,0 +1,314 @@ +OpenSBI Domain Support +====================== + +An OpenSBI domain is a system-level partition (subset) of underlying hardware +having it's own memory regions (RAM and MMIO devices) and HARTs. The OpenSBI +will try to achieve secure isolation between domains using RISC-V platform +features such as PMP, ePMP, IOPMP, SiFive Shield, etc. + +Important entities which help implement OpenSBI domain support are: + +* **struct sbi_domain_memregion** - Representation of a domain memory region +* **struct sbi_hartmask** - Representation of domain HART set +* **struct sbi_domain** - Representation of a domain instance + +Each HART of a RISC-V platform must have an OpenSBI domain assigned to it. +The OpenSBI platform support is responsible for populating domains and +providing HART id to domain mapping. The OpenSBI domain support will by +default assign **the ROOT domain** to all HARTs of a RISC-V platform so +it is not mandatory for the OpenSBI platform support to populate domains. + +Domain Memory Region +-------------------- + +A domain memory region is represented by **struct sbi_domain_memregion** in +OpenSBI and has following details: + +* **order** - The size of a memory region is **2 ^ order** where **order** + must be **3 <= order <= __riscv_xlen** +* **base** - The base address of a memory region is **2 ^ order** + aligned start address +* **flags** - The flags of a memory region represent memory type (i.e. + RAM or MMIO) and allowed accesses (i.e. READ, WRITE, EXECUTE, etc) + +Domain Instance +--------------- + +A domain instance is represented by **struct sbi_domain** in OpenSBI and +has following details: + +* **index** - Logical index of this domain +* **name** - Name of this domain +* **assigned_harts** - HARTs assigned to this domain +* **possible_harts** - HARTs possible in this domain +* **regions** - Array of memory regions terminated by a memory region + with order zero +* **boot_hartid** - HART id of the HART booting this domain. The domain + boot HART will be started at boot-time if boot HART is possible and + assigned for this domain. +* **next_addr** - Address of the next booting stage for this domain +* **next_arg1** - Arg1 (or 'a1' register) of the next booting stage for + this domain +* **next_mode** - Privilege mode of the next booting stage for this + domain. This can be either S-mode or U-mode. +* **system_reset_allowed** - Is domain allowed to reset the system? + +The memory regions represented by **regions** in **struct sbi_domain** have +following additional constraints to align with RISC-V PMP requirements: + +* A memory region to protect OpenSBI firmware from S-mode and U-mode + should always be present +* For two overlapping memory regions, one should be sub-region of another +* Two overlapping memory regions should not be of same size +* Two overlapping memory regions cannot have same flags +* Memory access checks on overlapping address should prefer smallest + overlapping memory region flags. + +ROOT Domain +----------- + +**The ROOT domain** is the default OpenSBI domain which is assigned by +default to all HARTs of a RISC-V platform. The OpenSBI domain support +will hand-craft **the ROOT domain** very early at boot-time in the +following manner: + +* **index** - Logical index of the ROOT domain is always zero +* **name** - Name of the ROOT domain is "root" +* **assigned_harts** - At boot-time all valid HARTs of a RISC-V platform + are assigned the ROOT domain which changes later based on OpenSBI + platform support +* **possible_harts** - All valid HARTs of a RISC-V platform are possible + HARTs of the ROOT domain +* **regions** - Two memory regions available to the ROOT domain: + **A)** A memory region to protect OpenSBI firmware from S-mode and U-mode + **B)** A memory region of **order=__riscv_xlen** allowing S-mode and + U-mode access to full memory address space +* **boot_hartid** - Coldboot HART is the HART booting the ROOT domain +* **next_addr** - Next booting stage address in coldboot HART scratch + space is the next address for the ROOT domain +* **next_arg1** - Next booting stage arg1 in coldboot HART scratch space + is the next arg1 for the ROOT domain +* **next_mode** - Next booting stage mode in coldboot HART scratch space + is the next mode for the ROOT domain +* **system_reset_allowed** - The ROOT domain is allowed to reset the system + +Domain Effects +-------------- + +Few noteworthy effects of a system partitioned into domains are as follows: + +* At any point in time, a HART is running in exactly one OpenSBI domain context +* The SBI IPI and RFENCE calls from HART A are restricted to the HARTs in + domain assigned to HART A +* The SBI HSM calls which try to change/read state of HART B from HART A will + only work if both HART A and HART B are assigned same domain +* A HART running in S-mode or U-mode can only access memory based on the + memory regions of the domain assigned to the HART + +Domain Device Tree Bindings +--------------------------- + +The OpenSBI domains can be described in the **device tree (DT) blob** (or +flattened device tree) passed to the OpenSBI firmwares by the previous +booting stage. This allows OpenSBI platform support to parse and populate +OpenSBI domains from the device tree blob (or flattened device tree). + +### Domain Configuration Node + +All OpenSBI domain description related DT nodes should be under the domain +configuration DT node. The **/chosen** DT node is the preferred parent of +the domain configuration DT node. + +The DT properties of a domain configuration DT node are as follows: + +* **compatible** (Mandatory) - The compatible string of the domain + configuration. This DT property should have value *"opensbi,domain,config"* + +### Domain Memory Region Node + +The domain memory region DT node describes details of a memory region and +can be pointed by multiple domain instance DT nodes. The access permissions +of the memory region are specified separately in domain instance node. + +The DT properties of a domain memory region DT node are as follows: + +* **compatible** (Mandatory) - The compatible string of the domain memory + region. This DT property should have value *"opensbi,domain,memregion"* +* **base** (Mandatory) - The base address of the domain memory region. This + DT property should have a **2 ^ order** aligned 64 bit address (i.e. two + DT cells). +* **order** (Mandatory) - The order of the domain memory region. This DT + property should have a 32 bit value (i.e. one DT cell) in the range + **3 <= order <= __riscv_xlen**. +* **mmio** (Optional) - A boolean flag representing whether the domain + memory region is a memory-mapped I/O (MMIO) region. +* **devices** (Optional) - The list of device DT node phandles for devices + which fall under this domain memory region. + +### Domain Instance Node + +The domain instance DT node describes set of possible HARTs, set of memory +regions, and other details of a domain instance. + +The DT properties of a domain instance DT node are as follows: + +* **compatible** (Mandatory) - The compatible string of the domain instance. + This DT property should have value *"opensbi,domain,instance"* +* **possible-harts** (Optional) - The list of CPU DT node phandles for the + the domain instance. This list represents the possible HARTs of the + domain instance. +* **regions** (Optional) - The list of domain memory region DT node phandle + and access permissions for the domain instance. Each list entry is a pair + of DT node phandle and access permissions. The access permissions are + represented as a 32bit bitmask having bits: **readable** (BIT[0]), + **writeable** (BIT[1]), **executable** (BIT[2]), and **m-mode** (BIT[3]). +* **boot-hart** (Optional) - The DT node phandle of the HART booting the + domain instance. If coldboot HART is assigned to the domain instance then + this DT property is ignored and the coldboot HART is assumed to be the + boot HART of the domain instance. +* **next-arg1** (Optional) - The 64 bit next booting stage arg1 for the + domain instance. If this DT property is not available and coldboot HART + is not assigned to the domain instance then **0x0** is used as default + value. If this DT property is not available and coldboot HART is assigned + to the domain instance then **next booting stage arg1 of coldboot HART** + is used as default value. +* **next-addr** (Optional) - The 64 bit next booting stage address for the + domain instance. If this DT property is not available and coldboot HART + is not assigned to the domain instance then **0x0** is used as default + value. If this DT property is not available and coldboot HART is assigned + to the domain instance then **next booting stage address of coldboot HART** + is used as default value. +* **next-mode** (Optional) - The 32 bit next booting stage mode for the + domain instance. The possible values of this DT property are: **0x1** + (s-mode), and **0x0** (u-mode). If this DT property is not available + and coldboot HART is not assigned to the domain instance then **0x1** + is used as default value. If this DT property is not available and + coldboot HART is assigned to the domain instance then **next booting + stage mode of coldboot HART** is used as default value. +* **system-reset-allowed** (Optional) - A boolean flag representing + whether the domain instance is allowed to do system reset. + +### Assigning HART To Domain Instance + +By default, all HARTs are assigned to **the ROOT domain**. The OpenSBI +platform support can provide the HART to domain instance assignment using +platform specific callback. + +The HART to domain instance assignment can be parsed from the device tree +using optional DT property **opensbi,domain** in each CPU DT node. The +value of DT property **opensbi,domain** is the DT phandle of the domain +instance DT node. If **opensbi,domain** DT property is not specified then +corresponding HART is assigned to **the ROOT domain**. + +### Domain Configuration Only Accessible to OpenSBI + +The software running inside a domain instance should only be aware of +devices and hardware resources accessible to itself. + +To hide domain configuration from domain instances, the following should +be done: + +* The previous booting stage should preferably provide a separate device + tree for each domain instance and mention location of device tree in + respective domain instance DT nodes using **next-arg1** DT property. +* If domain assigned to a HART does not have separate device tree then + OpenSBI platform support should remove all domain configuration details + from the device tree passed by previous booting stage before passing it + to the next booting stage. + +### Example + +``` + chosen { + opensbi-domains { + compatible = "opensbi,domain,config"; + + tmem: tmem { + compatible = "opensbi,domain,memregion"; + base = <0x0 0x80100000>; + order = <20>; + }; + + tuart: tuart { + compatible = "opensbi,domain,memregion"; + base = <0x0 0x10011000>; + order = <12>; + mmio; + devices = <&uart1>; + }; + + allmem: allmem { + compatible = "opensbi,domain,memregion"; + base = <0x0 0x0>; + order = <64>; + }; + + tdomain: trusted-domain { + compatible = "opensbi,domain,instance"; + possible-harts = <&cpu0>; + regions = <&tmem 0x7>, <&tuart 0x7>; + boot-hart = <&cpu0>; + next-arg1 = <0x0 0x0>; + next-addr = <0x0 0x80100000>; + next-mode = <0x0>; + system-reset-allowed; + }; + + udomain: untrusted-domain { + compatible = "opensbi,domain,instance"; + possible-harts = <&cpu1 &cpu2 &cpu3 &cpu4>; + regions = <&tmem 0x0>, <&tuart 0x0>, <&allmem 0x7>; + }; + }; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + timebase-frequency = <10000000>; + + cpu0: cpu@0 { + device_type = "cpu"; + reg = <0x00>; + compatible = "riscv"; + opensbi-domain = <&tdomain>; + ... + }; + + cpu1: cpu@1 { + device_type = "cpu"; + reg = <0x01>; + compatible = "riscv"; + opensbi-domain = <&udomain>; + ... + }; + + cpu2: cpu@2 { + device_type = "cpu"; + reg = <0x02>; + compatible = "riscv"; + opensbi-domain = <&udomain>; + ... + }; + + cpu3: cpu@3 { + device_type = "cpu"; + reg = <0x03>; + compatible = "riscv"; + opensbi-domain = <&udomain>; + ... + }; + + cpu4: cpu@4 { + device_type = "cpu"; + reg = <0x04>; + compatible = "riscv"; + opensbi-domain = <&udomain>; + ... + }; + }; + + uart1: serial@10011000 { + ... + }; +``` diff --git a/roms/opensbi/docs/doxygen.cfg b/roms/opensbi/docs/doxygen.cfg new file mode 100644 index 000000000..82f31a7ae --- /dev/null +++ b/roms/opensbi/docs/doxygen.cfg @@ -0,0 +1,2462 @@ +# Doxyfile 1.8.13 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "RISC-V OpenSBI" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = "v@@OPENSBI_MAJOR@@.@@OPENSBI_MINOR@@" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Open source implemenation of the supervisor binary interface" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @@BUILD_DIR@@/docs + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = YES + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @@SRC_DIR@@/README.md \ + @@SRC_DIR@@/docs/contributing.md \ + @@SRC_DIR@@/docs/platform_guide.md \ + @@SRC_DIR@@/docs/platform_requirements.md \ + @@SRC_DIR@@/docs/library_usage.md \ + @@SRC_DIR@@/docs/domain_support.md \ + @@SRC_DIR@@/docs/firmware \ + @@SRC_DIR@@/docs/platform \ + @@SRC_DIR@@/include \ + @@SRC_DIR@@/lib + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.c \ + *.h \ + *.md + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = README.md + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when enabling USE_PDFLATEX this option is only used for generating +# bitmaps for formulas in the HTML output, but not in the Makefile that is +# written to the output directory. +# The default file is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's config +# file, i.e. a series of assignments. You only have to provide replacements, +# missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's config file. A template extensions file can be generated +# using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sf.net) file that captures the +# structure of the code including all documentation. Note that this feature is +# still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: YES. + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = NO + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES diff --git a/roms/opensbi/docs/external/coreboot.md b/roms/opensbi/docs/external/coreboot.md new file mode 100644 index 000000000..de32e902c --- /dev/null +++ b/roms/opensbi/docs/external/coreboot.md @@ -0,0 +1,32 @@ +OpenSBI as coreboot payload +=========================== + +[coreboot] is a free/libre and open source firmware platform support multiple +hardware architectures(x86, ARMv7, arm64, PowerPC64, MIPS and RISC-V) and +diverse hardware models. In RISC-V world, coreboot currently support HiFive +Unleashed with OpenSBI as a payload to boot GNU/Linux: + +``` +SiFive HiFive unleashed's original firmware boot process: + +-----------+ ++------+ +------+ +------+ | BBL | +| MSEL |--->| ZSBL |--->| FSBL |--->| +-------+ ++------+ +------+ +------+ | | linux | + +---+-------+ + +coreboot boot process: + +---------------------------------------------------------------------+ + | coreboot | ++------+ +------+ | +-----------+ +----------+ +----------+ +-----------------------+ +| MSEL |-->| ZSBL |-->| | bootblock |->| romstage |->| ramstage |->| payload ( OpenSBI) | ++------+ +------+ | +-----------+ +----------+ +----------+ | +-------+ | + | | | linux | | + +---------------------------------------------+-------------+-------+-+ +``` + +The upstreaming work is still in progress. There's a [documentation] about how +to build [out-of-tree code] to load OpenSBI. + +[coreboot]: https://www.coreboot.org/ +[documentation]: https://github.com/hardenedlinux/embedded-iot_profile/blob/master/docs/riscv/hifiveunleashed_coreboot_notes-en.md +[out-of-tree code]: https://github.com/hardenedlinux/coreboot-HiFiveUnleashed diff --git a/roms/opensbi/docs/firmware/fw.md b/roms/opensbi/docs/firmware/fw.md new file mode 100644 index 000000000..cc0cc9e56 --- /dev/null +++ b/roms/opensbi/docs/firmware/fw.md @@ -0,0 +1,111 @@ +OpenSBI Platform Firmwares +========================== + +OpenSBI provides firmware builds for specific platforms. Different types of +firmwares are supported to deal with the differences between different platforms +early boot stage. All firmwares will execute the same initialization procedure +of the platform hardware according to the platform specific code as well as +OpenSBI generic library code. The supported firmwares type will differ in how +the arguments passed by the platform early boot stage are handled, as well as +how the boot stage following the firmware will be handled and executed. + +OpenSBI currently supports three different types of firmwares. + +Firmware with Dynamic Information (*FW_DYNAMIC*) +------------------------------------------------ + +The *FW_DYNAMIC* firmware gets information about the next booting stage entry, +e.g. a bootloader or an OS kernel, from previous booting stage at runtime. + +A *FW_DYNAMIC* firmware is particularly useful when the booting stage executed +prior to OpenSBI firmware is capable of loading both the OpenSBI firmware +and the booting stage binary to follow OpenSBI firmware. + +Firmware with Jump Address (*FW_JUMP*) +-------------------------------------- + +The *FW_JUMP* firmware assumes a fixed address of the next booting stage +entry, e.g. a bootloader or an OS kernel, without directly including the +binary code for this next stage. + +A *FW_JUMP* firmware is particularly useful when the booting stage executed +prior to OpenSBI firmware is capable of loading both the OpenSBI firmware +and the booting stage binary to follow OpenSBI firmware. + +Firmware with Payload (*FW_PAYLOAD*) +------------------------------------ + +The *FW_PAYLOAD* firmware directly includes the binary code for the booting +stage to follow OpenSBI firmware execution. Typically, this payload will be a +bootloader or an OS kernel. + +A *FW_PAYLOAD* firmware is particularly useful when the booting stage executed +prior to OpenSBI firmware is not capable of loading both OpenSBI firmware and +the booting stage to follow OpenSBI firmware. + +A *FW_PAYLOAD* firmware is also useful for cases where the booting stage prior +to OpenSBI firmware does not pass a *flattened device tree (FDT file)*. In such +case, a *FW_PAYLOAD* firmware allows embedding a flattened device tree in the +.text section of the final firmware. + +Firmware Configuration and Compilation +-------------------------------------- + +All firmware types support the following common compile time configuration +parameters: + +* **FW_TEXT_ADDR** - Defines the execution address of the OpenSBI firmware. + This configuration parameter is mandatory. +* **FW_FDT_PATH** - Path to an external flattened device tree binary file to + be embedded in the *.rodata* section of the final firmware. If this option + is not provided then the firmware will expect the FDT to be passed as an + argument by the prior booting stage. +* **FW_FDT_PADDING** - Optional zero bytes padding to the embedded flattened + device tree binary file specified by **FW_FDT_PATH** option. + +Additionally, each firmware type as a set of type specific configuration +parameters. Detailed information for each firmware type can be found in the +following documents. + +* *[FW_DYNAMIC]*: The *Firmware with Dynamic Information (FW_DYNAMIC)* is + described in more details in the file *fw_dynamic.md*. +* *[FW_JUMP]*: The *Firmware with Jump Address (FW_JUMP)* is described in more + details in the file *fw_jump.md*. +* *[FW_PAYLOAD]*: The *Firmware with Payload (FW_PAYLOAD)* is described in more + details in the file *fw_payload.md*. + +[FW_DYNAMIC]: fw_dynamic.md +[FW_JUMP]: fw_jump.md +[FW_PAYLOAD]: fw_payload.md + +Providing different payloads to OpenSBI Firmware +------------------------------------------------ +OpenSBI firmware can accept various payloads using a compile time option. +Typically, these payloads refer to the next stage boot loader (e.g. U-Boot) +or operating system kernel images (e.g. Linux). By default, OpenSBI +automatically provides a test payload if no specific payload is specified +at compile time. + +To specify a payload at compile time, the make variable _FW_PAYLOAD_PATH_ is +used. +``` +make PLATFORM=<platform_subdir> FW_PAYLOAD_PATH=<payload path> +``` +The instructions to build each payload is different and the details can +be found in the +*docs/firmware/payload_<payload_name>.md* files. + +Options for OpenSBI Firmware behaviors +-------------------------------------- +An optional compile time flag FW_OPTIONS can be used to control the OpenSBI +firmware run-time behaviors. + +``` +make PLATFORM=<platform_subdir> FW_OPTIONS=<options> +``` + +FW_OPTIONS is a bitwise or'ed value of various options, eg: *FW_OPTIONS=0x1* +stands for disabling boot prints from the OpenSBI library. + +For all supported options, please check "enum sbi_scratch_options" in the +*include/sbi/sbi_scratch.h* header file. diff --git a/roms/opensbi/docs/firmware/fw_dynamic.md b/roms/opensbi/docs/firmware/fw_dynamic.md new file mode 100644 index 000000000..01f43f9f4 --- /dev/null +++ b/roms/opensbi/docs/firmware/fw_dynamic.md @@ -0,0 +1,35 @@ +OpenSBI Firmware with Dynamic Information (FW_DYNAMIC) +====================================================== + +OpenSBI **firmware with dynamic info (FW_DYNAMIC)** is a firmware which gets +information about next booting stage (e.g. a bootloader or an OS) and runtime +OpenSBI library options from previous booting stage. + +The previous booting stage will pass information to *FW_DYNAMIC* by creating +*struct fw_dynamic_info* in memory and passing it's address to *FW_DYNAMIC* +via *a2* register of RISC-V CPU. + +A *FW_DYNAMIC* firmware is particularly useful when the booting stage executed +prior to OpenSBI firmware is capable of loading both the OpenSBI firmware and +the booting stage binary to follow OpenSBI firmware. + +*FW_DYNAMIC* Compilation +------------------------ + +A platform can enable *FW_DYNAMIC* firmware using any of the following methods. + +1. Specifying `FW_DYNAMIC=y` on the top level `make` command line. +2. Specifying `FW_DYNAMIC=y` in the target platform *config.mk* configuration +file. + +The compiled *FW_DYNAMIC* firmware ELF file is named *fw_dynamic.elf*. It's +expanded image file is *fw_dynamic.bin*. Both files are created in the platform +specific build directory under the *build/platform/<platform_subdir>/firmware* +directory. + +*FW_DYNAMIC* Firmware Configuration Options +------------------------------------------- + +The *FW_DYNAMIC* firmware does not requires any platform specific configuration +parameters because all required information is passed by previous booting stage +at runtime via *struct fw_dynamic_info*. diff --git a/roms/opensbi/docs/firmware/fw_jump.md b/roms/opensbi/docs/firmware/fw_jump.md new file mode 100644 index 000000000..eea301387 --- /dev/null +++ b/roms/opensbi/docs/firmware/fw_jump.md @@ -0,0 +1,51 @@ +OpenSBI Firmware with Jump Address (FW_JUMP) +============================================ + +OpenSBI **firmware with Jump Address (FW_JUMP)** is a firmware which only +handles the address of the next booting stage entry, e.g. a bootloader or an OS +kernel, without directly including the binary code for this next stage. + +A *FW_JUMP* firmware is particularly useful when the booting stage executed +prior to the OpenSBI firmware is capable of loading both the OpenSBI firmware +and the booting stage binary to follow the OpenSBI firmware. + +*FW_JUMP* Compilation +--------------------- + +A platform *FW_JUMP* firmware can be enabled by any of the following methods: + +1. Specifying `FW_JUMP=y` on the top level `make` command line. +2. Specifying `FW_JUMP=y` in the target platform *config.mk* configuration file. + +The compiled *FW_JUMP* firmware ELF file is named *fw_jump.elf*. Its expanded +image file is *fw_jump.bin*. Both files are created in the platform-specific +build directory under the *build/platform/<platform_subdir>/firmware* directory. + +*FW_JUMP* Firmware Configuration Options +---------------------------------------- + +To operate correctly, a *FW_JUMP* firmware requires some configuration +parameters to be defined using either the top level `make` command line or the +target platform *config.mk* configuration file. The possible parameters are as +follows: + +* **FW_JUMP_ADDR** - Address of the entry point of the booting stage to be + executed following OpenSBI firmware. This address generally corresponds + exactly to the address where this next booting stage was loaded. This is a + mandatory parameter. Compilation errors will result from not defining this + address. + +* **FW_JUMP_FDT_ADDR** - Address where the *flattened device tree (FDT file)* + passed by the prior booting stage will be placed in memory before executing + the booting stage following the OpenSBI firmware. If this option is not + provided, then the OpenSBI firmware will pass the FDT address passed by the + previous booting stage to the next booting stage. + +*FW_JUMP* Example +----------------- + +The *[qemu/virt]* platform illustrates how to configure and use a *FW_JUMP* +firmware. Detailed information regarding these platforms can be found in the +platform documentation files. + +[qemu/virt]: ../platform/qemu_virt.md diff --git a/roms/opensbi/docs/firmware/fw_payload.md b/roms/opensbi/docs/firmware/fw_payload.md new file mode 100644 index 000000000..094744806 --- /dev/null +++ b/roms/opensbi/docs/firmware/fw_payload.md @@ -0,0 +1,72 @@ +OpenSBI Firmware with Payload (FW_PAYLOAD) +========================================== + +OpenSBI **firmware with Payload (FW_PAYLOAD)** is a firmware which directly +includes the binary for the booting stage to follow the OpenSBI firmware +execution. Typically, this payload will be a bootloader or an OS kernel. + +A *FW_PAYLOAD* firmware is particularly useful when the booting stage executed +prior to the OpenSBI firmware is not capable of loading both the OpenSBI +firmware and the booting stage to follow OpenSBI firmware. + +A *FW_PAYLOAD* firmware is also useful for cases where the booting stage prior +to the OpenSBI firmware does not pass a *flattened device tree (FDT file)*. In +such a case, a *FW_PAYLOAD* firmware allows embedding a flattened device tree +in the .text section of the final firmware. + +Enabling *FW_PAYLOAD* compilation +--------------------------------- + +The *FW_PAYLOAD* firmware can be enabled by any of the following methods: + +1. Specifying `FW_PAYLOAD=y` on the top level `make` command line. +2. Specifying `FW_PAYLOAD=y` in the target platform *config.mk* configuration + file. + +The compiled *FW_PAYLOAD* firmware ELF file is named *fw_jump.elf*. Its +expanded image file is *fw_payload.bin*. Both files are created in the +platform-specific build directory under the +*build/platform/<platform_subdir>/firmware* directory. + +Configuration Options +--------------------- + +A *FW_PAYLOAD* firmware is built according to configuration parameters and +options. These configuration parameters can be defined using either the top +level `make` command line or the target platform *config.mk* configuration +file. The parameters currently defined are as follows: + +* **FW_PAYLOAD_OFFSET** - Offset from *FW_TEXT_BASE* where the payload binary + will be linked in the final *FW_PAYLOAD* firmware binary image. This + configuration parameter is mandatory if *FW_PAYLOAD_ALIGN* is not defined. + Compilation errors will result from an incorrect definition of + *FW_PAYLOAD_OFFSET* or of *FW_PAYLOAD_ALIGN*, or if neither of these + parameters are defined. + +* **FW_PAYLOAD_ALIGN** - Address alignment constraint where the payload binary + will be linked after the end of the base firmware binary in the final + *FW_PAYLOAD* firmware binary image. This configuration parameter is mandatory + if *FW_PAYLOAD_OFFSET* is not defined. If both *FW_PAYLOAD_OFFSET* and + *FW_PAYLOAD_ALIGN* are defined, *FW_PAYLOAD_OFFSET* is used and + *FW_PAYLOAD_ALIGN* is ignored. + +* **FW_PAYLOAD_PATH** - Path to the image file of the next booting stage + binary. If this option is not provided then a simple test payload is + automatically generated and used as a payload. This test payload executes + an infinite `while (1)` loop after printing a message on the platform console. + +* **FW_PAYLOAD_FDT_ADDR** - Address where the FDT passed by the prior booting + stage or specified by the *FW_FDT_PATH* parameter and embedded in the + *.rodata* section will be placed before executing the next booting stage, + that is, the payload firmware. If this option is not provided, then the + firmware will pass the FDT address passed by the previous booting stage + to the next booting stage. + +*FW_PAYLOAD* Example +-------------------- + +The *[qemu/virt]* platforms illustrate how to configure and use a *FW_PAYLOAD* +firmware. Detailed information regarding these platforms can be found in the +platform documentation files. + +[qemu/virt]: ../platform/qemu_virt.md diff --git a/roms/opensbi/docs/firmware/payload_linux.md b/roms/opensbi/docs/firmware/payload_linux.md new file mode 100644 index 000000000..e896567ab --- /dev/null +++ b/roms/opensbi/docs/firmware/payload_linux.md @@ -0,0 +1,9 @@ +Linux as a direct payload to OpenSBI +==================================== + +OpenSBI has the capability to load a Linux kernel image directly in supervisor +mode. The flattened image generated by the Linux kernel build process can be +provided as a payload to OpenSBI. + +Detailed examples can be found in both the [QEMU](../platform/qemu_virt.md) +and the [HiFive Unleashed](../platform/sifive_fu540.md) platform guides. diff --git a/roms/opensbi/docs/firmware/payload_uboot.md b/roms/opensbi/docs/firmware/payload_uboot.md new file mode 100644 index 000000000..b8f0803e2 --- /dev/null +++ b/roms/opensbi/docs/firmware/payload_uboot.md @@ -0,0 +1,15 @@ +U-Boot as a payload to OpenSBI +============================== + +[U-Boot](https://www.denx.de/wiki/U-Boot) is an open-source primary boot loader. +It can be used as first and/or second stage boot loader in an embedded +environment. In the context of OpenSBI, U-Boot can be specified as a payload to +the OpenSBI firmware, becoming the boot stage following the OpenSBI firmware +execution. + +Building and Generating U-Boot images +===================================== +Please refer to the U-Boot build documentation for detailed instructions on +how to build U-Boot image and boot high level operating systems from U-Boot +prompt. + diff --git a/roms/opensbi/docs/library_usage.md b/roms/opensbi/docs/library_usage.md new file mode 100644 index 000000000..ff99801fc --- /dev/null +++ b/roms/opensbi/docs/library_usage.md @@ -0,0 +1,89 @@ +OpenSBI Library Usage +===================== + +OpenSBI provides two types of static libraries: + +1. *libsbi.a* - A platform-independent generic static library implementing the + interface defined by the SBI specifications. Platform-specific processing + hooks for the execution of this interface must be provided by the firmware or + bootloader linking with this library. This library is installed as + *<install_directory>/lib/libsbi.a* +2. *libsbiutils.a* - A static library that will contain all common code required + by any platform supported in OpenSBI. It will be built by default and included + in libplatsbi.a. This library is installed as + *<install_directory>/lib/libsbiutils.a*. +3. *libplatsbi.a* - An example platform-specific static library integrating + *libsbi.a* with platform-specific hooks. This library is available only for + the platforms supported by OpenSBI. This library is installed as + *<install_directory>/platform/<platform_subdir>/lib/libplatsbi.a* + +Implementations may choose either *libsbi.a* or *libplatsbi.a* to link with +their firmware or bootloader. In the case of *libsbi.a*, platform-specific +hooks in the form of a *struct sbi_platform* instance need to be provided. + +The platform-specific example firmwares provided by OpenSBI are not mandatory. +An implementation may choose to link the OpenSBI generic static library together +with an M-mode firmware or bootloader providing the hardware-specific hooks. +Since OpenSBI is a statically linked library, users must ensure that the +license of these external components is compatible with the OpenSBI license. + +Constraints on OpenSBI usage from external firmware +--------------------------------------------------- + +Users have to ensure that an external firmware or bootloader linking against +OpenSBI static libraries (*libsbi.a* or *libplatsbi.a*) is compiled with the +same GCC target options *-mabi*, *-march*, and *-mcmodel*. + +There are only two constraints on calling any OpenSBI library function from an +external M-mode firmware or bootloader: + +1. The RISC-V *MSCRATCH* CSR must point to a valid OpenSBI scratch space + (i.e. a *struct sbi_scratch* instance). +2. The RISC-V *SP* register (i.e. the stack pointer) must be set per-HART + pointing to distinct non-overlapping stacks. + +The most important functions from an external firmware or bootloader +perspective are *sbi_init()* and *sbi_trap_handler()*. + +In addition to the above constraints, the external firmware or bootloader must +ensure that interrupts are disabled in the *MSTATUS* and *MIE* CSRs when calling +the functions *sbi_init()* and *sbi_trap_handler()*. + +The *sbi_init()* function should be called by the external firmware or +bootloader for each HART that is powered-up at boot-time or in response to a +CPU hotplug event. + +The *sbi_trap_handler()* function should be called by the external firmware or +bootloader to service the following interrupts and traps: + +1. M-mode timer interrupt +2. M-mode software interrupt +3. Illegal instruction trap +4. Misaligned load trap +5. Misaligned store trap +6. Supervisor ecall trap +7. Hypervisor ecall trap + +**Note:** external firmwares or bootloaders can be more conservative by +forwarding all traps and interrupts to *sbi_trap_handler()*. + +Definitions of OpenSBI Data Types for the External Firmware +----------------------------------------------------------- + +OpenSBI can be built as library using external firmware build system such as EDK2 +code base (The open source of UEFI firmware implementation) and linked with external +firmware drivers based on the external firmware architecture. + +**OPENSBI_EXTERNAL_SBI_TYPES** identifier is introduced to *sbi_types.h* for selecting +external header file during the build preprocess in order to define OpensSBI data types +based on external firmware data type binding. +For example, *bool* is declared as *int* in sbi_types.h. However in EDK2 build system, +*bool* is declared as *BOOLEAN* which is defined as *unsigned char* data type. + +External firmware can define **OPENSBI_EXTERNAL_SBI_TYPES** in CFLAGS and specify it to the +header file maintained in its code tree. However, the external build system has to address +the additional include directory for the external header file based on its own build system. +For example, +*-D***OPENSBI_EXTERNAL_SBI_TYPES***=OpensbiTypes.h* +Above tells *sbi_types.h* to refer to *OpensbiTypes.h* instead of using original definitions of +data types. diff --git a/roms/opensbi/docs/platform/andes-ae350.md b/roms/opensbi/docs/platform/andes-ae350.md new file mode 100644 index 000000000..46889a1f1 --- /dev/null +++ b/roms/opensbi/docs/platform/andes-ae350.md @@ -0,0 +1,30 @@ +Andes AE350 SoC Platform +======================== +The AE350 AXI/AHB-based platform N25(F)/NX25(F)/D25F/A25/AX25 CPU with level-one +memories,interrupt controller, debug module, AXI and AHB Bus Matrix Controller, +AXI-to-AHB Bridge and a collection of fundamentalAHB/APB bus IP components +pre-integrated together as a system design.The high-quality and configurable +AHB/APB IPs suites a majority embedded systems, and the verified platform serves +as a starting point to jump start SoC designs. + +To build platform specific library and firmwares, provide the +*PLATFORM=andes/ae350* parameter to the top level make command. + +Platform Options +---------------- + +The Andes AE350 platform does not have any platform-specific options. + +Building Andes AE350 Platform +----------------------------- + +To use Linux v5.2 should be used to build Andes AE350 OpenSBI binaries by using +the compile time option FW_FDT_PATH. + +AE350's dts is included in https://github.com/andestech/linux/tree/ast-v3_2_0-release-public + +**Linux Kernel Payload** + +``` +make PLATFORM=andes/ae350 FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image FW_FDT_PATH=<ae350.dtb path> +``` diff --git a/roms/opensbi/docs/platform/fpga-ariane.md b/roms/opensbi/docs/platform/fpga-ariane.md new file mode 100644 index 000000000..f95001fb3 --- /dev/null +++ b/roms/opensbi/docs/platform/fpga-ariane.md @@ -0,0 +1,38 @@ +Ariane FPGA SoC Platform +======================== +Ariane is a 6-stage, single issue, in-order CPU which implements the 64-bit +RISC-V instruction set. The Ariane FPGA development platform is based on FPGA +SoC (which currently supports only Genesys 2 board) and is capable of running +Linux. + +The FPGA SoC currently contains the following peripherals: +- DDR3 memory controller +- SPI controller to conncet to an SDCard +- Ethernet controller +- JTAG port (see debugging section below) +- Bootrom containing zero stage bootloader and device tree. + +To build platform specific library and firmwares, provide the +*PLATFORM=fpga/ariane* parameter to the top level `make` command. + +Platform Options +---------------- + +The *Ariane FPGA* platform does not have any platform-specific options. + +Building Ariane FPGA Platform +----------------------------- + +**Linux Kernel Payload** + +``` +make PLATFORM=fpga/ariane FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image +``` + +Booting Ariane FPGA Platform +---------------------------- + +**Linux Kernel Payload** + +As Linux kernel image is embedded in the OpenSBI firmware binary, Ariane will +directly boot into Linux directly after powered on. diff --git a/roms/opensbi/docs/platform/fpga-openpiton.md b/roms/opensbi/docs/platform/fpga-openpiton.md new file mode 100644 index 000000000..7861a197c --- /dev/null +++ b/roms/opensbi/docs/platform/fpga-openpiton.md @@ -0,0 +1,33 @@ +OpenPiton FPGA SoC Platform +======================== +OpenPiton is the world's first open source, general purpose, multithreaded +manycore processor. It is a tiled manycore framework scalable from one to +1/2 billion cores. Currently, OpenPiton supports the 64bit Ariane RISC-V +processor from ETH Zurich. To this end, Ariane has been equipped with a +different L1 cache subsystem that follows a write-through protocol and that has +support for cache invalidations and atomics. + +To build platform specific library and firmwares, provide the +*PLATFORM=fpga/openpiton* parameter to the top level `make` command. + +Platform Options +---------------- + +The *OpenPiton* platform does not have any platform-specific options. + +Building Ariane FPGA Platform +----------------------------- + +**Linux Kernel Payload** + +``` +make PLATFORM=fpga/openpiton FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image +``` + +Booting Ariane FPGA Platform +---------------------------- + +**Linux Kernel Payload** + +As Linux kernel image is embedded in the OpenSBI firmware binary, Ariane will +directly boot into Linux directly after powered on. diff --git a/roms/opensbi/docs/platform/generic.md b/roms/opensbi/docs/platform/generic.md new file mode 100644 index 000000000..f1f7f6424 --- /dev/null +++ b/roms/opensbi/docs/platform/generic.md @@ -0,0 +1,54 @@ +Generic Platform +================ + +The **Generic** platform is a flattened device tree (FDT) based platform +where all platform specific functionality is provided based on FDT passed +by previous booting stage. The **Generic** platform allows us to use same +OpenSBI firmware binaries on various emulators, simulators, FPGAs, and +boards. + +By default, the generic FDT platform makes following assumptions: + +1. platform FW_TEXT_START is 0x80000000 +2. platform features are default +3. platform stack size is default +4. platform has no quirks or work-arounds + +The above assumptions (except 1) can be overridden by adding special platform +callbacks which will be called based on FDT root node compatible string. + +Users of the generic FDT platform will have to ensure that: + +1. Various FDT based drivers under lib/utils directory are upto date + based on their platform requirements +2. The FDT passed by previous booting stage has DT compatible strings and + DT properties in sync with the FDT based drivers under lib/utils directory +3. The FDT must have "stdout-path" DT property in the "/chosen" DT node when + a platform has multiple serial ports or consoles +4. On multi-HART platform, the FDT must have a DT node for IPI device and + lib/utils/ipi directory must have corresponding FDT based IPI driver +5. The FDT must have a DT node for timer device and lib/utils/timer directory + must have corresponding FDT based timer driver + +To build the platform-specific library and firmware images, provide the +*PLATFORM=generic* parameter to the top level `make` command. + +For custom FW_TEXT_START, we can build the platform-specific library and +firmware images by passing *PLATFORM=generic FW_TEXT_START=<custom_text_start>* +parameter to the top level `make` command. + +Platform Options +---------------- + +The *Generic* platform does not have any platform-specific options. + +RISC-V Platforms Using Generic Platform +--------------------------------------- + +* **QEMU RISC-V Virt Machine** (*[qemu_virt.md]*) +* **Spike** (*[spike.md]*) +* **Shakti C-class SoC Platform** (*[shakti_cclass.md]*) + +[qemu_virt.md]: qemu_virt.md +[spike.md]: spike.md +[shakti_cclass.md]: shakti_cclass.md diff --git a/roms/opensbi/docs/platform/nuclei_ux600.md b/roms/opensbi/docs/platform/nuclei_ux600.md new file mode 100644 index 000000000..273b6b438 --- /dev/null +++ b/roms/opensbi/docs/platform/nuclei_ux600.md @@ -0,0 +1,22 @@ + +Nuclei UX600 Platform +===================== + +The **Nuclei UX600** is a 64-bit RISC-V Core which is capable of running Linux. + +> Nuclei UX600: single core, pipeline as single-issue and 6~9 variable stages, in-order dispatch and out-of-order write-back, running up to >1.2GHz + +To build the platform-specific library and firmware images, provide the +*PLATFORM=nuclei/ux600* parameter to the top level `make` command. + +Platform Options +---------------- + +The *Nuclei UX600* platform does not have any platform-specific options. + +Building Nuclei UX600 Platform +------------------------------ + +``` +make PLATFORM=nuclei/ux600 clean all +``` diff --git a/roms/opensbi/docs/platform/platform.md b/roms/opensbi/docs/platform/platform.md new file mode 100644 index 000000000..7f4706813 --- /dev/null +++ b/roms/opensbi/docs/platform/platform.md @@ -0,0 +1,59 @@ +OpenSBI Supported Platforms +=========================== + +OpenSBI currently supports the following virtual and hardware platforms: + +* **Generic**: Flattened device tree (FDT) based platform where platform + specific functionality is provided based on the FDT passed by previous + booting stage. More details on this platform can be found in the file + *[generic.md]*. + +* **QEMU RISC-V Virt Machine**: Platform support for the QEMU *virt* virtual + RISC-V machine. This virtual machine is intended for RISC-V software + development and tests. More details on this platform can be found in the + file *[qemu_virt.md]*. + +* **SiFive FU540 SoC**: Platform support for SiFive FU540 SoC used on the + HiFive Unleashed board, as well as the *sifive_u* QEMU virtual RISC-V + machine. More details on this platform can be found in the file + *[sifive_fu540.md]*. + +* **Kendryte K210 SoC**: Platform support for the Kendryte K210 SoC used on + boards such as the Kendryte KD233 or the Sipeed MAIX Dock. + +* **Ariane FPGA SoC**: Platform support for the Ariane FPGA SoC used on + Genesys 2 board. More details on this platform can be found in the file + *[fpga-ariane.md]*. + +* **Andes AE350 SoC**: Platform support for the Andes's SoC (AE350). More + details on this platform can be found in the file *[andes-ae350.md]*. + +* **T-HEAD C910**: Platform support for the T-HEAD C910 Processor. More + details on this platform can be found in the file *[thead-c910.md]*. + +* **Spike**: Platform support for the Spike emulator. More + details on this platform can be found in the file *[spike.md]*. + +* **OpenPiton FPGA SoC**: Platform support OpenPiton research platform based + on ariane core. More details on this platform can be found in the file + *[fpga_openpiton.md]*. + +* **Shakti C-class SoC Platform**: Platform support for Shakti C-class + processor based SOCs. More details on this platform can be found in the + file *[shakti_cclass.md]*. + +The code for these supported platforms can be used as example to implement +support for other platforms. The *platform/template* directory also provides +template files for implementing support for a new platform. The *object.mk*, +*config.mk* and *platform.c* template files provides enough comments to +facilitate the implementation. + +[generic.md]: generic.md +[qemu_virt.md]: qemu_virt.md +[sifive_fu540.md]: sifive_fu540.md +[fpga-ariane.md]: fpga-ariane.md +[andes-ae350.md]: andes-ae350.md +[thead-c910.md]: thead-c910.md +[spike.md]: spike.md +[fpga_openpiton.md]: fpga_openpiton.md +[shakti_cclass.md]: shakti_cclass.md diff --git a/roms/opensbi/docs/platform/qemu_virt.md b/roms/opensbi/docs/platform/qemu_virt.md new file mode 100644 index 000000000..27771485c --- /dev/null +++ b/roms/opensbi/docs/platform/qemu_virt.md @@ -0,0 +1,149 @@ +QEMU RISC-V Virt Machine Platform +================================= + +The **QEMU RISC-V Virt Machine** is a virtual platform created for RISC-V +software development and testing. It is also referred to as +*QEMU RISC-V VirtIO machine* because it uses VirtIO devices for network, +storage, and other types of IO. + +To build the platform-specific library and firmware images, provide the +*PLATFORM=generic* parameter to the top level `make` command. + +Platform Options +---------------- + +The *QEMU RISC-V Virt Machine* platform does not have any platform-specific +options. + +Execution on QEMU RISC-V 64-bit +------------------------------- + +**No Payload Case** + +Build: +``` +make PLATFORM=generic +``` + +Run: +``` +qemu-system-riscv64 -M virt -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_payload.bin +``` + +**U-Boot Payload** + +Note: the command line examples here assume that U-Boot was compiled using +the `qemu-riscv64_smode_defconfig` configuration. + +Build: +``` +make PLATFORM=generic FW_PAYLOAD_PATH=<uboot_build_directory>/u-boot.bin +``` + +Run: +``` +qemu-system-riscv64 -M virt -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_payload.elf +``` +or +``` +qemu-system-riscv64 -M virt -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_jump.bin \ + -kernel <uboot_build_directory>/u-boot.bin +``` + +**Linux Kernel Payload** + +Note: We assume that the Linux kernel is compiled using +*arch/riscv/configs/defconfig*. + +Build: +``` +make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image +``` + +Run: +``` +qemu-system-riscv64 -M virt -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_payload.elf \ + -drive file=<path_to_linux_rootfs>,format=raw,id=hd0 \ + -device virtio-blk-device,drive=hd0 \ + -append "root=/dev/vda rw console=ttyS0" +``` +or +``` +qemu-system-riscv64 -M virt -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_jump.bin \ + -kernel <linux_build_directory>/arch/riscv/boot/Image \ + -drive file=<path_to_linux_rootfs>,format=raw,id=hd0 \ + -device virtio-blk-device,drive=hd0 \ + -append "root=/dev/vda rw console=ttyS0" +``` + + +Execution on QEMU RISC-V 32-bit +------------------------------- + +**No Payload Case** + +Build: +``` +make PLATFORM=generic PLATFORM_RISCV_XLEN=32 +``` + +Run: +``` +qemu-system-riscv32 -M virt -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_payload.bin +``` + +**U-Boot Payload** + +Note: the command line examples here assume that U-Boot was compiled using +the `qemu-riscv32_smode_defconfig` configuration. + +Build: +``` +make PLATFORM=generic PLATFORM_RISCV_XLEN=32 FW_PAYLOAD_PATH=<uboot_build_directory>/u-boot.bin +``` + +Run: +``` +qemu-system-riscv32 -M virt -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_payload.elf +``` +or +``` +qemu-system-riscv32 -M virt -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_jump.bin \ + -kernel <uboot_build_directory>/u-boot.bin +``` + +**Linux Kernel Payload** + +Note: We assume that the Linux kernel is compiled using +*arch/riscv/configs/rv32_defconfig*. + +Build: +``` +make PLATFORM=generic PLATFORM_RISCV_XLEN=32 FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image +``` + +Run: +``` +qemu-system-riscv32 -M virt -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_payload.elf \ + -drive file=<path_to_linux_rootfs>,format=raw,id=hd0 \ + -device virtio-blk-device,drive=hd0 \ + -append "root=/dev/vda rw console=ttyS0" +``` +or +``` +qemu-system-riscv32 -M virt -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_jump.bin \ + -kernel <linux_build_directory>/arch/riscv/boot/Image \ + -drive file=<path_to_linux_rootfs>,format=raw,id=hd0 \ + -device virtio-blk-device,drive=hd0 \ + -append "root=/dev/vda rw console=ttyS0" +``` diff --git a/roms/opensbi/docs/platform/shakti_cclass.md b/roms/opensbi/docs/platform/shakti_cclass.md new file mode 100644 index 000000000..05c9b3a4c --- /dev/null +++ b/roms/opensbi/docs/platform/shakti_cclass.md @@ -0,0 +1,33 @@ +Shakti C-class SoC Platform +=========================== +C-Class is a member of the SHAKTI family of processors from +Indian Institute of Technology - Madras (IIT-M). + +It is an extremely configurable and commercial-grade 5-stage +in-order core supporting the standard RV64GCSUN ISA extensions. + +For more details, refer: +* https://gitlab.com/shaktiproject/cores/c-class/blob/master/README.md +* https://c-class.readthedocs.io/en/latest +* https://shakti.org.in + +Platform Options +---------------- + +The *Shakti C-class SoC* platform does not have any platform-specific +options. + +Building Shakti C-class Platform +-------------------------------- + +**Linux Kernel Payload** + +``` +make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image FW_FDT_PATH=<shakti.dtb path> +``` + +**Test Payload** + +``` +make PLATFORM=generic FW_FDT_PATH=<shakti.dtb path> +``` diff --git a/roms/opensbi/docs/platform/sifive_fu540.md b/roms/opensbi/docs/platform/sifive_fu540.md new file mode 100644 index 000000000..b4d3c6051 --- /dev/null +++ b/roms/opensbi/docs/platform/sifive_fu540.md @@ -0,0 +1,195 @@ +SiFive FU540 SoC Platform +========================= +The FU540-C000 is the world’s first 4+1 64-bit RISC-V SoC from SiFive. +The HiFive Unleashed development platform is based on FU540-C000 and capable +of running Linux. + +With QEMU v4.2 or above release, the 'sifive_u' machine can be used to test +OpenSBI image built for the real hardware as well. + +To build platform specific library and firmwares, provide the +*PLATFORM=sifive/fu540* parameter to the top level `make` command. + +Platform Options +---------------- + +The *SiFive FU540 SoC* platform does not have any platform-specific +options. + +Building SiFive Fu540 Platform +------------------------------ + +In order to boot SMP Linux in U-Boot, Linux v5.1 (or higher) and latest +U-Boot v2020.01 (or higher) should be used. + +**Linux Kernel Payload** + +The HiFive Unleashed device tree(DT) is merged in Linux v5.2 release. This +DT (device tree) is not backward compatible with the DT passed from FSBL. + +To use Linux v5.2 (or higher), the pre-built DTB (DT binary) from Linux v5.2 +(or higher) should be used to build SiFive FU540 OpenSBI binaries by using +the compile time option *FW_FDT_PATH*. + +``` +make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image +or +(For Linux v5.2 or higher) +make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image FW_FDT_PATH=<hifive-unleashed-a00.dtb path from Linux kernel> +``` + +**U-Boot Payload** + +The command-line example here assumes that U-Boot was compiled using the +sifive_fu540_defconfig configuration and with U-Boot v2020.01, and up to +v2020.07-rc3. + +``` +make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot-dtb.bin +``` +For U-Boot v2020.07-rc4 or later releases, SPL support was added in U-Boot. +Please refer to the detailed U-Boot booting guide available at [U-Boot]. + +Flashing the OpenSBI firmware binary to storage media: +------------------------------------------------------ +The first stage boot loader ([FSBL]) expects the storage media to have a GPT +partition table. It tries to look for a partition with following GUID to load +the next stage boot loader (OpenSBI in this case). + +``` +2E54B353-1271-4842-806F-E436D6AF6985 +``` + +That's why the generated firmware binary in above steps should be copied to +the partition of the sdcard with above GUID. + +``` +dd if=build/platform/sifive/fu540/firmware/fw_payload.bin of=/dev/disk2s1 bs=1024 +``` + +In my case, it is the first partition is **disk2s1** that has been formatted +with the above specified GUID. + +In case of a brand new sdcard, it should be formatted with below partition +tables as described here. + +``` +sgdisk --clear \ + --new=1:2048:67583 --change-name=1:bootloader --typecode=1:2E54B353-1271-4842-806F-E436D6AF6985 \ + --new=2:264192: --change-name=2:root --typecode=2:0FC63DAF-8483-4772-8E79-3D69D8477DE4 \ + ${DISK} +``` + +Booting SiFive Fu540 Platform +----------------------------- + +**Linux Kernel Payload** + +As Linux kernel image is embedded in the OpenSBI firmware binary, HiFive +Unleashed will directly boot into Linux directly after powered on. + +**U-Boot Payload** + +As U-Boot image is used as payload, HiFive Unleashed will boot into a U-Boot +prompt. U-Boot tftp boot method can be used to load kernel image in U-Boot +prompt. Here are the steps do a tftpboot. + +1. Set the ip address of the board. +``` +setenv ipaddr <ipaddr of the board> +``` +2. Set the tftpboot server IP. +``` +setenv serverip <ipaddr of the tftp server> +``` +3. Set the network gateway address. +``` +setenv gatewayip <ipaddress of the network gateway> +``` +4. Load the Linux kernel image from the tftp server. +``` +tftpboot ${kernel_addr_r} <Image path in tftpboot directory> +``` +5. Load the ramdisk image from the tftp server. This is only required if +ramdisk is loaded from tftp server. This step is optional, if rootfs is +already part of the kernel or loaded from an external storage by kernel. +``` +tftpboot ${ramdisk_addr_r} <ramdisk path in tftpboot directory> +``` +6. Load the pre-compiled device tree via tftpboot. +``` +tftpboot ${fdt_addr_r} <hifive-unleashed-a00.dtb path in tftpboot directory> +``` +7. Set the boot command-line arguments. +``` +setenv bootargs "root=<root partition> rw console=ttySIF0 earlycon=sbi" +``` +(Note: root partition should point to +** /dev/ram ** - If a ramdisk is used +** root=/dev/mmcblk0pX ** - If a rootfs is already on some other partition +of sdcard) +8. Now boot into Linux. +``` +booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r} +or +(If ramdisk is not loaded from network) +booti ${kernel_addr_r} - ${fdt_addr_r} +``` + +**U-Boot & Linux Kernel as a single payload** + +At U-Boot prompt execute the following boot command to boot Linux. + +``` +booti ${kernel_addr_r} - ${fdt_addr_r} +``` + +QEMU Specific Instructions +-------------------------- +If you want to test OpenSBI with QEMU 'sifive_u' machine, please follow the +same instructions above, with the exception of not passing FW_FDT_PATH. + +This is because QEMU generates a device tree blob on the fly based on the +command line parameters and it's compatible with the one used in the upstream +Linux kernel. + +When U-Boot v2020.01 (or higher) is used as the payload, as the SiFive FU540 +DTB for the real hardware is embedded in U-Boot binary itself, due to the same +reason above, we need to switch the U-Boot sifive_fu540_defconfig configuration +from **CONFIG_OF_SEPARATE** to **CONFIG_OF_PRIOR_STAGE** so that U-Boot uses the +DTB generated by QEMU, and u-boot.bin should be used as the payload image, like: + +``` +make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot.bin +``` + +U-Boot v2020.07 release added SPL support to SiFive HiFive Unleashed board, +hence a build error will be seen after you switch to **CONFIG_OF_PRIOR_STAGE**. + +``` +./tools/mkimage: Can't open arch/riscv/dts/hifive-unleashed-a00.dtb: No such file or directory +./tools/mkimage: failed to build FIT +Makefile:1402: recipe for target 'u-boot.img' failed +make: *** [u-boot.img] Error 1 +``` + +The above errors can be safely ignored as we don't run U-Boot SPL under QEMU. + +Run: +``` +qemu-system-riscv64 -M sifive_u -m 256M -nographic \ + -bios build/platform/sifive/fu540/firmware/fw_payload.bin +``` +or +``` +qemu-system-riscv64 -M sifive_u -m 256M -nographic \ + -bios build/platform/sifive/fu540/firmware/fw_jump.bin \ + -kernel <uboot_build_dir>/u-boot.bin +``` + +While the real hardware operates at the 64-bit mode, it's possible for QEMU to +test the 32-bit OpenSBI firmware. This can be helpful for testing 32-bit SiFive +specific drivers. + +[U-Boot]: https://gitlab.denx.de/u-boot/u-boot/blob/master/doc/board/sifive/fu540.rst +[FSBL]: https://github.com/sifive/freedom-u540-c000-bootloader diff --git a/roms/opensbi/docs/platform/spike.md b/roms/opensbi/docs/platform/spike.md new file mode 100644 index 000000000..79e2eb2b0 --- /dev/null +++ b/roms/opensbi/docs/platform/spike.md @@ -0,0 +1,89 @@ +Spike Simulator Platform +======================== + +The **Spike** is a RISC-V ISA simulator which implements a functional model +of one or more RISC-V harts. The **Spike** compatible virtual platform is +also available on QEMU. In fact, we can use same OpenSBI firmware binaries +on **Spike** simulator and QEMU Spike machine. + +For more details, refer [Spike on GitHub](https://github.com/riscv/riscv-isa-sim) + +To build the platform-specific library and firmware images, provide the +*PLATFORM=generic* parameter to the top level `make` command. + +Platform Options +---------------- + +The *Spike* platform does not have any platform-specific options. + +Execution on Spike Simulator +---------------------------- + +**No Payload Case** + +Build: +``` +make PLATFORM=generic +``` + +Run: +``` +spike build/platform/generic/firmware/fw_payload.elf +``` + +**Linux Kernel Payload** + +Note: We assume that the Linux kernel is compiled using +*arch/riscv/configs/defconfig*. + +Build: +``` +make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image +``` + +Run: +``` +spike --initrd <path_to_cpio_ramdisk> build/platform/generic/firmware/fw_payload.elf +``` + +Execution on QEMU RISC-V 64-bit +------------------------------- + +**No Payload Case** + +Build: +``` +make PLATFORM=generic +``` + +Run: +``` +qemu-system-riscv64 -M spike -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_payload.elf +``` + +**Linux Kernel Payload** + +Note: We assume that the Linux kernel is compiled using +*arch/riscv/configs/defconfig*. + +Build: +``` +make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image +``` + +Run: +``` +qemu-system-riscv64 -M spike -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_payload.elf \ + -initrd <path_to_cpio_ramdisk> \ + -append "root=/dev/ram rw console=hvc0 earlycon=sbi" +``` +or +``` +qemu-system-riscv64 -M spike -m 256M -nographic \ + -bios build/platform/generic/firmware/fw_jump.elf \ + -kernel <linux_build_directory>/arch/riscv/boot/Image \ + -initrd <path_to_cpio_ramdisk> \ + -append "root=/dev/ram rw console=hvc0 earlycon=sbi" +``` diff --git a/roms/opensbi/docs/platform/thead-c910.md b/roms/opensbi/docs/platform/thead-c910.md new file mode 100644 index 000000000..786b47d03 --- /dev/null +++ b/roms/opensbi/docs/platform/thead-c910.md @@ -0,0 +1,34 @@ +T-HEAD C910 Processor +===================== +C910 is a 12-stage, 3 issues, 8 executions, out-of-order 64-bit RISC-V CPU which +supports 16 cores, runs with 2.5GHz, and is capable of running Linux. + +To build platform specific library and firmwares, provide the +*PLATFORM=thead/c910* parameter to the top level make command. + +Platform Options +---------------- + +The *T-HEAD C910* platform does not have any platform-specific options. + +Building T-HEAD C910 Platform +----------------------------- + +``` +make PLATFORM=thead/c910 +``` + +Booting T-HEAD C910 Platform +---------------------------- + +**No Payload** + +As there's no payload, you may download vmlinux or u-boot to FW_JUMP_ADDR which +specified in config.mk or compile commands with GDB. And the execution flow will +turn to vmlinux or u-boot when opensbi ends. + +**Linux Kernel Payload** + +You can also choose to use Linux kernel as payload by enabling FW_PAYLOAD=y +along with specifying FW_PAYLOAD_OFFSET. The kernel image will be embedded in +the OPENSBI firmware binary, T-head will directly boot into Linux after OpenSBI. diff --git a/roms/opensbi/docs/platform_guide.md b/roms/opensbi/docs/platform_guide.md new file mode 100644 index 000000000..7920fac2a --- /dev/null +++ b/roms/opensbi/docs/platform_guide.md @@ -0,0 +1,42 @@ +OpenSBI Platform Support Guideline +================================== + +The OpenSBI platform support allows an implementation to define a set of +platform-specific hooks (hardware manipulation functions) in the form of a +*struct sbi_platform* data structure instance. This instance is required by +the platform-independent *libsbi.a* to execute platform-specific operations. + +Each of the reference platform supports provided by OpenSBI defines an instance +of the *struct sbi_platform* data structure. For each supported platform, +*libplatsbi.a* integrates this instance with *libsbi.a* to create a +platform-specific OpenSBI static library. This library is installed +in *<install_directory>/platform/<platform_subdir>/lib/libplatsbi.a* + +OpenSBI also provides implementation examples of bootable runtime firmwares for +the supported platforms. These firmwares are linked against *libplatsbi.a*. +Firmware binaries are installed in +*<install_directory>/platform/<platform_subdir>/bin*. These firmwares can be +used as executable runtime firmwares on the supported platforms as a replacement +for the legacy *riskv-pk* boot loader (BBL). + +A complete doxygen-style documentation of *struct sbi_platform* and related +APIs is available in the file *include/sbi/sbi_platform.h*. + +Adding support for a new platform +--------------------------------- + +Support for a new platform named *<xyz>* can be added as follows: + +1. Create a directory named *<xyz>* under the *platform/* directory. +2. Create a platform configuration file named *config.mk* under the + *platform/<xyz>/* directory. This configuration file will provide + compiler flags, and select firmware options. +3. Create a *platform/<xyz>/objects.mk* file for listing the + platform-specific object files to be compiled. +4. Create a *platform/<xyz>/platform.c* file providing a *struct sbi_platform* + instance. + +A platform support code template is available under the *platform/template* +directory. Copying this directory and its content as a new directory named +*<xyz>* under the *platform/* directory will create all the files mentioned +above. diff --git a/roms/opensbi/docs/platform_requirements.md b/roms/opensbi/docs/platform_requirements.md new file mode 100644 index 000000000..68dc3935a --- /dev/null +++ b/roms/opensbi/docs/platform_requirements.md @@ -0,0 +1,44 @@ +OpenSBI Platform Requirements +============================= + +The RISC-V platform requirements for OpenSBI can change over time +with advances in RISC-V specifications and ecosystem. + +To handle this, we have two types of RISC-V platform requirements: + +1. **Base platform requirements** which apply to all OpenSBI releases +2. **Release specific platform requirements** which apply to a OpenSBI + release and later releases + +Currently, we don't have any **Release specific platform requirements** +but such platform requirements will be added in future. + +Base Platform Requirements +-------------------------- + +The base RISC-V platform requirements for OpenSBI are as follows: + +1. At least rv32ima or rv64ima required on all HARTs +2. At least one HART should have S-mode support because: + + * SBI calls are meant for RISC-V S-mode (Supervisor mode) + * OpenSBI implements SBI calls for S-mode software + +3. The MTVEC CSR on all HARTs must support direct mode +4. The PMP CSRs are optional. If PMP CSRs are not implemented then + OpenSBI cannot protect M-mode firmware and secured memory regions +5. The TIME CSR is optional. If TIME CSR is not implemented in + hardware then a 64-bit MMIO counter is required to track time + and emulate TIME CSR +6. Hardware support for injecting M-mode software interrupts on + a multi-HART platform + +The RISC-V extensions not covered by rv32ima or rv64ima are optional +for OpenSBI. Although, OpenSBI will detect and handle some of these +optional RISC-V extensions at runtime. + +The optional RISC-V extensions handled by OpenSBI at runtime are: + +* D-extension: Double precision floating point +* F-extension: Single precision floating point +* H-extension: Hypervisor diff --git a/roms/opensbi/firmware/external_deps.mk b/roms/opensbi/firmware/external_deps.mk new file mode 100644 index 000000000..6264005bc --- /dev/null +++ b/roms/opensbi/firmware/external_deps.mk @@ -0,0 +1,14 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +$(platform_build_dir)/firmware/fw_dynamic.o: $(FW_FDT_PATH) +$(platform_build_dir)/firmware/fw_jump.o: $(FW_FDT_PATH) +$(platform_build_dir)/firmware/fw_payload.o: $(FW_FDT_PATH) + +$(platform_build_dir)/firmware/fw_payload.o: $(FW_PAYLOAD_PATH_FINAL) diff --git a/roms/opensbi/firmware/fw_base.S b/roms/opensbi/firmware/fw_base.S new file mode 100644 index 000000000..ab33e1102 --- /dev/null +++ b/roms/opensbi/firmware/fw_base.S @@ -0,0 +1,769 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_trap.h> + +#define BOOT_STATUS_RELOCATE_DONE 1 +#define BOOT_STATUS_BOOT_HART_DONE 2 + +.macro MOV_3R __d0, __s0, __d1, __s1, __d2, __s2 + add \__d0, \__s0, zero + add \__d1, \__s1, zero + add \__d2, \__s2, zero +.endm + +.macro MOV_5R __d0, __s0, __d1, __s1, __d2, __s2, __d3, __s3, __d4, __s4 + add \__d0, \__s0, zero + add \__d1, \__s1, zero + add \__d2, \__s2, zero + add \__d3, \__s3, zero + add \__d4, \__s4, zero +.endm + +/* + * If __start_reg <= __check_reg and __check_reg < __end_reg then + * jump to __pass + */ +.macro BRANGE __start_reg, __end_reg, __check_reg, __jump_lable + blt \__check_reg, \__start_reg, 999f + bge \__check_reg, \__end_reg, 999f + j \__jump_lable +999: +.endm + + .section .entry, "ax", %progbits + .align 3 + .globl _start + .globl _start_warm +_start: + /* Find preferred boot HART id */ + MOV_3R s0, a0, s1, a1, s2, a2 + call fw_boot_hart + add a6, a0, zero + MOV_3R a0, s0, a1, s1, a2, s2 + li a7, -1 + beq a6, a7, _try_lottery + /* Jump to relocation wait loop if we are not boot hart */ + bne a0, a6, _wait_relocate_copy_done +_try_lottery: + /* Jump to relocation wait loop if we don't get relocation lottery */ + la a6, _relocate_lottery + li a7, 1 + amoadd.w a6, a7, (a6) + bnez a6, _wait_relocate_copy_done + + /* Save load address */ + la t0, _load_start + la t1, _start + REG_S t1, 0(t0) + + /* Relocate if load address != link address */ +_relocate: + la t0, _link_start + REG_L t0, 0(t0) + la t1, _link_end + REG_L t1, 0(t1) + la t2, _load_start + REG_L t2, 0(t2) + sub t3, t1, t0 + add t3, t3, t2 + beq t0, t2, _relocate_done + la t4, _relocate_done + sub t4, t4, t2 + add t4, t4, t0 + blt t2, t0, _relocate_copy_to_upper +_relocate_copy_to_lower: + ble t1, t2, _relocate_copy_to_lower_loop + la t3, _relocate_lottery + BRANGE t2, t1, t3, _start_hang + la t3, _boot_status + BRANGE t2, t1, t3, _start_hang + la t3, _relocate + la t5, _relocate_done + BRANGE t2, t1, t3, _start_hang + BRANGE t2, t1, t5, _start_hang + BRANGE t3, t5, t2, _start_hang +_relocate_copy_to_lower_loop: + REG_L t3, 0(t2) + REG_S t3, 0(t0) + add t0, t0, __SIZEOF_POINTER__ + add t2, t2, __SIZEOF_POINTER__ + blt t0, t1, _relocate_copy_to_lower_loop + jr t4 +_relocate_copy_to_upper: + ble t3, t0, _relocate_copy_to_upper_loop + la t2, _relocate_lottery + BRANGE t0, t3, t2, _start_hang + la t2, _boot_status + BRANGE t0, t3, t2, _start_hang + la t2, _relocate + la t5, _relocate_done + BRANGE t0, t3, t2, _start_hang + BRANGE t0, t3, t5, _start_hang + BRANGE t2, t5, t0, _start_hang +_relocate_copy_to_upper_loop: + add t3, t3, -__SIZEOF_POINTER__ + add t1, t1, -__SIZEOF_POINTER__ + REG_L t2, 0(t3) + REG_S t2, 0(t1) + blt t0, t1, _relocate_copy_to_upper_loop + jr t4 +_wait_relocate_copy_done: + la t0, _start + la t1, _link_start + REG_L t1, 0(t1) + beq t0, t1, _wait_for_boot_hart + la t2, _boot_status + la t3, _wait_for_boot_hart + sub t3, t3, t0 + add t3, t3, t1 +1: + /* waitting for relocate copy done (_boot_status == 1) */ + li t4, BOOT_STATUS_RELOCATE_DONE + REG_L t5, 0(t2) + /* Reduce the bus traffic so that boot hart may proceed faster */ + nop + nop + nop + bgt t4, t5, 1b + jr t3 +_relocate_done: + + /* + * Mark relocate copy done + * Use _boot_status copy relative to the load address + */ + la t0, _boot_status + la t1, _link_start + REG_L t1, 0(t1) + la t2, _load_start + REG_L t2, 0(t2) + sub t0, t0, t1 + add t0, t0, t2 + li t1, BOOT_STATUS_RELOCATE_DONE + REG_S t1, 0(t0) + fence rw, rw + + /* At this point we are running from link address */ + + /* Reset all registers for boot HART */ + li ra, 0 + call _reset_regs + + /* Zero-out BSS */ + la s4, _bss_start + la s5, _bss_end +_bss_zero: + REG_S zero, (s4) + add s4, s4, __SIZEOF_POINTER__ + blt s4, s5, _bss_zero + + /* Setup temporary trap handler */ + la s4, _start_hang + csrw CSR_MTVEC, s4 + + /* Setup temporary stack */ + la s4, _fw_end + li s5, (SBI_SCRATCH_SIZE * 2) + add sp, s4, s5 + + /* Allow main firmware to save info */ + MOV_5R s0, a0, s1, a1, s2, a2, s3, a3, s4, a4 + call fw_save_info + MOV_5R a0, s0, a1, s1, a2, s2, a3, s3, a4, s4 + +#ifdef FW_FDT_PATH + /* Override previous arg1 */ + la a1, fw_fdt_bin +#endif + + /* + * Initialize platform + * Note: The a0 to a4 registers passed to the + * firmware are parameters to this function. + */ + MOV_5R s0, a0, s1, a1, s2, a2, s3, a3, s4, a4 + call fw_platform_init + add t0, a0, zero + MOV_5R a0, s0, a1, s1, a2, s2, a3, s3, a4, s4 + add a1, t0, zero + + /* Preload HART details + * s7 -> HART Count + * s8 -> HART Stack Size + */ + la a4, platform +#if __riscv_xlen == 64 + lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) +#else + lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) +#endif + + /* Setup scratch space for all the HARTs*/ + la tp, _fw_end + mul a5, s7, s8 + add tp, tp, a5 + /* Keep a copy of tp */ + add t3, tp, zero + /* Counter */ + li t2, 1 + /* hartid 0 is mandated by ISA */ + li t1, 0 +_scratch_init: + add tp, t3, zero + mul a5, s8, t1 + sub tp, tp, a5 + li a5, SBI_SCRATCH_SIZE + sub tp, tp, a5 + + /* Initialize scratch space */ + /* Store fw_start and fw_size in scratch space */ + la a4, _fw_start + la a5, _fw_end + mul t0, s7, s8 + add a5, a5, t0 + sub a5, a5, a4 + REG_S a4, SBI_SCRATCH_FW_START_OFFSET(tp) + REG_S a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp) + /* Store next arg1 in scratch space */ + MOV_3R s0, a0, s1, a1, s2, a2 + call fw_next_arg1 + REG_S a0, SBI_SCRATCH_NEXT_ARG1_OFFSET(tp) + MOV_3R a0, s0, a1, s1, a2, s2 + /* Store next address in scratch space */ + MOV_3R s0, a0, s1, a1, s2, a2 + call fw_next_addr + REG_S a0, SBI_SCRATCH_NEXT_ADDR_OFFSET(tp) + MOV_3R a0, s0, a1, s1, a2, s2 + /* Store next mode in scratch space */ + MOV_3R s0, a0, s1, a1, s2, a2 + call fw_next_mode + REG_S a0, SBI_SCRATCH_NEXT_MODE_OFFSET(tp) + MOV_3R a0, s0, a1, s1, a2, s2 + /* Store warm_boot address in scratch space */ + la a4, _start_warm + REG_S a4, SBI_SCRATCH_WARMBOOT_ADDR_OFFSET(tp) + /* Store platform address in scratch space */ + la a4, platform + REG_S a4, SBI_SCRATCH_PLATFORM_ADDR_OFFSET(tp) + /* Store hartid-to-scratch function address in scratch space */ + la a4, _hartid_to_scratch + REG_S a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) + /* Store trap-exit function address in scratch space */ + la a4, _trap_exit + REG_S a4, SBI_SCRATCH_TRAP_EXIT_OFFSET(tp) + /* Clear tmp0 in scratch space */ + REG_S zero, SBI_SCRATCH_TMP0_OFFSET(tp) + /* Store firmware options in scratch space */ + MOV_3R s0, a0, s1, a1, s2, a2 +#ifdef FW_OPTIONS + li a0, FW_OPTIONS +#else + call fw_options +#endif + REG_S a0, SBI_SCRATCH_OPTIONS_OFFSET(tp) + MOV_3R a0, s0, a1, s1, a2, s2 + /* Move to next scratch space */ + add t1, t1, t2 + blt t1, s7, _scratch_init + + /* + * Relocate Flatened Device Tree (FDT) + * source FDT address = previous arg1 + * destination FDT address = next arg1 + * + * Note: We will preserve a0 and a1 passed by + * previous booting stage. + */ + beqz a1, _fdt_reloc_done + /* Mask values in a3 and a4 */ + li a3, ~(__SIZEOF_POINTER__ - 1) + li a4, 0xff + /* t1 = destination FDT start address */ + MOV_3R s0, a0, s1, a1, s2, a2 + call fw_next_arg1 + add t1, a0, zero + MOV_3R a0, s0, a1, s1, a2, s2 + beqz t1, _fdt_reloc_done + beq t1, a1, _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 */ +#if __riscv_xlen == 64 + lwu t2, 4(t0) +#else + lw t2, 4(t0) +#endif + /* 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 = destination 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: + + /* mark boot hart done */ + li t0, BOOT_STATUS_BOOT_HART_DONE + la t1, _boot_status + REG_S t0, 0(t1) + fence rw, rw + j _start_warm + + /* waiting for boot hart to be done (_boot_status == 2) */ +_wait_for_boot_hart: + li t0, BOOT_STATUS_BOOT_HART_DONE + la t1, _boot_status + REG_L t1, 0(t1) + /* Reduce the bus traffic so that boot hart may proceed faster */ + nop + nop + nop + bne t0, t1, _wait_for_boot_hart + +_start_warm: + /* Reset all registers for non-boot HARTs */ + li ra, 0 + call _reset_regs + + /* Disable and clear all interrupts */ + csrw CSR_MIE, zero + csrw CSR_MIP, zero + + /* Find HART count and HART stack size */ + la a4, platform +#if __riscv_xlen == 64 + lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) +#else + lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) +#endif + REG_L s9, SBI_PLATFORM_HART_INDEX2ID_OFFSET(a4) + + /* Find HART id */ + csrr s6, CSR_MHARTID + + /* Find HART index */ + beqz s9, 3f + li a4, 0 +1: +#if __riscv_xlen == 64 + lwu a5, (s9) +#else + lw a5, (s9) +#endif + beq a5, s6, 2f + add s9, s9, 4 + add a4, a4, 1 + blt a4, s7, 1b + li a4, -1 +2: add s6, a4, zero +3: bge s6, s7, _start_hang + + /* Find the scratch space based on HART index */ + la tp, _fw_end + mul a5, s7, s8 + add tp, tp, a5 + mul a5, s8, s6 + sub tp, tp, a5 + li a5, SBI_SCRATCH_SIZE + sub tp, tp, a5 + + /* update the mscratch */ + csrw CSR_MSCRATCH, tp + + /* Setup stack */ + add sp, tp, zero + + /* Setup trap handler */ + la a4, _trap_handler +#if __riscv_xlen == 32 + csrr a5, CSR_MISA + srli a5, a5, ('H' - 'A') + andi a5, a5, 0x1 + beq a5, zero, _skip_trap_handler_rv32_hyp + la a4, _trap_handler_rv32_hyp +_skip_trap_handler_rv32_hyp: +#endif + csrw CSR_MTVEC, a4 + +#if __riscv_xlen == 32 + /* Override trap exit for H-extension */ + csrr a5, CSR_MISA + srli a5, a5, ('H' - 'A') + andi a5, a5, 0x1 + beq a5, zero, _skip_trap_exit_rv32_hyp + la a4, _trap_exit_rv32_hyp + csrr a5, CSR_MSCRATCH + REG_S a4, SBI_SCRATCH_TRAP_EXIT_OFFSET(a5) +_skip_trap_exit_rv32_hyp: +#endif + + /* Initialize SBI runtime */ + csrr a0, CSR_MSCRATCH + call sbi_init + + /* We don't expect to reach here hence just hang */ + j _start_hang + + .align 3 +_relocate_lottery: + RISCV_PTR 0 +_boot_status: + RISCV_PTR 0 +_load_start: + RISCV_PTR _fw_start +_link_start: + RISCV_PTR _fw_start +_link_end: + RISCV_PTR _fw_reloc_end + + .section .entry, "ax", %progbits + .align 3 + .globl _hartid_to_scratch +_hartid_to_scratch: + /* + * a0 -> HART ID (passed by caller) + * a1 -> HART Index (passed by caller) + * t0 -> HART Stack Size + * t1 -> HART Stack End + * t2 -> Temporary + */ + la t2, platform +#if __riscv_xlen == 64 + lwu t0, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(t2) + lwu t2, SBI_PLATFORM_HART_COUNT_OFFSET(t2) +#else + lw t0, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(t2) + lw t2, SBI_PLATFORM_HART_COUNT_OFFSET(t2) +#endif + sub t2, t2, a1 + mul t2, t2, t0 + la t1, _fw_end + add t1, t1, t2 + li t2, SBI_SCRATCH_SIZE + sub a0, t1, t2 + ret + + .section .entry, "ax", %progbits + .align 3 + .globl _start_hang +_start_hang: + wfi + j _start_hang + + .section .entry, "ax", %progbits + .align 3 + .globl fw_platform_init + .weak fw_platform_init +fw_platform_init: + add a0, a1, zero + ret + +.macro TRAP_SAVE_AND_SETUP_SP_T0 + /* Swap TP and MSCRATCH */ + csrrw tp, CSR_MSCRATCH, tp + + /* Save T0 in scratch space */ + REG_S t0, SBI_SCRATCH_TMP0_OFFSET(tp) + + /* + * Set T0 to appropriate exception stack + * + * Came_From_M_Mode = ((MSTATUS.MPP < PRV_M) ? 1 : 0) - 1; + * Exception_Stack = TP ^ (Came_From_M_Mode & (SP ^ TP)) + * + * Came_From_M_Mode = 0 ==> Exception_Stack = TP + * Came_From_M_Mode = -1 ==> Exception_Stack = SP + */ + csrr t0, CSR_MSTATUS + srl t0, t0, MSTATUS_MPP_SHIFT + and t0, t0, PRV_M + slti t0, t0, PRV_M + add t0, t0, -1 + xor sp, sp, tp + and t0, t0, sp + xor sp, sp, tp + xor t0, tp, t0 + + /* Save original SP on exception stack */ + REG_S sp, (SBI_TRAP_REGS_OFFSET(sp) - SBI_TRAP_REGS_SIZE)(t0) + + /* Set SP to exception stack and make room for trap registers */ + add sp, t0, -(SBI_TRAP_REGS_SIZE) + + /* Restore T0 from scratch space */ + REG_L t0, SBI_SCRATCH_TMP0_OFFSET(tp) + + /* Save T0 on stack */ + REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp) + + /* Swap TP and MSCRATCH */ + csrrw tp, CSR_MSCRATCH, tp +.endm + +.macro TRAP_SAVE_MEPC_MSTATUS have_mstatush + /* Save MEPC and MSTATUS CSRs */ + csrr t0, CSR_MEPC + REG_S t0, SBI_TRAP_REGS_OFFSET(mepc)(sp) + csrr t0, CSR_MSTATUS + REG_S t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp) + .if \have_mstatush + csrr t0, CSR_MSTATUSH + REG_S t0, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) + .else + REG_S zero, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) + .endif +.endm + +.macro TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 + /* Save all general regisers except SP and T0 */ + REG_S zero, SBI_TRAP_REGS_OFFSET(zero)(sp) + REG_S ra, SBI_TRAP_REGS_OFFSET(ra)(sp) + REG_S gp, SBI_TRAP_REGS_OFFSET(gp)(sp) + REG_S tp, SBI_TRAP_REGS_OFFSET(tp)(sp) + REG_S t1, SBI_TRAP_REGS_OFFSET(t1)(sp) + REG_S t2, SBI_TRAP_REGS_OFFSET(t2)(sp) + REG_S s0, SBI_TRAP_REGS_OFFSET(s0)(sp) + REG_S s1, SBI_TRAP_REGS_OFFSET(s1)(sp) + REG_S a0, SBI_TRAP_REGS_OFFSET(a0)(sp) + REG_S a1, SBI_TRAP_REGS_OFFSET(a1)(sp) + REG_S a2, SBI_TRAP_REGS_OFFSET(a2)(sp) + REG_S a3, SBI_TRAP_REGS_OFFSET(a3)(sp) + REG_S a4, SBI_TRAP_REGS_OFFSET(a4)(sp) + REG_S a5, SBI_TRAP_REGS_OFFSET(a5)(sp) + REG_S a6, SBI_TRAP_REGS_OFFSET(a6)(sp) + REG_S a7, SBI_TRAP_REGS_OFFSET(a7)(sp) + REG_S s2, SBI_TRAP_REGS_OFFSET(s2)(sp) + REG_S s3, SBI_TRAP_REGS_OFFSET(s3)(sp) + REG_S s4, SBI_TRAP_REGS_OFFSET(s4)(sp) + REG_S s5, SBI_TRAP_REGS_OFFSET(s5)(sp) + REG_S s6, SBI_TRAP_REGS_OFFSET(s6)(sp) + REG_S s7, SBI_TRAP_REGS_OFFSET(s7)(sp) + REG_S s8, SBI_TRAP_REGS_OFFSET(s8)(sp) + REG_S s9, SBI_TRAP_REGS_OFFSET(s9)(sp) + REG_S s10, SBI_TRAP_REGS_OFFSET(s10)(sp) + REG_S s11, SBI_TRAP_REGS_OFFSET(s11)(sp) + REG_S t3, SBI_TRAP_REGS_OFFSET(t3)(sp) + REG_S t4, SBI_TRAP_REGS_OFFSET(t4)(sp) + REG_S t5, SBI_TRAP_REGS_OFFSET(t5)(sp) + REG_S t6, SBI_TRAP_REGS_OFFSET(t6)(sp) +.endm + +.macro TRAP_CALL_C_ROUTINE + /* Call C routine */ + add a0, sp, zero + call sbi_trap_handler +.endm + +.macro TRAP_RESTORE_GENERAL_REGS_EXCEPT_SP_T0 + /* Restore all general regisers except SP and T0 */ + REG_L ra, SBI_TRAP_REGS_OFFSET(ra)(sp) + REG_L gp, SBI_TRAP_REGS_OFFSET(gp)(sp) + REG_L tp, SBI_TRAP_REGS_OFFSET(tp)(sp) + REG_L t1, SBI_TRAP_REGS_OFFSET(t1)(sp) + REG_L t2, SBI_TRAP_REGS_OFFSET(t2)(sp) + REG_L s0, SBI_TRAP_REGS_OFFSET(s0)(sp) + REG_L s1, SBI_TRAP_REGS_OFFSET(s1)(sp) + REG_L a0, SBI_TRAP_REGS_OFFSET(a0)(sp) + REG_L a1, SBI_TRAP_REGS_OFFSET(a1)(sp) + REG_L a2, SBI_TRAP_REGS_OFFSET(a2)(sp) + REG_L a3, SBI_TRAP_REGS_OFFSET(a3)(sp) + REG_L a4, SBI_TRAP_REGS_OFFSET(a4)(sp) + REG_L a5, SBI_TRAP_REGS_OFFSET(a5)(sp) + REG_L a6, SBI_TRAP_REGS_OFFSET(a6)(sp) + REG_L a7, SBI_TRAP_REGS_OFFSET(a7)(sp) + REG_L s2, SBI_TRAP_REGS_OFFSET(s2)(sp) + REG_L s3, SBI_TRAP_REGS_OFFSET(s3)(sp) + REG_L s4, SBI_TRAP_REGS_OFFSET(s4)(sp) + REG_L s5, SBI_TRAP_REGS_OFFSET(s5)(sp) + REG_L s6, SBI_TRAP_REGS_OFFSET(s6)(sp) + REG_L s7, SBI_TRAP_REGS_OFFSET(s7)(sp) + REG_L s8, SBI_TRAP_REGS_OFFSET(s8)(sp) + REG_L s9, SBI_TRAP_REGS_OFFSET(s9)(sp) + REG_L s10, SBI_TRAP_REGS_OFFSET(s10)(sp) + REG_L s11, SBI_TRAP_REGS_OFFSET(s11)(sp) + REG_L t3, SBI_TRAP_REGS_OFFSET(t3)(sp) + REG_L t4, SBI_TRAP_REGS_OFFSET(t4)(sp) + REG_L t5, SBI_TRAP_REGS_OFFSET(t5)(sp) + REG_L t6, SBI_TRAP_REGS_OFFSET(t6)(sp) +.endm + +.macro TRAP_RESTORE_MEPC_MSTATUS have_mstatush + /* Restore MEPC and MSTATUS CSRs */ + REG_L t0, SBI_TRAP_REGS_OFFSET(mepc)(sp) + csrw CSR_MEPC, t0 + REG_L t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp) + csrw CSR_MSTATUS, t0 + .if \have_mstatush + REG_L t0, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) + csrw CSR_MSTATUSH, t0 + .endif +.endm + +.macro TRAP_RESTORE_SP_T0 + /* Restore T0 */ + REG_L t0, SBI_TRAP_REGS_OFFSET(t0)(sp) + + /* Restore SP */ + REG_L sp, SBI_TRAP_REGS_OFFSET(sp)(sp) +.endm + + .section .entry, "ax", %progbits + .align 3 + .globl _trap_handler +_trap_handler: + TRAP_SAVE_AND_SETUP_SP_T0 + + TRAP_SAVE_MEPC_MSTATUS 0 + + TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_CALL_C_ROUTINE + + TRAP_RESTORE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_RESTORE_MEPC_MSTATUS 0 + + TRAP_RESTORE_SP_T0 + + mret + + .section .entry, "ax", %progbits + .align 3 + .globl _trap_exit +_trap_exit: + add sp, a0, zero + + TRAP_RESTORE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_RESTORE_MEPC_MSTATUS 0 + + TRAP_RESTORE_SP_T0 + + mret + +#if __riscv_xlen == 32 + .section .entry, "ax", %progbits + .align 3 + .globl _trap_handler_rv32_hyp +_trap_handler_rv32_hyp: + TRAP_SAVE_AND_SETUP_SP_T0 + + TRAP_SAVE_MEPC_MSTATUS 1 + + TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_CALL_C_ROUTINE + + TRAP_RESTORE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_RESTORE_MEPC_MSTATUS 1 + + TRAP_RESTORE_SP_T0 + + mret + + .section .entry, "ax", %progbits + .align 3 + .globl _trap_exit_rv32_hyp +_trap_exit_rv32_hyp: + add sp, a0, zero + + TRAP_RESTORE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_RESTORE_MEPC_MSTATUS 1 + + TRAP_RESTORE_SP_T0 + + mret +#endif + + .section .entry, "ax", %progbits + .align 3 + .globl _reset_regs +_reset_regs: + + /* flush the instruction cache */ + fence.i + /* Reset all registers except ra, a0, a1 and a2 */ + li sp, 0 + li gp, 0 + li tp, 0 + li t0, 0 + li t1, 0 + li t2, 0 + li s0, 0 + li s1, 0 + li a3, 0 + li a4, 0 + li a5, 0 + li a6, 0 + li a7, 0 + li s2, 0 + li s3, 0 + li s4, 0 + li s5, 0 + li s6, 0 + li s7, 0 + li s8, 0 + li s9, 0 + li s10, 0 + li s11, 0 + li t3, 0 + li t4, 0 + li t5, 0 + li t6, 0 + csrw CSR_MSCRATCH, 0 + + ret + +#ifdef FW_FDT_PATH + .section .rodata + .align 4 + .globl fw_fdt_bin +fw_fdt_bin: + .incbin FW_FDT_PATH +#ifdef FW_FDT_PADDING + .fill FW_FDT_PADDING, 1, 0 +#endif +#endif diff --git a/roms/opensbi/firmware/fw_base.ldS b/roms/opensbi/firmware/fw_base.ldS new file mode 100644 index 000000000..0ac75f200 --- /dev/null +++ b/roms/opensbi/firmware/fw_base.ldS @@ -0,0 +1,81 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + + . = FW_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 = .); + + *(.sdata) + *(.sdata.*) + *(.data) + *(.data.*) + *(.readmostly.data) + *(*.data) + . = ALIGN(8); + + PROVIDE(_data_end = .); + } + + . = ALIGN(0x1000); /* Ensure next section is page aligned */ + + .bss : + { + PROVIDE(_bss_start = .); + *(.sbss) + *(.sbss.*) + *(.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/roms/opensbi/firmware/fw_dynamic.S b/roms/opensbi/firmware/fw_dynamic.S new file mode 100644 index 000000000..8b56947af --- /dev/null +++ b/roms/opensbi/firmware/fw_dynamic.S @@ -0,0 +1,151 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/fw_dynamic.h> + +#include "fw_base.S" + + .section .entry, "ax", %progbits + .align 3 +_bad_dynamic_info: + wfi + j _bad_dynamic_info + + .section .entry, "ax", %progbits + .align 3 + .global fw_boot_hart + /* + * This function is called very early even before + * fw_save_info() is called. + * We can only use a0, a1, and a2 registers here. + * The boot HART id should be returned in 'a0'. + */ +fw_boot_hart: + /* Sanity checks */ + li a1, FW_DYNAMIC_INFO_MAGIC_VALUE + REG_L a0, FW_DYNAMIC_INFO_MAGIC_OFFSET(a2) + bne a0, a1, _bad_dynamic_info + li a1, FW_DYNAMIC_INFO_VERSION_MAX + REG_L a0, FW_DYNAMIC_INFO_VERSION_OFFSET(a2) + bgt a0, a1, _bad_dynamic_info + + /* Read boot HART id */ + li a1, 0x2 + blt a0, a1, 2f + REG_L a0, FW_DYNAMIC_INFO_BOOT_HART_OFFSET(a2) + ret +2: li a0, -1 + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_save_info + /* + * We can only use a0, a1, a2, a3, and a4 registers here. + * The a0, a1, and a2 registers will be same as passed by + * previous booting stage. + * Nothing to be returned here. + */ +fw_save_info: + /* Save next arg1 in 'a1' */ + la a4, _dynamic_next_arg1 + REG_S a1, (a4) + + /* Sanity checks */ + li a4, FW_DYNAMIC_INFO_MAGIC_VALUE + REG_L a3, FW_DYNAMIC_INFO_MAGIC_OFFSET(a2) + bne a3, a4, _bad_dynamic_info + li a4, FW_DYNAMIC_INFO_VERSION_MAX + REG_L a3, FW_DYNAMIC_INFO_VERSION_OFFSET(a2) + bgt a3, a4, _bad_dynamic_info + + /* Save version == 0x1 fields */ + la a4, _dynamic_next_addr + REG_L a3, FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET(a2) + REG_S a3, (a4) + la a4, _dynamic_next_mode + REG_L a3, FW_DYNAMIC_INFO_NEXT_MODE_OFFSET(a2) + REG_S a3, (a4) + la a4, _dynamic_options + REG_L a3, FW_DYNAMIC_INFO_OPTIONS_OFFSET(a2) + REG_S a3, (a4) + + /* Save version == 0x2 fields */ + li a4, 0x2 + REG_L a3, FW_DYNAMIC_INFO_VERSION_OFFSET(a2) + blt a3, a4, 2f + la a4, _dynamic_boot_hart + REG_L a3, FW_DYNAMIC_INFO_BOOT_HART_OFFSET(a2) + REG_S a3, (a4) +2: + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_next_arg1 + /* + * We can only use a0, a1, and a2 registers here. + * The a0, a1, and a2 registers will be same as passed by + * previous booting stage. + * The next arg1 should be returned in 'a0'. + */ +fw_next_arg1: + la a0, _dynamic_next_arg1 + REG_L a0, (a0) + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_next_addr + /* + * We can only use a0, a1, and a2 registers here. + * The next address should be returned in 'a0'. + */ +fw_next_addr: + la a0, _dynamic_next_addr + REG_L a0, (a0) + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_next_mode + /* + * We can only use a0, a1, and a2 registers here. + * The next address should be returned in 'a0' + */ +fw_next_mode: + la a0, _dynamic_next_mode + REG_L a0, (a0) + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_options + /* + * We can only use a0, a1, and a2 registers here. + * The 'a4' register will have default options. + * The next address should be returned in 'a0'. + */ +fw_options: + la a0, _dynamic_options + REG_L a0, (a0) + ret + + .section .entry, "ax", %progbits + .align 3 +_dynamic_next_arg1: + RISCV_PTR 0x0 +_dynamic_next_addr: + RISCV_PTR 0x0 +_dynamic_next_mode: + RISCV_PTR PRV_S +_dynamic_options: + RISCV_PTR 0x0 +_dynamic_boot_hart: + RISCV_PTR -1 diff --git a/roms/opensbi/firmware/fw_dynamic.elf.ldS b/roms/opensbi/firmware/fw_dynamic.elf.ldS new file mode 100644 index 000000000..d1e2ea89e --- /dev/null +++ b/roms/opensbi/firmware/fw_dynamic.elf.ldS @@ -0,0 +1,18 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +SECTIONS +{ + #include "fw_base.ldS" + + PROVIDE(_fw_reloc_end = .); +} diff --git a/roms/opensbi/firmware/fw_jump.S b/roms/opensbi/firmware/fw_jump.S new file mode 100644 index 000000000..8553f8c7f --- /dev/null +++ b/roms/opensbi/firmware/fw_jump.S @@ -0,0 +1,96 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include "fw_base.S" + + .section .entry, "ax", %progbits + .align 3 + .global fw_boot_hart + /* + * This function is called very early even before + * fw_save_info() is called. + * We can only use a0, a1, and a2 registers here. + * The boot HART id should be returned in 'a0'. + */ +fw_boot_hart: + li a0, -1 + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_save_info + /* + * We can only use a0, a1, a2, a3, and a4 registers here. + * The a0, a1, and a2 registers will be same as passed by + * previous booting stage. + * Nothing to be returned here. + */ +fw_save_info: + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_next_arg1 + /* + * We can only use a0, a1, and a2 registers here. + * The a0, a1, and a2 registers will be same as passed by + * previous booting stage. + * The next arg1 should be returned in 'a0'. + */ +fw_next_arg1: +#ifdef FW_JUMP_FDT_ADDR + li a0, FW_JUMP_FDT_ADDR +#else + add a0, a1, zero +#endif + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_next_addr + /* + * We can only use a0, a1, and a2 registers here. + * The next address should be returned in 'a0'. + */ +fw_next_addr: + la a0, _jump_addr + REG_L a0, (a0) + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_next_mode + /* + * We can only use a0, a1, and a2 registers here. + * The next address should be returned in 'a0' + */ +fw_next_mode: + li a0, PRV_S + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_options + /* + * We can only use a0, a1, and a2 registers here. + * The 'a4' register will have default options. + * The next address should be returned in 'a0'. + */ +fw_options: + add a0, zero, zero + ret + +#ifndef FW_JUMP_ADDR +#error "Must define FW_JUMP_ADDR" +#endif + + .section .entry, "ax", %progbits + .align 3 +_jump_addr: + RISCV_PTR FW_JUMP_ADDR diff --git a/roms/opensbi/firmware/fw_jump.elf.ldS b/roms/opensbi/firmware/fw_jump.elf.ldS new file mode 100644 index 000000000..d1e2ea89e --- /dev/null +++ b/roms/opensbi/firmware/fw_jump.elf.ldS @@ -0,0 +1,18 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +SECTIONS +{ + #include "fw_base.ldS" + + PROVIDE(_fw_reloc_end = .); +} diff --git a/roms/opensbi/firmware/fw_payload.S b/roms/opensbi/firmware/fw_payload.S new file mode 100644 index 000000000..1ef121e28 --- /dev/null +++ b/roms/opensbi/firmware/fw_payload.S @@ -0,0 +1,97 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include "fw_base.S" + + .section .entry, "ax", %progbits + .align 3 + .global fw_boot_hart + /* + * This function is called very early even before + * fw_save_info() is called. + * We can only use a0, a1, and a2 registers here. + * The boot HART id should be returned in 'a0'. + */ +fw_boot_hart: + li a0, -1 + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_save_info + /* + * We can only use a0, a1, a2, a3, and a4 registers here. + * The a0, a1, and a2 registers will be same as passed by + * previous booting stage. + * Nothing to be returned here. + */ +fw_save_info: + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_next_arg1 + /* + * We can only use a0, a1, and a2 registers here. + * The a0, a1, and a2 registers will be same as passed by + * previous booting stage. + * The next arg1 should be returned in 'a0'. + */ +fw_next_arg1: +#ifdef FW_PAYLOAD_FDT_ADDR + li a0, FW_PAYLOAD_FDT_ADDR +#else + add a0, a1, zero +#endif + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_next_addr + /* + * We can only use a0, a1, and a2 registers here. + * The next address should be returned in 'a0'. + */ +fw_next_addr: + la a0, payload_bin + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_next_mode + /* + * We can only use a0, a1, and a2 registers here. + * The next address should be returned in 'a0'. + */ +fw_next_mode: + li a0, PRV_S + ret + + .section .entry, "ax", %progbits + .align 3 + .global fw_options + /* + * We can only use a0, a1, and a2 registers here. + * The 'a4' register will have default options. + * The next address should be returned in 'a0'. + */ +fw_options: + add a0, zero, zero + ret + + .section .payload, "ax", %progbits + .align 4 + .globl payload_bin +payload_bin: +#ifndef FW_PAYLOAD_PATH + wfi + j payload_bin +#else + .incbin FW_PAYLOAD_PATH +#endif diff --git a/roms/opensbi/firmware/fw_payload.elf.ldS b/roms/opensbi/firmware/fw_payload.elf.ldS new file mode 100644 index 000000000..f1a544b80 --- /dev/null +++ b/roms/opensbi/firmware/fw_payload.elf.ldS @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +SECTIONS +{ + #include "fw_base.ldS" + +#ifdef FW_PAYLOAD_OFFSET + . = FW_TEXT_START + FW_PAYLOAD_OFFSET; +#else + . = ALIGN(FW_PAYLOAD_ALIGN); +#endif + + .payload : + { + PROVIDE(_payload_start = .); + *(.payload) + . = ALIGN(8); + PROVIDE(_payload_end = .); + } + + PROVIDE(_fw_reloc_end = .); +} diff --git a/roms/opensbi/firmware/objects.mk b/roms/opensbi/firmware/objects.mk new file mode 100644 index 000000000..b2ace7512 --- /dev/null +++ b/roms/opensbi/firmware/objects.mk @@ -0,0 +1,57 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +firmware-genflags-y = +firmware-cppflags-y += +firmware-cflags-y += +firmware-asflags-y += +firmware-ldflags-y += + +ifdef FW_TEXT_START +firmware-genflags-y += -DFW_TEXT_START=$(FW_TEXT_START) +endif + +ifdef FW_FDT_PATH +firmware-genflags-y += -DFW_FDT_PATH=\"$(FW_FDT_PATH)\" +ifdef FW_FDT_PADDING +firmware-genflags-y += -DFW_FDT_PADDING=$(FW_FDT_PADDING) +endif +endif + +firmware-bins-$(FW_DYNAMIC) += fw_dynamic.bin + +firmware-bins-$(FW_JUMP) += fw_jump.bin +ifdef FW_JUMP_ADDR +firmware-genflags-$(FW_JUMP) += -DFW_JUMP_ADDR=$(FW_JUMP_ADDR) +endif +ifdef FW_JUMP_FDT_ADDR +firmware-genflags-$(FW_JUMP) += -DFW_JUMP_FDT_ADDR=$(FW_JUMP_FDT_ADDR) +endif + +firmware-bins-$(FW_PAYLOAD) += fw_payload.bin +ifdef FW_PAYLOAD_PATH +FW_PAYLOAD_PATH_FINAL=$(FW_PAYLOAD_PATH) +else +FW_PAYLOAD_PATH_FINAL=$(platform_build_dir)/firmware/payloads/test.bin +endif +firmware-genflags-$(FW_PAYLOAD) += -DFW_PAYLOAD_PATH=\"$(FW_PAYLOAD_PATH_FINAL)\" +ifdef FW_PAYLOAD_OFFSET +firmware-genflags-$(FW_PAYLOAD) += -DFW_PAYLOAD_OFFSET=$(FW_PAYLOAD_OFFSET) +endif +ifdef FW_PAYLOAD_ALIGN +firmware-genflags-$(FW_PAYLOAD) += -DFW_PAYLOAD_ALIGN=$(FW_PAYLOAD_ALIGN) +endif + +ifdef FW_PAYLOAD_FDT_ADDR +firmware-genflags-$(FW_PAYLOAD) += -DFW_PAYLOAD_FDT_ADDR=$(FW_PAYLOAD_FDT_ADDR) +endif + +ifdef FW_OPTIONS +firmware-genflags-y += -DFW_OPTIONS=$(FW_OPTIONS) +endif diff --git a/roms/opensbi/firmware/payloads/objects.mk b/roms/opensbi/firmware/payloads/objects.mk new file mode 100644 index 000000000..21e0185a8 --- /dev/null +++ b/roms/opensbi/firmware/payloads/objects.mk @@ -0,0 +1,19 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +firmware-bins-$(FW_PAYLOAD) += payloads/test.bin + +test-y += test_head.o +test-y += test_main.o + +%/test.o: $(foreach obj,$(test-y),%/$(obj)) + $(call merge_objs,$@,$^) + +%/test.dep: $(foreach dep,$(test-y:.o=.dep),%/$(dep)) + $(call merge_deps,$@,$^) diff --git a/roms/opensbi/firmware/payloads/test.elf.ldS b/roms/opensbi/firmware/payloads/test.elf.ldS new file mode 100644 index 000000000..f3f3242ab --- /dev/null +++ b/roms/opensbi/firmware/payloads/test.elf.ldS @@ -0,0 +1,87 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +SECTIONS +{ +#ifdef FW_PAYLOAD_OFFSET + . = FW_TEXT_START + FW_PAYLOAD_OFFSET; +#else + . = ALIGN(FW_PAYLOAD_ALIGN); +#endif + + PROVIDE(_payload_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 */ + + .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(_payload_end = .); +} diff --git a/roms/opensbi/firmware/payloads/test_head.S b/roms/opensbi/firmware/payloads/test_head.S new file mode 100644 index 000000000..840013e4e --- /dev/null +++ b/roms/opensbi/firmware/payloads/test_head.S @@ -0,0 +1,88 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_encoding.h> +#define __ASM_STR(x) x + +#if __riscv_xlen == 64 +#define __REG_SEL(a, b) __ASM_STR(a) +#define RISCV_PTR .dword +#elif __riscv_xlen == 32 +#define __REG_SEL(a, b) __ASM_STR(b) +#define RISCV_PTR .word +#else +#error "Unexpected __riscv_xlen" +#endif + +#define REG_L __REG_SEL(ld, lw) +#define REG_S __REG_SEL(sd, sw) + + .section .entry, "ax", %progbits + .align 3 + .globl _start +_start: + /* Pick one hart to run the main boot sequence */ + la a3, _hart_lottery + li a2, 1 + amoadd.w a3, a2, (a3) + bnez a3, _start_hang + + /* Save a0 and a1 */ + la a3, _boot_a0 + REG_S a0, 0(a3) + la a3, _boot_a1 + REG_S a1, 0(a3) + + /* 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 + +_start_warm: + /* Disable and clear all interrupts */ + csrw CSR_SIE, zero + csrw CSR_SIP, zero + + /* Setup exception vectors */ + la a3, _start_hang + csrw CSR_STVEC, a3 + + /* Setup stack */ + la a3, _payload_end + li a4, 0x2000 + add sp, a3, a4 + + /* Jump to C main */ + la a3, _boot_a0 + REG_L a0, 0(a3) + la a3, _boot_a1 + REG_L a1, 0(a3) + call test_main + + /* We don't expect to reach here hence just hang */ + j _start_hang + + .section .entry, "ax", %progbits + .align 3 + .globl _start_hang +_start_hang: + wfi + j _start_hang + + .section .entry, "ax", %progbits + .align 3 +_hart_lottery: + RISCV_PTR 0 +_boot_a0: + RISCV_PTR 0 +_boot_a1: + RISCV_PTR 0 diff --git a/roms/opensbi/firmware/payloads/test_main.c b/roms/opensbi/firmware/payloads/test_main.c new file mode 100644 index 000000000..0d6593023 --- /dev/null +++ b/roms/opensbi/firmware/payloads/test_main.c @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_ecall_interface.h> + +#define SBI_ECALL(__num, __a0, __a1, __a2) \ + ({ \ + register unsigned long a0 asm("a0") = (unsigned long)(__a0); \ + register unsigned long a1 asm("a1") = (unsigned long)(__a1); \ + register unsigned long a2 asm("a2") = (unsigned long)(__a2); \ + register unsigned long a7 asm("a7") = (unsigned long)(__num); \ + asm volatile("ecall" \ + : "+r"(a0) \ + : "r"(a1), "r"(a2), "r"(a7) \ + : "memory"); \ + a0; \ + }) + +#define SBI_ECALL_0(__num) SBI_ECALL(__num, 0, 0, 0) +#define SBI_ECALL_1(__num, __a0) SBI_ECALL(__num, __a0, 0, 0) +#define SBI_ECALL_2(__num, __a0, __a1) SBI_ECALL(__num, __a0, __a1, 0) + +#define sbi_ecall_console_putc(c) SBI_ECALL_1(SBI_EXT_0_1_CONSOLE_PUTCHAR, (c)) + +static inline void sbi_ecall_console_puts(const char *str) +{ + while (str && *str) + sbi_ecall_console_putc(*str++); +} + +#define wfi() \ + do { \ + __asm__ __volatile__("wfi" ::: "memory"); \ + } while (0) + +void test_main(unsigned long a0, unsigned long a1) +{ + sbi_ecall_console_puts("\nTest payload running\n"); + + while (1) + wfi(); +} diff --git a/roms/opensbi/include/sbi/fw_dynamic.h b/roms/opensbi/include/sbi/fw_dynamic.h new file mode 100644 index 000000000..3c088311f --- /dev/null +++ b/roms/opensbi/include/sbi/fw_dynamic.h @@ -0,0 +1,79 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __FW_DYNAMIC_H__ +#define __FW_DYNAMIC_H__ + +#include <sbi/riscv_asm.h> + +/* clang-format off */ + +/** Offset of magic member in fw_dynamic_info */ +#define FW_DYNAMIC_INFO_MAGIC_OFFSET (0 * __SIZEOF_POINTER__) +/** Offset of version member in fw_dynamic_info */ +#define FW_DYNAMIC_INFO_VERSION_OFFSET (1 * __SIZEOF_POINTER__) +/** Offset of next_addr member in fw_dynamic_info (version >= 1) */ +#define FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET (2 * __SIZEOF_POINTER__) +/** Offset of next_mode member in fw_dynamic_info (version >= 1) */ +#define FW_DYNAMIC_INFO_NEXT_MODE_OFFSET (3 * __SIZEOF_POINTER__) +/** Offset of options member in fw_dynamic_info (version >= 1) */ +#define FW_DYNAMIC_INFO_OPTIONS_OFFSET (4 * __SIZEOF_POINTER__) +/** Offset of boot_hart member in fw_dynamic_info (version >= 2) */ +#define FW_DYNAMIC_INFO_BOOT_HART_OFFSET (5 * __SIZEOF_POINTER__) + +/** Expected value of info magic ('OSBI' ascii string in hex) */ +#define FW_DYNAMIC_INFO_MAGIC_VALUE 0x4942534f + +/** Maximum supported info version */ +#define FW_DYNAMIC_INFO_VERSION_MAX 0x2 + +/** Possible next mode values */ +#define FW_DYNAMIC_INFO_NEXT_MODE_U 0x0 +#define FW_DYNAMIC_INFO_NEXT_MODE_S 0x1 +#define FW_DYNAMIC_INFO_NEXT_MODE_M 0x3 + +/* clang-format on */ + +#ifndef __ASSEMBLY__ + +#include <sbi/sbi_types.h> + +/** Representation dynamic info passed by previous booting stage */ +struct fw_dynamic_info { + /** Info magic */ + unsigned long magic; + /** Info version */ + unsigned long version; + /** Next booting stage address */ + unsigned long next_addr; + /** Next booting stage mode */ + unsigned long next_mode; + /** Options for OpenSBI library */ + unsigned long options; + /** + * Preferred boot HART id + * + * It is possible that the previous booting stage uses same link + * address as the FW_DYNAMIC firmware. In this case, the relocation + * lottery mechanism can potentially overwrite the previous booting + * stage while other HARTs are still running in the previous booting + * stage leading to boot-time crash. To avoid this boot-time crash, + * the previous booting stage can specify last HART that will jump + * to the FW_DYNAMIC firmware as the preferred boot HART. + * + * To avoid specifying a preferred boot HART, the previous booting + * stage can set it to -1UL which will force the FW_DYNAMIC firmware + * to use the relocation lottery mechanism. + */ + unsigned long boot_hart; +} __packed; + +#endif + +#endif diff --git a/roms/opensbi/include/sbi/riscv_asm.h b/roms/opensbi/include/sbi/riscv_asm.h new file mode 100644 index 000000000..10f31a7fc --- /dev/null +++ b/roms/opensbi/include/sbi/riscv_asm.h @@ -0,0 +1,187 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __RISCV_ASM_H__ +#define __RISCV_ASM_H__ + +#include <sbi/riscv_encoding.h> + +/* clang-format off */ + +#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 PAGE_SHIFT (12) +#define PAGE_SIZE (_AC(1, UL) << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE - 1)) + +#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 + +/* clang-format on */ + +#ifndef __ASSEMBLY__ + +#define csr_swap(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrrw %0, " __ASM_STR(csr) ", %1" \ + : "=r"(__v) \ + : "rK"(__v) \ + : "memory"); \ + __v; \ + }) + +#define csr_read(csr) \ + ({ \ + register unsigned long __v; \ + __asm__ __volatile__("csrr %0, " __ASM_STR(csr) \ + : "=r"(__v) \ + : \ + : "memory"); \ + __v; \ + }) + +#define csr_write(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrw " __ASM_STR(csr) ", %0" \ + : \ + : "rK"(__v) \ + : "memory"); \ + }) + +#define csr_read_set(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrrs %0, " __ASM_STR(csr) ", %1" \ + : "=r"(__v) \ + : "rK"(__v) \ + : "memory"); \ + __v; \ + }) + +#define csr_set(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrs " __ASM_STR(csr) ", %0" \ + : \ + : "rK"(__v) \ + : "memory"); \ + }) + +#define csr_read_clear(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrrc %0, " __ASM_STR(csr) ", %1" \ + : "=r"(__v) \ + : "rK"(__v) \ + : "memory"); \ + __v; \ + }) + +#define csr_clear(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrc " __ASM_STR(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) + +/* Get current HART id */ +#define current_hartid() ((unsigned int)csr_read(CSR_MHARTID)) + +/* determine CPU extension, return non-zero support */ +int misa_extension_imp(char ext); + +#define misa_extension(c)\ +({\ + _Static_assert(((c >= 'A') && (c <= 'Z')),\ + "The parameter of misa_extension must be [A-Z]");\ + misa_extension_imp(c);\ +}) + +/* Get MXL field of misa, return -1 on error */ +int misa_xlen(void); + +/* Get RISC-V ISA string representation */ +void misa_string(int xlen, char *out, unsigned int out_sz); + +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); + +#endif /* !__ASSEMBLY__ */ + +#endif diff --git a/roms/opensbi/include/sbi/riscv_atomic.h b/roms/opensbi/include/sbi/riscv_atomic.h new file mode 100644 index 000000000..3972e0b72 --- /dev/null +++ b/roms/opensbi/include/sbi/riscv_atomic.h @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#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 atomic_cmpxchg(atomic_t *atom, long oldval, long newval); + +long atomic_xchg(atomic_t *atom, long newval); + +unsigned int atomic_raw_xchg_uint(volatile unsigned int *ptr, + unsigned int newval); + +unsigned long atomic_raw_xchg_ulong(volatile unsigned long *ptr, + unsigned long newval); +/** + * Set a bit in an atomic variable and return the new value. + * @nr : Bit to set. + * @atom: atomic variable to modify + */ +int atomic_set_bit(int nr, atomic_t *atom); + +/** + * Clear a bit in an atomic variable and return the new value. + * @nr : Bit to set. + * @atom: atomic variable to modify + */ + +int atomic_clear_bit(int nr, atomic_t *atom); + +/** + * Set a bit in any address and return the new value . + * @nr : Bit to set. + * @addr: Address to modify + */ +int atomic_raw_set_bit(int nr, volatile unsigned long *addr); + +/** + * Clear a bit in any address and return the new value . + * @nr : Bit to set. + * @addr: Address to modify + */ +int atomic_raw_clear_bit(int nr, volatile unsigned long *addr); + +#endif diff --git a/roms/opensbi/include/sbi/riscv_barrier.h b/roms/opensbi/include/sbi/riscv_barrier.h new file mode 100644 index 000000000..905ecb40f --- /dev/null +++ b/roms/opensbi/include/sbi/riscv_barrier.h @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __RISCV_BARRIER_H__ +#define __RISCV_BARRIER_H__ + +/* clang-format off */ + +#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") + +/* clang-format on */ + +#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/roms/opensbi/include/sbi/riscv_encoding.h b/roms/opensbi/include/sbi/riscv_encoding.h new file mode 100644 index 000000000..e1d0b463c --- /dev/null +++ b/roms/opensbi/include/sbi/riscv_encoding.h @@ -0,0 +1,699 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __RISCV_ENCODING_H__ +#define __RISCV_ENCODING_H__ + +#include <sbi/sbi_const.h> + +/* clang-format off */ +#define MSTATUS_SIE _UL(0x00000002) +#define MSTATUS_MIE _UL(0x00000008) +#define MSTATUS_SPIE_SHIFT 5 +#define MSTATUS_SPIE (_UL(1) << MSTATUS_SPIE_SHIFT) +#define MSTATUS_UBE _UL(0x00000040) +#define MSTATUS_MPIE _UL(0x00000080) +#define MSTATUS_SPP_SHIFT 8 +#define MSTATUS_SPP (_UL(1) << MSTATUS_SPP_SHIFT) +#define MSTATUS_MPP_SHIFT 11 +#define MSTATUS_MPP (_UL(3) << MSTATUS_MPP_SHIFT) +#define MSTATUS_FS _UL(0x00006000) +#define MSTATUS_XS _UL(0x00018000) +#define MSTATUS_VS _UL(0x01800000) +#define MSTATUS_MPRV _UL(0x00020000) +#define MSTATUS_SUM _UL(0x00040000) +#define MSTATUS_MXR _UL(0x00080000) +#define MSTATUS_TVM _UL(0x00100000) +#define MSTATUS_TW _UL(0x00200000) +#define MSTATUS_TSR _UL(0x00400000) +#define MSTATUS32_SD _UL(0x80000000) +#if __riscv_xlen == 64 +#define MSTATUS_UXL _ULL(0x0000000300000000) +#define MSTATUS_SXL _ULL(0x0000000C00000000) +#define MSTATUS_SBE _ULL(0x0000001000000000) +#define MSTATUS_MBE _ULL(0x0000002000000000) +#define MSTATUS_MPV _ULL(0x0000008000000000) +#else +#define MSTATUSH_SBE _UL(0x00000010) +#define MSTATUSH_MBE _UL(0x00000020) +#define MSTATUSH_MPV _UL(0x00000080) +#endif +#define MSTATUS32_SD _UL(0x80000000) +#define MSTATUS64_SD _ULL(0x8000000000000000) + +#define SSTATUS_SIE MSTATUS_SIE +#define SSTATUS_SPIE_SHIFT MSTATUS_SPIE_SHIFT +#define SSTATUS_SPIE MSTATUS_SPIE +#define SSTATUS_SPP_SHIFT MSTATUS_SPP_SHIFT +#define SSTATUS_SPP MSTATUS_SPP +#define SSTATUS_FS MSTATUS_FS +#define SSTATUS_XS MSTATUS_XS +#define SSTATUS_VS MSTATUS_VS +#define SSTATUS_SUM MSTATUS_SUM +#define SSTATUS_MXR MSTATUS_MXR +#define SSTATUS32_SD MSTATUS32_SD +#define SSTATUS64_UXL MSTATUS_UXL +#define SSTATUS64_SD MSTATUS64_SD + +#if __riscv_xlen == 64 +#define HSTATUS_VSXL _UL(0x300000000) +#define HSTATUS_VSXL_SHIFT 32 +#endif +#define HSTATUS_VTSR _UL(0x00400000) +#define HSTATUS_VTW _UL(0x00200000) +#define HSTATUS_VTVM _UL(0x00100000) +#define HSTATUS_VGEIN _UL(0x0003f000) +#define HSTATUS_VGEIN_SHIFT 12 +#define HSTATUS_HU _UL(0x00000200) +#define HSTATUS_SPVP _UL(0x00000100) +#define HSTATUS_SPV _UL(0x00000080) +#define HSTATUS_GVA _UL(0x00000040) +#define HSTATUS_VSBE _UL(0x00000020) + +#define IRQ_S_SOFT 1 +#define IRQ_VS_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_S_TIMER 5 +#define IRQ_VS_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_S_EXT 9 +#define IRQ_VS_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_S_GEXT 12 + +#define MIP_SSIP (_UL(1) << IRQ_S_SOFT) +#define MIP_VSSIP (_UL(1) << IRQ_VS_SOFT) +#define MIP_MSIP (_UL(1) << IRQ_M_SOFT) +#define MIP_STIP (_UL(1) << IRQ_S_TIMER) +#define MIP_VSTIP (_UL(1) << IRQ_VS_TIMER) +#define MIP_MTIP (_UL(1) << IRQ_M_TIMER) +#define MIP_SEIP (_UL(1) << IRQ_S_EXT) +#define MIP_VSEIP (_UL(1) << IRQ_VS_EXT) +#define MIP_MEIP (_UL(1) << IRQ_M_EXT) +#define MIP_SGEIP (_UL(1) << IRQ_S_GEXT) + +#define SIP_SSIP MIP_SSIP +#define SIP_STIP MIP_STIP + +#define PRV_U _UL(0) +#define PRV_S _UL(1) +#define PRV_M _UL(3) + +#define SATP32_MODE _UL(0x80000000) +#define SATP32_ASID _UL(0x7FC00000) +#define SATP32_PPN _UL(0x003FFFFF) +#define SATP64_MODE _ULL(0xF000000000000000) +#define SATP64_ASID _ULL(0x0FFFF00000000000) +#define SATP64_PPN _ULL(0x00000FFFFFFFFFFF) + +#define SATP_MODE_OFF _UL(0) +#define SATP_MODE_SV32 _UL(1) +#define SATP_MODE_SV39 _UL(8) +#define SATP_MODE_SV48 _UL(9) +#define SATP_MODE_SV57 _UL(10) +#define SATP_MODE_SV64 _UL(11) + +#define HGATP_MODE_OFF _UL(0) +#define HGATP_MODE_SV32X4 _UL(1) +#define HGATP_MODE_SV39X4 _UL(8) +#define HGATP_MODE_SV48X4 _UL(9) + +#define HGATP32_MODE_SHIFT 31 +#define HGATP32_VMID_SHIFT 22 +#define HGATP32_VMID_MASK _UL(0x1FC00000) +#define HGATP32_PPN _UL(0x003FFFFF) + +#define HGATP64_MODE_SHIFT 60 +#define HGATP64_VMID_SHIFT 44 +#define HGATP64_VMID_MASK _ULL(0x03FFF00000000000) +#define HGATP64_PPN _ULL(0x00000FFFFFFFFFFF) + +#define PMP_R _UL(0x01) +#define PMP_W _UL(0x02) +#define PMP_X _UL(0x04) +#define PMP_A _UL(0x18) +#define PMP_A_TOR _UL(0x08) +#define PMP_A_NA4 _UL(0x10) +#define PMP_A_NAPOT _UL(0x18) +#define PMP_L _UL(0x80) + +#define PMP_SHIFT 2 +#define PMP_COUNT 64 +#if __riscv_xlen == 64 +#define PMP_ADDR_MASK ((_ULL(0x1) << 54) - 1) +#else +#define PMP_ADDR_MASK _UL(0xFFFFFFFF) +#endif + +#if __riscv_xlen == 64 +#define MSTATUS_SD MSTATUS64_SD +#define SSTATUS_SD SSTATUS64_SD +#define SATP_MODE SATP64_MODE + +#define HGATP_PPN HGATP64_PPN +#define HGATP_VMID_SHIFT HGATP64_VMID_SHIFT +#define HGATP_VMID_MASK HGATP64_VMID_MASK +#define HGATP_MODE_SHIFT HGATP64_MODE_SHIFT +#else +#define MSTATUS_SD MSTATUS32_SD +#define SSTATUS_SD SSTATUS32_SD +#define SATP_MODE SATP32_MODE + +#define HGATP_PPN HGATP32_PPN +#define HGATP_VMID_SHIFT HGATP32_VMID_SHIFT +#define HGATP_VMID_MASK HGATP32_VMID_MASK +#define HGATP_MODE_SHIFT HGATP32_MODE_SHIFT +#endif + +/* ===== User-level CSRs ===== */ + +/* User Trap Setup (N-extension) */ +#define CSR_USTATUS 0x000 +#define CSR_UIE 0x004 +#define CSR_UTVEC 0x005 + +/* User Trap Handling (N-extension) */ +#define CSR_USCRATCH 0x040 +#define CSR_UEPC 0x041 +#define CSR_UCAUSE 0x042 +#define CSR_UTVAL 0x043 +#define CSR_UIP 0x044 + +/* User Floating-point CSRs */ +#define CSR_FFLAGS 0x001 +#define CSR_FRM 0x002 +#define CSR_FCSR 0x003 + +/* User Counters/Timers */ +#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_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 + +/* ===== Supervisor-level CSRs ===== */ + +/* Supervisor Trap Setup */ +#define CSR_SSTATUS 0x100 +#define CSR_SEDELEG 0x102 +#define CSR_SIDELEG 0x103 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 + +/* Supervisor Trap Handling */ +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_STVAL 0x143 +#define CSR_SIP 0x144 + +/* Supervisor Protection and Translation */ +#define CSR_SATP 0x180 + +/* ===== Hypervisor-level CSRs ===== */ + +/* Hypervisor Trap Setup (H-extension) */ +#define CSR_HSTATUS 0x600 +#define CSR_HEDELEG 0x602 +#define CSR_HIDELEG 0x603 +#define CSR_HIE 0x604 +#define CSR_HCOUNTEREN 0x606 +#define CSR_HGEIE 0x607 + +/* Hypervisor Trap Handling (H-extension) */ +#define CSR_HTVAL 0x643 +#define CSR_HIP 0x644 +#define CSR_HVIP 0x645 +#define CSR_HTINST 0x64a +#define CSR_HGEIP 0xe12 + +/* Hypervisor Protection and Translation (H-extension) */ +#define CSR_HGATP 0x680 + +/* Hypervisor Counter/Timer Virtualization Registers (H-extension) */ +#define CSR_HTIMEDELTA 0x605 +#define CSR_HTIMEDELTAH 0x615 + +/* Virtual Supervisor Registers (H-extension) */ +#define CSR_VSSTATUS 0x200 +#define CSR_VSIE 0x204 +#define CSR_VSTVEC 0x205 +#define CSR_VSSCRATCH 0x240 +#define CSR_VSEPC 0x241 +#define CSR_VSCAUSE 0x242 +#define CSR_VSTVAL 0x243 +#define CSR_VSIP 0x244 +#define CSR_VSATP 0x280 + +/* ===== Machine-level CSRs ===== */ + +/* Machine Information Registers */ +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 + +/* Machine Trap Setup */ +#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_MSTATUSH 0x310 + +/* Machine Trap Handling */ +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MTVAL 0x343 +#define CSR_MIP 0x344 +#define CSR_MTINST 0x34a +#define CSR_MTVAL2 0x34b + +/* Machine Memory Protection */ +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPCFG1 0x3a1 +#define CSR_PMPCFG2 0x3a2 +#define CSR_PMPCFG3 0x3a3 +#define CSR_PMPCFG4 0x3a4 +#define CSR_PMPCFG5 0x3a5 +#define CSR_PMPCFG6 0x3a6 +#define CSR_PMPCFG7 0x3a7 +#define CSR_PMPCFG8 0x3a8 +#define CSR_PMPCFG9 0x3a9 +#define CSR_PMPCFG10 0x3aa +#define CSR_PMPCFG11 0x3ab +#define CSR_PMPCFG12 0x3ac +#define CSR_PMPCFG13 0x3ad +#define CSR_PMPCFG14 0x3ae +#define CSR_PMPCFG15 0x3af +#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_PMPADDR16 0x3c0 +#define CSR_PMPADDR17 0x3c1 +#define CSR_PMPADDR18 0x3c2 +#define CSR_PMPADDR19 0x3c3 +#define CSR_PMPADDR20 0x3c4 +#define CSR_PMPADDR21 0x3c5 +#define CSR_PMPADDR22 0x3c6 +#define CSR_PMPADDR23 0x3c7 +#define CSR_PMPADDR24 0x3c8 +#define CSR_PMPADDR25 0x3c9 +#define CSR_PMPADDR26 0x3ca +#define CSR_PMPADDR27 0x3cb +#define CSR_PMPADDR28 0x3cc +#define CSR_PMPADDR29 0x3cd +#define CSR_PMPADDR30 0x3ce +#define CSR_PMPADDR31 0x3cf +#define CSR_PMPADDR32 0x3d0 +#define CSR_PMPADDR33 0x3d1 +#define CSR_PMPADDR34 0x3d2 +#define CSR_PMPADDR35 0x3d3 +#define CSR_PMPADDR36 0x3d4 +#define CSR_PMPADDR37 0x3d5 +#define CSR_PMPADDR38 0x3d6 +#define CSR_PMPADDR39 0x3d7 +#define CSR_PMPADDR40 0x3d8 +#define CSR_PMPADDR41 0x3d9 +#define CSR_PMPADDR42 0x3da +#define CSR_PMPADDR43 0x3db +#define CSR_PMPADDR44 0x3dc +#define CSR_PMPADDR45 0x3dd +#define CSR_PMPADDR46 0x3de +#define CSR_PMPADDR47 0x3df +#define CSR_PMPADDR48 0x3e0 +#define CSR_PMPADDR49 0x3e1 +#define CSR_PMPADDR50 0x3e2 +#define CSR_PMPADDR51 0x3e3 +#define CSR_PMPADDR52 0x3e4 +#define CSR_PMPADDR53 0x3e5 +#define CSR_PMPADDR54 0x3e6 +#define CSR_PMPADDR55 0x3e7 +#define CSR_PMPADDR56 0x3e8 +#define CSR_PMPADDR57 0x3e9 +#define CSR_PMPADDR58 0x3ea +#define CSR_PMPADDR59 0x3eb +#define CSR_PMPADDR60 0x3ec +#define CSR_PMPADDR61 0x3ed +#define CSR_PMPADDR62 0x3ee +#define CSR_PMPADDR63 0x3ef + +/* Machine Counters/Timers */ +#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_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 + +/* Machine Counter Setup */ +#define CSR_MCOUNTINHIBIT 0x320 +#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 + +/* Debug/Trace Registers */ +#define CSR_TSELECT 0x7a0 +#define CSR_TDATA1 0x7a1 +#define CSR_TDATA2 0x7a2 +#define CSR_TDATA3 0x7a3 + +/* Debug Mode Registers */ +#define CSR_DCSR 0x7b0 +#define CSR_DPC 0x7b1 +#define CSR_DSCRATCH0 0x7b2 +#define CSR_DSCRATCH1 0x7b3 + +/* ===== Trap/Exception Causes ===== */ + +#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_VIRTUAL_SUPERVISOR_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 +#define CAUSE_FETCH_GUEST_PAGE_FAULT 0x14 +#define CAUSE_LOAD_GUEST_PAGE_FAULT 0x15 +#define CAUSE_VIRTUAL_INST_FAULT 0x16 +#define CAUSE_STORE_GUEST_PAGE_FAULT 0x17 + +/* ===== Instruction Encodings ===== */ + +#define INSN_MATCH_LB 0x3 +#define INSN_MASK_LB 0x707f +#define INSN_MATCH_LH 0x1003 +#define INSN_MASK_LH 0x707f +#define INSN_MATCH_LW 0x2003 +#define INSN_MASK_LW 0x707f +#define INSN_MATCH_LD 0x3003 +#define INSN_MASK_LD 0x707f +#define INSN_MATCH_LBU 0x4003 +#define INSN_MASK_LBU 0x707f +#define INSN_MATCH_LHU 0x5003 +#define INSN_MASK_LHU 0x707f +#define INSN_MATCH_LWU 0x6003 +#define INSN_MASK_LWU 0x707f +#define INSN_MATCH_SB 0x23 +#define INSN_MASK_SB 0x707f +#define INSN_MATCH_SH 0x1023 +#define INSN_MASK_SH 0x707f +#define INSN_MATCH_SW 0x2023 +#define INSN_MASK_SW 0x707f +#define INSN_MATCH_SD 0x3023 +#define INSN_MASK_SD 0x707f + +#define INSN_MATCH_FLW 0x2007 +#define INSN_MASK_FLW 0x707f +#define INSN_MATCH_FLD 0x3007 +#define INSN_MASK_FLD 0x707f +#define INSN_MATCH_FLQ 0x4007 +#define INSN_MASK_FLQ 0x707f +#define INSN_MATCH_FSW 0x2027 +#define INSN_MASK_FSW 0x707f +#define INSN_MATCH_FSD 0x3027 +#define INSN_MASK_FSD 0x707f +#define INSN_MATCH_FSQ 0x4027 +#define INSN_MASK_FSQ 0x707f + +#define INSN_MATCH_C_LD 0x6000 +#define INSN_MASK_C_LD 0xe003 +#define INSN_MATCH_C_SD 0xe000 +#define INSN_MASK_C_SD 0xe003 +#define INSN_MATCH_C_LW 0x4000 +#define INSN_MASK_C_LW 0xe003 +#define INSN_MATCH_C_SW 0xc000 +#define INSN_MASK_C_SW 0xe003 +#define INSN_MATCH_C_LDSP 0x6002 +#define INSN_MASK_C_LDSP 0xe003 +#define INSN_MATCH_C_SDSP 0xe002 +#define INSN_MASK_C_SDSP 0xe003 +#define INSN_MATCH_C_LWSP 0x4002 +#define INSN_MASK_C_LWSP 0xe003 +#define INSN_MATCH_C_SWSP 0xc002 +#define INSN_MASK_C_SWSP 0xe003 + +#define INSN_MATCH_C_FLD 0x2000 +#define INSN_MASK_C_FLD 0xe003 +#define INSN_MATCH_C_FLW 0x6000 +#define INSN_MASK_C_FLW 0xe003 +#define INSN_MATCH_C_FSD 0xa000 +#define INSN_MASK_C_FSD 0xe003 +#define INSN_MATCH_C_FSW 0xe000 +#define INSN_MASK_C_FSW 0xe003 +#define INSN_MATCH_C_FLDSP 0x2002 +#define INSN_MASK_C_FLDSP 0xe003 +#define INSN_MATCH_C_FSDSP 0xa002 +#define INSN_MASK_C_FSDSP 0xe003 +#define INSN_MATCH_C_FLWSP 0x6002 +#define INSN_MASK_C_FLWSP 0xe003 +#define INSN_MATCH_C_FSWSP 0xe002 +#define INSN_MASK_C_FSWSP 0xe003 + +#define INSN_MASK_WFI 0xffffff00 +#define INSN_MATCH_WFI 0x10500000 + +#define INSN_16BIT_MASK 0x3 +#define INSN_32BIT_MASK 0x1c + +#define INSN_IS_16BIT(insn) \ + (((insn) & INSN_16BIT_MASK) != INSN_16BIT_MASK) +#define INSN_IS_32BIT(insn) \ + (((insn) & INSN_16BIT_MASK) == INSN_16BIT_MASK && \ + ((insn) & INSN_32BIT_MASK) != INSN_32BIT_MASK) + +#define INSN_LEN(insn) (INSN_IS_16BIT(insn) ? 2 : 4) + +#if __riscv_xlen == 64 +#define LOG_REGBYTES 3 +#else +#define LOG_REGBYTES 2 +#endif +#define REGBYTES (1 << LOG_REGBYTES) + +#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 + +/* clang-format on */ + +#endif diff --git a/roms/opensbi/include/sbi/riscv_fp.h b/roms/opensbi/include/sbi/riscv_fp.h new file mode 100644 index 000000000..a685884a5 --- /dev/null +++ b/roms/opensbi/include/sbi/riscv_fp.h @@ -0,0 +1,94 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __RISCV_FP_H__ +#define __RISCV_FP_H__ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_types.h> + +#define GET_PRECISION(insn) (((insn) >> 25) & 3) +#define GET_RM(insn) (((insn) >> 12) & 7) +#define PRECISION_S 0 +#define PRECISION_D 1 + +#ifdef __riscv_flen + +#define GET_F32_REG(insn, pos, regs) \ + ({ \ + register s32 value asm("a0") = \ + SHIFT_RIGHT(insn, (pos)-3) & 0xf8; \ + ulong tmp; \ + asm("1: auipc %0, %%pcrel_hi(get_f32_reg); add %0, %0, %1; jalr t0, %0, %%pcrel_lo(1b)" \ + : "=&r"(tmp), "+&r"(value)::"t0"); \ + value; \ + }) +#define SET_F32_REG(insn, pos, regs, val) \ + ({ \ + register u32 value asm("a0") = (val); \ + ulong offset = SHIFT_RIGHT(insn, (pos)-3) & 0xf8; \ + ulong tmp; \ + asm volatile( \ + "1: auipc %0, %%pcrel_hi(put_f32_reg); add %0, %0, %2; jalr t0, %0, %%pcrel_lo(1b)" \ + : "=&r"(tmp) \ + : "r"(value), "r"(offset) \ + : "t0"); \ + }) +#define init_fp_reg(i) SET_F32_REG((i) << 3, 3, 0, 0) +#define GET_F64_REG(insn, pos, regs) \ + ({ \ + register ulong value asm("a0") = \ + SHIFT_RIGHT(insn, (pos)-3) & 0xf8; \ + ulong tmp; \ + asm("1: auipc %0, %%pcrel_hi(get_f64_reg); add %0, %0, %1; jalr t0, %0, %%pcrel_lo(1b)" \ + : "=&r"(tmp), "+&r"(value)::"t0"); \ + sizeof(ulong) == 4 ? *(int64_t *)value : (int64_t)value; \ + }) +#define SET_F64_REG(insn, pos, regs, val) \ + ({ \ + uint64_t __val = (val); \ + register ulong value asm("a0") = \ + sizeof(ulong) == 4 ? (ulong)&__val : (ulong)__val; \ + ulong offset = SHIFT_RIGHT(insn, (pos)-3) & 0xf8; \ + ulong tmp; \ + asm volatile( \ + "1: auipc %0, %%pcrel_hi(put_f64_reg); add %0, %0, %2; jalr t0, %0, %%pcrel_lo(1b)" \ + : "=&r"(tmp) \ + : "r"(value), "r"(offset) \ + : "t0"); \ + }) +#define GET_FCSR() csr_read(CSR_FCSR) +#define SET_FCSR(value) csr_write(CSR_FCSR, (value)) +#define GET_FRM() csr_read(CSR_FRM) +#define SET_FRM(value) csr_write(CSR_FRM, (value)) +#define GET_FFLAGS() csr_read(CSR_FFLAGS) +#define SET_FFLAGS(value) csr_write(CSR_FFLAGS, (value)) + +#define SET_FS_DIRTY() ((void)0) + +#define GET_F32_RS1(insn, regs) (GET_F32_REG(insn, 15, regs)) +#define GET_F32_RS2(insn, regs) (GET_F32_REG(insn, 20, regs)) +#define GET_F32_RS3(insn, regs) (GET_F32_REG(insn, 27, regs)) +#define GET_F64_RS1(insn, regs) (GET_F64_REG(insn, 15, regs)) +#define GET_F64_RS2(insn, regs) (GET_F64_REG(insn, 20, regs)) +#define GET_F64_RS3(insn, regs) (GET_F64_REG(insn, 27, regs)) +#define SET_F32_RD(insn, regs, val) \ + (SET_F32_REG(insn, 7, regs, val), SET_FS_DIRTY()) +#define SET_F64_RD(insn, regs, val) \ + (SET_F64_REG(insn, 7, regs, val), SET_FS_DIRTY()) + +#define GET_F32_RS2C(insn, regs) (GET_F32_REG(insn, 2, regs)) +#define GET_F32_RS2S(insn, regs) (GET_F32_REG(RVC_RS2S(insn), 0, regs)) +#define GET_F64_RS2C(insn, regs) (GET_F64_REG(insn, 2, regs)) +#define GET_F64_RS2S(insn, regs) (GET_F64_REG(RVC_RS2S(insn), 0, regs)) + +#endif + +#endif diff --git a/roms/opensbi/include/sbi/riscv_io.h b/roms/opensbi/include/sbi/riscv_io.h new file mode 100644 index 000000000..491323283 --- /dev/null +++ b/roms/opensbi/include/sbi/riscv_io.h @@ -0,0 +1,114 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __RISCV_IO_H__ +#define __RISCV_IO_H__ + +#include <sbi/riscv_barrier.h> +#include <sbi/sbi_types.h> + +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 */ + +/* clang-format off */ + +#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 + +/* clang-format on */ + +#endif diff --git a/roms/opensbi/include/sbi/riscv_locks.h b/roms/opensbi/include/sbi/riscv_locks.h new file mode 100644 index 000000000..55da7c01c --- /dev/null +++ b/roms/opensbi/include/sbi/riscv_locks.h @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#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/roms/opensbi/include/sbi/sbi_bitmap.h b/roms/opensbi/include/sbi/sbi_bitmap.h new file mode 100644 index 000000000..4f0ebb63a --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_bitmap.h @@ -0,0 +1,128 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_BITMAP_H__ +#define __SBI_BITMAP_H__ + +#include <sbi/sbi_bitops.h> + +#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG)) +#define BITMAP_LAST_WORD_MASK(nbits) \ +( \ + ((nbits) % BITS_PER_LONG) ? \ + ((1UL << ((nbits) % BITS_PER_LONG)) - 1) : ~0UL \ +) + +#define small_const_nbits(nbits) \ + (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG) + +#define DECLARE_BITMAP(name, nbits) unsigned long name[BITS_TO_LONGS(nbits)] +#define DEFINE_BITMAP(name) extern unsigned long name[] + +static inline unsigned long bitmap_estimate_size(int nbits) +{ + return (BITS_TO_LONGS(nbits) * sizeof(unsigned long)); +} + +void __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits); +void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits); +void __bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits); + +static inline void bitmap_set(unsigned long *bmap, int start, int len) +{ + int bit; + for (bit = start; bit < (start + len); bit++) + bmap[BIT_WORD(bit)] |= (0x1UL << BIT_WORD_OFFSET(bit)); +} + +static inline void bitmap_clear(unsigned long *bmap, int start, int len) +{ + int bit; + for (bit = start; bit < (start + len); bit++) + bmap[BIT_WORD(bit)] &= ~(0x1UL << BIT_WORD_OFFSET(bit)); +} + +static inline void bitmap_zero(unsigned long *dst, int nbits) +{ + if (small_const_nbits(nbits)) + *dst = 0UL; + else { + size_t i, len = BITS_TO_LONGS(nbits); + for (i = 0; i < len; i++) + dst[i] = 0; + } +} + +static inline void bitmap_zero_except(unsigned long *dst, + int exception, int nbits) +{ + if (small_const_nbits(nbits)) + *dst = 0UL; + else { + size_t i, len = BITS_TO_LONGS(nbits); + for (i = 0; i < len; i++) + dst[i] = 0; + } + if (exception < nbits) + __set_bit(exception, dst); +} + +static inline void bitmap_fill(unsigned long *dst, int nbits) +{ + size_t i, nlongs = BITS_TO_LONGS(nbits); + if (!small_const_nbits(nbits)) { + for (i = 0; i < (nlongs - 1); i++) + dst[i] = -1UL; + } + dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits); +} + +static inline void bitmap_copy(unsigned long *dst, + const unsigned long *src, int nbits) +{ + if (small_const_nbits(nbits)) + *dst = *src; + else { + size_t i, len = BITS_TO_LONGS(nbits); + for (i = 0; i < len; i++) + dst[i] = src[i]; + } +} + +static inline void bitmap_and(unsigned long *dst, const unsigned long *src1, + const unsigned long *src2, int nbits) +{ + if (small_const_nbits(nbits)) + *dst = *src1 & *src2; + else + __bitmap_and(dst, src1, src2, nbits); +} + +static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, + const unsigned long *src2, int nbits) +{ + if (small_const_nbits(nbits)) + *dst = *src1 | *src2; + else + __bitmap_or(dst, src1, src2, nbits); +} + +static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1, + const unsigned long *src2, int nbits) +{ + if (small_const_nbits(nbits)) + *dst = *src1 ^ *src2; + else + __bitmap_xor(dst, src1, src2, nbits); +} + +#endif diff --git a/roms/opensbi/include/sbi/sbi_bitops.h b/roms/opensbi/include/sbi/sbi_bitops.h new file mode 100644 index 000000000..879430d4b --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_bitops.h @@ -0,0 +1,320 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +#ifndef __SBI_BITOPS_H__ +#define __SBI_BITOPS_H__ + +#include <sbi/sbi_types.h> + +#if __SIZEOF_POINTER__ == 8 +#define BITS_PER_LONG 64 +#elif __SIZEOF_POINTER__ == 4 +#define BITS_PER_LONG 32 +#else +#error "Unexpected __SIZEOF_POINTER__" +#endif + +#define EXTRACT_FIELD(val, which) \ + (((val) & (which)) / ((which) & ~((which)-1))) +#define INSERT_FIELD(val, which, fieldval) \ + (((val) & ~(which)) | ((fieldval) * ((which) & ~((which)-1)))) + +#define BITS_TO_LONGS(nbits) (((nbits) + BITS_PER_LONG - 1) / \ + BITS_PER_LONG) + +#define BIT(nr) (1UL << (nr)) +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(bit) ((bit) / BITS_PER_LONG) +#define BIT_WORD_OFFSET(bit) ((bit) & (BITS_PER_LONG - 1)) + +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) + +/** + * ffs - Find first bit set + * @x: the word to search + * + * This is defined the same way as + * the libc and compiler builtin ffs routines, therefore + * differs in spirit from the above ffz (man ffs). + */ +static inline int ffs(int x) +{ + int r = 1; + + if (!x) + return 0; + if (!(x & 0xffff)) { + x >>= 16; + r += 16; + } + if (!(x & 0xff)) { + x >>= 8; + r += 8; + } + if (!(x & 0xf)) { + x >>= 4; + r += 4; + } + if (!(x & 3)) { + x >>= 2; + r += 2; + } + if (!(x & 1)) + r += 1; + return r; +} + +/** + * __ffs - find first bit in word. + * @word: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static inline int __ffs(unsigned long word) +{ + int num = 0; + +#if BITS_PER_LONG == 64 + if ((word & 0xffffffff) == 0) { + num += 32; + word >>= 32; + } +#endif + if ((word & 0xffff) == 0) { + num += 16; + word >>= 16; + } + if ((word & 0xff) == 0) { + num += 8; + word >>= 8; + } + if ((word & 0xf) == 0) { + num += 4; + word >>= 4; + } + if ((word & 0x3) == 0) { + num += 2; + word >>= 2; + } + if ((word & 0x1) == 0) + num += 1; + return num; +} + +/* + * ffz - find first zero in word. + * @word: The word to search + * + * Undefined if no zero exists, so code should check against ~0UL first. + */ +#define ffz(x) __ffs(~(x)) + +/** + * fls - find last (most-significant) bit set + * @x: the word to search + * + * This is defined the same way as ffs. + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ + +static inline int fls(int x) +{ + int r = 32; + + if (!x) + return 0; + if (!(x & 0xffff0000u)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xff000000u)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xf0000000u)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xc0000000u)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000u)) + r -= 1; + return r; +} + +/** + * __fls - find last (most-significant) set bit in a long word + * @word: the word to search + * + * Undefined if no set bit exists, so code should check against 0 first. + */ +static inline unsigned long __fls(unsigned long word) +{ + int num = BITS_PER_LONG - 1; + +#if BITS_PER_LONG == 64 + if (!(word & (~0ul << 32))) { + num -= 32; + word <<= 32; + } +#endif + if (!(word & (~0ul << (BITS_PER_LONG-16)))) { + num -= 16; + word <<= 16; + } + if (!(word & (~0ul << (BITS_PER_LONG-8)))) { + num -= 8; + word <<= 8; + } + if (!(word & (~0ul << (BITS_PER_LONG-4)))) { + num -= 4; + word <<= 4; + } + if (!(word & (~0ul << (BITS_PER_LONG-2)))) { + num -= 2; + word <<= 2; + } + if (!(word & (~0ul << (BITS_PER_LONG-1)))) + num -= 1; + return num; +} + +#define for_each_set_bit(bit, addr, size) \ + for ((bit) = find_first_bit((addr), (size)); \ + (bit) < (size); \ + (bit) = find_next_bit((addr), (size), (bit) + 1)) + +/* same as for_each_set_bit() but use bit as value to start with */ +#define for_each_set_bit_from(bit, addr, size) \ + for ((bit) = find_next_bit((addr), (size), (bit)); \ + (bit) < (size); \ + (bit) = find_next_bit((addr), (size), (bit) + 1)) + +#define for_each_clear_bit(bit, addr, size) \ + for ((bit) = find_first_zero_bit((addr), (size)); \ + (bit) < (size); \ + (bit) = find_next_zero_bit((addr), (size), (bit) + 1)) + +/* same as for_each_clear_bit() but use bit as value to start with */ +#define for_each_clear_bit_from(bit, addr, size) \ + for ((bit) = find_next_zero_bit((addr), (size), (bit)); \ + (bit) < (size); \ + (bit) = find_next_zero_bit((addr), (size), (bit) + 1)) + +unsigned long find_first_bit(const unsigned long *addr, + unsigned long size); + +unsigned long find_first_zero_bit(const unsigned long *addr, + unsigned long size); + +unsigned long find_last_bit(const unsigned long *addr, + unsigned long size); + +unsigned long find_next_bit(const unsigned long *addr, + unsigned long size, unsigned long offset); + +unsigned long find_next_zero_bit(const unsigned long *addr, + unsigned long size, + unsigned long offset); + +/** + * __set_bit - Set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * This function is non-atomic and may be reordered. + */ +static inline void __set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + + *p |= mask; +} + +/** + * __clear_bit - Clear a bit in memory + * @nr: the bit to clear + * @addr: the address to start counting from + * + * This function is non-atomic and may be reordered. + */ +static inline void __clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + + *p &= ~mask; +} + +/** + * __change_bit - Toggle a bit in memory + * @nr: the bit to change + * @addr: the address to start counting from + * + * This function is non-atomic and may be reordered. + */ +static inline void __change_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + + *p ^= mask; +} + +/** + * __test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is non-atomic and can be reordered. + */ +static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + unsigned long old = *p; + + *p = old | mask; + return (old & mask) != 0; +} + +/** + * __test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to clear + * @addr: Address to count from + * + * This operation is non-atomic and can be reordered. + */ +static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + unsigned long old = *p; + + *p = old & ~mask; + return (old & mask) != 0; +} + +/** + * __test_bit - Determine whether a bit is set + * @nr: bit number to test + * @addr: Address to start counting from + * + * This operation is non-atomic and can be reordered. + */ +static inline int __test_bit(int nr, const volatile unsigned long *addr) +{ + return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); +} + +#endif diff --git a/roms/opensbi/include/sbi/sbi_console.h b/roms/opensbi/include/sbi/sbi_console.h new file mode 100644 index 000000000..7d648f0b5 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_console.h @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_CONSOLE_H__ +#define __SBI_CONSOLE_H__ + +#include <sbi/sbi_types.h> + +#define __printf(a, b) __attribute__((format(printf, a, b))) + +bool sbi_isprintable(char ch); + +int 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, ...); + +int __printf(1, 2) sbi_dprintf(const char *format, ...); + +struct sbi_scratch; + +int sbi_console_init(struct sbi_scratch *scratch); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_const.h b/roms/opensbi/include/sbi/sbi_const.h new file mode 100644 index 000000000..37866546a --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_const.h @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#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. + */ + +/* clang-format off */ + +#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) + +/* clang-format on */ + +#endif diff --git a/roms/opensbi/include/sbi/sbi_csr_detect.h b/roms/opensbi/include/sbi/sbi_csr_detect.h new file mode 100644 index 000000000..f29488845 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_csr_detect.h @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +#ifndef __SBI_CSR_DETECT__H +#define __SBI_CSR_DETECT__H + +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_hart.h> + +#define csr_read_allowed(csr_num, trap) \ + ({ \ + register ulong tinfo asm("a3") = (ulong)trap; \ + register ulong ttmp asm("a4"); \ + register ulong mtvec = sbi_hart_expected_trap_addr(); \ + register ulong ret = 0; \ + asm volatile( \ + "add %[ttmp], %[tinfo], zero\n" \ + "csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n" \ + "csrr %[ret], %[csr]\n" \ + "csrw " STR(CSR_MTVEC) ", %[mtvec]" \ + : [mtvec] "+&r"(mtvec), [tinfo] "+&r"(tinfo), \ + [ttmp] "+&r"(ttmp), [ret] "=&r" (ret) \ + : [csr] "i" (csr_num) \ + : "memory"); \ + ret; \ + }) \ + +#define csr_write_allowed(csr_num, trap, value) \ + ({ \ + register ulong tinfo asm("a3") = (ulong)trap; \ + register ulong ttmp asm("a4"); \ + register ulong mtvec = sbi_hart_expected_trap_addr(); \ + asm volatile( \ + "add %[ttmp], %[tinfo], zero\n" \ + "csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n" \ + "csrw %[csr], %[val]\n" \ + "csrw " STR(CSR_MTVEC) ", %[mtvec]" \ + : [mtvec] "+&r"(mtvec), \ + [tinfo] "+&r"(tinfo), [ttmp] "+&r"(ttmp) \ + : [csr] "i" (csr_num), [val] "r" (value) \ + : "memory"); \ + }) \ + +#endif diff --git a/roms/opensbi/include/sbi/sbi_domain.h b/roms/opensbi/include/sbi/sbi_domain.h new file mode 100644 index 000000000..1f8b942c4 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_domain.h @@ -0,0 +1,165 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_DOMAIN_H__ +#define __SBI_DOMAIN_H__ + +#include <sbi/sbi_types.h> +#include <sbi/sbi_hartmask.h> + +struct sbi_scratch; + +/** Domain access types */ +enum sbi_domain_access { + SBI_DOMAIN_READ = (1UL << 0), + SBI_DOMAIN_WRITE = (1UL << 1), + SBI_DOMAIN_EXECUTE = (1UL << 2), + SBI_DOMAIN_MMIO = (1UL << 3) +}; + +/** Representation of OpenSBI domain memory region */ +struct sbi_domain_memregion { + /** + * Size of memory region as power of 2 + * It has to be minimum 3 and maximum __riscv_xlen + */ + unsigned long order; + /** + * Base address of memory region + * It must be 2^order aligned address + */ + unsigned long base; + /** Flags representing memory region attributes */ +#define SBI_DOMAIN_MEMREGION_READABLE (1UL << 0) +#define SBI_DOMAIN_MEMREGION_WRITEABLE (1UL << 1) +#define SBI_DOMAIN_MEMREGION_EXECUTABLE (1UL << 2) +#define SBI_DOMAIN_MEMREGION_MMODE (1UL << 3) +#define SBI_DOMAIN_MEMREGION_ACCESS_MASK (0xfUL) + +#define SBI_DOMAIN_MEMREGION_MMIO (1UL << 31) + unsigned long flags; +}; + +/** Maximum number of domains */ +#define SBI_DOMAIN_MAX_INDEX 32 + +/** Representation of OpenSBI domain */ +struct sbi_domain { + /** + * Logical index of this domain + * Note: This set by sbi_domain_finalize() in the coldboot path + */ + u32 index; + /** + * HARTs assigned to this domain + * Note: This set by sbi_domain_init() and sbi_domain_finalize() + * in the coldboot path + */ + struct sbi_hartmask assigned_harts; + /** Name of this domain */ + char name[64]; + /** Possible HARTs in this domain */ + const struct sbi_hartmask *possible_harts; + /** Array of memory regions terminated by a region with order zero */ + struct sbi_domain_memregion *regions; + /** HART id of the HART booting this domain */ + u32 boot_hartid; + /** Arg1 (or 'a1' register) of next booting stage for this domain */ + unsigned long next_arg1; + /** Address of next booting stage for this domain */ + unsigned long next_addr; + /** Privilege mode of next booting stage for this domain */ + unsigned long next_mode; + /** Is domain allowed to reset the system */ + bool system_reset_allowed; +}; + +/** HART id to domain table */ +extern struct sbi_domain *hartid_to_domain_table[]; + +/** Get pointer to sbi_domain from HART id */ +#define sbi_hartid_to_domain(__hartid) \ + hartid_to_domain_table[__hartid] + +/** Get pointer to sbi_domain for current HART */ +#define sbi_domain_thishart_ptr() \ + sbi_hartid_to_domain(current_hartid()) + +/** Index to domain table */ +extern struct sbi_domain *domidx_to_domain_table[]; + +/** Get pointer to sbi_domain from index */ +#define sbi_index_to_domain(__index) \ + domidx_to_domain_table[__index] + +/** Iterate over each domain */ +#define sbi_domain_for_each(__i, __d) \ + for ((__i) = 0; ((__d) = sbi_index_to_domain(__i)); (__i)++) + +/** Iterate over each memory region of a domain */ +#define sbi_domain_for_each_memregion(__d, __r) \ + for ((__r) = (__d)->regions; (__r)->order; (__r)++) + +/** + * Check whether given HART is assigned to specified domain + * @param dom pointer to domain + * @param hartid the HART ID + * @return TRUE if HART is assigned to domain otherwise FALSE + */ +bool sbi_domain_is_assigned_hart(const struct sbi_domain *dom, u32 hartid); + +/** + * Get ulong assigned HART mask for given domain and HART base ID + * @param dom pointer to domain + * @param hbase the HART base ID + * @return ulong possible HART mask + * Note: the return ulong mask will be set to zero on failure. + */ +ulong sbi_domain_get_assigned_hartmask(const struct sbi_domain *dom, + ulong hbase); + +/** Initialize a domain memory region as firmware region */ +void sbi_domain_memregion_initfw(struct sbi_domain_memregion *reg); + +/** + * Check whether we can access specified address for given mode and + * memory region flags under a domain + * @param dom pointer to domain + * @param addr the address to be checked + * @param mode the privilege mode of access + * @param access_flags bitmask of domain access types (enum sbi_domain_access) + * @return TRUE if access allowed otherwise FALSE + */ +bool sbi_domain_check_addr(const struct sbi_domain *dom, + unsigned long addr, unsigned long mode, + unsigned long access_flags); + +/** Dump domain details on the console */ +void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix); + +/** Dump all domain details on the console */ +void sbi_domain_dump_all(const char *suffix); + +/** + * Register a new domain + * @param dom pointer to domain + * @param assign_mask pointer to HART mask of HARTs assigned to the domain + * + * @return 0 on success and negative error code on failure + */ +int sbi_domain_register(struct sbi_domain *dom, + const struct sbi_hartmask *assign_mask); + +/** Finalize domain tables and startup non-root domains */ +int sbi_domain_finalize(struct sbi_scratch *scratch, u32 cold_hartid); + +/** Initialize domains */ +int sbi_domain_init(struct sbi_scratch *scratch, u32 cold_hartid); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_ecall.h b/roms/opensbi/include/sbi/sbi_ecall.h new file mode 100644 index 000000000..d35708544 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_ecall.h @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_ECALL_H__ +#define __SBI_ECALL_H__ + +#include <sbi/sbi_types.h> +#include <sbi/sbi_list.h> + +#define SBI_ECALL_VERSION_MAJOR 0 +#define SBI_ECALL_VERSION_MINOR 2 +#define SBI_OPENSBI_IMPID 1 + +struct sbi_trap_regs; +struct sbi_trap_info; + +struct sbi_ecall_extension { + struct sbi_dlist head; + unsigned long extid_start; + unsigned long extid_end; + int (* probe)(unsigned long extid, unsigned long *out_val); + int (* handle)(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap); +}; + +extern struct sbi_ecall_extension ecall_base; +extern struct sbi_ecall_extension ecall_legacy; +extern struct sbi_ecall_extension ecall_time; +extern struct sbi_ecall_extension ecall_rfence; +extern struct sbi_ecall_extension ecall_ipi; +extern struct sbi_ecall_extension ecall_vendor; +extern struct sbi_ecall_extension ecall_hsm; +extern struct sbi_ecall_extension ecall_srst; + +u16 sbi_ecall_version_major(void); + +u16 sbi_ecall_version_minor(void); + +unsigned long sbi_ecall_get_impid(void); + +void sbi_ecall_set_impid(unsigned long impid); + +struct sbi_ecall_extension *sbi_ecall_find_extension(unsigned long extid); + +int sbi_ecall_register_extension(struct sbi_ecall_extension *ext); + +void sbi_ecall_unregister_extension(struct sbi_ecall_extension *ext); + +int sbi_ecall_handler(struct sbi_trap_regs *regs); + +int sbi_ecall_init(void); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_ecall_interface.h b/roms/opensbi/include/sbi/sbi_ecall_interface.h new file mode 100644 index 000000000..002c6f9cb --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_ecall_interface.h @@ -0,0 +1,98 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_ECALL_INTERFACE_H__ +#define __SBI_ECALL_INTERFACE_H__ + +/* clang-format off */ + +/* SBI Extension IDs */ +#define SBI_EXT_0_1_SET_TIMER 0x0 +#define SBI_EXT_0_1_CONSOLE_PUTCHAR 0x1 +#define SBI_EXT_0_1_CONSOLE_GETCHAR 0x2 +#define SBI_EXT_0_1_CLEAR_IPI 0x3 +#define SBI_EXT_0_1_SEND_IPI 0x4 +#define SBI_EXT_0_1_REMOTE_FENCE_I 0x5 +#define SBI_EXT_0_1_REMOTE_SFENCE_VMA 0x6 +#define SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID 0x7 +#define SBI_EXT_0_1_SHUTDOWN 0x8 +#define SBI_EXT_BASE 0x10 +#define SBI_EXT_TIME 0x54494D45 +#define SBI_EXT_IPI 0x735049 +#define SBI_EXT_RFENCE 0x52464E43 +#define SBI_EXT_HSM 0x48534D +#define SBI_EXT_SRST 0x53525354 + +/* SBI function IDs for BASE extension*/ +#define SBI_EXT_BASE_GET_SPEC_VERSION 0x0 +#define SBI_EXT_BASE_GET_IMP_ID 0x1 +#define SBI_EXT_BASE_GET_IMP_VERSION 0x2 +#define SBI_EXT_BASE_PROBE_EXT 0x3 +#define SBI_EXT_BASE_GET_MVENDORID 0x4 +#define SBI_EXT_BASE_GET_MARCHID 0x5 +#define SBI_EXT_BASE_GET_MIMPID 0x6 + +/* SBI function IDs for TIME extension*/ +#define SBI_EXT_TIME_SET_TIMER 0x0 + +/* SBI function IDs for IPI extension*/ +#define SBI_EXT_IPI_SEND_IPI 0x0 + +/* SBI function IDs for RFENCE extension*/ +#define SBI_EXT_RFENCE_REMOTE_FENCE_I 0x0 +#define SBI_EXT_RFENCE_REMOTE_SFENCE_VMA 0x1 +#define SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID 0x2 +#define SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA 0x3 +#define SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID 0x4 +#define SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA 0x5 +#define SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID 0x6 + +/* SBI function IDs for HSM extension */ +#define SBI_EXT_HSM_HART_START 0x0 +#define SBI_EXT_HSM_HART_STOP 0x1 +#define SBI_EXT_HSM_HART_GET_STATUS 0x2 + +#define SBI_HSM_HART_STATUS_STARTED 0x0 +#define SBI_HSM_HART_STATUS_STOPPED 0x1 +#define SBI_HSM_HART_STATUS_START_PENDING 0x2 +#define SBI_HSM_HART_STATUS_STOP_PENDING 0x3 + +/* SBI function IDs for SRST extension */ +#define SBI_EXT_SRST_RESET 0x0 + +#define SBI_SRST_RESET_TYPE_SHUTDOWN 0x0 +#define SBI_SRST_RESET_TYPE_COLD_REBOOT 0x1 +#define SBI_SRST_RESET_TYPE_WARM_REBOOT 0x2 +#define SBI_SRST_RESET_TYPE_LAST SBI_SRST_RESET_TYPE_WARM_REBOOT + +#define SBI_SRST_RESET_REASON_NONE 0x0 +#define SBI_SRST_RESET_REASON_SYSFAIL 0x1 + +#define SBI_SPEC_VERSION_MAJOR_OFFSET 24 +#define SBI_SPEC_VERSION_MAJOR_MASK 0x7f +#define SBI_SPEC_VERSION_MINOR_MASK 0xffffff +#define SBI_EXT_VENDOR_START 0x09000000 +#define SBI_EXT_VENDOR_END 0x09FFFFFF +#define SBI_EXT_FIRMWARE_START 0x0A000000 +#define SBI_EXT_FIRMWARE_END 0x0AFFFFFF + +/* SBI return error codes */ +#define SBI_SUCCESS 0 +#define SBI_ERR_FAILED -1 +#define SBI_ERR_NOT_SUPPORTED -2 +#define SBI_ERR_INVALID_PARAM -3 +#define SBI_ERR_DENIED -4 +#define SBI_ERR_INVALID_ADDRESS -5 +#define SBI_ERR_ALREADY_AVAILABLE -6 + +#define SBI_LAST_ERR SBI_ERR_ALREADY_AVAILABLE + +/* clang-format on */ + +#endif diff --git a/roms/opensbi/include/sbi/sbi_emulate_csr.h b/roms/opensbi/include/sbi/sbi_emulate_csr.h new file mode 100644 index 000000000..548056a11 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_emulate_csr.h @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_EMULATE_CSR_H__ +#define __SBI_EMULATE_CSR_H__ + +#include <sbi/sbi_types.h> + +struct sbi_trap_regs; + +int sbi_emulate_csr_read(int csr_num, struct sbi_trap_regs *regs, + ulong *csr_val); + +int sbi_emulate_csr_write(int csr_num, struct sbi_trap_regs *regs, + ulong csr_val); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_error.h b/roms/opensbi/include/sbi/sbi_error.h new file mode 100644 index 000000000..3655d1220 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_error.h @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_ERROR_H__ +#define __SBI_ERROR_H__ + +#include <sbi/sbi_ecall_interface.h> + +/* clang-format off */ + +#define SBI_OK 0 +#define SBI_EFAIL SBI_ERR_FAILED +#define SBI_ENOTSUPP SBI_ERR_NOT_SUPPORTED +#define SBI_EINVAL SBI_ERR_INVALID_PARAM +#define SBI_EDENIED SBI_ERR_DENIED +#define SBI_EINVALID_ADDR SBI_ERR_INVALID_ADDRESS +#define SBI_EALREADY SBI_ERR_ALREADY_AVAILABLE + +#define SBI_ENODEV -1000 +#define SBI_ENOSYS -1001 +#define SBI_ETIMEDOUT -1002 +#define SBI_EIO -1003 +#define SBI_EILL -1004 +#define SBI_ENOSPC -1005 +#define SBI_ENOMEM -1006 +#define SBI_ETRAP -1007 +#define SBI_EUNKNOWN -1008 +#define SBI_ENOENT -1009 + +/* clang-format on */ + +#endif diff --git a/roms/opensbi/include/sbi/sbi_fifo.h b/roms/opensbi/include/sbi/sbi_fifo.h new file mode 100644 index 000000000..bc8f8f600 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_fifo.h @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra<atish.patra@wdc.com> + * + */ + +#ifndef __SBI_FIFO_H__ +#define __SBI_FIFO_H__ + +#include <sbi/riscv_locks.h> +#include <sbi/sbi_types.h> + +struct sbi_fifo { + void *queue; + spinlock_t qlock; + u16 entry_size; + u16 num_entries; + u16 avail; + u16 tail; +}; + +enum sbi_fifo_inplace_update_types { + SBI_FIFO_SKIP, + SBI_FIFO_UPDATED, + SBI_FIFO_UNCHANGED, +}; + +int sbi_fifo_dequeue(struct sbi_fifo *fifo, void *data); +int sbi_fifo_enqueue(struct sbi_fifo *fifo, void *data); +void sbi_fifo_init(struct sbi_fifo *fifo, void *queue_mem, u16 entries, + u16 entry_size); +bool sbi_fifo_is_empty(struct sbi_fifo *fifo); +bool sbi_fifo_is_full(struct sbi_fifo *fifo); +int sbi_fifo_inplace_update(struct sbi_fifo *fifo, void *in, + int (*fptr)(void *in, void *data)); +u16 sbi_fifo_avail(struct sbi_fifo *fifo); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_hart.h b/roms/opensbi/include/sbi/sbi_hart.h new file mode 100644 index 000000000..ec9e30f91 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_hart.h @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_HART_H__ +#define __SBI_HART_H__ + +#include <sbi/sbi_types.h> + +/** Possible feature flags of a hart */ +enum sbi_hart_features { + /** Hart has S-mode counter enable */ + SBI_HART_HAS_SCOUNTEREN = (1 << 0), + /** Hart has M-mode counter enable */ + SBI_HART_HAS_MCOUNTEREN = (1 << 1), + /** HART has timer csr implementation in hardware */ + SBI_HART_HAS_TIME = (1 << 2), + + /** Last index of Hart features*/ + SBI_HART_HAS_LAST_FEATURE = SBI_HART_HAS_TIME, +}; + +struct sbi_scratch; + +int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot); + +extern void (*sbi_hart_expected_trap)(void); +static inline ulong sbi_hart_expected_trap_addr(void) +{ + return (ulong)sbi_hart_expected_trap; +} + +unsigned int sbi_hart_mhpm_count(struct sbi_scratch *scratch); +void sbi_hart_delegation_dump(struct sbi_scratch *scratch, + const char *prefix, const char *suffix); +unsigned int sbi_hart_pmp_count(struct sbi_scratch *scratch); +unsigned long sbi_hart_pmp_granularity(struct sbi_scratch *scratch); +unsigned int sbi_hart_pmp_addrbits(struct sbi_scratch *scratch); +int sbi_hart_pmp_configure(struct sbi_scratch *scratch); +bool sbi_hart_has_feature(struct sbi_scratch *scratch, unsigned long feature); +void sbi_hart_get_features_str(struct sbi_scratch *scratch, + char *features_str, int nfstr); + +void __attribute__((noreturn)) sbi_hart_hang(void); + +void __attribute__((noreturn)) +sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1, + unsigned long next_addr, unsigned long next_mode, + bool next_virt); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_hartmask.h b/roms/opensbi/include/sbi/sbi_hartmask.h new file mode 100644 index 000000000..f1cef0c2a --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_hartmask.h @@ -0,0 +1,141 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_HARTMASK_H__ +#define __SBI_HARTMASK_H__ + +#include <sbi/sbi_bitmap.h> + +/** + * Maximum number of bits in a hartmask + * + * The hartmask is indexed using physical HART id so this define + * also represents the maximum number of HART ids generic OpenSBI + * can handle. + */ +#define SBI_HARTMASK_MAX_BITS 128 + +/** Representation of hartmask */ +struct sbi_hartmask { + DECLARE_BITMAP(bits, SBI_HARTMASK_MAX_BITS); +}; + +/** Initialize hartmask to zero */ +#define SBI_HARTMASK_INIT(__m) \ + bitmap_zero(((__m)->bits), SBI_HARTMASK_MAX_BITS) + +/** Initialize hartmask to zero except a particular HART id */ +#define SBI_HARTMASK_INIT_EXCEPT(__m, __h) \ + bitmap_zero_except(((__m)->bits), (__h), SBI_HARTMASK_MAX_BITS) + +/** + * Get underlying bitmap of hartmask + * @param m the hartmask pointer + */ +#define sbi_hartmask_bits(__m) ((__m)->bits) + +/** + * Set a HART in hartmask + * @param h HART id to set + * @param m the hartmask pointer + */ +static inline void sbi_hartmask_set_hart(u32 h, struct sbi_hartmask *m) +{ + if (h < SBI_HARTMASK_MAX_BITS) + __set_bit(h, m->bits); +} + +/** + * Clear a HART in hartmask + * @param h HART id to clear + * @param m the hartmask pointer + */ +static inline void sbi_hartmask_clear_hart(u32 h, struct sbi_hartmask *m) +{ + if (h < SBI_HARTMASK_MAX_BITS) + __clear_bit(h, m->bits); +} + +/** + * Test a HART in hartmask + * @param h HART id to test + * @param m the hartmask pointer + */ +static inline int sbi_hartmask_test_hart(u32 h, const struct sbi_hartmask *m) +{ + if (h < SBI_HARTMASK_MAX_BITS) + return __test_bit(h, m->bits); + return 0; +} + +/** + * Set all HARTs in a hartmask + * @param dstp the hartmask pointer + */ +static inline void sbi_hartmask_set_all(struct sbi_hartmask *dstp) +{ + bitmap_fill(sbi_hartmask_bits(dstp), SBI_HARTMASK_MAX_BITS); +} + +/** + * Clear all HARTs in a hartmask + * @param dstp the hartmask pointer + */ +static inline void sbi_hartmask_clear_all(struct sbi_hartmask *dstp) +{ + bitmap_zero(sbi_hartmask_bits(dstp), SBI_HARTMASK_MAX_BITS); +} + +/** + * *dstp = *src1p & *src2p + * @param dstp the hartmask result + * @param src1p the first input + * @param src2p the second input + */ +static inline void sbi_hartmask_and(struct sbi_hartmask *dstp, + const struct sbi_hartmask *src1p, + const struct sbi_hartmask *src2p) +{ + bitmap_and(sbi_hartmask_bits(dstp), sbi_hartmask_bits(src1p), + sbi_hartmask_bits(src2p), SBI_HARTMASK_MAX_BITS); +} + +/** + * *dstp = *src1p | *src2p + * @param dstp the hartmask result + * @param src1p the first input + * @param src2p the second input + */ +static inline void sbi_hartmask_or(struct sbi_hartmask *dstp, + const struct sbi_hartmask *src1p, + const struct sbi_hartmask *src2p) +{ + bitmap_or(sbi_hartmask_bits(dstp), sbi_hartmask_bits(src1p), + sbi_hartmask_bits(src2p), SBI_HARTMASK_MAX_BITS); +} + +/** + * *dstp = *src1p ^ *src2p + * @param dstp the hartmask result + * @param src1p the first input + * @param src2p the second input + */ +static inline void sbi_hartmask_xor(struct sbi_hartmask *dstp, + const struct sbi_hartmask *src1p, + const struct sbi_hartmask *src2p) +{ + bitmap_xor(sbi_hartmask_bits(dstp), sbi_hartmask_bits(src1p), + sbi_hartmask_bits(src2p), SBI_HARTMASK_MAX_BITS); +} + +/** Iterate over each HART in hartmask */ +#define sbi_hartmask_for_each_hart(__h, __m) \ + for_each_set_bit(__h, (__m)->bits, SBI_HARTMASK_MAX_BITS) + +#endif diff --git a/roms/opensbi/include/sbi/sbi_hfence.h b/roms/opensbi/include/sbi/sbi_hfence.h new file mode 100644 index 000000000..4420f2793 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_hfence.h @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_FENCE_H__ +#define __SBI_FENCE_H__ + +/** Invalidate Stage2 TLBs for given VMID and guest physical address */ +void __sbi_hfence_gvma_vmid_gpa(unsigned long gpa, unsigned long vmid); + +/** Invalidate Stage2 TLBs for given VMID */ +void __sbi_hfence_gvma_vmid(unsigned long vmid); + +/** Invalidate Stage2 TLBs for given guest physical address */ +void __sbi_hfence_gvma_gpa(unsigned long gpa); + +/** Invalidate all possible Stage2 TLBs */ +void __sbi_hfence_gvma_all(void); + +/** Invalidate unified TLB entries for given asid and guest virtual address */ +void __sbi_hfence_vvma_asid_va(unsigned long va, unsigned long asid); + +/** Invalidate unified TLB entries for given ASID for a guest*/ +void __sbi_hfence_vvma_asid(unsigned long asid); + +/** Invalidate unified TLB entries for a given guest virtual address */ +void __sbi_hfence_vvma_va(unsigned long va); + +/** Invalidate all possible Stage2 TLBs */ +void __sbi_hfence_vvma_all(void); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_hsm.h b/roms/opensbi/include/sbi/sbi_hsm.h new file mode 100644 index 000000000..482338309 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_hsm.h @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +#ifndef __SBI_HSM_H__ +#define __SBI_HSM_H__ + +#include <sbi/sbi_types.h> + +/** Hart state values **/ +#define SBI_HART_STOPPED 0 +#define SBI_HART_STOPPING 1 +#define SBI_HART_STARTING 2 +#define SBI_HART_STARTED 3 +#define SBI_HART_UNKNOWN 4 + +struct sbi_domain; +struct sbi_scratch; + +int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot); +void __noreturn sbi_hsm_exit(struct sbi_scratch *scratch); + +int sbi_hsm_hart_start(struct sbi_scratch *scratch, + const struct sbi_domain *dom, + u32 hartid, ulong saddr, ulong smode, ulong priv); +int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow); +int sbi_hsm_hart_get_state(const struct sbi_domain *dom, u32 hartid); +int sbi_hsm_hart_state_to_status(int state); +int sbi_hsm_hart_started_mask(const struct sbi_domain *dom, + ulong hbase, ulong *out_hmask); +void sbi_hsm_prepare_next_jump(struct sbi_scratch *scratch, u32 hartid); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_illegal_insn.h b/roms/opensbi/include/sbi/sbi_illegal_insn.h new file mode 100644 index 000000000..0397935e2 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_illegal_insn.h @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_ILLEGAl_INSN_H__ +#define __SBI_ILLEGAl_INSN_H__ + +#include <sbi/sbi_types.h> + +struct sbi_trap_regs; + +int sbi_illegal_insn_handler(ulong insn, struct sbi_trap_regs *regs); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_init.h b/roms/opensbi/include/sbi/sbi_init.h new file mode 100644 index 000000000..74eb1c075 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_init.h @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_INIT_H__ +#define __SBI_INIT_H__ + +#include <sbi/sbi_types.h> + +struct sbi_scratch; + +void __noreturn sbi_init(struct sbi_scratch *scratch); + +unsigned long sbi_init_count(u32 hartid); + +void __noreturn sbi_exit(struct sbi_scratch *scratch); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_ipi.h b/roms/opensbi/include/sbi/sbi_ipi.h new file mode 100644 index 000000000..617872c1b --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_ipi.h @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_IPI_H__ +#define __SBI_IPI_H__ + +#include <sbi/sbi_types.h> + +/* clang-format off */ + +#define SBI_IPI_EVENT_MAX __riscv_xlen + +/* clang-format on */ + +struct sbi_scratch; + +/** IPI event operations or callbacks */ +struct sbi_ipi_event_ops { + /** Name of the IPI event operations */ + char name[32]; + + /** + * Update callback to save/enqueue data for remote HART + * Note: This is an optional callback and it is called just before + * triggering IPI to remote HART. + */ + int (* update)(struct sbi_scratch *scratch, + struct sbi_scratch *remote_scratch, + u32 remote_hartid, void *data); + + /** + * Sync callback to wait for remote HART + * Note: This is an optional callback and it is called just after + * triggering IPI to remote HART. + */ + void (* sync)(struct sbi_scratch *scratch); + + /** + * Process callback to handle IPI event + * Note: This is a mandatory callback and it is called on the + * remote HART after IPI is triggered. + */ + void (* process)(struct sbi_scratch *scratch); +}; + +int sbi_ipi_send_many(ulong hmask, ulong hbase, u32 event, void *data); + +int sbi_ipi_event_create(const struct sbi_ipi_event_ops *ops); + +void sbi_ipi_event_destroy(u32 event); + +int sbi_ipi_send_smode(ulong hmask, ulong hbase); + +void sbi_ipi_clear_smode(void); + +int sbi_ipi_send_halt(ulong hmask, ulong hbase); + +void sbi_ipi_process(void); + +int sbi_ipi_init(struct sbi_scratch *scratch, bool cold_boot); + +void sbi_ipi_exit(struct sbi_scratch *scratch); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_list.h b/roms/opensbi/include/sbi/sbi_list.h new file mode 100644 index 000000000..1174ad274 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_list.h @@ -0,0 +1,152 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Simple doubly-linked list library. + * + * Adapted from Xvisor source file libs/include/libs/list.h + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_LIST_H__ +#define __SBI_LIST_H__ + +#include <sbi/sbi_types.h> + +#define SBI_LIST_POISON_PREV 0xDEADBEEF +#define SBI_LIST_POISON_NEXT 0xFADEBABE + +struct sbi_dlist { + struct sbi_dlist *next, *prev; +}; + +#define SBI_LIST_HEAD_INIT(__lname) { &(__lname), &(__lname) } + +#define SBI_LIST_HEAD(_lname) \ +struct sbi_dlist _lname = SBI_LIST_HEAD_INIT(_lname) + +#define SBI_INIT_LIST_HEAD(ptr) \ +do { \ + (ptr)->next = ptr; (ptr)->prev = ptr; \ +} while (0); + +static inline void __sbi_list_add(struct sbi_dlist *new, + struct sbi_dlist *prev, + struct sbi_dlist *next) +{ + new->prev = prev; + new->next = next; + prev->next = new; + next->prev = new; +} + +/** + * Adds the new node after the given head. + * @param new New node that needs to be added to list. + * @param head List head after which the "new" node should be added. + * Note: the new node is added after the head. + */ +static inline void sbi_list_add(struct sbi_dlist *new, struct sbi_dlist *head) +{ + __sbi_list_add(new, head, head->next); +} + +/** + * Adds a node at the tail where tnode points to tail node. + * @param new The new node to be added before tail. + * @param tnode The current tail node. + * Note: the new node is added before tail node. + */ +static inline void sbi_list_add_tail(struct sbi_dlist *new, + struct sbi_dlist *tnode) +{ + __sbi_list_add(new, tnode->prev, tnode); +} + +static inline void __sbi_list_del(struct sbi_dlist *prev, + struct sbi_dlist *next) +{ + prev->next = next; + next->prev = prev; +} + +static inline void __sbi_list_del_entry(struct sbi_dlist *entry) +{ + __sbi_list_del(entry->prev, entry->next); +} + +/** + * Deletes a given entry from list. + * @param node Node to be deleted. + */ +static inline void sbi_list_del(struct sbi_dlist *entry) +{ + __sbi_list_del(entry->prev, entry->next); + entry->next = (void *)SBI_LIST_POISON_NEXT; + entry->prev = (void *)SBI_LIST_POISON_PREV; +} + +/** + * Deletes entry from list and reinitialize it. + * @param entry the element to delete from the list. + */ +static inline void sbi_list_del_init(struct sbi_dlist *entry) +{ + __sbi_list_del_entry(entry); + SBI_INIT_LIST_HEAD(entry); +} + +/** + * Get the struct for this entry + * @param ptr the &struct list_head pointer. + * @param type the type of the struct this is embedded in. + * @param member the name of the list_struct within the struct. + */ +#define sbi_list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * Get the first element from a list + * @param ptr the list head to take the element from. + * @param type the type of the struct this is embedded in. + * @param member the name of the list_struct within the struct. + * + * Note: that list is expected to be not empty. + */ +#define sbi_list_first_entry(ptr, type, member) \ + sbi_list_entry((ptr)->next, type, member) + +/** + * Get the last element from a list + * @param ptr the list head to take the element from. + * @param type the type of the struct this is embedded in. + * @param member the name of the list_head within the struct. + * + * Note: that list is expected to be not empty. + */ +#define sbi_list_last_entry(ptr, type, member) \ + sbi_list_entry((ptr)->prev, type, member) + +/** + * Iterate over a list + * @param pos the &struct list_head to use as a loop cursor. + * @param head the head for your list. + */ +#define sbi_list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * Iterate over list of given type + * @param pos the type * to use as a loop cursor. + * @param head the head for your list. + * @param member the name of the list_struct within the struct. + */ +#define sbi_list_for_each_entry(pos, head, member) \ + for (pos = sbi_list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = sbi_list_entry(pos->member.next, typeof(*pos), member)) + +#endif diff --git a/roms/opensbi/include/sbi/sbi_math.h b/roms/opensbi/include/sbi/sbi_math.h new file mode 100644 index 000000000..564fd585c --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_math.h @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +#ifndef __SBI_MATH_H__ +#define __SBI_MATH_H__ + +unsigned long log2roundup(unsigned long x); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_misaligned_ldst.h b/roms/opensbi/include/sbi/sbi_misaligned_ldst.h new file mode 100644 index 000000000..ab27eb422 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_misaligned_ldst.h @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_MISALIGNED_LDST_H__ +#define __SBI_MISALIGNED_LDST_H__ + +#include <sbi/sbi_types.h> + +struct sbi_trap_regs; + +int sbi_misaligned_load_handler(ulong addr, ulong tval2, ulong tinst, + struct sbi_trap_regs *regs); + +int sbi_misaligned_store_handler(ulong addr, ulong tval2, ulong tinst, + struct sbi_trap_regs *regs); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_platform.h b/roms/opensbi/include/sbi/sbi_platform.h new file mode 100644 index 000000000..cc7e3ff99 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_platform.h @@ -0,0 +1,757 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_PLATFORM_H__ +#define __SBI_PLATFORM_H__ + +/** + * OpenSBI 32-bit platform version with: + * 1. upper 16-bits as major number + * 2. lower 16-bits as minor number + */ +#define SBI_PLATFORM_VERSION(Major, Minor) ((Major << 16) | Minor) + +/** Offset of opensbi_version in struct sbi_platform */ +#define SBI_PLATFORM_OPENSBI_VERSION_OFFSET (0x00) +/** Offset of platform_version in struct sbi_platform */ +#define SBI_PLATFORM_VERSION_OFFSET (0x04) +/** Offset of name in struct sbi_platform */ +#define SBI_PLATFORM_NAME_OFFSET (0x08) +/** Offset of features in struct sbi_platform */ +#define SBI_PLATFORM_FEATURES_OFFSET (0x48) +/** Offset of hart_count in struct sbi_platform */ +#define SBI_PLATFORM_HART_COUNT_OFFSET (0x50) +/** Offset of hart_stack_size in struct sbi_platform */ +#define SBI_PLATFORM_HART_STACK_SIZE_OFFSET (0x54) +/** Offset of platform_ops_addr in struct sbi_platform */ +#define SBI_PLATFORM_OPS_OFFSET (0x58) +/** Offset of firmware_context in struct sbi_platform */ +#define SBI_PLATFORM_FIRMWARE_CONTEXT_OFFSET (0x58 + __SIZEOF_POINTER__) +/** Offset of hart_index2id in struct sbi_platform */ +#define SBI_PLATFORM_HART_INDEX2ID_OFFSET (0x58 + (__SIZEOF_POINTER__ * 2)) + +#define SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_DEFAULT (1UL << 12) + +#ifndef __ASSEMBLY__ + +#include <sbi/sbi_ecall_interface.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_version.h> + +struct sbi_domain_memregion; +struct sbi_trap_info; +struct sbi_trap_regs; + +/** Possible feature flags of a platform */ +enum sbi_platform_features { + /** Platform has timer value */ + SBI_PLATFORM_HAS_TIMER_VALUE = (1 << 0), + /** Platform has HART hotplug support */ + SBI_PLATFORM_HAS_HART_HOTPLUG = (1 << 1), + /** Platform has fault delegation support */ + SBI_PLATFORM_HAS_MFAULTS_DELEGATION = (1 << 2), + /** Platform has custom secondary hart booting support */ + SBI_PLATFORM_HAS_HART_SECONDARY_BOOT = (1 << 3), + + /** Last index of Platform features*/ + SBI_PLATFORM_HAS_LAST_FEATURE = SBI_PLATFORM_HAS_HART_SECONDARY_BOOT, +}; + +/** Default feature set for a platform */ +#define SBI_PLATFORM_DEFAULT_FEATURES \ + (SBI_PLATFORM_HAS_TIMER_VALUE | SBI_PLATFORM_HAS_MFAULTS_DELEGATION) + +/** Platform functions */ +struct sbi_platform_operations { + /** Platform early initialization */ + int (*early_init)(bool cold_boot); + /** Platform final initialization */ + int (*final_init)(bool cold_boot); + + /** Platform early exit */ + void (*early_exit)(void); + /** Platform final exit */ + void (*final_exit)(void); + + /** + * For platforms that do not implement misa, non-standard + * methods are needed to determine cpu extension. + */ + int (*misa_check_extension)(char ext); + + /** + * For platforms that do not implement misa, non-standard + * methods are needed to get MXL field of misa. + */ + int (*misa_get_xlen)(void); + + /** Get platform specific root domain memory regions */ + struct sbi_domain_memregion *(*domains_root_regions)(void); + /** Initialize (or populate) domains for the platform */ + int (*domains_init)(void); + + /** Write a character to the platform console output */ + void (*console_putc)(char ch); + /** Read a character from the platform console input */ + int (*console_getc)(void); + /** Initialize the platform console */ + int (*console_init)(void); + + /** Initialize the platform interrupt controller for current HART */ + int (*irqchip_init)(bool cold_boot); + /** Exit the platform interrupt controller for current HART */ + void (*irqchip_exit)(void); + + /** Send IPI to a target HART */ + void (*ipi_send)(u32 target_hart); + /** Clear IPI for a target HART */ + void (*ipi_clear)(u32 target_hart); + /** Initialize IPI for current HART */ + int (*ipi_init)(bool cold_boot); + /** Exit IPI for current HART */ + void (*ipi_exit)(void); + + /** Get tlb flush limit value **/ + u64 (*get_tlbr_flush_limit)(void); + + /** Get platform timer value */ + u64 (*timer_value)(void); + /** Start platform timer event for current HART */ + void (*timer_event_start)(u64 next_event); + /** Stop platform timer event for current HART */ + void (*timer_event_stop)(void); + /** Initialize platform timer for current HART */ + int (*timer_init)(bool cold_boot); + /** Exit platform timer for current HART */ + void (*timer_exit)(void); + + /** Bringup the given hart */ + int (*hart_start)(u32 hartid, ulong saddr); + /** + * Stop the current hart from running. This call doesn't expect to + * return if success. + */ + int (*hart_stop)(void); + + /* Check whether reset type and reason supported by the platform */ + int (*system_reset_check)(u32 reset_type, u32 reset_reason); + /** Reset the platform */ + void (*system_reset)(u32 reset_type, u32 reset_reason); + + /** platform specific SBI extension implementation probe function */ + int (*vendor_ext_check)(long extid); + /** platform specific SBI extension implementation provider */ + int (*vendor_ext_provider)(long extid, long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_value, + struct sbi_trap_info *out_trap); +}; + +/** Platform default per-HART stack size for exception/interrupt handling */ +#define SBI_PLATFORM_DEFAULT_HART_STACK_SIZE 8192 + +/** Representation of a platform */ +struct sbi_platform { + /** + * OpenSBI version this sbi_platform is based on. + * It's a 32-bit value where upper 16-bits are major number + * and lower 16-bits are minor number + */ + u32 opensbi_version; + /** + * OpenSBI platform version released by vendor. + * It's a 32-bit value where upper 16-bits are major number + * and lower 16-bits are minor number + */ + u32 platform_version; + /** Name of the platform */ + char name[64]; + /** Supported features */ + u64 features; + /** Total number of HARTs */ + u32 hart_count; + /** Per-HART stack size for exception/interrupt handling */ + u32 hart_stack_size; + /** Pointer to sbi platform operations */ + unsigned long platform_ops_addr; + /** Pointer to system firmware specific context */ + unsigned long firmware_context; + /** + * HART index to HART id table + * + * For used HART index <abc>: + * hart_index2id[<abc>] = some HART id + * For unused HART index <abc>: + * hart_index2id[<abc>] = -1U + * + * If hart_index2id == NULL then we assume identity mapping + * hart_index2id[<abc>] = <abc> + * + * We have only two restrictions: + * 1. HART index < sbi_platform hart_count + * 2. HART id < SBI_HARTMASK_MAX_BITS + */ + const u32 *hart_index2id; +}; + +/** Get pointer to sbi_platform for sbi_scratch pointer */ +#define sbi_platform_ptr(__s) \ + ((const struct sbi_platform *)((__s)->platform_addr)) +/** Get pointer to sbi_platform for current HART */ +#define sbi_platform_thishart_ptr() ((const struct sbi_platform *) \ + (sbi_scratch_thishart_ptr()->platform_addr)) +/** Get pointer to platform_ops_addr from platform pointer **/ +#define sbi_platform_ops(__p) \ + ((const struct sbi_platform_operations *)(__p)->platform_ops_addr) + +/** Check whether the platform supports timer value */ +#define sbi_platform_has_timer_value(__p) \ + ((__p)->features & SBI_PLATFORM_HAS_TIMER_VALUE) +/** Check whether the platform supports HART hotplug */ +#define sbi_platform_has_hart_hotplug(__p) \ + ((__p)->features & SBI_PLATFORM_HAS_HART_HOTPLUG) +/** Check whether the platform supports fault delegation */ +#define sbi_platform_has_mfaults_delegation(__p) \ + ((__p)->features & SBI_PLATFORM_HAS_MFAULTS_DELEGATION) +/** Check whether the platform supports custom secondary hart booting support */ +#define sbi_platform_has_hart_secondary_boot(__p) \ + ((__p)->features & SBI_PLATFORM_HAS_HART_SECONDARY_BOOT) + +/** + * Get HART index for the given HART + * + * @param plat pointer to struct sbi_platform + * @param hartid HART ID + * + * @return 0 <= value < hart_count for valid HART otherwise -1U + */ +u32 sbi_platform_hart_index(const struct sbi_platform *plat, u32 hartid); + +/** + * Get the platform features in string format + * + * @param plat pointer to struct sbi_platform + * @param features_str pointer to a char array where the features string will be + * updated + * @param nfstr length of the features_str. The feature string will be truncated + * if nfstr is not long enough. + */ +void sbi_platform_get_features_str(const struct sbi_platform *plat, + char *features_str, int nfstr); + +/** + * Get name of the platform + * + * @param plat pointer to struct sbi_platform + * + * @return pointer to platform name on success and "Unknown" on failure + */ +static inline const char *sbi_platform_name(const struct sbi_platform *plat) +{ + if (plat) + return plat->name; + return "Unknown"; +} + +/** + * Get the platform features + * + * @param plat pointer to struct sbi_platform + * + * @return the features value currently set for the given platform + */ +static inline unsigned long sbi_platform_get_features( + const struct sbi_platform *plat) +{ + if (plat) + return plat->features; + return 0; +} + +/** + * Get platform specific tlb range flush maximum value. Any request with size + * higher than this is upgraded to a full flush. + * + * @param plat pointer to struct sbi_platform + * + * @return tlb range flush limit value. Returns a default (page size) if not + * defined by platform. + */ +static inline u64 sbi_platform_tlbr_flush_limit(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->get_tlbr_flush_limit) + return sbi_platform_ops(plat)->get_tlbr_flush_limit(); + return SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_DEFAULT; +} + +/** + * Get total number of HARTs supported by the platform + * + * @param plat pointer to struct sbi_platform + * + * @return total number of HARTs + */ +static inline u32 sbi_platform_hart_count(const struct sbi_platform *plat) +{ + if (plat) + return plat->hart_count; + return 0; +} + +/** + * Get per-HART stack size for exception/interrupt handling + * + * @param plat pointer to struct sbi_platform + * + * @return stack size in bytes + */ +static inline u32 sbi_platform_hart_stack_size(const struct sbi_platform *plat) +{ + if (plat) + return plat->hart_stack_size; + return 0; +} + +/** + * Check whether given HART is invalid + * + * @param plat pointer to struct sbi_platform + * @param hartid HART ID + * + * @return TRUE if HART is invalid and FALSE otherwise + */ +static inline bool sbi_platform_hart_invalid(const struct sbi_platform *plat, + u32 hartid) +{ + if (!plat) + return TRUE; + if (plat->hart_count <= sbi_platform_hart_index(plat, hartid)) + return TRUE; + return FALSE; +} + +/** + * Bringup a given hart from previous stage. Platform should implement this + * operation if they support a custom mechanism to start a hart. Otherwise, + * a generic WFI based approach will be used to start/stop a hart in OpenSBI. + * + * @param plat pointer to struct sbi_platform + * @param hartid HART id + * @param saddr M-mode start physical address for the HART + * + * @return 0 if sucessful and negative error code on failure + */ +static inline int sbi_platform_hart_start(const struct sbi_platform *plat, + u32 hartid, ulong saddr) +{ + if (plat && sbi_platform_ops(plat)->hart_start) + return sbi_platform_ops(plat)->hart_start(hartid, saddr); + return SBI_ENOTSUPP; +} + +/** + * Stop the current hart in OpenSBI. + * + * @param plat pointer to struct sbi_platform + * + * @return Negative error code on failure. It doesn't return on success. + */ +static inline int sbi_platform_hart_stop(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->hart_stop) + return sbi_platform_ops(plat)->hart_stop(); + return SBI_ENOTSUPP; +} + +/** + * Early initialization for current HART + * + * @param plat pointer to struct sbi_platform + * @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE) + * + * @return 0 on success and negative error code on failure + */ +static inline int sbi_platform_early_init(const struct sbi_platform *plat, + bool cold_boot) +{ + if (plat && sbi_platform_ops(plat)->early_init) + return sbi_platform_ops(plat)->early_init(cold_boot); + return 0; +} + +/** + * Final initialization for current HART + * + * @param plat pointer to struct sbi_platform + * @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE) + * + * @return 0 on success and negative error code on failure + */ +static inline int sbi_platform_final_init(const struct sbi_platform *plat, + bool cold_boot) +{ + if (plat && sbi_platform_ops(plat)->final_init) + return sbi_platform_ops(plat)->final_init(cold_boot); + return 0; +} + +/** + * Early exit for current HART + * + * @param plat pointer to struct sbi_platform + */ +static inline void sbi_platform_early_exit(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->early_exit) + sbi_platform_ops(plat)->early_exit(); +} + +/** + * Final exit for current HART + * + * @param plat pointer to struct sbi_platform + */ +static inline void sbi_platform_final_exit(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->final_exit) + sbi_platform_ops(plat)->final_exit(); +} + +/** + * Check CPU extension in MISA + * + * @param plat pointer to struct sbi_platform + * @param ext shorthand letter for CPU extensions + * + * @return zero for not-supported and non-zero for supported + */ +static inline int sbi_platform_misa_extension(const struct sbi_platform *plat, + char ext) +{ + if (plat && sbi_platform_ops(plat)->misa_check_extension) + return sbi_platform_ops(plat)->misa_check_extension(ext); + return 0; +} + +/** + * Get MXL field of MISA + * + * @param plat pointer to struct sbi_platform + * + * @return 1/2/3 on success and error code on failure + */ +static inline int sbi_platform_misa_xlen(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->misa_get_xlen) + return sbi_platform_ops(plat)->misa_get_xlen(); + return -1; +} + +/** + * Get platform specific root domain memory regions + * + * @param plat pointer to struct sbi_platform + * + * @return an array of memory regions terminated by a region with order zero + * or NULL for no memory regions + */ +static inline struct sbi_domain_memregion * +sbi_platform_domains_root_regions(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->domains_root_regions) + return sbi_platform_ops(plat)->domains_root_regions(); + return NULL; +} + +/** + * Initialize (or populate) domains for the platform + * + * @param plat pointer to struct sbi_platform + * + * @return 0 on success and negative error code on failure + */ +static inline int sbi_platform_domains_init(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->domains_init) + return sbi_platform_ops(plat)->domains_init(); + return 0; +} + +/** + * Write a character to the platform console output + * + * @param plat pointer to struct sbi_platform + * @param ch character to write + */ +static inline void sbi_platform_console_putc(const struct sbi_platform *plat, + char ch) +{ + if (plat && sbi_platform_ops(plat)->console_putc) + sbi_platform_ops(plat)->console_putc(ch); +} + +/** + * Read a character from the platform console input + * + * @param plat pointer to struct sbi_platform + * + * @return character read from console input + */ +static inline int sbi_platform_console_getc(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->console_getc) + return sbi_platform_ops(plat)->console_getc(); + return -1; +} + +/** + * Initialize the platform console + * + * @param plat pointer to struct sbi_platform + * + * @return 0 on success and negative error code on failure + */ +static inline int sbi_platform_console_init(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->console_init) + return sbi_platform_ops(plat)->console_init(); + return 0; +} + +/** + * Initialize the platform interrupt controller for current HART + * + * @param plat pointer to struct sbi_platform + * @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE) + * + * @return 0 on success and negative error code on failure + */ +static inline int sbi_platform_irqchip_init(const struct sbi_platform *plat, + bool cold_boot) +{ + if (plat && sbi_platform_ops(plat)->irqchip_init) + return sbi_platform_ops(plat)->irqchip_init(cold_boot); + return 0; +} + +/** + * Exit the platform interrupt controller for current HART + * + * @param plat pointer to struct sbi_platform + */ +static inline void sbi_platform_irqchip_exit(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->irqchip_exit) + sbi_platform_ops(plat)->irqchip_exit(); +} + +/** + * Send IPI to a target HART + * + * @param plat pointer to struct sbi_platform + * @param target_hart HART ID of IPI target + */ +static inline void sbi_platform_ipi_send(const struct sbi_platform *plat, + u32 target_hart) +{ + if (plat && sbi_platform_ops(plat)->ipi_send) + sbi_platform_ops(plat)->ipi_send(target_hart); +} + +/** + * Clear IPI for a target HART + * + * @param plat pointer to struct sbi_platform + * @param target_hart HART ID of IPI target + */ +static inline void sbi_platform_ipi_clear(const struct sbi_platform *plat, + u32 target_hart) +{ + if (plat && sbi_platform_ops(plat)->ipi_clear) + sbi_platform_ops(plat)->ipi_clear(target_hart); +} + +/** + * Initialize the platform IPI support for current HART + * + * @param plat pointer to struct sbi_platform + * @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE) + * + * @return 0 on success and negative error code on failure + */ +static inline int sbi_platform_ipi_init(const struct sbi_platform *plat, + bool cold_boot) +{ + if (plat && sbi_platform_ops(plat)->ipi_init) + return sbi_platform_ops(plat)->ipi_init(cold_boot); + return 0; +} + +/** + * Exit the platform IPI support for current HART + * + * @param plat pointer to struct sbi_platform + */ +static inline void sbi_platform_ipi_exit(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->ipi_exit) + sbi_platform_ops(plat)->ipi_exit(); +} + +/** + * Get platform timer value + * + * @param plat pointer to struct sbi_platform + * + * @return 64-bit timer value + */ +static inline u64 sbi_platform_timer_value(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->timer_value) + return sbi_platform_ops(plat)->timer_value(); + return 0; +} + +/** + * Start platform timer event for current HART + * + * @param plat pointer to struct struct sbi_platform + * @param next_event timer value when timer event will happen + */ +static inline void +sbi_platform_timer_event_start(const struct sbi_platform *plat, u64 next_event) +{ + if (plat && sbi_platform_ops(plat)->timer_event_start) + sbi_platform_ops(plat)->timer_event_start(next_event); +} + +/** + * Stop platform timer event for current HART + * + * @param plat pointer to struct sbi_platform + */ +static inline void +sbi_platform_timer_event_stop(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->timer_event_stop) + sbi_platform_ops(plat)->timer_event_stop(); +} + +/** + * Initialize the platform timer for current HART + * + * @param plat pointer to struct sbi_platform + * @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE) + * + * @return 0 on success and negative error code on failure + */ +static inline int sbi_platform_timer_init(const struct sbi_platform *plat, + bool cold_boot) +{ + if (plat && sbi_platform_ops(plat)->timer_init) + return sbi_platform_ops(plat)->timer_init(cold_boot); + return 0; +} + +/** + * Exit the platform timer for current HART + * + * @param plat pointer to struct sbi_platform + */ +static inline void sbi_platform_timer_exit(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->timer_exit) + sbi_platform_ops(plat)->timer_exit(); +} + +/** + * Check whether reset type and reason supported by the platform + * + * @param plat pointer to struct sbi_platform + * @param reset_type type of reset + * @param reset_reason reason for reset + * + * @return 0 if reset type and reason not supported and 1 if supported + */ +static inline int sbi_platform_system_reset_check( + const struct sbi_platform *plat, + u32 reset_type, u32 reset_reason) +{ + if (plat && sbi_platform_ops(plat)->system_reset_check) + return sbi_platform_ops(plat)->system_reset_check(reset_type, + reset_reason); + return 0; +} + +/** + * Reset the platform + * + * This function will not return for supported reset type and reset reason + * + * @param plat pointer to struct sbi_platform + * @param reset_type type of reset + * @param reset_reason reason for reset + */ +static inline void sbi_platform_system_reset(const struct sbi_platform *plat, + u32 reset_type, u32 reset_reason) +{ + if (plat && sbi_platform_ops(plat)->system_reset) + sbi_platform_ops(plat)->system_reset(reset_type, reset_reason); +} + +/** + * Check if a vendor extension is implemented or not. + * + * @param plat pointer to struct sbi_platform + * @param extid vendor SBI extension id + * + * @return 0 if extid is not implemented and 1 if implemented + */ +static inline int sbi_platform_vendor_ext_check(const struct sbi_platform *plat, + long extid) +{ + if (plat && sbi_platform_ops(plat)->vendor_ext_check) + return sbi_platform_ops(plat)->vendor_ext_check(extid); + + return 0; +} + +/** + * Invoke platform specific vendor SBI extension implementation. + * + * @param plat pointer to struct sbi_platform + * @param extid vendor SBI extension id + * @param funcid SBI function id within the extension id + * @param regs pointer to trap registers passed by the caller + * @param out_value output value that can be filled by the callee + * @param out_trap trap info that can be filled by the callee + * + * @return 0 on success and negative error code on failure + */ +static inline int sbi_platform_vendor_ext_provider( + const struct sbi_platform *plat, + long extid, long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_value, + struct sbi_trap_info *out_trap) +{ + if (plat && sbi_platform_ops(plat)->vendor_ext_provider) { + return sbi_platform_ops(plat)->vendor_ext_provider(extid, + funcid, regs, + out_value, + out_trap); + } + + return SBI_ENOTSUPP; +} + +#endif + +#endif diff --git a/roms/opensbi/include/sbi/sbi_scratch.h b/roms/opensbi/include/sbi/sbi_scratch.h new file mode 100644 index 000000000..e35122bed --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_scratch.h @@ -0,0 +1,128 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_SCRATCH_H__ +#define __SBI_SCRATCH_H__ + +#include <sbi/riscv_asm.h> + +/* clang-format off */ + +/** Offset of fw_start member in sbi_scratch */ +#define SBI_SCRATCH_FW_START_OFFSET (0 * __SIZEOF_POINTER__) +/** Offset of fw_size member in sbi_scratch */ +#define SBI_SCRATCH_FW_SIZE_OFFSET (1 * __SIZEOF_POINTER__) +/** Offset of next_arg1 member in sbi_scratch */ +#define SBI_SCRATCH_NEXT_ARG1_OFFSET (2 * __SIZEOF_POINTER__) +/** Offset of next_addr member in sbi_scratch */ +#define SBI_SCRATCH_NEXT_ADDR_OFFSET (3 * __SIZEOF_POINTER__) +/** Offset of next_mode member in sbi_scratch */ +#define SBI_SCRATCH_NEXT_MODE_OFFSET (4 * __SIZEOF_POINTER__) +/** Offset of warmboot_addr member in sbi_scratch */ +#define SBI_SCRATCH_WARMBOOT_ADDR_OFFSET (5 * __SIZEOF_POINTER__) +/** Offset of platform_addr member in sbi_scratch */ +#define SBI_SCRATCH_PLATFORM_ADDR_OFFSET (6 * __SIZEOF_POINTER__) +/** Offset of hartid_to_scratch member in sbi_scratch */ +#define SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET (7 * __SIZEOF_POINTER__) +/** Offset of trap_exit member in sbi_scratch */ +#define SBI_SCRATCH_TRAP_EXIT_OFFSET (8 * __SIZEOF_POINTER__) +/** Offset of tmp0 member in sbi_scratch */ +#define SBI_SCRATCH_TMP0_OFFSET (9 * __SIZEOF_POINTER__) +/** Offset of options member in sbi_scratch */ +#define SBI_SCRATCH_OPTIONS_OFFSET (10 * __SIZEOF_POINTER__) +/** Offset of extra space in sbi_scratch */ +#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (11 * __SIZEOF_POINTER__) +/** Maximum size of sbi_scratch (4KB) */ +#define SBI_SCRATCH_SIZE (0x1000) + +/* clang-format on */ + +#ifndef __ASSEMBLY__ + +#include <sbi/sbi_types.h> + +/** Representation of per-HART scratch space */ +struct sbi_scratch { + /** Start (or base) address of firmware linked to OpenSBI library */ + unsigned long fw_start; + /** Size (in bytes) of firmware linked to OpenSBI library */ + unsigned long fw_size; + /** Arg1 (or 'a1' register) of next booting stage for this HART */ + unsigned long next_arg1; + /** Address of next booting stage for this HART */ + unsigned long next_addr; + /** Priviledge mode of next booting stage for this HART */ + unsigned long next_mode; + /** Warm boot entry point address for this HART */ + unsigned long warmboot_addr; + /** Address of sbi_platform */ + unsigned long platform_addr; + /** Address of HART ID to sbi_scratch conversion function */ + unsigned long hartid_to_scratch; + /** Address of trap exit function */ + unsigned long trap_exit; + /** Temporary storage */ + unsigned long tmp0; + /** Options for OpenSBI library */ + unsigned long options; +}; + +/** Possible options for OpenSBI library */ +enum sbi_scratch_options { + /** Disable prints during boot */ + SBI_SCRATCH_NO_BOOT_PRINTS = (1 << 0), + /** Enable runtime debug prints */ + SBI_SCRATCH_DEBUG_PRINTS = (1 << 1), +}; + +/** Get pointer to sbi_scratch for current HART */ +#define sbi_scratch_thishart_ptr() \ + ((struct sbi_scratch *)csr_read(CSR_MSCRATCH)) + +/** Get Arg1 of next booting stage for current HART */ +#define sbi_scratch_thishart_arg1_ptr() \ + ((void *)(sbi_scratch_thishart_ptr()->next_arg1)) + +/** Initialize scratch table and allocator */ +int sbi_scratch_init(struct sbi_scratch *scratch); + +/** + * Allocate from extra space in sbi_scratch + * + * @return zero on failure and non-zero (>= SBI_SCRATCH_EXTRA_SPACE_OFFSET) + * on success + */ +unsigned long sbi_scratch_alloc_offset(unsigned long size, const char *owner); + +/** Free-up extra space in sbi_scratch */ +void sbi_scratch_free_offset(unsigned long offset); + +/** Get pointer from offset in sbi_scratch */ +#define sbi_scratch_offset_ptr(scratch, offset) ((void *)scratch + (offset)) + +/** Get pointer from offset in sbi_scratch for current HART */ +#define sbi_scratch_thishart_offset_ptr(offset) \ + ((void *)sbi_scratch_thishart_ptr() + (offset)) + +/** HART id to scratch table */ +extern struct sbi_scratch *hartid_to_scratch_table[]; + +/** Get sbi_scratch from HART id */ +#define sbi_hartid_to_scratch(__hartid) \ + hartid_to_scratch_table[__hartid] + +/** Last HART id having a sbi_scratch pointer */ +extern u32 last_hartid_having_scratch; + +/** Get last HART id having a sbi_scratch pointer */ +#define sbi_scratch_last_hartid() last_hartid_having_scratch + +#endif + +#endif diff --git a/roms/opensbi/include/sbi/sbi_string.h b/roms/opensbi/include/sbi/sbi_string.h new file mode 100644 index 000000000..b7c2bc22a --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_string.h @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +#ifndef __STRING_H__ +#define __STRING_H__ + +#include <sbi/sbi_types.h> + +/* + Provides sbi_strcmp for the completeness of supporting string functions. + it is not recommended to use sbi_strcmp() but use sbi_strncmp instead. +*/ + +int sbi_strcmp(const char *a, const char *b); + +int sbi_strncmp(const char *a, const char *b, size_t count); + +size_t sbi_strlen(const char *str); + +size_t sbi_strnlen(const char *str, size_t count); + +char *sbi_strcpy(char *dest, const char *src); + +char *sbi_strncpy(char *dest, const char *src, size_t count); + +char *sbi_strchr(const char *s, int c); + +char *sbi_strrchr(const char *s, int c); + +void *sbi_memset(void *s, int c, size_t count); + +void *sbi_memcpy(void *dest, const void *src, size_t count); + +void *sbi_memmove(void *dest, const void *src, size_t count); + +int sbi_memcmp(const void *s1, const void *s2, size_t count); + +void *sbi_memchr(const void *s, int c, size_t count); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_system.h b/roms/opensbi/include/sbi/sbi_system.h new file mode 100644 index 000000000..34ba7669a --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_system.h @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_SYSTEM_H__ +#define __SBI_SYSTEM_H__ + +#include <sbi/sbi_types.h> + +bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason); + +void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_timer.h b/roms/opensbi/include/sbi/sbi_timer.h new file mode 100644 index 000000000..87bbdbfa0 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_timer.h @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_TIMER_H__ +#define __SBI_TIMER_H__ + +#include <sbi/sbi_types.h> + +struct sbi_scratch; + +/** Get timer value for current HART */ +u64 sbi_timer_value(void); + +/** Get virtualized timer value for current HART */ +u64 sbi_timer_virt_value(void); + +/** Get timer delta value for current HART */ +u64 sbi_timer_get_delta(void); + +/** Set timer delta value for current HART */ +void sbi_timer_set_delta(ulong delta); + +/** Set upper 32-bits of timer delta value for current HART */ +void sbi_timer_set_delta_upper(ulong delta_upper); + +/** Start timer event for current HART */ +void sbi_timer_event_start(u64 next_event); + +/** Process timer event for current HART */ +void sbi_timer_process(void); + +/* Initialize timer */ +int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot); + +/* Exit timer */ +void sbi_timer_exit(struct sbi_scratch *scratch); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_tlb.h b/roms/opensbi/include/sbi/sbi_tlb.h new file mode 100644 index 000000000..48f1962d7 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_tlb.h @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_TLB_H__ +#define __SBI_TLB_H__ + +#include <sbi/sbi_types.h> +#include <sbi/sbi_hartmask.h> + +/* clang-format off */ + +#define SBI_TLB_FLUSH_ALL ((unsigned long)-1) + +/* clang-format on */ + +#define SBI_TLB_FIFO_NUM_ENTRIES 8 + +struct sbi_scratch; + +struct sbi_tlb_info { + unsigned long start; + unsigned long size; + unsigned long asid; + unsigned long vmid; + void (*local_fn)(struct sbi_tlb_info *tinfo); + struct sbi_hartmask smask; +}; + +void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo); +void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo); +void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo); +void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo); +void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo); +void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo); +void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo); + +#define SBI_TLB_INFO_INIT(__p, __start, __size, __asid, __vmid, __lfn, __src) \ +do { \ + (__p)->start = (__start); \ + (__p)->size = (__size); \ + (__p)->asid = (__asid); \ + (__p)->vmid = (__vmid); \ + (__p)->local_fn = (__lfn); \ + SBI_HARTMASK_INIT_EXCEPT(&(__p)->smask, (__src)); \ +} while (0) + +#define SBI_TLB_INFO_SIZE sizeof(struct sbi_tlb_info) + +int sbi_tlb_request(ulong hmask, ulong hbase, struct sbi_tlb_info *tinfo); + +int sbi_tlb_init(struct sbi_scratch *scratch, bool cold_boot); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_trap.h b/roms/opensbi/include/sbi/sbi_trap.h new file mode 100644 index 000000000..5fb94f980 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_trap.h @@ -0,0 +1,214 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_TRAP_H__ +#define __SBI_TRAP_H__ + +/* clang-format off */ + +/** Index of zero member in sbi_trap_regs */ +#define SBI_TRAP_REGS_zero 0 +/** Index of ra member in sbi_trap_regs */ +#define SBI_TRAP_REGS_ra 1 +/** Index of sp member in sbi_trap_regs */ +#define SBI_TRAP_REGS_sp 2 +/** Index of gp member in sbi_trap_regs */ +#define SBI_TRAP_REGS_gp 3 +/** Index of tp member in sbi_trap_regs */ +#define SBI_TRAP_REGS_tp 4 +/** Index of t0 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_t0 5 +/** Index of t1 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_t1 6 +/** Index of t2 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_t2 7 +/** Index of s0 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s0 8 +/** Index of s1 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s1 9 +/** Index of a0 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_a0 10 +/** Index of a1 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_a1 11 +/** Index of a2 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_a2 12 +/** Index of a3 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_a3 13 +/** Index of a4 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_a4 14 +/** Index of a5 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_a5 15 +/** Index of a6 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_a6 16 +/** Index of a7 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_a7 17 +/** Index of s2 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s2 18 +/** Index of s3 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s3 19 +/** Index of s4 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s4 20 +/** Index of s5 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s5 21 +/** Index of s6 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s6 22 +/** Index of s7 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s7 23 +/** Index of s8 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s8 24 +/** Index of s9 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s9 25 +/** Index of s10 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s10 26 +/** Index of s11 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_s11 27 +/** Index of t3 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_t3 28 +/** Index of t4 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_t4 29 +/** Index of t5 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_t5 30 +/** Index of t6 member in sbi_trap_regs */ +#define SBI_TRAP_REGS_t6 31 +/** Index of mepc member in sbi_trap_regs */ +#define SBI_TRAP_REGS_mepc 32 +/** Index of mstatus member in sbi_trap_regs */ +#define SBI_TRAP_REGS_mstatus 33 +/** Index of mstatusH member in sbi_trap_regs */ +#define SBI_TRAP_REGS_mstatusH 34 +/** Last member index in sbi_trap_regs */ +#define SBI_TRAP_REGS_last 35 + +/** Index of epc member in sbi_trap_info */ +#define SBI_TRAP_INFO_epc 0 +/** Index of cause member in sbi_trap_info */ +#define SBI_TRAP_INFO_cause 1 +/** Index of tval member in sbi_trap_info */ +#define SBI_TRAP_INFO_tval 2 +/** Index of tval2 member in sbi_trap_info */ +#define SBI_TRAP_INFO_tval2 3 +/** Index of tinst member in sbi_trap_info */ +#define SBI_TRAP_INFO_tinst 4 +/** Last member index in sbi_trap_info */ +#define SBI_TRAP_INFO_last 5 + +/* clang-format on */ + +/** Get offset of member with name 'x' in sbi_trap_regs */ +#define SBI_TRAP_REGS_OFFSET(x) ((SBI_TRAP_REGS_##x) * __SIZEOF_POINTER__) +/** Size (in bytes) of sbi_trap_regs */ +#define SBI_TRAP_REGS_SIZE SBI_TRAP_REGS_OFFSET(last) + +/** Get offset of member with name 'x' in sbi_trap_info */ +#define SBI_TRAP_INFO_OFFSET(x) ((SBI_TRAP_INFO_##x) * __SIZEOF_POINTER__) +/** Size (in bytes) of sbi_trap_info */ +#define SBI_TRAP_INFO_SIZE SBI_TRAP_INFO_OFFSET(last) + +#ifndef __ASSEMBLY__ + +#include <sbi/sbi_types.h> + +/** Representation of register state at time of trap/interrupt */ +struct sbi_trap_regs { + /** zero register state */ + unsigned long zero; + /** ra register state */ + unsigned long ra; + /** sp register state */ + unsigned long sp; + /** gp register state */ + unsigned long gp; + /** tp register state */ + unsigned long tp; + /** t0 register state */ + unsigned long t0; + /** t1 register state */ + unsigned long t1; + /** t2 register state */ + unsigned long t2; + /** s0 register state */ + unsigned long s0; + /** s1 register state */ + unsigned long s1; + /** a0 register state */ + unsigned long a0; + /** a1 register state */ + unsigned long a1; + /** a2 register state */ + unsigned long a2; + /** a3 register state */ + unsigned long a3; + /** a4 register state */ + unsigned long a4; + /** a5 register state */ + unsigned long a5; + /** a6 register state */ + unsigned long a6; + /** a7 register state */ + unsigned long a7; + /** s2 register state */ + unsigned long s2; + /** s3 register state */ + unsigned long s3; + /** s4 register state */ + unsigned long s4; + /** s5 register state */ + unsigned long s5; + /** s6 register state */ + unsigned long s6; + /** s7 register state */ + unsigned long s7; + /** s8 register state */ + unsigned long s8; + /** s9 register state */ + unsigned long s9; + /** s10 register state */ + unsigned long s10; + /** s11 register state */ + unsigned long s11; + /** t3 register state */ + unsigned long t3; + /** t4 register state */ + unsigned long t4; + /** t5 register state */ + unsigned long t5; + /** t6 register state */ + unsigned long t6; + /** mepc register state */ + unsigned long mepc; + /** mstatus register state */ + unsigned long mstatus; + /** mstatusH register state (only for 32-bit) */ + unsigned long mstatusH; +}; + +/** Representation of trap details */ +struct sbi_trap_info { + /** epc Trap program counter */ + unsigned long epc; + /** cause Trap exception cause */ + unsigned long cause; + /** tval Trap value */ + unsigned long tval; + /** tval2 Trap value 2 */ + unsigned long tval2; + /** tinst Trap instruction */ + unsigned long tinst; +}; + +int sbi_trap_redirect(struct sbi_trap_regs *regs, + struct sbi_trap_info *trap); + +void sbi_trap_handler(struct sbi_trap_regs *regs); + +void __noreturn sbi_trap_exit(const struct sbi_trap_regs *regs); + +#endif + +#endif diff --git a/roms/opensbi/include/sbi/sbi_types.h b/roms/opensbi/include/sbi/sbi_types.h new file mode 100644 index 000000000..0952d5c82 --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_types.h @@ -0,0 +1,109 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_TYPES_H__ +#define __SBI_TYPES_H__ + +#ifndef OPENSBI_EXTERNAL_SBI_TYPES + +/* clang-format off */ + +typedef char s8; +typedef unsigned char u8; +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; +#define PRILX "016lx" +#elif __riscv_xlen == 32 +typedef long long s64; +typedef unsigned long long u64; +typedef long long int64_t; +typedef unsigned long long uint64_t; +#define PRILX "08lx" +#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 true TRUE +#define false FALSE + +#define NULL ((void *)0) + +#define __packed __attribute__((packed)) +#define __noreturn __attribute__((noreturn)) + +#define likely(x) __builtin_expect((x), 1) +#define unlikely(x) __builtin_expect((x), 0) + +#undef offsetof +#ifdef __compiler_offsetof +#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE,MEMBER) +#else +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member) * __mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); }) + +#define array_size(x) (sizeof(x) / sizeof((x)[0])) + +#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 STR(x) XSTR(x) +#define XSTR(x) #x + +#define ROUNDUP(a, b) ((((a)-1) / (b) + 1) * (b)) +#define ROUNDDOWN(a, b) ((a) / (b) * (b)) + +/* clang-format on */ + +#else +/* + * OPENSBI_EXTERNAL_SBI_TYPES could be defined in CFLAGS for using the + * external definitions of data types and common macros. + * OPENSBI_EXTERNAL_SBI_TYPES is the file name to external header file, + * the external build system should address the additional include + * directory ccordingly. + */ + +#define XSTR(x) #x +#define STR(x) XSTR(x) +#include STR(OPENSBI_EXTERNAL_SBI_TYPES) +#endif + +#endif diff --git a/roms/opensbi/include/sbi/sbi_unpriv.h b/roms/opensbi/include/sbi/sbi_unpriv.h new file mode 100644 index 000000000..8cbd3de0c --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_unpriv.h @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_UNPRIV_H__ +#define __SBI_UNPRIV_H__ + +#include <sbi/sbi_types.h> + +struct sbi_scratch; +struct sbi_trap_info; + +#define DECLARE_UNPRIVILEGED_LOAD_FUNCTION(type) \ + type sbi_load_##type(const type *addr, \ + struct sbi_trap_info *trap); + +#define DECLARE_UNPRIVILEGED_STORE_FUNCTION(type) \ + void sbi_store_##type(type *addr, type val, \ + struct sbi_trap_info *trap); + +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u8) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u16) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(s8) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(s16) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(s32) +DECLARE_UNPRIVILEGED_STORE_FUNCTION(u8) +DECLARE_UNPRIVILEGED_STORE_FUNCTION(u16) +DECLARE_UNPRIVILEGED_STORE_FUNCTION(u32) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u32) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u64) +DECLARE_UNPRIVILEGED_STORE_FUNCTION(u64) +DECLARE_UNPRIVILEGED_LOAD_FUNCTION(ulong) + +ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap); + +#endif diff --git a/roms/opensbi/include/sbi/sbi_version.h b/roms/opensbi/include/sbi/sbi_version.h new file mode 100644 index 000000000..3f734fe9d --- /dev/null +++ b/roms/opensbi/include/sbi/sbi_version.h @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SBI_VERSION_H__ +#define __SBI_VERSION_H__ + +#define OPENSBI_VERSION_MAJOR 0 +#define OPENSBI_VERSION_MINOR 9 + +/** + * OpenSBI 32-bit version with: + * 1. upper 16-bits as major number + * 2. lower 16-bits as minor number + */ +#define OPENSBI_VERSION ((OPENSBI_VERSION_MAJOR << 16) | \ + (OPENSBI_VERSION_MINOR)) + +#endif diff --git a/roms/opensbi/include/sbi_utils/fdt/fdt_domain.h b/roms/opensbi/include/sbi_utils/fdt/fdt_domain.h new file mode 100644 index 000000000..5448eb4d9 --- /dev/null +++ b/roms/opensbi/include/sbi_utils/fdt/fdt_domain.h @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * fdt_domain.c - Flat Device Tree Domain helper routines + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __FDT_DOMAIN_H__ +#define __FDT_DOMAIN_H__ + +#include <sbi/sbi_types.h> + +struct sbi_domain; + +/** + * Iterate over each domains in device tree + * + * @param fdt device tree blob + * @param opaque private pointer for each iteration + * @param fn callback function for each iteration + * + * @return 0 on success and negative error code on failure + */ +int fdt_iterate_each_domain(void *fdt, void *opaque, + int (*fn)(void *fdt, int domain_offset, + void *opaque)); + +/** + * Iterate over each memregion of a domain in device tree + * + * @param fdt device tree blob + * @param domain_offset domain DT node offset + * @param opaque private pointer for each iteration + * @param fn callback function for each iteration + * + * @return 0 on success and negative error code on failure + */ +int fdt_iterate_each_memregion(void *fdt, int domain_offset, void *opaque, + int (*fn)(void *fdt, int domain_offset, + int region_offset, u32 region_access, + void *opaque)); + +/** + * Fix up the domain configuration in the device tree + * + * This routine: + * 1. Disables MMIO devices not accessible to the coldboot HART domain + * 2. Removes "opensbi-domain" DT property from CPU DT nodes + * 3. Removes domain configuration DT node under /chosen DT node + * + * It is recommended that platform support call this function in + * their final_init() platform operation. + * + * @param fdt device tree blob + */ +void fdt_domain_fixup(void *fdt); + +/** + * Populate domains from device tree + * + * It is recommended that platform support call this function in + * their domains_init() platform operation. + * + * @param fdt device tree blob + * + * @return 0 on success and negative error code on failure + */ +int fdt_domains_populate(void *fdt); + +#endif /* __FDT_DOMAIN_H__ */ diff --git a/roms/opensbi/include/sbi_utils/fdt/fdt_fixup.h b/roms/opensbi/include/sbi_utils/fdt/fdt_fixup.h new file mode 100644 index 000000000..0697a18ee --- /dev/null +++ b/roms/opensbi/include/sbi_utils/fdt/fdt_fixup.h @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * fdt_fixup.h - Flat Device Tree manipulation helper routines + * Implement platform specific DT fixups on top of libfdt. + * + * Copyright (C) 2020 Bin Meng <bmeng.cn@gmail.com> + */ + +#ifndef __FDT_FIXUP_H__ +#define __FDT_FIXUP_H__ + +/** + * Fix up the CPU node in the device tree + * + * This routine updates the "status" property of a CPU node in the device tree + * to "disabled" if that hart is in disabled state in OpenSBI. + * + * It is recommended that platform codes call this helper in their final_init() + * + * @param fdt: device tree blob + */ +void fdt_cpu_fixup(void *fdt); + +/** + * Fix up the PLIC node in the device tree + * + * This routine updates the "interrupt-extended" property of the PLIC node in + * the device tree to hide the M-mode external interrupt from CPUs. + * + * It is recommended that platform codes call this helper in their final_init() + * + * @param fdt: device tree blob + * @param compat: PLIC node compatible string + */ +void fdt_plic_fixup(void *fdt, const char *compat); + +/** + * Fix up the reserved memory node in the device tree + * + * This routine inserts a child node of the reserved memory node in the device + * tree that describes the protected memory region done by OpenSBI via PMP. + * + * It is recommended that platform codes call this helper in their final_init() + * + * @param fdt: device tree blob + * @return zero on success and -ve on failure + */ +int fdt_reserved_memory_fixup(void *fdt); + +/** + * Fix up the reserved memory subnodes in the device tree + * + * This routine adds the no-map property to the reserved memory subnodes so + * that the OS does not map those PMP protected memory regions. + * + * Platform codes must call this helper in their final_init() after fdt_fixups() + * if the OS should not map the PMP protected reserved regions. + * + * @param fdt: device tree blob + * @return zero on success and -ve on failure + */ +int fdt_reserved_memory_nomap_fixup(void *fdt); + +/** + * General device tree fix-up + * + * This routine do all required device tree fix-ups for a typical platform. + * It fixes up the PLIC node and the reserved memory node in the device tree + * by calling the corresponding helper routines to accomplish the task. + * + * It is recommended that platform codes call this helper in their final_init() + * + * @param fdt: device tree blob + */ +void fdt_fixups(void *fdt); + +#endif /* __FDT_FIXUP_H__ */ + diff --git a/roms/opensbi/include/sbi_utils/fdt/fdt_helper.h b/roms/opensbi/include/sbi_utils/fdt/fdt_helper.h new file mode 100644 index 000000000..f5222deb4 --- /dev/null +++ b/roms/opensbi/include/sbi_utils/fdt/fdt_helper.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * fdt_helper.h - Flat Device Tree parsing helper routines + * Implement helper routines to parse FDT nodes on top of + * libfdt for OpenSBI usage + * + * Copyright (C) 2020 Bin Meng <bmeng.cn@gmail.com> + */ + +#ifndef __FDT_HELPER_H__ +#define __FDT_HELPER_H__ + +#include <sbi/sbi_types.h> + +struct fdt_match { + const char *compatible; + void *data; +}; + +struct platform_uart_data { + unsigned long addr; + unsigned long freq; + unsigned long baud; + unsigned long reg_shift; + unsigned long reg_io_width; +}; + +const struct fdt_match *fdt_match_node(void *fdt, int nodeoff, + const struct fdt_match *match_table); + +int fdt_find_match(void *fdt, int startoff, + const struct fdt_match *match_table, + const struct fdt_match **out_match); + +int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr, + unsigned long *size); + +int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid); + +int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid); + +int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset, + struct platform_uart_data *uart); + +int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset, + struct platform_uart_data *uart); + +int fdt_parse_uart8250_node(void *fdt, int nodeoffset, + struct platform_uart_data *uart); + +int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart, + const char *compatible); + +struct plic_data; + +int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic); + +int fdt_parse_plic(void *fdt, struct plic_data *plic, const char *compat); + +struct clint_data; + +int fdt_parse_clint_node(void *fdt, int nodeoffset, bool for_timer, + struct clint_data *clint); + +int fdt_parse_compat_addr(void *fdt, unsigned long *addr, + const char *compatible); + +#endif /* __FDT_HELPER_H__ */ diff --git a/roms/opensbi/include/sbi_utils/ipi/fdt_ipi.h b/roms/opensbi/include/sbi_utils/ipi/fdt_ipi.h new file mode 100644 index 000000000..e817141ac --- /dev/null +++ b/roms/opensbi/include/sbi_utils/ipi/fdt_ipi.h @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __FDT_IPI_H__ +#define __FDT_IPI_H__ + +#include <sbi/sbi_types.h> + +struct fdt_ipi { + const struct fdt_match *match_table; + int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match); + int (*warm_init)(void); + void (*exit)(void); + void (*send)(u32 target_hart); + void (*clear)(u32 target_hart); +}; + +void fdt_ipi_send(u32 target_hart); + +void fdt_ipi_clear(u32 target_hart); + +void fdt_ipi_exit(void); + +int fdt_ipi_init(bool cold_boot); + +#endif diff --git a/roms/opensbi/include/sbi_utils/irqchip/fdt_irqchip.h b/roms/opensbi/include/sbi_utils/irqchip/fdt_irqchip.h new file mode 100644 index 000000000..13ef6f711 --- /dev/null +++ b/roms/opensbi/include/sbi_utils/irqchip/fdt_irqchip.h @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __FDT_IRQCHIP_H__ +#define __FDT_IRQCHIP_H__ + +#include <sbi/sbi_types.h> + +struct fdt_irqchip { + const struct fdt_match *match_table; + int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match); + int (*warm_init)(void); + void (*exit)(void); +}; + +void fdt_irqchip_exit(void); + +int fdt_irqchip_init(bool cold_boot); + +#endif diff --git a/roms/opensbi/include/sbi_utils/irqchip/plic.h b/roms/opensbi/include/sbi_utils/irqchip/plic.h new file mode 100644 index 000000000..0e56d801e --- /dev/null +++ b/roms/opensbi/include/sbi_utils/irqchip/plic.h @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __IRQCHIP_PLIC_H__ +#define __IRQCHIP_PLIC_H__ + +#include <sbi/sbi_types.h> + +struct plic_data { + unsigned long addr; + unsigned long num_src; +}; + +int plic_warm_irqchip_init(struct plic_data *plic, + int m_cntx_id, int s_cntx_id); + +int plic_cold_irqchip_init(struct plic_data *plic); + +void plic_set_thresh(struct plic_data *plic, u32 cntxid, u32 val); + +void plic_set_ie(struct plic_data *plic, u32 cntxid, u32 word_index, u32 val); + +#endif diff --git a/roms/opensbi/include/sbi_utils/reset/fdt_reset.h b/roms/opensbi/include/sbi_utils/reset/fdt_reset.h new file mode 100644 index 000000000..cce441a84 --- /dev/null +++ b/roms/opensbi/include/sbi_utils/reset/fdt_reset.h @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __FDT_RESET_H__ +#define __FDT_RESET_H__ + +#include <sbi/sbi_types.h> + +struct fdt_reset { + const struct fdt_match *match_table; + int (*init)(void *fdt, int nodeoff, const struct fdt_match *match); + int (*system_reset_check)(u32 reset_type, u32 reset_reason); + void (*system_reset)(u32 reset_type, u32 reset_reason); +}; + +int fdt_system_reset_check(u32 reset_type, u32 reset_reason); + +void fdt_system_reset(u32 reset_type, u32 reset_reason); + +int fdt_reset_init(void); + +#endif diff --git a/roms/opensbi/include/sbi_utils/serial/fdt_serial.h b/roms/opensbi/include/sbi_utils/serial/fdt_serial.h new file mode 100644 index 000000000..08f97996f --- /dev/null +++ b/roms/opensbi/include/sbi_utils/serial/fdt_serial.h @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __FDT_SERIAL_H__ +#define __FDT_SERIAL_H__ + +#include <sbi/sbi_types.h> + +struct fdt_serial { + const struct fdt_match *match_table; + int (*init)(void *fdt, int nodeoff, const struct fdt_match *match); + void (*putc)(char ch); + int (*getc)(void); +}; + +void fdt_serial_putc(char ch); + +int fdt_serial_getc(void); + +int fdt_serial_init(void); + +#endif diff --git a/roms/opensbi/include/sbi_utils/serial/shakti-uart.h b/roms/opensbi/include/sbi_utils/serial/shakti-uart.h new file mode 100644 index 000000000..08043be11 --- /dev/null +++ b/roms/opensbi/include/sbi_utils/serial/shakti-uart.h @@ -0,0 +1,18 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Vijai Kumar K <vijai@behindbytes.com> + */ + +#ifndef __SERIAL_SHAKTI_UART_H__ +#define __SERIAL_SHAKTI_UART_H__ + +#include <sbi/sbi_types.h> + +void shakti_uart_putc(char ch); + +int shakti_uart_getc(void); + +int shakti_uart_init(unsigned long base, u32 in_freq, u32 baudrate); + +#endif diff --git a/roms/opensbi/include/sbi_utils/serial/sifive-uart.h b/roms/opensbi/include/sbi_utils/serial/sifive-uart.h new file mode 100644 index 000000000..f3233921f --- /dev/null +++ b/roms/opensbi/include/sbi_utils/serial/sifive-uart.h @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SERIAL_SIFIVE_UART_H__ +#define __SERIAL_SIFIVE_UART_H__ + +#include <sbi/sbi_types.h> + +void sifive_uart_putc(char ch); + +int sifive_uart_getc(void); + +int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate); + +#endif diff --git a/roms/opensbi/include/sbi_utils/serial/uart8250.h b/roms/opensbi/include/sbi_utils/serial/uart8250.h new file mode 100644 index 000000000..0a1b5d368 --- /dev/null +++ b/roms/opensbi/include/sbi_utils/serial/uart8250.h @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SERIAL_UART8250_H__ +#define __SERIAL_UART8250_H__ + +#include <sbi/sbi_types.h> + +void uart8250_putc(char ch); + +int uart8250_getc(void); + +int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift, + u32 reg_width); + +#endif diff --git a/roms/opensbi/include/sbi_utils/sys/clint.h b/roms/opensbi/include/sbi_utils/sys/clint.h new file mode 100644 index 000000000..b07cf62ef --- /dev/null +++ b/roms/opensbi/include/sbi_utils/sys/clint.h @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SYS_CLINT_H__ +#define __SYS_CLINT_H__ + +#include <sbi/sbi_types.h> + +struct clint_data { + /* Public details */ + unsigned long addr; + u32 first_hartid; + u32 hart_count; + bool has_64bit_mmio; + /* Private details (initialized and used by CLINT library)*/ + u32 *ipi; + struct clint_data *time_delta_reference; + unsigned long time_delta_computed; + u64 time_delta; + u64 *time_val; + u64 *time_cmp; + u64 (*time_rd)(volatile u64 *addr); + void (*time_wr)(u64 value, volatile u64 *addr); +}; + +void clint_ipi_send(u32 target_hart); + +void clint_ipi_clear(u32 target_hart); + +int clint_warm_ipi_init(void); + +int clint_cold_ipi_init(struct clint_data *clint); + +u64 clint_timer_value(void); + +void clint_timer_event_stop(void); + +void clint_timer_event_start(u64 next_event); + +int clint_warm_timer_init(void); + +int clint_cold_timer_init(struct clint_data *clint, + struct clint_data *reference); + +#endif diff --git a/roms/opensbi/include/sbi_utils/sys/htif.h b/roms/opensbi/include/sbi_utils/sys/htif.h new file mode 100644 index 000000000..a43172348 --- /dev/null +++ b/roms/opensbi/include/sbi_utils/sys/htif.h @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2010-2020, The Regents of the University of California + * (Regents). All Rights Reserved. + */ + +#ifndef __SYS_HTIF_H__ +#define __SYS_HTIF_H__ + +#include <sbi/sbi_types.h> + +void htif_putc(char ch); + +int htif_getc(void); + +int htif_system_reset_check(u32 type, u32 reason); + +void htif_system_reset(u32 type, u32 reason); + +#endif diff --git a/roms/opensbi/include/sbi_utils/sys/sifive_test.h b/roms/opensbi/include/sbi_utils/sys/sifive_test.h new file mode 100644 index 000000000..958622e6a --- /dev/null +++ b/roms/opensbi/include/sbi_utils/sys/sifive_test.h @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __SYS_SIFIVE_TEST_H__ +#define __SYS_SIFIVE_TEST_H__ + +#include <sbi/sbi_types.h> + +int sifive_test_system_reset_check(u32 type, u32 reason); + +void sifive_test_system_reset(u32 type, u32 reason); + +int sifive_test_init(unsigned long base); + +#endif diff --git a/roms/opensbi/include/sbi_utils/timer/fdt_timer.h b/roms/opensbi/include/sbi_utils/timer/fdt_timer.h new file mode 100644 index 000000000..770a0f3c5 --- /dev/null +++ b/roms/opensbi/include/sbi_utils/timer/fdt_timer.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __FDT_TIMER_H__ +#define __FDT_TIMER_H__ + +#include <sbi/sbi_types.h> + +struct fdt_timer { + const struct fdt_match *match_table; + int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match); + int (*warm_init)(void); + void (*exit)(void); + u64 (*value)(void); + void (*event_stop)(void); + void (*event_start)(u64 next_event); +}; + +u64 fdt_timer_value(void); + +void fdt_timer_event_stop(void); + +void fdt_timer_event_start(u64 next_event); + +void fdt_timer_exit(void); + +int fdt_timer_init(bool cold_boot); + +#endif diff --git a/roms/opensbi/lib/sbi/objects.mk b/roms/opensbi/lib/sbi/objects.mk new file mode 100644 index 000000000..6f2c06f5b --- /dev/null +++ b/roms/opensbi/lib/sbi/objects.mk @@ -0,0 +1,43 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +libsbi-objs-y += riscv_asm.o +libsbi-objs-y += riscv_atomic.o +libsbi-objs-y += riscv_hardfp.o +libsbi-objs-y += riscv_locks.o + +libsbi-objs-y += sbi_bitmap.o +libsbi-objs-y += sbi_bitops.o +libsbi-objs-y += sbi_console.o +libsbi-objs-y += sbi_domain.o +libsbi-objs-y += sbi_ecall.o +libsbi-objs-y += sbi_ecall_base.o +libsbi-objs-y += sbi_ecall_hsm.o +libsbi-objs-y += sbi_ecall_legacy.o +libsbi-objs-y += sbi_ecall_replace.o +libsbi-objs-y += sbi_ecall_vendor.o +libsbi-objs-y += sbi_emulate_csr.o +libsbi-objs-y += sbi_fifo.o +libsbi-objs-y += sbi_hart.o +libsbi-objs-y += sbi_math.o +libsbi-objs-y += sbi_hfence.o +libsbi-objs-y += sbi_hsm.o +libsbi-objs-y += sbi_illegal_insn.o +libsbi-objs-y += sbi_init.o +libsbi-objs-y += sbi_ipi.o +libsbi-objs-y += sbi_misaligned_ldst.o +libsbi-objs-y += sbi_platform.o +libsbi-objs-y += sbi_scratch.o +libsbi-objs-y += sbi_string.o +libsbi-objs-y += sbi_system.o +libsbi-objs-y += sbi_timer.o +libsbi-objs-y += sbi_tlb.o +libsbi-objs-y += sbi_trap.o +libsbi-objs-y += sbi_unpriv.o +libsbi-objs-y += sbi_expected_trap.o diff --git a/roms/opensbi/lib/sbi/riscv_asm.c b/roms/opensbi/lib/sbi/riscv_asm.c new file mode 100644 index 000000000..8c54c1114 --- /dev/null +++ b/roms/opensbi/lib/sbi/riscv_asm.c @@ -0,0 +1,295 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_platform.h> + +/* determine CPU extension, return non-zero support */ +int misa_extension_imp(char ext) +{ + unsigned long misa = csr_read(CSR_MISA); + + if (misa) { + if ('A' <= ext && ext <= 'Z') + return misa & (1 << (ext - 'A')); + if ('a' <= ext && ext <= 'z') + return misa & (1 << (ext - 'a')); + return 0; + } + + return sbi_platform_misa_extension(sbi_platform_thishart_ptr(), ext); +} + +int misa_xlen(void) +{ + long r; + + if (csr_read(CSR_MISA) == 0) + return sbi_platform_misa_xlen(sbi_platform_thishart_ptr()); + + __asm__ __volatile__( + "csrr t0, misa\n\t" + "slti t1, t0, 0\n\t" + "slli t1, t1, 1\n\t" + "slli t0, t0, 1\n\t" + "slti t0, t0, 0\n\t" + "add %0, t0, t1" + : "=r"(r) + : + : "t0", "t1"); + + return r ? r : -1; +} + +void misa_string(int xlen, char *out, unsigned int out_sz) +{ + unsigned int i, pos = 0; + const char valid_isa_order[] = "iemafdqclbjtpvnsuhkorwxyzg"; + + if (!out) + return; + + if (5 <= (out_sz - pos)) { + out[pos++] = 'r'; + out[pos++] = 'v'; + switch (xlen) { + case 1: + out[pos++] = '3'; + out[pos++] = '2'; + break; + case 2: + out[pos++] = '6'; + out[pos++] = '4'; + break; + case 3: + out[pos++] = '1'; + out[pos++] = '2'; + out[pos++] = '8'; + break; + default: + return; + } + } + + for (i = 0; i < array_size(valid_isa_order) && (pos < out_sz); i++) { + if (misa_extension_imp(valid_isa_order[i])) + out[pos++] = valid_isa_order[i]; + } + + if (pos < out_sz) + out[pos++] = '\0'; +} + +unsigned long csr_read_num(int csr_num) +{ +#define switchcase_csr_read(__csr_num, __val) \ + case __csr_num: \ + __val = csr_read(__csr_num); \ + break; +#define switchcase_csr_read_2(__csr_num, __val) \ + switchcase_csr_read(__csr_num + 0, __val) \ + switchcase_csr_read(__csr_num + 1, __val) +#define switchcase_csr_read_4(__csr_num, __val) \ + switchcase_csr_read_2(__csr_num + 0, __val) \ + switchcase_csr_read_2(__csr_num + 2, __val) +#define switchcase_csr_read_8(__csr_num, __val) \ + switchcase_csr_read_4(__csr_num + 0, __val) \ + switchcase_csr_read_4(__csr_num + 4, __val) +#define switchcase_csr_read_16(__csr_num, __val) \ + switchcase_csr_read_8(__csr_num + 0, __val) \ + switchcase_csr_read_8(__csr_num + 8, __val) +#define switchcase_csr_read_32(__csr_num, __val) \ + switchcase_csr_read_16(__csr_num + 0, __val) \ + switchcase_csr_read_16(__csr_num + 16, __val) +#define switchcase_csr_read_64(__csr_num, __val) \ + switchcase_csr_read_32(__csr_num + 0, __val) \ + switchcase_csr_read_32(__csr_num + 32, __val) + + unsigned long ret = 0; + + switch (csr_num) { + switchcase_csr_read_16(CSR_PMPCFG0, ret) + switchcase_csr_read_64(CSR_PMPADDR0, ret) + default: + break; + }; + + return ret; + +#undef switchcase_csr_read_64 +#undef switchcase_csr_read_32 +#undef switchcase_csr_read_16 +#undef switchcase_csr_read_8 +#undef switchcase_csr_read_4 +#undef switchcase_csr_read_2 +#undef switchcase_csr_read +} + +void csr_write_num(int csr_num, unsigned long val) +{ +#define switchcase_csr_write(__csr_num, __val) \ + case __csr_num: \ + csr_write(__csr_num, __val); \ + break; +#define switchcase_csr_write_2(__csr_num, __val) \ + switchcase_csr_write(__csr_num + 0, __val) \ + switchcase_csr_write(__csr_num + 1, __val) +#define switchcase_csr_write_4(__csr_num, __val) \ + switchcase_csr_write_2(__csr_num + 0, __val) \ + switchcase_csr_write_2(__csr_num + 2, __val) +#define switchcase_csr_write_8(__csr_num, __val) \ + switchcase_csr_write_4(__csr_num + 0, __val) \ + switchcase_csr_write_4(__csr_num + 4, __val) +#define switchcase_csr_write_16(__csr_num, __val) \ + switchcase_csr_write_8(__csr_num + 0, __val) \ + switchcase_csr_write_8(__csr_num + 8, __val) +#define switchcase_csr_write_32(__csr_num, __val) \ + switchcase_csr_write_16(__csr_num + 0, __val) \ + switchcase_csr_write_16(__csr_num + 16, __val) +#define switchcase_csr_write_64(__csr_num, __val) \ + switchcase_csr_write_32(__csr_num + 0, __val) \ + switchcase_csr_write_32(__csr_num + 32, __val) + + switch (csr_num) { + switchcase_csr_write_16(CSR_PMPCFG0, val) + switchcase_csr_write_64(CSR_PMPADDR0, val) + default: + break; + }; + +#undef switchcase_csr_write_64 +#undef switchcase_csr_write_32 +#undef switchcase_csr_write_16 +#undef switchcase_csr_write_8 +#undef switchcase_csr_write_4 +#undef switchcase_csr_write_2 +#undef switchcase_csr_write +} + +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 = ~(0xffUL << 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) +{ + int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr; + unsigned long cfgmask, pmpcfg, prot; + unsigned long t1, addr, len; + + /* check parameters */ + if (n >= PMP_COUNT || !prot_out || !addr_out || !log2len) + return SBI_EINVAL; + *prot_out = *addr_out = *log2len = 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 = (0xffUL << 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; + len = __riscv_xlen; + } else { + t1 = ctz(~addr); + addr = (addr & ~((1UL << t1) - 1)) << PMP_SHIFT; + len = (t1 + PMP_SHIFT + 1); + } + } else { + addr = csr_read_num(pmpaddr_csr) << PMP_SHIFT; + len = PMP_SHIFT; + } + + /* return details */ + *prot_out = prot; + *addr_out = addr; + *log2len = len; + + return 0; +} diff --git a/roms/opensbi/lib/sbi/riscv_atomic.c b/roms/opensbi/lib/sbi/riscv_atomic.c new file mode 100644 index 000000000..558bca8c1 --- /dev/null +++ b/roms/opensbi/lib/sbi/riscv_atomic.c @@ -0,0 +1,255 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_bitops.h> +#include <sbi/riscv_asm.h> +#include <sbi/riscv_atomic.h> +#include <sbi/riscv_barrier.h> + +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 __axchg(ptr, new, size) \ + ({ \ + __typeof__(ptr) __ptr = (ptr); \ + __typeof__(new) __new = (new); \ + __typeof__(*(ptr)) __ret; \ + switch (size) { \ + case 4: \ + __asm__ __volatile__ ( \ + " amoswap.w.aqrl %0, %2, %1\n" \ + : "=r" (__ret), "+A" (*__ptr) \ + : "r" (__new) \ + : "memory"); \ + break; \ + case 8: \ + __asm__ __volatile__ ( \ + " amoswap.d.aqrl %0, %2, %1\n" \ + : "=r" (__ret), "+A" (*__ptr) \ + : "r" (__new) \ + : "memory"); \ + break; \ + default: \ + break; \ + } \ + __ret; \ + }) + +#define axchg(ptr, x) \ + ({ \ + __typeof__(*(ptr)) _x_ = (x); \ + (__typeof__(*(ptr))) __axchg((ptr), _x_, sizeof(*(ptr))); \ + }) + + +#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 atomic_cmpxchg(atomic_t *atom, long oldval, long newval) +{ +#ifdef __riscv_atomic + return __sync_val_compare_and_swap(&atom->counter, oldval, newval); +#else + return cmpxchg(&atom->counter, oldval, newval); +#endif +} + +long atomic_xchg(atomic_t *atom, long newval) +{ + /* Atomically set new value and return old value. */ +#ifdef __riscv_atomic + return axchg(&atom->counter, newval); +#else + return xchg(&atom->counter, newval); +#endif +} + +unsigned int atomic_raw_xchg_uint(volatile unsigned int *ptr, + unsigned int newval) +{ + /* Atomically set new value and return old value. */ +#ifdef __riscv_atomic + return axchg(ptr, newval); +#else + return xchg(ptr, newval); +#endif +} + +unsigned long atomic_raw_xchg_ulong(volatile unsigned long *ptr, + unsigned long newval) +{ + /* Atomically set new value and return old value. */ +#ifdef __riscv_atomic + return axchg(ptr, newval); +#else + return xchg(ptr, newval); +#endif +} + +#if (__SIZEOF_POINTER__ == 8) +#define __AMO(op) "amo" #op ".d" +#elif (__SIZEOF_POINTER__ == 4) +#define __AMO(op) "amo" #op ".w" +#else +#error "Unexpected __SIZEOF_POINTER__" +#endif + +#define __atomic_op_bit_ord(op, mod, nr, addr, ord) \ + ({ \ + unsigned long __res, __mask; \ + __mask = BIT_MASK(nr); \ + __asm__ __volatile__(__AMO(op) #ord " %0, %2, %1" \ + : "=r"(__res), "+A"(addr[BIT_WORD(nr)]) \ + : "r"(mod(__mask)) \ + : "memory"); \ + __res; \ + }) + +#define __atomic_op_bit(op, mod, nr, addr) \ + __atomic_op_bit_ord(op, mod, nr, addr, .aqrl) + +/* Bitmask modifiers */ +#define __NOP(x) (x) +#define __NOT(x) (~(x)) + +inline int atomic_raw_set_bit(int nr, volatile unsigned long *addr) +{ + return __atomic_op_bit(or, __NOP, nr, addr); +} + +inline int atomic_raw_clear_bit(int nr, volatile unsigned long *addr) +{ + return __atomic_op_bit(and, __NOT, nr, addr); +} + +inline int atomic_set_bit(int nr, atomic_t *atom) +{ + return atomic_raw_set_bit(nr, (unsigned long *)&atom->counter); +} + +inline int atomic_clear_bit(int nr, atomic_t *atom) +{ + return atomic_raw_clear_bit(nr, (unsigned long *)&atom->counter); +} diff --git a/roms/opensbi/lib/sbi/riscv_hardfp.S b/roms/opensbi/lib/sbi/riscv_hardfp.S new file mode 100644 index 000000000..f363908a4 --- /dev/null +++ b/roms/opensbi/lib/sbi/riscv_hardfp.S @@ -0,0 +1,171 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifdef __riscv_flen + +#if __riscv_flen != 64 +# error single-float only is not supported +#endif + +#define get_f32(which) fmv.x.s a0, which; jr t0 +#define put_f32(which) fmv.s.x which, a0; jr t0 +#if __riscv_xlen == 64 +# define get_f64(which) fmv.x.d a0, which; jr t0 +# define put_f64(which) fmv.d.x which, a0; jr t0 +#else +# define get_f64(which) fsd which, 0(a0); jr t0 +# define put_f64(which) fld which, 0(a0); jr t0 +#endif + + .text + .option norvc + .globl get_f32_reg + get_f32_reg: + get_f32(f0) + get_f32(f1) + get_f32(f2) + get_f32(f3) + get_f32(f4) + get_f32(f5) + get_f32(f6) + get_f32(f7) + get_f32(f8) + get_f32(f9) + get_f32(f10) + get_f32(f11) + get_f32(f12) + get_f32(f13) + get_f32(f14) + get_f32(f15) + get_f32(f16) + get_f32(f17) + get_f32(f18) + get_f32(f19) + get_f32(f20) + get_f32(f21) + get_f32(f22) + get_f32(f23) + get_f32(f24) + get_f32(f25) + get_f32(f26) + get_f32(f27) + get_f32(f28) + get_f32(f29) + get_f32(f30) + get_f32(f31) + + .text + .globl put_f32_reg + put_f32_reg: + put_f32(f0) + put_f32(f1) + put_f32(f2) + put_f32(f3) + put_f32(f4) + put_f32(f5) + put_f32(f6) + put_f32(f7) + put_f32(f8) + put_f32(f9) + put_f32(f10) + put_f32(f11) + put_f32(f12) + put_f32(f13) + put_f32(f14) + put_f32(f15) + put_f32(f16) + put_f32(f17) + put_f32(f18) + put_f32(f19) + put_f32(f20) + put_f32(f21) + put_f32(f22) + put_f32(f23) + put_f32(f24) + put_f32(f25) + put_f32(f26) + put_f32(f27) + put_f32(f28) + put_f32(f29) + put_f32(f30) + put_f32(f31) + + .text + .globl get_f64_reg + get_f64_reg: + get_f64(f0) + get_f64(f1) + get_f64(f2) + get_f64(f3) + get_f64(f4) + get_f64(f5) + get_f64(f6) + get_f64(f7) + get_f64(f8) + get_f64(f9) + get_f64(f10) + get_f64(f11) + get_f64(f12) + get_f64(f13) + get_f64(f14) + get_f64(f15) + get_f64(f16) + get_f64(f17) + get_f64(f18) + get_f64(f19) + get_f64(f20) + get_f64(f21) + get_f64(f22) + get_f64(f23) + get_f64(f24) + get_f64(f25) + get_f64(f26) + get_f64(f27) + get_f64(f28) + get_f64(f29) + get_f64(f30) + get_f64(f31) + + .text + .globl put_f64_reg + put_f64_reg: + put_f64(f0) + put_f64(f1) + put_f64(f2) + put_f64(f3) + put_f64(f4) + put_f64(f5) + put_f64(f6) + put_f64(f7) + put_f64(f8) + put_f64(f9) + put_f64(f10) + put_f64(f11) + put_f64(f12) + put_f64(f13) + put_f64(f14) + put_f64(f15) + put_f64(f16) + put_f64(f17) + put_f64(f18) + put_f64(f19) + put_f64(f20) + put_f64(f21) + put_f64(f22) + put_f64(f23) + put_f64(f24) + put_f64(f25) + put_f64(f26) + put_f64(f27) + put_f64(f28) + put_f64(f29) + put_f64(f30) + put_f64(f31) + +#endif diff --git a/roms/opensbi/lib/sbi/riscv_locks.c b/roms/opensbi/lib/sbi/riscv_locks.c new file mode 100644 index 000000000..4d1d9c0c1 --- /dev/null +++ b/roms/opensbi/lib/sbi/riscv_locks.c @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_barrier.h> +#include <sbi/riscv_locks.h> + +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/roms/opensbi/lib/sbi/sbi_bitmap.c b/roms/opensbi/lib/sbi/sbi_bitmap.c new file mode 100644 index 000000000..e74b6bbec --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_bitmap.c @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_bitmap.h> + +void __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits) +{ + int k; + int nr = BITS_TO_LONGS(bits); + + for (k = 0; k < nr; k++) + dst[k] = bitmap1[k] & bitmap2[k]; +} + +void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits) +{ + int k; + int nr = BITS_TO_LONGS(bits); + + for (k = 0; k < nr; k++) + dst[k] = bitmap1[k] | bitmap2[k]; +} + +void __bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits) +{ + int k; + int nr = BITS_TO_LONGS(bits); + + for (k = 0; k < nr; k++) + dst[k] = bitmap1[k] ^ bitmap2[k]; +} diff --git a/roms/opensbi/lib/sbi/sbi_bitops.c b/roms/opensbi/lib/sbi/sbi_bitops.c new file mode 100644 index 000000000..de9d0457b --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_bitops.c @@ -0,0 +1,200 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_bitops.h> + +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +/** + * find_first_bit - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first set bit. + */ +unsigned long find_first_bit(const unsigned long *addr, + unsigned long size) +{ + const unsigned long *p = addr; + unsigned long result = 0; + unsigned long tmp; + + while (size & ~(BITS_PER_LONG-1)) { + if ((tmp = *(p++))) + goto found; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + + tmp = (*p) & (~0UL >> (BITS_PER_LONG - size)); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found: + return result + __ffs(tmp); +} + +/** + * find_first_zero_bit - find the first cleared bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first cleared bit. + */ +unsigned long find_first_zero_bit(const unsigned long *addr, + unsigned long size) +{ + const unsigned long *p = addr; + unsigned long result = 0; + unsigned long tmp; + + while (size & ~(BITS_PER_LONG-1)) { + if (~(tmp = *(p++))) + goto found; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + + tmp = (*p) | (~0UL << size); + if (tmp == ~0UL) /* Are any bits zero? */ + return result + size; /* Nope. */ +found: + return result + ffz(tmp); +} + +/** + * find_last_bit - find the last set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first set bit, or size. + */ +unsigned long find_last_bit(const unsigned long *addr, + unsigned long size) +{ + unsigned long words; + unsigned long tmp; + + /* Start at final word. */ + words = size / BITS_PER_LONG; + + /* Partial final word? */ + if (size & (BITS_PER_LONG-1)) { + tmp = (addr[words] & (~0UL >> (BITS_PER_LONG + - (size & (BITS_PER_LONG-1))))); + if (tmp) + goto found; + } + + while (words) { + tmp = addr[--words]; + if (tmp) { +found: + return words * BITS_PER_LONG + __fls(tmp); + } + } + + /* Not found */ + return size; +} + +/** + * find_next_bit - find the next set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The bitmap size in bits + */ +unsigned long find_next_bit(const unsigned long *addr, + unsigned long size, unsigned long offset) +{ + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG-1); + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset %= BITS_PER_LONG; + if (offset) { + tmp = *(p++); + tmp &= (~0UL << offset); + if (size < BITS_PER_LONG) + goto found_first; + if (tmp) + goto found_middle; + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + while (size & ~(BITS_PER_LONG-1)) { + if ((tmp = *(p++))) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp &= (~0UL >> (BITS_PER_LONG - size)); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __ffs(tmp); +} + +/** + * find_next_zero_bit - find the next cleared bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The bitmap size in bits + */ +unsigned long find_next_zero_bit(const unsigned long *addr, + unsigned long size, + unsigned long offset) +{ + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG-1); + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset %= BITS_PER_LONG; + if (offset) { + tmp = *(p++); + tmp |= ~0UL >> (BITS_PER_LONG - offset); + if (size < BITS_PER_LONG) + goto found_first; + if (~tmp) + goto found_middle; + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + while (size & ~(BITS_PER_LONG-1)) { + if (~(tmp = *(p++))) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) /* Are any bits zero? */ + return result + size; /* Nope. */ +found_middle: + return result + ffz(tmp); +} diff --git a/roms/opensbi/lib/sbi/sbi_console.c b/roms/opensbi/lib/sbi/sbi_console.c new file mode 100644 index 000000000..7189b9bf2 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_console.c @@ -0,0 +1,398 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_locks.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_scratch.h> + +static const struct sbi_platform *console_plat = NULL; +static spinlock_t console_out_lock = SPIN_LOCK_INITIALIZER; + +bool sbi_isprintable(char c) +{ + if (((31 < c) && (c < 127)) || (c == '\f') || (c == '\r') || + (c == '\n') || (c == '\t')) { + return TRUE; + } + return FALSE; +} + +int sbi_getc(void) +{ + return sbi_platform_console_getc(console_plat); +} + +void sbi_putc(char ch) +{ + if (ch == '\n') + sbi_platform_console_putc(console_plat, '\r'); + sbi_platform_console_putc(console_plat, ch); +} + +void sbi_puts(const char *str) +{ + spin_lock(&console_out_lock); + while (*str) { + sbi_putc(*str); + str++; + } + spin_unlock(&console_out_lock); +} + +void sbi_gets(char *s, int maxwidth, char endchar) +{ + int ch; + char *retval = s; + + while ((ch = sbi_getc()) != endchar && ch >= 0 && maxwidth > 1) { + *retval = (char)ch; + retval++; + maxwidth--; + } + *retval = '\0'; +} + +#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) == 'u') { + format += 1; + pc += printi( + out, out_len, + va_arg(args, unsigned long), 10, + 0, width, flags, 'a'); + } else if (*(format + 1) == 'x') { + format += 1; + pc += printi( + out, out_len, + va_arg(args, unsigned long), 16, + 0, width, flags, 'a'); + 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; + + spin_lock(&console_out_lock); + va_start(args, format); + retval = print(NULL, NULL, format, args); + va_end(args); + spin_unlock(&console_out_lock); + + return retval; +} + +int sbi_dprintf(const char *format, ...) +{ + va_list args; + int retval = 0; + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + va_start(args, format); + if (scratch->options & SBI_SCRATCH_DEBUG_PRINTS) + 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/roms/opensbi/lib/sbi/sbi_domain.c b/roms/opensbi/lib/sbi/sbi_domain.c new file mode 100644 index 000000000..195c9413c --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_domain.c @@ -0,0 +1,539 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_hartmask.h> +#include <sbi/sbi_hsm.h> +#include <sbi/sbi_math.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_string.h> + +struct sbi_domain *hartid_to_domain_table[SBI_HARTMASK_MAX_BITS] = { 0 }; +struct sbi_domain *domidx_to_domain_table[SBI_DOMAIN_MAX_INDEX] = { 0 }; + +static u32 domain_count = 0; + +static struct sbi_hartmask root_hmask = { 0 }; + +#define ROOT_FW_REGION 0 +#define ROOT_ALL_REGION 1 +#define ROOT_END_REGION 2 +static struct sbi_domain_memregion root_memregs[ROOT_END_REGION + 1] = { 0 }; + +static struct sbi_domain root = { + .name = "root", + .possible_harts = &root_hmask, + .regions = root_memregs, + .system_reset_allowed = TRUE, +}; + +bool sbi_domain_is_assigned_hart(const struct sbi_domain *dom, u32 hartid) +{ + if (dom) + return sbi_hartmask_test_hart(hartid, &dom->assigned_harts); + + return FALSE; +} + +ulong sbi_domain_get_assigned_hartmask(const struct sbi_domain *dom, + ulong hbase) +{ + ulong ret, bword, boff; + + if (!dom) + return 0; + + bword = BIT_WORD(hbase); + boff = BIT_WORD_OFFSET(hbase); + + ret = sbi_hartmask_bits(&dom->assigned_harts)[bword++] >> boff; + if (boff && bword < BIT_WORD(SBI_HARTMASK_MAX_BITS)) { + ret |= (sbi_hartmask_bits(&dom->assigned_harts)[bword] & + (BIT(boff) - 1UL)) << (BITS_PER_LONG - boff); + } + + return ret; +} + +void sbi_domain_memregion_initfw(struct sbi_domain_memregion *reg) +{ + if (!reg) + return; + + sbi_memcpy(reg, &root_memregs[ROOT_FW_REGION], sizeof(*reg)); +} + +bool sbi_domain_check_addr(const struct sbi_domain *dom, + unsigned long addr, unsigned long mode, + unsigned long access_flags) +{ + bool mmio = FALSE; + struct sbi_domain_memregion *reg; + unsigned long rstart, rend, rflags, rwx = 0; + + if (!dom) + return FALSE; + + if (access_flags & SBI_DOMAIN_READ) + rwx |= SBI_DOMAIN_MEMREGION_READABLE; + if (access_flags & SBI_DOMAIN_WRITE) + rwx |= SBI_DOMAIN_MEMREGION_WRITEABLE; + if (access_flags & SBI_DOMAIN_EXECUTE) + rwx |= SBI_DOMAIN_MEMREGION_EXECUTABLE; + if (access_flags & SBI_DOMAIN_MMIO) + mmio = TRUE; + + sbi_domain_for_each_memregion(dom, reg) { + rflags = reg->flags; + if (mode == PRV_M && !(rflags & SBI_DOMAIN_MEMREGION_MMODE)) + continue; + + rstart = reg->base; + rend = (reg->order < __riscv_xlen) ? + rstart + ((1UL << reg->order) - 1) : -1UL; + if (rstart <= addr && addr <= rend) { + if ((mmio && !(rflags & SBI_DOMAIN_MEMREGION_MMIO)) || + (!mmio && (rflags & SBI_DOMAIN_MEMREGION_MMIO))) + return FALSE; + return ((rflags & rwx) == rwx) ? TRUE : FALSE; + } + } + + return (mode == PRV_M) ? TRUE : FALSE; +} + +/* Check if region complies with constraints */ +static bool is_region_valid(const struct sbi_domain_memregion *reg) +{ + if (reg->order < 3 || __riscv_xlen < reg->order) + return FALSE; + + if (reg->base & (BIT(reg->order) - 1)) + return FALSE; + + return TRUE; +} + +/** Check if regionA is sub-region of regionB */ +static bool is_region_subset(const struct sbi_domain_memregion *regA, + const struct sbi_domain_memregion *regB) +{ + ulong regA_start = regA->base; + ulong regA_end = regA->base + (BIT(regA->order) - 1); + ulong regB_start = regB->base; + ulong regB_end = regB->base + (BIT(regA->order) - 1); + + if ((regB_start <= regA_start) && + (regA_start < regB_end) && + (regB_start < regA_end) && + (regA_end <= regB_end)) + return TRUE; + + return FALSE; +} + +/** Check if regionA conflicts regionB */ +static bool is_region_conflict(const struct sbi_domain_memregion *regA, + const struct sbi_domain_memregion *regB) +{ + if ((is_region_subset(regA, regB) || is_region_subset(regB, regA)) && + regA->flags == regB->flags) + return TRUE; + + return FALSE; +} + +/** Check if regionA should be placed before regionB */ +static bool is_region_before(const struct sbi_domain_memregion *regA, + const struct sbi_domain_memregion *regB) +{ + if (regA->order < regB->order) + return TRUE; + + if ((regA->order == regB->order) && + (regA->base < regB->base)) + return TRUE; + + return FALSE; +} + +static int sanitize_domain(const struct sbi_platform *plat, + struct sbi_domain *dom) +{ + u32 i, j, count; + bool have_fw_reg; + struct sbi_domain_memregion treg, *reg, *reg1; + + /* Check possible HARTs */ + if (!dom->possible_harts) { + sbi_printf("%s: %s possible HART mask is NULL\n", + __func__, dom->name); + return SBI_EINVAL; + } + sbi_hartmask_for_each_hart(i, dom->possible_harts) { + if (sbi_platform_hart_invalid(plat, i)) { + sbi_printf("%s: %s possible HART mask has invalid " + "hart %d\n", __func__, dom->name, i); + return SBI_EINVAL; + } + }; + + /* Check memory regions */ + if (!dom->regions) { + sbi_printf("%s: %s regions is NULL\n", + __func__, dom->name); + return SBI_EINVAL; + } + sbi_domain_for_each_memregion(dom, reg) { + if (!is_region_valid(reg)) { + sbi_printf("%s: %s has invalid region base=0x%lx " + "order=%lu flags=0x%lx\n", __func__, + dom->name, reg->base, reg->order, + reg->flags); + return SBI_EINVAL; + } + } + + /* Count memory regions and check presence of firmware region */ + count = 0; + have_fw_reg = FALSE; + sbi_domain_for_each_memregion(dom, reg) { + if (reg->order == root_memregs[ROOT_FW_REGION].order && + reg->base == root_memregs[ROOT_FW_REGION].base && + reg->flags == root_memregs[ROOT_FW_REGION].flags) + have_fw_reg = TRUE; + count++; + } + if (!have_fw_reg) { + sbi_printf("%s: %s does not have firmware region\n", + __func__, dom->name); + return SBI_EINVAL; + } + + /* Sort the memory regions */ + for (i = 0; i < (count - 1); i++) { + reg = &dom->regions[i]; + for (j = i + 1; j < count; j++) { + reg1 = &dom->regions[j]; + + if (is_region_conflict(reg1, reg)) { + sbi_printf("%s: %s conflict between regions " + "(base=0x%lx order=%lu flags=0x%lx) and " + "(base=0x%lx order=%lu flags=0x%lx)\n", + __func__, dom->name, + reg->base, reg->order, reg->flags, + reg1->base, reg1->order, reg1->flags); + return SBI_EINVAL; + } + + if (!is_region_before(reg1, reg)) + continue; + + sbi_memcpy(&treg, reg1, sizeof(treg)); + sbi_memcpy(reg1, reg, sizeof(treg)); + sbi_memcpy(reg, &treg, sizeof(treg)); + } + } + + /* + * We don't need to check boot HART id of domain because if boot + * HART id is not possible/assigned to this domain then it won't + * be started at boot-time by sbi_domain_finalize(). + */ + + /* + * Check next mode + * + * We only allow next mode to be S-mode or U-mode.so that we can + * protect M-mode context and enforce checks on memory accesses. + */ + if (dom->next_mode != PRV_S && + dom->next_mode != PRV_U) { + sbi_printf("%s: %s invalid next booting stage mode 0x%lx\n", + __func__, dom->name, dom->next_mode); + return SBI_EINVAL; + } + + /* Check next address and next mode*/ + if (!sbi_domain_check_addr(dom, dom->next_addr, dom->next_mode, + SBI_DOMAIN_EXECUTE)) { + sbi_printf("%s: %s next booting stage addres 0x%lx can't " + "execute\n", __func__, dom->name, dom->next_addr); + return SBI_EINVAL; + } + + return 0; +} + +void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix) +{ + u32 i, k; + unsigned long rstart, rend; + struct sbi_domain_memregion *reg; + + sbi_printf("Domain%d Name %s: %s\n", + dom->index, suffix, dom->name); + + sbi_printf("Domain%d Boot HART %s: %d\n", + dom->index, suffix, dom->boot_hartid); + + k = 0; + sbi_printf("Domain%d HARTs %s: ", dom->index, suffix); + sbi_hartmask_for_each_hart(i, dom->possible_harts) + sbi_printf("%s%d%s", (k++) ? "," : "", + i, sbi_domain_is_assigned_hart(dom, i) ? "*" : ""); + sbi_printf("\n"); + + i = 0; + sbi_domain_for_each_memregion(dom, reg) { + rstart = reg->base; + rend = (reg->order < __riscv_xlen) ? + rstart + ((1UL << reg->order) - 1) : -1UL; + +#if __riscv_xlen == 32 + sbi_printf("Domain%d Region%02d %s: 0x%08lx-0x%08lx ", +#else + sbi_printf("Domain%d Region%02d %s: 0x%016lx-0x%016lx ", +#endif + dom->index, i, suffix, rstart, rend); + + k = 0; + if (reg->flags & SBI_DOMAIN_MEMREGION_MMODE) + sbi_printf("%cM", (k++) ? ',' : '('); + if (reg->flags & SBI_DOMAIN_MEMREGION_MMIO) + sbi_printf("%cI", (k++) ? ',' : '('); + if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE) + sbi_printf("%cR", (k++) ? ',' : '('); + if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE) + sbi_printf("%cW", (k++) ? ',' : '('); + if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE) + sbi_printf("%cX", (k++) ? ',' : '('); + sbi_printf("%s\n", (k++) ? ")" : "()"); + + i++; + } + +#if __riscv_xlen == 32 + sbi_printf("Domain%d Next Address%s: 0x%08lx\n", +#else + sbi_printf("Domain%d Next Address%s: 0x%016lx\n", +#endif + dom->index, suffix, dom->next_addr); + +#if __riscv_xlen == 32 + sbi_printf("Domain%d Next Arg1 %s: 0x%08lx\n", +#else + sbi_printf("Domain%d Next Arg1 %s: 0x%016lx\n", +#endif + dom->index, suffix, dom->next_arg1); + + sbi_printf("Domain%d Next Mode %s: ", dom->index, suffix); + switch (dom->next_mode) { + case PRV_M: + sbi_printf("M-mode\n"); + break; + case PRV_S: + sbi_printf("S-mode\n"); + break; + case PRV_U: + sbi_printf("U-mode\n"); + break; + default: + sbi_printf("Unknown\n"); + break; + }; + + sbi_printf("Domain%d SysReset %s: %s\n", + dom->index, suffix, (dom->system_reset_allowed) ? "yes" : "no"); +} + +void sbi_domain_dump_all(const char *suffix) +{ + u32 i; + const struct sbi_domain *dom; + + sbi_domain_for_each(i, dom) { + sbi_domain_dump(dom, suffix); + sbi_printf("\n"); + } +} + +int sbi_domain_register(struct sbi_domain *dom, + const struct sbi_hartmask *assign_mask) +{ + u32 i; + int rc; + struct sbi_domain *tdom; + u32 cold_hartid = current_hartid(); + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); + + if (!dom || !assign_mask) + return SBI_EINVAL; + + /* Check if domain already discovered */ + sbi_domain_for_each(i, tdom) { + if (tdom == dom) + return SBI_EALREADY; + } + + /* + * Ensure that we have room for Domain Index to + * HART ID mapping + */ + if (SBI_DOMAIN_MAX_INDEX <= domain_count) { + sbi_printf("%s: No room for %s\n", + __func__, dom->name); + return SBI_ENOSPC; + } + + /* Sanitize discovered domain */ + rc = sanitize_domain(plat, dom); + if (rc) { + sbi_printf("%s: sanity checks failed for" + " %s (error %d)\n", __func__, + dom->name, rc); + return rc; + } + + /* Assign index to domain */ + dom->index = domain_count++; + domidx_to_domain_table[dom->index] = dom; + + /* Clear assigned HARTs of domain */ + sbi_hartmask_clear_all(&dom->assigned_harts); + + /* Assign domain to HART if HART is a possible HART */ + sbi_hartmask_for_each_hart(i, assign_mask) { + if (!sbi_hartmask_test_hart(i, dom->possible_harts)) + continue; + + tdom = hartid_to_domain_table[i]; + if (tdom) + sbi_hartmask_clear_hart(i, + &tdom->assigned_harts); + hartid_to_domain_table[i] = dom; + sbi_hartmask_set_hart(i, &dom->assigned_harts); + + /* + * If cold boot HART is assigned to this domain then + * override boot HART of this domain. + */ + if (i == cold_hartid && + dom->boot_hartid != cold_hartid) { + sbi_printf("Domain%d Boot HARTID forced to" + " %d\n", dom->index, cold_hartid); + dom->boot_hartid = cold_hartid; + } + } + + return 0; +} + +int sbi_domain_finalize(struct sbi_scratch *scratch, u32 cold_hartid) +{ + int rc; + u32 i, dhart; + struct sbi_domain *dom; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + /* Initialize and populate domains for the platform */ + rc = sbi_platform_domains_init(plat); + if (rc) { + sbi_printf("%s: platform domains_init() failed (error %d)\n", + __func__, rc); + return rc; + } + + /* Startup boot HART of domains */ + sbi_domain_for_each(i, dom) { + /* Domain boot HART */ + dhart = dom->boot_hartid; + + /* Ignore of boot HART is off limits */ + if (SBI_HARTMASK_MAX_BITS <= dhart) + continue; + + /* Ignore if boot HART not possible for this domain */ + if (!sbi_hartmask_test_hart(dhart, dom->possible_harts)) + continue; + + /* Ignore if boot HART assigned different domain */ + if (sbi_hartid_to_domain(dhart) != dom || + !sbi_hartmask_test_hart(dhart, &dom->assigned_harts)) + continue; + + /* Startup boot HART of domain */ + if (dhart == cold_hartid) { + scratch->next_addr = dom->next_addr; + scratch->next_mode = dom->next_mode; + scratch->next_arg1 = dom->next_arg1; + } else { + rc = sbi_hsm_hart_start(scratch, NULL, dhart, + dom->next_addr, + dom->next_mode, + dom->next_arg1); + if (rc) { + sbi_printf("%s: failed to start boot HART %d" + " for %s (error %d)\n", __func__, + dhart, dom->name, rc); + return rc; + } + } + } + + return 0; +} + +int sbi_domain_init(struct sbi_scratch *scratch, u32 cold_hartid) +{ + u32 i; + struct sbi_domain_memregion *memregs; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + /* Root domain firmware memory region */ + root_memregs[ROOT_FW_REGION].order = log2roundup(scratch->fw_size); + root_memregs[ROOT_FW_REGION].base = scratch->fw_start & + ~((1UL << root_memregs[0].order) - 1UL); + root_memregs[ROOT_FW_REGION].flags = 0; + + /* Root domain allow everything memory region */ + root_memregs[ROOT_ALL_REGION].order = __riscv_xlen; + root_memregs[ROOT_ALL_REGION].base = 0; + root_memregs[ROOT_ALL_REGION].flags = (SBI_DOMAIN_MEMREGION_READABLE | + SBI_DOMAIN_MEMREGION_WRITEABLE | + SBI_DOMAIN_MEMREGION_EXECUTABLE); + + /* Root domain memory region end */ + root_memregs[ROOT_END_REGION].order = 0; + + /* Use platform specific root memory regions when available */ + memregs = sbi_platform_domains_root_regions(plat); + if (memregs) + root.regions = memregs; + + /* Root domain boot HART id is same as coldboot HART id */ + root.boot_hartid = cold_hartid; + + /* Root domain next booting stage details */ + root.next_arg1 = scratch->next_arg1; + root.next_addr = scratch->next_addr; + root.next_mode = scratch->next_mode; + + /* Root domain possible and assigned HARTs */ + for (i = 0; i < SBI_HARTMASK_MAX_BITS; i++) { + if (sbi_platform_hart_invalid(plat, i)) + continue; + sbi_hartmask_set_hart(i, &root_hmask); + } + + return sbi_domain_register(&root, &root_hmask); +} diff --git a/roms/opensbi/lib/sbi/sbi_ecall.c b/roms/opensbi/lib/sbi/sbi_ecall.c new file mode 100644 index 000000000..e92a53930 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_ecall.c @@ -0,0 +1,175 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_console.h> +#include <sbi/sbi_ecall.h> +#include <sbi/sbi_ecall_interface.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_trap.h> + +u16 sbi_ecall_version_major(void) +{ + return SBI_ECALL_VERSION_MAJOR; +} + +u16 sbi_ecall_version_minor(void) +{ + return SBI_ECALL_VERSION_MINOR; +} + +static unsigned long ecall_impid = SBI_OPENSBI_IMPID; + +unsigned long sbi_ecall_get_impid(void) +{ + return ecall_impid; +} + +void sbi_ecall_set_impid(unsigned long impid) +{ + ecall_impid = impid; +} + +static SBI_LIST_HEAD(ecall_exts_list); + +struct sbi_ecall_extension *sbi_ecall_find_extension(unsigned long extid) +{ + struct sbi_ecall_extension *t, *ret = NULL; + + sbi_list_for_each_entry(t, &ecall_exts_list, head) { + if (t->extid_start <= extid && extid <= t->extid_end) { + ret = t; + break; + } + } + + return ret; +} + +int sbi_ecall_register_extension(struct sbi_ecall_extension *ext) +{ + struct sbi_ecall_extension *t; + + if (!ext || (ext->extid_end < ext->extid_start) || !ext->handle) + return SBI_EINVAL; + + sbi_list_for_each_entry(t, &ecall_exts_list, head) { + unsigned long start = t->extid_start; + unsigned long end = t->extid_end; + if (end < ext->extid_start || ext->extid_end < start) + /* no overlap */; + else + return SBI_EINVAL; + } + + SBI_INIT_LIST_HEAD(&ext->head); + sbi_list_add_tail(&ext->head, &ecall_exts_list); + + return 0; +} + +void sbi_ecall_unregister_extension(struct sbi_ecall_extension *ext) +{ + bool found = FALSE; + struct sbi_ecall_extension *t; + + if (!ext) + return; + + sbi_list_for_each_entry(t, &ecall_exts_list, head) { + if (t == ext) { + found = TRUE; + break; + } + } + + if (found) + sbi_list_del_init(&ext->head); +} + +int sbi_ecall_handler(struct sbi_trap_regs *regs) +{ + int ret = 0; + struct sbi_ecall_extension *ext; + unsigned long extension_id = regs->a7; + unsigned long func_id = regs->a6; + struct sbi_trap_info trap = {0}; + unsigned long out_val = 0; + bool is_0_1_spec = 0; + + ext = sbi_ecall_find_extension(extension_id); + if (ext && ext->handle) { + ret = ext->handle(extension_id, func_id, + regs, &out_val, &trap); + if (extension_id >= SBI_EXT_0_1_SET_TIMER && + extension_id <= SBI_EXT_0_1_SHUTDOWN) + is_0_1_spec = 1; + } else { + ret = SBI_ENOTSUPP; + } + + if (ret == SBI_ETRAP) { + trap.epc = regs->mepc; + sbi_trap_redirect(regs, &trap); + } else { + if (ret < SBI_LAST_ERR) { + sbi_printf("%s: Invalid error %d for ext=0x%lx " + "func=0x%lx\n", __func__, ret, + extension_id, func_id); + ret = SBI_ERR_FAILED; + } + + /* + * This function should return non-zero value only in case of + * fatal error. However, there is no good way to distinguish + * between a fatal and non-fatal errors yet. That's why we treat + * every return value except ETRAP as non-fatal and just return + * accordingly for now. Once fatal errors are defined, that + * case should be handled differently. + */ + regs->mepc += 4; + regs->a0 = ret; + if (!is_0_1_spec) + regs->a1 = out_val; + } + + return 0; +} + +int sbi_ecall_init(void) +{ + int ret; + + /* The order of below registrations is performance optimized */ + ret = sbi_ecall_register_extension(&ecall_time); + if (ret) + return ret; + ret = sbi_ecall_register_extension(&ecall_rfence); + if (ret) + return ret; + ret = sbi_ecall_register_extension(&ecall_ipi); + if (ret) + return ret; + ret = sbi_ecall_register_extension(&ecall_base); + if (ret) + return ret; + ret = sbi_ecall_register_extension(&ecall_hsm); + if (ret) + return ret; + ret = sbi_ecall_register_extension(&ecall_srst); + if (ret) + return ret; + ret = sbi_ecall_register_extension(&ecall_legacy); + if (ret) + return ret; + ret = sbi_ecall_register_extension(&ecall_vendor); + if (ret) + return ret; + + return 0; +} diff --git a/roms/opensbi/lib/sbi/sbi_ecall_base.c b/roms/opensbi/lib/sbi/sbi_ecall_base.c new file mode 100644 index 000000000..786d2ac67 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_ecall_base.c @@ -0,0 +1,79 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * Atish Patra <atish.patra@wdc.com> + */ + +#include <sbi/sbi_ecall.h> +#include <sbi/sbi_ecall_interface.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_trap.h> +#include <sbi/sbi_version.h> +#include <sbi/riscv_asm.h> + +static int sbi_ecall_base_probe(unsigned long extid, unsigned long *out_val) +{ + struct sbi_ecall_extension *ext; + + ext = sbi_ecall_find_extension(extid); + if (!ext) { + *out_val = 0; + return 0; + } + + if (ext->probe) + return ext->probe(extid, out_val); + + *out_val = 1; + return 0; +} + +static int sbi_ecall_base_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + int ret = 0; + + switch (funcid) { + case SBI_EXT_BASE_GET_SPEC_VERSION: + *out_val = (SBI_ECALL_VERSION_MAJOR << + SBI_SPEC_VERSION_MAJOR_OFFSET) & + (SBI_SPEC_VERSION_MAJOR_MASK << + SBI_SPEC_VERSION_MAJOR_OFFSET); + *out_val = *out_val | SBI_ECALL_VERSION_MINOR; + break; + case SBI_EXT_BASE_GET_IMP_ID: + *out_val = sbi_ecall_get_impid(); + break; + case SBI_EXT_BASE_GET_IMP_VERSION: + *out_val = OPENSBI_VERSION; + break; + case SBI_EXT_BASE_GET_MVENDORID: + *out_val = csr_read(CSR_MVENDORID); + break; + case SBI_EXT_BASE_GET_MARCHID: + *out_val = csr_read(CSR_MARCHID); + break; + case SBI_EXT_BASE_GET_MIMPID: + *out_val = csr_read(CSR_MIMPID); + break; + case SBI_EXT_BASE_PROBE_EXT: + ret = sbi_ecall_base_probe(regs->a0, out_val); + break; + default: + ret = SBI_ENOTSUPP; + } + + return ret; +} + +struct sbi_ecall_extension ecall_base = { + .extid_start = SBI_EXT_BASE, + .extid_end = SBI_EXT_BASE, + .handle = sbi_ecall_base_handler, +}; diff --git a/roms/opensbi/lib/sbi/sbi_ecall_hsm.c b/roms/opensbi/lib/sbi/sbi_ecall_hsm.c new file mode 100644 index 000000000..df29d5196 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_ecall_hsm.c @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +#include <sbi/sbi_domain.h> +#include <sbi/sbi_ecall.h> +#include <sbi/sbi_ecall_interface.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_trap.h> +#include <sbi/sbi_version.h> +#include <sbi/sbi_hsm.h> +#include <sbi/sbi_scratch.h> +#include <sbi/riscv_asm.h> + +static int sbi_ecall_hsm_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + ulong smode; + int ret = 0, hstate; + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + switch (funcid) { + case SBI_EXT_HSM_HART_START: + smode = csr_read(CSR_MSTATUS); + smode = (smode & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT; + ret = sbi_hsm_hart_start(scratch, sbi_domain_thishart_ptr(), + regs->a0, regs->a1, smode, regs->a2); + break; + case SBI_EXT_HSM_HART_STOP: + ret = sbi_hsm_hart_stop(scratch, TRUE); + break; + case SBI_EXT_HSM_HART_GET_STATUS: + hstate = sbi_hsm_hart_get_state(sbi_domain_thishart_ptr(), + regs->a0); + ret = sbi_hsm_hart_state_to_status(hstate); + break; + default: + ret = SBI_ENOTSUPP; + }; + if (ret >= 0) { + *out_val = ret; + ret = 0; + } + + return ret; +} + +struct sbi_ecall_extension ecall_hsm = { + .extid_start = SBI_EXT_HSM, + .extid_end = SBI_EXT_HSM, + .handle = sbi_ecall_hsm_handler, +}; diff --git a/roms/opensbi/lib/sbi/sbi_ecall_legacy.c b/roms/opensbi/lib/sbi/sbi_ecall_legacy.c new file mode 100644 index 000000000..1a7fe26e4 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_ecall_legacy.c @@ -0,0 +1,124 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * Atish Patra <atish.patra@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_ecall.h> +#include <sbi/sbi_ecall_interface.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hsm.h> +#include <sbi/sbi_ipi.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_system.h> +#include <sbi/sbi_timer.h> +#include <sbi/sbi_tlb.h> +#include <sbi/sbi_trap.h> +#include <sbi/sbi_unpriv.h> +#include <sbi/sbi_hart.h> + +static int sbi_load_hart_mask_unpriv(ulong *pmask, ulong *hmask, + struct sbi_trap_info *uptrap) +{ + ulong mask = 0; + + if (pmask) { + mask = sbi_load_ulong(pmask, uptrap); + if (uptrap->cause) + return SBI_ETRAP; + } else { + sbi_hsm_hart_started_mask(sbi_domain_thishart_ptr(), + 0, &mask); + } + *hmask = mask; + + return 0; +} + +static int sbi_ecall_legacy_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + int ret = 0; + struct sbi_tlb_info tlb_info; + u32 source_hart = current_hartid(); + ulong hmask = 0; + + switch (extid) { + case SBI_EXT_0_1_SET_TIMER: +#if __riscv_xlen == 32 + sbi_timer_event_start((((u64)regs->a1 << 32) | (u64)regs->a0)); +#else + sbi_timer_event_start((u64)regs->a0); +#endif + break; + case SBI_EXT_0_1_CONSOLE_PUTCHAR: + sbi_putc(regs->a0); + break; + case SBI_EXT_0_1_CONSOLE_GETCHAR: + ret = sbi_getc(); + break; + case SBI_EXT_0_1_CLEAR_IPI: + sbi_ipi_clear_smode(); + break; + case SBI_EXT_0_1_SEND_IPI: + ret = sbi_load_hart_mask_unpriv((ulong *)regs->a0, + &hmask, out_trap); + if (ret != SBI_ETRAP) + ret = sbi_ipi_send_smode(hmask, 0); + break; + case SBI_EXT_0_1_REMOTE_FENCE_I: + ret = sbi_load_hart_mask_unpriv((ulong *)regs->a0, + &hmask, out_trap); + if (ret != SBI_ETRAP) { + SBI_TLB_INFO_INIT(&tlb_info, 0, 0, 0, 0, + sbi_tlb_local_fence_i, + source_hart); + ret = sbi_tlb_request(hmask, 0, &tlb_info); + } + break; + case SBI_EXT_0_1_REMOTE_SFENCE_VMA: + ret = sbi_load_hart_mask_unpriv((ulong *)regs->a0, + &hmask, out_trap); + if (ret != SBI_ETRAP) { + SBI_TLB_INFO_INIT(&tlb_info, regs->a1, regs->a2, 0, 0, + sbi_tlb_local_sfence_vma, + source_hart); + ret = sbi_tlb_request(hmask, 0, &tlb_info); + } + break; + case SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID: + ret = sbi_load_hart_mask_unpriv((ulong *)regs->a0, + &hmask, out_trap); + if (ret != SBI_ETRAP) { + SBI_TLB_INFO_INIT(&tlb_info, regs->a1, + regs->a2, regs->a3, 0, + sbi_tlb_local_sfence_vma_asid, + source_hart); + ret = sbi_tlb_request(hmask, 0, &tlb_info); + } + break; + case SBI_EXT_0_1_SHUTDOWN: + sbi_system_reset(SBI_SRST_RESET_TYPE_SHUTDOWN, + SBI_SRST_RESET_REASON_NONE); + break; + default: + ret = SBI_ENOTSUPP; + }; + + return ret; +} + +struct sbi_ecall_extension ecall_legacy = { + .extid_start = SBI_EXT_0_1_SET_TIMER, + .extid_end = SBI_EXT_0_1_SHUTDOWN, + .handle = sbi_ecall_legacy_handler, +}; diff --git a/roms/opensbi/lib/sbi/sbi_ecall_replace.c b/roms/opensbi/lib/sbi/sbi_ecall_replace.c new file mode 100644 index 000000000..a7935d973 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_ecall_replace.c @@ -0,0 +1,196 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * Atish Patra <atish.patra@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/sbi_ecall.h> +#include <sbi/sbi_ecall_interface.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_ipi.h> +#include <sbi/sbi_system.h> +#include <sbi/sbi_timer.h> +#include <sbi/sbi_tlb.h> +#include <sbi/sbi_trap.h> + +static int sbi_ecall_time_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + int ret = 0; + + if (funcid == SBI_EXT_TIME_SET_TIMER) { +#if __riscv_xlen == 32 + sbi_timer_event_start((((u64)regs->a1 << 32) | (u64)regs->a0)); +#else + sbi_timer_event_start((u64)regs->a0); +#endif + } else + ret = SBI_ENOTSUPP; + + return ret; +} + +struct sbi_ecall_extension ecall_time = { + .extid_start = SBI_EXT_TIME, + .extid_end = SBI_EXT_TIME, + .handle = sbi_ecall_time_handler, +}; + +static int sbi_ecall_rfence_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + int ret = 0; + unsigned long vmid; + struct sbi_tlb_info tlb_info; + u32 source_hart = current_hartid(); + + if (funcid >= SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA && + funcid <= SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID) + if (!misa_extension('H')) + return SBI_ENOTSUPP; + + switch (funcid) { + case SBI_EXT_RFENCE_REMOTE_FENCE_I: + SBI_TLB_INFO_INIT(&tlb_info, 0, 0, 0, 0, + sbi_tlb_local_fence_i, source_hart); + ret = sbi_tlb_request(regs->a0, regs->a1, &tlb_info); + break; + case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA: + SBI_TLB_INFO_INIT(&tlb_info, regs->a2, regs->a3, 0, 0, + sbi_tlb_local_hfence_gvma, source_hart); + ret = sbi_tlb_request(regs->a0, regs->a1, &tlb_info); + break; + case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID: + SBI_TLB_INFO_INIT(&tlb_info, regs->a2, regs->a3, 0, regs->a4, + sbi_tlb_local_hfence_gvma_vmid, + source_hart); + ret = sbi_tlb_request(regs->a0, regs->a1, &tlb_info); + break; + case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA: + vmid = (csr_read(CSR_HGATP) & HGATP_VMID_MASK); + vmid = vmid >> HGATP_VMID_SHIFT; + SBI_TLB_INFO_INIT(&tlb_info, regs->a2, regs->a3, 0, vmid, + sbi_tlb_local_hfence_vvma, source_hart); + ret = sbi_tlb_request(regs->a0, regs->a1, &tlb_info); + break; + case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID: + vmid = (csr_read(CSR_HGATP) & HGATP_VMID_MASK); + vmid = vmid >> HGATP_VMID_SHIFT; + SBI_TLB_INFO_INIT(&tlb_info, regs->a2, regs->a3, regs->a4, + vmid, sbi_tlb_local_hfence_vvma_asid, + source_hart); + ret = sbi_tlb_request(regs->a0, regs->a1, &tlb_info); + break; + case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA: + SBI_TLB_INFO_INIT(&tlb_info, regs->a2, regs->a3, 0, 0, + sbi_tlb_local_sfence_vma, source_hart); + ret = sbi_tlb_request(regs->a0, regs->a1, &tlb_info); + break; + case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID: + SBI_TLB_INFO_INIT(&tlb_info, regs->a2, regs->a3, regs->a4, 0, + sbi_tlb_local_sfence_vma_asid, source_hart); + ret = sbi_tlb_request(regs->a0, regs->a1, &tlb_info); + break; + default: + ret = SBI_ENOTSUPP; + }; + + return ret; +} + +struct sbi_ecall_extension ecall_rfence = { + .extid_start = SBI_EXT_RFENCE, + .extid_end = SBI_EXT_RFENCE, + .handle = sbi_ecall_rfence_handler, +}; + +static int sbi_ecall_ipi_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + int ret = 0; + + if (funcid == SBI_EXT_IPI_SEND_IPI) + ret = sbi_ipi_send_smode(regs->a0, regs->a1); + else + ret = SBI_ENOTSUPP; + + return ret; +} + +struct sbi_ecall_extension ecall_ipi = { + .extid_start = SBI_EXT_IPI, + .extid_end = SBI_EXT_IPI, + .handle = sbi_ecall_ipi_handler, +}; + +static int sbi_ecall_srst_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + if (funcid == SBI_EXT_SRST_RESET) { + if ((((u32)-1U) <= ((u64)regs->a0)) || + (((u32)-1U) <= ((u64)regs->a1))) + return SBI_EINVAL; + + switch (regs->a0) { + case SBI_SRST_RESET_TYPE_SHUTDOWN: + case SBI_SRST_RESET_TYPE_COLD_REBOOT: + case SBI_SRST_RESET_TYPE_WARM_REBOOT: + break; + default: + return SBI_ENOTSUPP; + } + + switch (regs->a1) { + case SBI_SRST_RESET_REASON_NONE: + case SBI_SRST_RESET_REASON_SYSFAIL: + break; + default: + return SBI_ENOTSUPP; + } + + if (sbi_system_reset_supported(regs->a0, regs->a1)) + sbi_system_reset(regs->a0, regs->a1); + } + + return SBI_ENOTSUPP; +} + +static int sbi_ecall_srst_probe(unsigned long extid, unsigned long *out_val) +{ + u32 type, count = 0; + + /* + * At least one standard reset types should be supported by + * the platform for SBI SRST extension to be usable. + */ + + for (type = 0; type <= SBI_SRST_RESET_TYPE_LAST; type++) { + if (sbi_system_reset_supported(type, + SBI_SRST_RESET_REASON_NONE)) + count++; + } + + *out_val = (count) ? 1 : 0; + return 0; +} + +struct sbi_ecall_extension ecall_srst = { + .extid_start = SBI_EXT_SRST, + .extid_end = SBI_EXT_SRST, + .handle = sbi_ecall_srst_handler, + .probe = sbi_ecall_srst_probe, +}; diff --git a/roms/opensbi/lib/sbi/sbi_ecall_vendor.c b/roms/opensbi/lib/sbi/sbi_ecall_vendor.c new file mode 100644 index 000000000..925282963 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_ecall_vendor.c @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * Atish Patra <atish.patra@wdc.com> + */ + +#include <sbi/sbi_ecall.h> +#include <sbi/sbi_ecall_interface.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_trap.h> + +static int sbi_ecall_vendor_probe(unsigned long extid, + unsigned long *out_val) +{ + *out_val = sbi_platform_vendor_ext_check(sbi_platform_thishart_ptr(), + extid); + return 0; +} + +static int sbi_ecall_vendor_handler(unsigned long extid, unsigned long funcid, + const struct sbi_trap_regs *regs, + unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + return sbi_platform_vendor_ext_provider(sbi_platform_thishart_ptr(), + extid, funcid, regs, + out_val, out_trap); +} + +struct sbi_ecall_extension ecall_vendor = { + .extid_start = SBI_EXT_VENDOR_START, + .extid_end = SBI_EXT_VENDOR_END, + .probe = sbi_ecall_vendor_probe, + .handle = sbi_ecall_vendor_handler, +}; diff --git a/roms/opensbi/lib/sbi/sbi_emulate_csr.c b/roms/opensbi/lib/sbi/sbi_emulate_csr.c new file mode 100644 index 000000000..bee7761c4 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_emulate_csr.c @@ -0,0 +1,188 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_bitops.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_emulate_csr.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_timer.h> +#include <sbi/sbi_trap.h> + +static bool hpm_allowed(int hpm_num, ulong prev_mode, bool virt) +{ + ulong cen = -1UL; + + if (prev_mode <= PRV_S) { + cen &= csr_read(CSR_MCOUNTEREN); + if (virt) + cen &= csr_read(CSR_HCOUNTEREN); + } + if (prev_mode == PRV_U) + cen &= csr_read(CSR_SCOUNTEREN); + + return ((cen >> hpm_num) & 1) ? TRUE : FALSE; +} + +int sbi_emulate_csr_read(int csr_num, struct sbi_trap_regs *regs, + ulong *csr_val) +{ + int ret = 0; + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + ulong prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT; +#if __riscv_xlen == 32 + bool virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE; +#else + bool virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE; +#endif + + switch (csr_num) { + case CSR_HTIMEDELTA: + if (prev_mode == PRV_S && !virt) + *csr_val = sbi_timer_get_delta(); + else + ret = SBI_ENOTSUPP; + break; + case CSR_CYCLE: + if (!hpm_allowed(csr_num - CSR_CYCLE, prev_mode, virt)) + return SBI_ENOTSUPP; + *csr_val = csr_read(CSR_MCYCLE); + break; + case CSR_TIME: + /* + * We emulate TIME CSR for both Host (HS/U-mode) and + * Guest (VS/VU-mode). + * + * Faster TIME CSR reads are critical for good performance + * in S-mode software so we don't check CSR permissions. + */ + *csr_val = (virt) ? sbi_timer_virt_value(): + sbi_timer_value(); + break; + case CSR_INSTRET: + if (!hpm_allowed(csr_num - CSR_CYCLE, prev_mode, virt)) + return SBI_ENOTSUPP; + *csr_val = csr_read(CSR_MINSTRET); + break; + +#if __riscv_xlen == 32 + case CSR_HTIMEDELTAH: + if (prev_mode == PRV_S && !virt) + *csr_val = sbi_timer_get_delta() >> 32; + else + ret = SBI_ENOTSUPP; + break; + case CSR_CYCLEH: + if (!hpm_allowed(csr_num - CSR_CYCLEH, prev_mode, virt)) + return SBI_ENOTSUPP; + *csr_val = csr_read(CSR_MCYCLEH); + break; + case CSR_TIMEH: + /* Refer comments on TIME CSR above. */ + *csr_val = (virt) ? sbi_timer_virt_value() >> 32: + sbi_timer_value() >> 32; + break; + case CSR_INSTRETH: + if (!hpm_allowed(csr_num - CSR_CYCLEH, prev_mode, virt)) + return SBI_ENOTSUPP; + *csr_val = csr_read(CSR_MINSTRETH); + break; +#endif + +#define switchcase_hpm(__uref, __mref, __csr) \ + case __csr: \ + if ((sbi_hart_mhpm_count(scratch) + 3) <= (__csr - __uref))\ + return SBI_ENOTSUPP; \ + if (!hpm_allowed(__csr - __uref, prev_mode, virt)) \ + return SBI_ENOTSUPP; \ + *csr_val = csr_read(__mref + __csr - __uref); \ + break; +#define switchcase_hpm_2(__uref, __mref, __csr) \ + switchcase_hpm(__uref, __mref, __csr + 0) \ + switchcase_hpm(__uref, __mref, __csr + 1) +#define switchcase_hpm_4(__uref, __mref, __csr) \ + switchcase_hpm_2(__uref, __mref, __csr + 0) \ + switchcase_hpm_2(__uref, __mref, __csr + 2) +#define switchcase_hpm_8(__uref, __mref, __csr) \ + switchcase_hpm_4(__uref, __mref, __csr + 0) \ + switchcase_hpm_4(__uref, __mref, __csr + 4) +#define switchcase_hpm_16(__uref, __mref, __csr) \ + switchcase_hpm_8(__uref, __mref, __csr + 0) \ + switchcase_hpm_8(__uref, __mref, __csr + 8) + + switchcase_hpm(CSR_CYCLE, CSR_MCYCLE, CSR_HPMCOUNTER3) + switchcase_hpm_4(CSR_CYCLE, CSR_MCYCLE, CSR_HPMCOUNTER4) + switchcase_hpm_8(CSR_CYCLE, CSR_MCYCLE, CSR_HPMCOUNTER8) + switchcase_hpm_16(CSR_CYCLE, CSR_MCYCLE, CSR_HPMCOUNTER16) + +#if __riscv_xlen == 32 + switchcase_hpm(CSR_CYCLEH, CSR_MCYCLEH, CSR_HPMCOUNTER3H) + switchcase_hpm_4(CSR_CYCLEH, CSR_MCYCLEH, CSR_HPMCOUNTER4H) + switchcase_hpm_8(CSR_CYCLEH, CSR_MCYCLEH, CSR_HPMCOUNTER8H) + switchcase_hpm_16(CSR_CYCLEH, CSR_MCYCLEH, CSR_HPMCOUNTER16H) +#endif + +#undef switchcase_hpm_16 +#undef switchcase_hpm_8 +#undef switchcase_hpm_4 +#undef switchcase_hpm_2 +#undef switchcase_hpm + + default: + ret = SBI_ENOTSUPP; + break; + }; + + if (ret) + sbi_dprintf("%s: hartid%d: invalid csr_num=0x%x\n", + __func__, current_hartid(), csr_num); + + return ret; +} + +int sbi_emulate_csr_write(int csr_num, struct sbi_trap_regs *regs, + ulong csr_val) +{ + int ret = 0; + ulong prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT; +#if __riscv_xlen == 32 + bool virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE; +#else + bool virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE; +#endif + + switch (csr_num) { + case CSR_HTIMEDELTA: + if (prev_mode == PRV_S && !virt) + sbi_timer_set_delta(csr_val); + else + ret = SBI_ENOTSUPP; + break; +#if __riscv_xlen == 32 + case CSR_HTIMEDELTAH: + if (prev_mode == PRV_S && !virt) + sbi_timer_set_delta_upper(csr_val); + else + ret = SBI_ENOTSUPP; + break; +#endif + default: + ret = SBI_ENOTSUPP; + break; + }; + + if (ret) + sbi_dprintf("%s: hartid%d: invalid csr_num=0x%x\n", + __func__, current_hartid(), csr_num); + + return ret; +} diff --git a/roms/opensbi/lib/sbi/sbi_expected_trap.S b/roms/opensbi/lib/sbi/sbi_expected_trap.S new file mode 100644 index 000000000..24891c74c --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_expected_trap.S @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/sbi_trap.h> + + /* + * We assume that faulting instruction is is 4-byte long and blindly + * increment SEPC by 4. + * + * The trap info will be saved as follows: + * A3 <- pointer struct sbi_trap_info + * A4 <- temporary + */ + + .align 3 + .global __sbi_expected_trap +__sbi_expected_trap: + /* Without H-extension so, MTVAL2 and MTINST CSRs not available */ + csrr a4, CSR_MEPC + REG_S a4, SBI_TRAP_INFO_OFFSET(epc)(a3) + csrr a4, CSR_MCAUSE + REG_S a4, SBI_TRAP_INFO_OFFSET(cause)(a3) + csrr a4, CSR_MTVAL + REG_S a4, SBI_TRAP_INFO_OFFSET(tval)(a3) + REG_S zero, SBI_TRAP_INFO_OFFSET(tval2)(a3) + REG_S zero, SBI_TRAP_INFO_OFFSET(tinst)(a3) + csrr a4, CSR_MEPC + addi a4, a4, 4 + csrw CSR_MEPC, a4 + mret + + .align 3 + .global __sbi_expected_trap_hext +__sbi_expected_trap_hext: + /* With H-extension so, MTVAL2 and MTINST CSRs available */ + csrr a4, CSR_MEPC + REG_S a4, SBI_TRAP_INFO_OFFSET(epc)(a3) + csrr a4, CSR_MCAUSE + REG_S a4, SBI_TRAP_INFO_OFFSET(cause)(a3) + csrr a4, CSR_MTVAL + REG_S a4, SBI_TRAP_INFO_OFFSET(tval)(a3) + csrr a4, CSR_MTVAL2 + REG_S a4, SBI_TRAP_INFO_OFFSET(tval2)(a3) + csrr a4, CSR_MTINST + REG_S a4, SBI_TRAP_INFO_OFFSET(tinst)(a3) + csrr a4, CSR_MEPC + addi a4, a4, 4 + csrw CSR_MEPC, a4 + mret diff --git a/roms/opensbi/lib/sbi/sbi_fifo.c b/roms/opensbi/lib/sbi/sbi_fifo.c new file mode 100644 index 000000000..8d1dbf044 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_fifo.c @@ -0,0 +1,192 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra<atish.patra@wdc.com> + * + */ +#include <sbi/riscv_locks.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_fifo.h> +#include <sbi/sbi_string.h> + +void sbi_fifo_init(struct sbi_fifo *fifo, void *queue_mem, u16 entries, + u16 entry_size) +{ + fifo->queue = queue_mem; + fifo->num_entries = entries; + fifo->entry_size = entry_size; + SPIN_LOCK_INIT(&fifo->qlock); + fifo->avail = fifo->tail = 0; + sbi_memset(fifo->queue, 0, (size_t)entries * entry_size); +} + +/* Note: must be called with fifo->qlock held */ +static inline bool __sbi_fifo_is_full(struct sbi_fifo *fifo) +{ + return (fifo->avail == fifo->num_entries) ? TRUE : FALSE; +} + +u16 sbi_fifo_avail(struct sbi_fifo *fifo) +{ + u16 ret; + + if (!fifo) + return 0; + + spin_lock(&fifo->qlock); + ret = fifo->avail; + spin_unlock(&fifo->qlock); + + return ret; +} + +bool sbi_fifo_is_full(struct sbi_fifo *fifo) +{ + bool ret; + + spin_lock(&fifo->qlock); + ret = __sbi_fifo_is_full(fifo); + spin_unlock(&fifo->qlock); + + return ret; +} + +/* Note: must be called with fifo->qlock held */ +static inline void __sbi_fifo_enqueue(struct sbi_fifo *fifo, void *data) +{ + u32 head; + + head = (u32)fifo->tail + fifo->avail; + if (head >= fifo->num_entries) + head = head - fifo->num_entries; + + sbi_memcpy(fifo->queue + head * fifo->entry_size, data, fifo->entry_size); + + fifo->avail++; +} + + +/* Note: must be called with fifo->qlock held */ +static inline bool __sbi_fifo_is_empty(struct sbi_fifo *fifo) +{ + return (fifo->avail == 0) ? TRUE : FALSE; +} + +bool sbi_fifo_is_empty(struct sbi_fifo *fifo) +{ + bool ret; + + spin_lock(&fifo->qlock); + ret = __sbi_fifo_is_empty(fifo); + spin_unlock(&fifo->qlock); + + return ret; +} + +/* Note: must be called with fifo->qlock held */ +static inline void __sbi_fifo_reset(struct sbi_fifo *fifo) +{ + size_t size = (size_t)fifo->num_entries * fifo->entry_size; + + fifo->avail = 0; + fifo->tail = 0; + sbi_memset(fifo->queue, 0, size); +} + +bool sbi_fifo_reset(struct sbi_fifo *fifo) +{ + if (!fifo) + return FALSE; + + spin_lock(&fifo->qlock); + __sbi_fifo_reset(fifo); + spin_unlock(&fifo->qlock); + + return TRUE; +} + +/** + * Provide a helper function to do inplace update to the fifo. + * Note: The callback function is called with lock being held. + * + * **Do not** invoke any other fifo function from callback. Otherwise, it will + * lead to deadlock. + */ +int sbi_fifo_inplace_update(struct sbi_fifo *fifo, void *in, + int (*fptr)(void *in, void *data)) +{ + int i, index = 0; + int ret = SBI_FIFO_UNCHANGED; + void *entry; + + if (!fifo || !in) + return ret; + + spin_lock(&fifo->qlock); + + if (__sbi_fifo_is_empty(fifo)) { + spin_unlock(&fifo->qlock); + return ret; + } + + for (i = 0; i < fifo->avail; i++) { + index = fifo->tail + i; + if (index >= fifo->num_entries) + index = index - fifo->num_entries; + entry = (void *)fifo->queue + (u32)index * fifo->entry_size; + ret = fptr(in, entry); + + if (ret == SBI_FIFO_SKIP || ret == SBI_FIFO_UPDATED) { + break; + } + } + spin_unlock(&fifo->qlock); + + return ret; +} + +int sbi_fifo_enqueue(struct sbi_fifo *fifo, void *data) +{ + if (!fifo || !data) + return SBI_EINVAL; + + spin_lock(&fifo->qlock); + + if (__sbi_fifo_is_full(fifo)) { + spin_unlock(&fifo->qlock); + return SBI_ENOSPC; + } + __sbi_fifo_enqueue(fifo, data); + + spin_unlock(&fifo->qlock); + + return 0; +} + +int sbi_fifo_dequeue(struct sbi_fifo *fifo, void *data) +{ + if (!fifo || !data) + return SBI_EINVAL; + + spin_lock(&fifo->qlock); + + if (__sbi_fifo_is_empty(fifo)) { + spin_unlock(&fifo->qlock); + return SBI_ENOENT; + } + + sbi_memcpy(data, fifo->queue + (u32)fifo->tail * fifo->entry_size, + fifo->entry_size); + + fifo->avail--; + fifo->tail++; + if (fifo->tail >= fifo->num_entries) + fifo->tail = 0; + + spin_unlock(&fifo->qlock); + + return 0; +} diff --git a/roms/opensbi/lib/sbi/sbi_hart.c b/roms/opensbi/lib/sbi/sbi_hart.c new file mode 100644 index 000000000..fc86e9f31 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_hart.c @@ -0,0 +1,536 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_barrier.h> +#include <sbi/riscv_encoding.h> +#include <sbi/riscv_fp.h> +#include <sbi/sbi_bitops.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_csr_detect.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_math.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_string.h> +#include <sbi/sbi_trap.h> + +extern void __sbi_expected_trap(void); +extern void __sbi_expected_trap_hext(void); + +void (*sbi_hart_expected_trap)(void) = &__sbi_expected_trap; + +struct hart_features { + unsigned long features; + unsigned int pmp_count; + unsigned int pmp_addr_bits; + unsigned long pmp_gran; + unsigned int mhpm_count; +}; +static unsigned long hart_features_offset; + +static void mstatus_init(struct sbi_scratch *scratch) +{ + unsigned long mstatus_val = 0; + + /* Enable FPU */ + if (misa_extension('D') || misa_extension('F')) + mstatus_val |= MSTATUS_FS; + + /* Enable Vector context */ + if (misa_extension('V')) + mstatus_val |= MSTATUS_VS; + + csr_write(CSR_MSTATUS, mstatus_val); + + /* Enable user/supervisor use of perf counters */ + if (misa_extension('S') && + sbi_hart_has_feature(scratch, SBI_HART_HAS_SCOUNTEREN)) + csr_write(CSR_SCOUNTEREN, -1); + if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTEREN)) + csr_write(CSR_MCOUNTEREN, -1); + + /* Disable all interrupts */ + csr_write(CSR_MIE, 0); + + /* Disable S-mode paging */ + if (misa_extension('S')) + csr_write(CSR_SATP, 0); +} + +static int fp_init(struct sbi_scratch *scratch) +{ +#ifdef __riscv_flen + int i; +#endif + + if (!misa_extension('D') && !misa_extension('F')) + return 0; + + if (!(csr_read(CSR_MSTATUS) & MSTATUS_FS)) + return SBI_EINVAL; + +#ifdef __riscv_flen + for (i = 0; i < 32; i++) + init_fp_reg(i); + csr_write(CSR_FCSR, 0); +#endif + + return 0; +} + +static int delegate_traps(struct sbi_scratch *scratch) +{ + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + unsigned long interrupts, exceptions; + + if (!misa_extension('S')) + /* No delegation possible as mideleg does not exist */ + return 0; + + /* Send M-mode interrupts and most exceptions to S-mode */ + interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP; + exceptions = (1U << CAUSE_MISALIGNED_FETCH) | (1U << CAUSE_BREAKPOINT) | + (1U << CAUSE_USER_ECALL); + if (sbi_platform_has_mfaults_delegation(plat)) + exceptions |= (1U << CAUSE_FETCH_PAGE_FAULT) | + (1U << CAUSE_LOAD_PAGE_FAULT) | + (1U << CAUSE_STORE_PAGE_FAULT); + + /* + * If hypervisor extension available then we only handle hypervisor + * calls (i.e. ecalls from HS-mode) in M-mode. + * + * The HS-mode will additionally handle supervisor calls (i.e. ecalls + * from VS-mode), Guest page faults and Virtual interrupts. + */ + if (misa_extension('H')) { + exceptions |= (1U << CAUSE_VIRTUAL_SUPERVISOR_ECALL); + exceptions |= (1U << CAUSE_FETCH_GUEST_PAGE_FAULT); + exceptions |= (1U << CAUSE_LOAD_GUEST_PAGE_FAULT); + exceptions |= (1U << CAUSE_VIRTUAL_INST_FAULT); + exceptions |= (1U << CAUSE_STORE_GUEST_PAGE_FAULT); + } + + csr_write(CSR_MIDELEG, interrupts); + csr_write(CSR_MEDELEG, exceptions); + + return 0; +} + +void sbi_hart_delegation_dump(struct sbi_scratch *scratch, + const char *prefix, const char *suffix) +{ + if (!misa_extension('S')) + /* No delegation possible as mideleg does not exist*/ + return; + +#if __riscv_xlen == 32 + sbi_printf("%sMIDELEG%s: 0x%08lx\n", + prefix, suffix, csr_read(CSR_MIDELEG)); + sbi_printf("%sMEDELEG%s: 0x%08lx\n", + prefix, suffix, csr_read(CSR_MEDELEG)); +#else + sbi_printf("%sMIDELEG%s: 0x%016lx\n", + prefix, suffix, csr_read(CSR_MIDELEG)); + sbi_printf("%sMEDELEG%s: 0x%016lx\n", + prefix, suffix, csr_read(CSR_MEDELEG)); +#endif +} + +unsigned int sbi_hart_mhpm_count(struct sbi_scratch *scratch) +{ + struct hart_features *hfeatures = + sbi_scratch_offset_ptr(scratch, hart_features_offset); + + return hfeatures->mhpm_count; +} + +unsigned int sbi_hart_pmp_count(struct sbi_scratch *scratch) +{ + struct hart_features *hfeatures = + sbi_scratch_offset_ptr(scratch, hart_features_offset); + + return hfeatures->pmp_count; +} + +unsigned long sbi_hart_pmp_granularity(struct sbi_scratch *scratch) +{ + struct hart_features *hfeatures = + sbi_scratch_offset_ptr(scratch, hart_features_offset); + + return hfeatures->pmp_gran; +} + +unsigned int sbi_hart_pmp_addrbits(struct sbi_scratch *scratch) +{ + struct hart_features *hfeatures = + sbi_scratch_offset_ptr(scratch, hart_features_offset); + + return hfeatures->pmp_addr_bits; +} + +int sbi_hart_pmp_configure(struct sbi_scratch *scratch) +{ + struct sbi_domain_memregion *reg; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); + unsigned int pmp_idx = 0, pmp_flags, pmp_bits, pmp_gran_log2; + unsigned int pmp_count = sbi_hart_pmp_count(scratch); + unsigned long pmp_addr = 0, pmp_addr_max = 0; + + if (!pmp_count) + return 0; + + pmp_gran_log2 = log2roundup(sbi_hart_pmp_granularity(scratch)); + pmp_bits = sbi_hart_pmp_addrbits(scratch) - 1; + pmp_addr_max = (1UL << pmp_bits) | ((1UL << pmp_bits) - 1); + + sbi_domain_for_each_memregion(dom, reg) { + if (pmp_count <= pmp_idx) + break; + + pmp_flags = 0; + if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE) + pmp_flags |= PMP_R; + if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE) + pmp_flags |= PMP_W; + if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE) + pmp_flags |= PMP_X; + if (reg->flags & SBI_DOMAIN_MEMREGION_MMODE) + pmp_flags |= PMP_L; + + pmp_addr = reg->base >> PMP_SHIFT; + if (pmp_gran_log2 <= reg->order && pmp_addr < pmp_addr_max) + pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order); + else { + sbi_printf("Can not configure pmp for domain %s", dom->name); + sbi_printf("because memory region address %lx or size %lx is not in range\n", + reg->base, reg->order); + } + } + + return 0; +} + +/** + * Check whether a particular hart feature is available + * + * @param scratch pointer to the HART scratch space + * @param feature the feature to check + * @returns true (feature available) or false (feature not available) + */ +bool sbi_hart_has_feature(struct sbi_scratch *scratch, unsigned long feature) +{ + struct hart_features *hfeatures = + sbi_scratch_offset_ptr(scratch, hart_features_offset); + + if (hfeatures->features & feature) + return true; + else + return false; +} + +static unsigned long hart_get_features(struct sbi_scratch *scratch) +{ + struct hart_features *hfeatures = + sbi_scratch_offset_ptr(scratch, hart_features_offset); + + return hfeatures->features; +} + +static inline char *sbi_hart_feature_id2string(unsigned long feature) +{ + char *fstr = NULL; + + if (!feature) + return NULL; + + switch (feature) { + case SBI_HART_HAS_SCOUNTEREN: + fstr = "scounteren"; + break; + case SBI_HART_HAS_MCOUNTEREN: + fstr = "mcounteren"; + break; + case SBI_HART_HAS_TIME: + fstr = "time"; + break; + default: + break; + } + + return fstr; +} + +/** + * Get the hart features in string format + * + * @param scratch pointer to the HART scratch space + * @param features_str pointer to a char array where the features string will be + * updated + * @param nfstr length of the features_str. The feature string will be truncated + * if nfstr is not long enough. + */ +void sbi_hart_get_features_str(struct sbi_scratch *scratch, + char *features_str, int nfstr) +{ + unsigned long features, feat = 1UL; + char *temp; + int offset = 0; + + if (!features_str || nfstr <= 0) + return; + sbi_memset(features_str, 0, nfstr); + + features = hart_get_features(scratch); + if (!features) + goto done; + + do { + if (features & feat) { + temp = sbi_hart_feature_id2string(feat); + if (temp) { + sbi_snprintf(features_str + offset, nfstr, + "%s,", temp); + offset = offset + sbi_strlen(temp) + 1; + } + } + feat = feat << 1; + } while (feat <= SBI_HART_HAS_LAST_FEATURE); + +done: + if (offset) + features_str[offset - 1] = '\0'; + else + sbi_strncpy(features_str, "none", nfstr); +} + +static unsigned long hart_pmp_get_allowed_addr(void) +{ + unsigned long val = 0; + struct sbi_trap_info trap = {0}; + + csr_write_allowed(CSR_PMPADDR0, (ulong)&trap, PMP_ADDR_MASK); \ + if (!trap.cause) { + val = csr_read_allowed(CSR_PMPADDR0, (ulong)&trap); + if (trap.cause) + val = 0; + } + + return val; +} + +static void hart_detect_features(struct sbi_scratch *scratch) +{ + struct sbi_trap_info trap = {0}; + struct hart_features *hfeatures; + unsigned long val; + + /* Reset hart features */ + hfeatures = sbi_scratch_offset_ptr(scratch, hart_features_offset); + hfeatures->features = 0; + hfeatures->pmp_count = 0; + hfeatures->mhpm_count = 0; + +#define __check_csr(__csr, __rdonly, __wrval, __field, __skip) \ + val = csr_read_allowed(__csr, (ulong)&trap); \ + if (!trap.cause) { \ + if (__rdonly) { \ + (hfeatures->__field)++; \ + } else { \ + csr_write_allowed(__csr, (ulong)&trap, __wrval);\ + if (!trap.cause) { \ + if (csr_swap(__csr, val) == __wrval) \ + (hfeatures->__field)++; \ + else \ + goto __skip; \ + } else { \ + goto __skip; \ + } \ + } \ + } else { \ + goto __skip; \ + } +#define __check_csr_2(__csr, __rdonly, __wrval, __field, __skip) \ + __check_csr(__csr + 0, __rdonly, __wrval, __field, __skip) \ + __check_csr(__csr + 1, __rdonly, __wrval, __field, __skip) +#define __check_csr_4(__csr, __rdonly, __wrval, __field, __skip) \ + __check_csr_2(__csr + 0, __rdonly, __wrval, __field, __skip) \ + __check_csr_2(__csr + 2, __rdonly, __wrval, __field, __skip) +#define __check_csr_8(__csr, __rdonly, __wrval, __field, __skip) \ + __check_csr_4(__csr + 0, __rdonly, __wrval, __field, __skip) \ + __check_csr_4(__csr + 4, __rdonly, __wrval, __field, __skip) +#define __check_csr_16(__csr, __rdonly, __wrval, __field, __skip) \ + __check_csr_8(__csr + 0, __rdonly, __wrval, __field, __skip) \ + __check_csr_8(__csr + 8, __rdonly, __wrval, __field, __skip) +#define __check_csr_32(__csr, __rdonly, __wrval, __field, __skip) \ + __check_csr_16(__csr + 0, __rdonly, __wrval, __field, __skip) \ + __check_csr_16(__csr + 16, __rdonly, __wrval, __field, __skip) +#define __check_csr_64(__csr, __rdonly, __wrval, __field, __skip) \ + __check_csr_32(__csr + 0, __rdonly, __wrval, __field, __skip) \ + __check_csr_32(__csr + 32, __rdonly, __wrval, __field, __skip) + + /** + * Detect the allowed address bits & granularity. At least PMPADDR0 + * should be implemented. + */ + val = hart_pmp_get_allowed_addr(); + if (val) { + hfeatures->pmp_gran = 1 << (__ffs(val) + 2); + hfeatures->pmp_addr_bits = __fls(val) + 1; + /* Detect number of PMP regions. At least PMPADDR0 should be implemented*/ + __check_csr_64(CSR_PMPADDR0, 0, val, pmp_count, __pmp_skip); + } +__pmp_skip: + + /* Detect number of MHPM counters */ + __check_csr(CSR_MHPMCOUNTER3, 0, 1UL, mhpm_count, __mhpm_skip); + __check_csr_4(CSR_MHPMCOUNTER4, 0, 1UL, mhpm_count, __mhpm_skip); + __check_csr_8(CSR_MHPMCOUNTER8, 0, 1UL, mhpm_count, __mhpm_skip); + __check_csr_16(CSR_MHPMCOUNTER16, 0, 1UL, mhpm_count, __mhpm_skip); +__mhpm_skip: + +#undef __check_csr_64 +#undef __check_csr_32 +#undef __check_csr_16 +#undef __check_csr_8 +#undef __check_csr_4 +#undef __check_csr_2 +#undef __check_csr + + /* Detect if hart supports SCOUNTEREN feature */ + trap.cause = 0; + val = csr_read_allowed(CSR_SCOUNTEREN, (unsigned long)&trap); + if (!trap.cause) { + csr_write_allowed(CSR_SCOUNTEREN, (unsigned long)&trap, val); + if (!trap.cause) + hfeatures->features |= SBI_HART_HAS_SCOUNTEREN; + } + + /* Detect if hart supports MCOUNTEREN feature */ + trap.cause = 0; + val = csr_read_allowed(CSR_MCOUNTEREN, (unsigned long)&trap); + if (!trap.cause) { + csr_write_allowed(CSR_MCOUNTEREN, (unsigned long)&trap, val); + if (!trap.cause) + hfeatures->features |= SBI_HART_HAS_MCOUNTEREN; + } + + /* Detect if hart supports time CSR */ + trap.cause = 0; + csr_read_allowed(CSR_TIME, (unsigned long)&trap); + if (!trap.cause) + hfeatures->features |= SBI_HART_HAS_TIME; +} + +int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot) +{ + int rc; + + if (cold_boot) { + if (misa_extension('H')) + sbi_hart_expected_trap = &__sbi_expected_trap_hext; + + hart_features_offset = sbi_scratch_alloc_offset( + sizeof(struct hart_features), + "HART_FEATURES"); + if (!hart_features_offset) + return SBI_ENOMEM; + } + + hart_detect_features(scratch); + + mstatus_init(scratch); + + rc = fp_init(scratch); + if (rc) + return rc; + + rc = delegate_traps(scratch); + if (rc) + return rc; + + return 0; +} + +void __attribute__((noreturn)) sbi_hart_hang(void) +{ + while (1) + wfi(); + __builtin_unreachable(); +} + +void __attribute__((noreturn)) +sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1, + unsigned long next_addr, unsigned long next_mode, + bool next_virt) +{ +#if __riscv_xlen == 32 + unsigned long val, valH; +#else + unsigned long val; +#endif + + switch (next_mode) { + case PRV_M: + break; + case PRV_S: + if (!misa_extension('S')) + sbi_hart_hang(); + break; + case PRV_U: + if (!misa_extension('U')) + sbi_hart_hang(); + break; + default: + sbi_hart_hang(); + } + + val = csr_read(CSR_MSTATUS); + val = INSERT_FIELD(val, MSTATUS_MPP, next_mode); + val = INSERT_FIELD(val, MSTATUS_MPIE, 0); +#if __riscv_xlen == 32 + if (misa_extension('H')) { + valH = csr_read(CSR_MSTATUSH); + if (next_virt) + valH = INSERT_FIELD(valH, MSTATUSH_MPV, 1); + else + valH = INSERT_FIELD(valH, MSTATUSH_MPV, 0); + csr_write(CSR_MSTATUSH, valH); + } +#else + if (misa_extension('H')) { + if (next_virt) + val = INSERT_FIELD(val, MSTATUS_MPV, 1); + else + val = INSERT_FIELD(val, MSTATUS_MPV, 0); + } +#endif + csr_write(CSR_MSTATUS, val); + csr_write(CSR_MEPC, next_addr); + + if (next_mode == PRV_S) { + csr_write(CSR_STVEC, next_addr); + csr_write(CSR_SSCRATCH, 0); + csr_write(CSR_SIE, 0); + csr_write(CSR_SATP, 0); + } else if (next_mode == PRV_U) { + if (misa_extension('N')) { + csr_write(CSR_UTVEC, next_addr); + csr_write(CSR_USCRATCH, 0); + csr_write(CSR_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(); +} diff --git a/roms/opensbi/lib/sbi/sbi_hfence.S b/roms/opensbi/lib/sbi/sbi_hfence.S new file mode 100644 index 000000000..d05becbf2 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_hfence.S @@ -0,0 +1,135 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * Atish Patra <anup.patel@wdc.com> + */ + + /* + * HFENCE.GVMA rs1, rs2 + * HFENCE.GVMA zero, rs2 + * HFENCE.GVMA rs1 + * HFENCE.GVMA + * + * rs1!=zero and rs2!=zero ==> HFENCE.GVMA rs1, rs2 + * rs1==zero and rs2!=zero ==> HFENCE.GVMA zero, rs2 + * rs1!=zero and rs2==zero ==> HFENCE.GVMA rs1 + * rs1==zero and rs2==zero ==> HFENCE.GVMA + * + * Instruction encoding of HFENCE.GVMA is: + * 0110001 rs2(5) rs1(5) 000 00000 1110011 + */ + + .align 3 + .global __sbi_hfence_gvma_vmid_gpa +__sbi_hfence_gvma_vmid_gpa: + /* + * rs1 = a0 (GPA) + * rs2 = a1 (VMID) + * HFENCE.GVMA a0, a1 + * 0110001 01011 01010 000 00000 1110011 + */ + .word 0x62b50073 + ret + + .align 3 + .global __sbi_hfence_gvma_vmid +__sbi_hfence_gvma_vmid: + /* + * rs1 = zero + * rs2 = a0 (VMID) + * HFENCE.GVMA zero, a0 + * 0110001 01010 00000 000 00000 1110011 + */ + .word 0x62a00073 + ret + + .align 3 + .global __sbi_hfence_gvma_gpa +__sbi_hfence_gvma_gpa: + /* + * rs1 = a0 (GPA) + * rs2 = zero + * HFENCE.GVMA a0 + * 0110001 00000 01010 000 00000 1110011 + */ + .word 0x62050073 + ret + + .align 3 + .global __sbi_hfence_gvma_all +__sbi_hfence_gvma_all: + /* + * rs1 = zero + * rs2 = zero + * HFENCE.GVMA + * 0110001 00000 00000 000 00000 1110011 + */ + .word 0x62000073 + ret + + /* + * HFENCE.VVMA rs1, rs2 + * HFENCE.VVMA zero, rs2 + * HFENCE.VVMA rs1 + * HFENCE.VVMA + * + * rs1!=zero and rs2!=zero ==> HFENCE.VVMA rs1, rs2 + * rs1==zero and rs2!=zero ==> HFENCE.VVMA zero, rs2 + * rs1!=zero and rs2==zero ==> HFENCE.VVMA rs1 + * rs1==zero and rs2==zero ==> HFENCE.vVMA + * + * Instruction encoding of HFENCE.VVMA is: + * 0010001 rs2(5) rs1(5) 000 00000 1110011 + */ + + .align 3 + .global __sbi_hfence_vvma_asid_va +__sbi_hfence_vvma_asid_va: + /* + * rs1 = a0 (VA) + * rs2 = a1 (ASID) + * HFENCE.VVMA a0, a1 + * 0010001 01011 01010 000 00000 1110011 + */ + .word 0x22b50073 + ret + + .align 3 + .global __sbi_hfence_vvma_asid +__sbi_hfence_vvma_asid: + /* + * rs1 = zero + * rs2 = a0 (ASID) + * HFENCE.VVMA zero, a0 + * 0010001 01010 00000 000 00000 1110011 + */ + .word 0x22a00073 + ret + + .align 3 + .global __sbi_hfence_vvma_va +__sbi_hfence_vvma_va: + /* + * rs1 = a0 (VA) + * rs2 = zero + * HFENCE.VVMA zero, a0 + * 0010001 00000 01010 000 00000 1110011 + */ + .word 0x22050073 + ret + + .align 3 + .global __sbi_hfence_vvma_all +__sbi_hfence_vvma_all: + /* + * rs1 = zero + * rs2 = zero + * HFENCE.VVMA + * 0010001 00000 00000 000 00000 1110011 + */ + .word 0x22000073 + ret diff --git a/roms/opensbi/lib/sbi/sbi_hsm.c b/roms/opensbi/lib/sbi/sbi_hsm.c new file mode 100644 index 000000000..e1b2b2c3d --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_hsm.c @@ -0,0 +1,291 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_barrier.h> +#include <sbi/riscv_encoding.h> +#include <sbi/riscv_atomic.h> +#include <sbi/sbi_bitops.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_ecall_interface.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_hartmask.h> +#include <sbi/sbi_hsm.h> +#include <sbi/sbi_init.h> +#include <sbi/sbi_ipi.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_system.h> +#include <sbi/sbi_timer.h> +#include <sbi/sbi_console.h> + +static unsigned long hart_data_offset; + +/** Per hart specific data to manage state transition **/ +struct sbi_hsm_data { + atomic_t state; +}; + +int sbi_hsm_hart_state_to_status(int state) +{ + int ret; + + switch (state) { + case SBI_HART_STOPPED: + ret = SBI_HSM_HART_STATUS_STOPPED; + break; + case SBI_HART_STOPPING: + ret = SBI_HSM_HART_STATUS_STOP_PENDING; + break; + case SBI_HART_STARTING: + ret = SBI_HSM_HART_STATUS_START_PENDING; + break; + case SBI_HART_STARTED: + ret = SBI_HSM_HART_STATUS_STARTED; + break; + default: + ret = SBI_EINVAL; + } + + return ret; +} + +static inline int __sbi_hsm_hart_get_state(u32 hartid) +{ + struct sbi_hsm_data *hdata; + struct sbi_scratch *scratch; + + scratch = sbi_hartid_to_scratch(hartid); + if (!scratch) + return SBI_HART_UNKNOWN; + + hdata = sbi_scratch_offset_ptr(scratch, hart_data_offset); + return atomic_read(&hdata->state); +} + +int sbi_hsm_hart_get_state(const struct sbi_domain *dom, u32 hartid) +{ + if (!sbi_domain_is_assigned_hart(dom, hartid)) + return SBI_HART_UNKNOWN; + + return __sbi_hsm_hart_get_state(hartid); +} + +static bool sbi_hsm_hart_started(const struct sbi_domain *dom, u32 hartid) +{ + if (sbi_hsm_hart_get_state(dom, hartid) == SBI_HART_STARTED) + return TRUE; + else + return FALSE; +} + +/** + * Get ulong HART mask for given HART base ID + * @param dom the domain to be used for output HART mask + * @param hbase the HART base ID + * @param out_hmask the output ulong HART mask + * @return 0 on success and SBI_Exxx (< 0) on failure + * Note: the output HART mask will be set to zero on failure as well. + */ +int sbi_hsm_hart_started_mask(const struct sbi_domain *dom, + ulong hbase, ulong *out_hmask) +{ + ulong i, hmask, dmask; + ulong hend = sbi_scratch_last_hartid() + 1; + + *out_hmask = 0; + if (hend <= hbase) + return SBI_EINVAL; + if (BITS_PER_LONG < (hend - hbase)) + hend = hbase + BITS_PER_LONG; + + dmask = sbi_domain_get_assigned_hartmask(dom, hbase); + for (i = hbase; i < hend; i++) { + hmask = 1UL << (i - hbase); + if ((dmask & hmask) && + (__sbi_hsm_hart_get_state(i) == SBI_HART_STARTED)) + *out_hmask |= hmask; + } + + return 0; +} + +void sbi_hsm_prepare_next_jump(struct sbi_scratch *scratch, u32 hartid) +{ + u32 oldstate; + struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, + hart_data_offset); + + oldstate = atomic_cmpxchg(&hdata->state, SBI_HART_STARTING, + SBI_HART_STARTED); + if (oldstate != SBI_HART_STARTING) + sbi_hart_hang(); +} + +static void sbi_hsm_hart_wait(struct sbi_scratch *scratch, u32 hartid) +{ + unsigned long saved_mie; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, + hart_data_offset); + /* Save MIE CSR */ + saved_mie = csr_read(CSR_MIE); + + /* Set MSIE bit to receive IPI */ + csr_set(CSR_MIE, MIP_MSIP); + + /* Wait for hart_add call*/ + while (atomic_read(&hdata->state) != SBI_HART_STARTING) { + wfi(); + }; + + /* Restore MIE CSR */ + csr_write(CSR_MIE, saved_mie); + + /* Clear current HART IPI */ + sbi_platform_ipi_clear(plat, hartid); +} + +int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot) +{ + u32 i; + struct sbi_scratch *rscratch; + struct sbi_hsm_data *hdata; + + if (cold_boot) { + hart_data_offset = sbi_scratch_alloc_offset(sizeof(*hdata), + "HART_DATA"); + if (!hart_data_offset) + return SBI_ENOMEM; + + /* Initialize hart state data for every hart */ + for (i = 0; i <= sbi_scratch_last_hartid(); i++) { + rscratch = sbi_hartid_to_scratch(i); + if (!rscratch) + continue; + + hdata = sbi_scratch_offset_ptr(rscratch, + hart_data_offset); + ATOMIC_INIT(&hdata->state, + (i == hartid) ? SBI_HART_STARTING : SBI_HART_STOPPED); + } + } else { + sbi_hsm_hart_wait(scratch, hartid); + } + + return 0; +} + +void __noreturn sbi_hsm_exit(struct sbi_scratch *scratch) +{ + u32 hstate; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, + hart_data_offset); + void (*jump_warmboot)(void) = (void (*)(void))scratch->warmboot_addr; + + hstate = atomic_cmpxchg(&hdata->state, SBI_HART_STOPPING, + SBI_HART_STOPPED); + if (hstate != SBI_HART_STOPPING) + goto fail_exit; + + if (sbi_platform_has_hart_hotplug(plat)) { + sbi_platform_hart_stop(plat); + /* It should never reach here */ + goto fail_exit; + } + + /** + * As platform is lacking support for hotplug, directly jump to warmboot + * and wait for interrupts in warmboot. We do it preemptively in order + * preserve the hart states and reuse the code path for hotplug. + */ + jump_warmboot(); + +fail_exit: + /* It should never reach here */ + sbi_printf("ERR: Failed stop hart [%u]\n", current_hartid()); + sbi_hart_hang(); +} + +int sbi_hsm_hart_start(struct sbi_scratch *scratch, + const struct sbi_domain *dom, + u32 hartid, ulong saddr, ulong smode, ulong priv) +{ + unsigned long init_count; + unsigned int hstate; + struct sbi_scratch *rscratch; + struct sbi_hsm_data *hdata; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + /* For now, we only allow start mode to be S-mode or U-mode. */ + if (smode != PRV_S && smode != PRV_U) + return SBI_EINVAL; + if (dom && !sbi_domain_is_assigned_hart(dom, hartid)) + return SBI_EINVAL; + if (dom && !sbi_domain_check_addr(dom, saddr, smode, + SBI_DOMAIN_EXECUTE)) + return SBI_EINVAL; + + rscratch = sbi_hartid_to_scratch(hartid); + if (!rscratch) + return SBI_EINVAL; + hdata = sbi_scratch_offset_ptr(rscratch, hart_data_offset); + hstate = atomic_cmpxchg(&hdata->state, SBI_HART_STOPPED, + SBI_HART_STARTING); + if (hstate == SBI_HART_STARTED) + return SBI_EALREADY; + + /** + * if a hart is already transition to start or stop, another start call + * is considered as invalid request. + */ + if (hstate != SBI_HART_STOPPED) + return SBI_EINVAL; + + init_count = sbi_init_count(hartid); + rscratch->next_arg1 = priv; + rscratch->next_addr = saddr; + rscratch->next_mode = smode; + + if (sbi_platform_has_hart_hotplug(plat) || + (sbi_platform_has_hart_secondary_boot(plat) && !init_count)) { + return sbi_platform_hart_start(plat, hartid, + scratch->warmboot_addr); + } else { + sbi_platform_ipi_send(plat, hartid); + } + + return 0; +} + +int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow) +{ + int oldstate; + u32 hartid = current_hartid(); + struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, + hart_data_offset); + + if (!sbi_hsm_hart_started(sbi_domain_thishart_ptr(), hartid)) + return SBI_EINVAL; + + oldstate = atomic_cmpxchg(&hdata->state, SBI_HART_STARTED, + SBI_HART_STOPPING); + if (oldstate != SBI_HART_STARTED) { + sbi_printf("%s: ERR: The hart is in invalid state [%u]\n", + __func__, oldstate); + return SBI_EDENIED; + } + + if (exitnow) + sbi_exit(scratch); + + return 0; +} diff --git a/roms/opensbi/lib/sbi/sbi_illegal_insn.c b/roms/opensbi/lib/sbi/sbi_illegal_insn.c new file mode 100644 index 000000000..9af3d24d7 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_illegal_insn.c @@ -0,0 +1,143 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_bitops.h> +#include <sbi/sbi_emulate_csr.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_illegal_insn.h> +#include <sbi/sbi_trap.h> +#include <sbi/sbi_unpriv.h> + +typedef int (*illegal_insn_func)(ulong insn, struct sbi_trap_regs *regs); + +static int truly_illegal_insn(ulong insn, struct sbi_trap_regs *regs) +{ + struct sbi_trap_info trap; + + trap.epc = regs->mepc; + trap.cause = CAUSE_ILLEGAL_INSTRUCTION; + trap.tval = insn; + trap.tval2 = 0; + trap.tinst = 0; + + return sbi_trap_redirect(regs, &trap); +} + +static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs) +{ + 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; + + /* TODO: Ensure that we got CSR read/write instruction */ + + if (sbi_emulate_csr_read(csr_num, regs, &csr_val)) + return truly_illegal_insn(insn, regs); + + 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, regs); + }; + + if (do_write && sbi_emulate_csr_write(csr_num, regs, new_csr_val)) + return truly_illegal_insn(insn, regs); + + 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(ulong insn, struct sbi_trap_regs *regs) +{ + struct sbi_trap_info uptrap; + + /* + * We only deal with 32-bit (or longer) illegal instructions. If we + * see instruction is zero OR instruction is 16-bit then we fetch and + * check the instruction encoding using unprivilege access. + * + * The program counter (PC) in RISC-V world is always 2-byte aligned + * so handling only 32-bit (or longer) illegal instructions also help + * the case where MTVAL CSR contains instruction address for illegal + * instruction trap. + */ + + if (unlikely((insn & 3) != 3)) { + insn = sbi_get_insn(regs->mepc, &uptrap); + if (uptrap.cause) { + uptrap.epc = regs->mepc; + return sbi_trap_redirect(regs, &uptrap); + } + if ((insn & 3) != 3) + return truly_illegal_insn(insn, regs); + } + + return illegal_insn_table[(insn & 0x7c) >> 2](insn, regs); +} diff --git a/roms/opensbi/lib/sbi/sbi_init.c b/roms/opensbi/lib/sbi/sbi_init.c new file mode 100644 index 000000000..0e824588e --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_init.c @@ -0,0 +1,475 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_atomic.h> +#include <sbi/riscv_barrier.h> +#include <sbi/riscv_locks.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_ecall.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_hartmask.h> +#include <sbi/sbi_hsm.h> +#include <sbi/sbi_ipi.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_system.h> +#include <sbi/sbi_string.h> +#include <sbi/sbi_timer.h> +#include <sbi/sbi_tlb.h> +#include <sbi/sbi_version.h> + +#define BANNER \ + " ____ _____ ____ _____\n" \ + " / __ \\ / ____| _ \\_ _|\n" \ + " | | | |_ __ ___ _ __ | (___ | |_) || |\n" \ + " | | | | '_ \\ / _ \\ '_ \\ \\___ \\| _ < | |\n" \ + " | |__| | |_) | __/ | | |____) | |_) || |_\n" \ + " \\____/| .__/ \\___|_| |_|_____/|____/_____|\n" \ + " | |\n" \ + " |_|\n\n" + +static void sbi_boot_print_banner(struct sbi_scratch *scratch) +{ + if (scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS) + return; + +#ifdef OPENSBI_VERSION_GIT + sbi_printf("\nOpenSBI %s\n", OPENSBI_VERSION_GIT); +#else + sbi_printf("\nOpenSBI v%d.%d\n", OPENSBI_VERSION_MAJOR, + OPENSBI_VERSION_MINOR); +#endif + + sbi_printf(BANNER); +} + +static void sbi_boot_print_general(struct sbi_scratch *scratch) +{ + char str[128]; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + if (scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS) + return; + + /* Platform details */ + sbi_printf("Platform Name : %s\n", + sbi_platform_name(plat)); + sbi_platform_get_features_str(plat, str, sizeof(str)); + sbi_printf("Platform Features : %s\n", str); + sbi_printf("Platform HART Count : %u\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)); + + /* SBI details */ + sbi_printf("Runtime SBI Version : %d.%d\n", + sbi_ecall_version_major(), sbi_ecall_version_minor()); + sbi_printf("\n"); +} + +static void sbi_boot_print_domains(struct sbi_scratch *scratch) +{ + if (scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS) + return; + + /* Domain details */ + sbi_domain_dump_all(" "); +} + +static void sbi_boot_print_hart(struct sbi_scratch *scratch, u32 hartid) +{ + int xlen; + char str[128]; + const struct sbi_domain *dom = sbi_domain_thishart_ptr(); + + if (scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS) + return; + + /* Determine MISA XLEN and MISA string */ + xlen = misa_xlen(); + if (xlen < 1) { + sbi_printf("Error %d getting MISA XLEN\n", xlen); + sbi_hart_hang(); + } + + /* Boot HART details */ + sbi_printf("Boot HART ID : %u\n", hartid); + sbi_printf("Boot HART Domain : %s\n", dom->name); + misa_string(xlen, str, sizeof(str)); + sbi_printf("Boot HART ISA : %s\n", str); + sbi_hart_get_features_str(scratch, str, sizeof(str)); + sbi_printf("Boot HART Features : %s\n", str); + sbi_printf("Boot HART PMP Count : %d\n", + sbi_hart_pmp_count(scratch)); + sbi_printf("Boot HART PMP Granularity : %lu\n", + sbi_hart_pmp_granularity(scratch)); + sbi_printf("Boot HART PMP Address Bits: %d\n", + sbi_hart_pmp_addrbits(scratch)); + sbi_printf("Boot HART MHPM Count : %d\n", + sbi_hart_mhpm_count(scratch)); + sbi_printf("Boot HART MHPM Count : %d\n", + sbi_hart_mhpm_count(scratch)); + sbi_hart_delegation_dump(scratch, "Boot HART ", " "); +} + +static spinlock_t coldboot_lock = SPIN_LOCK_INITIALIZER; +static struct sbi_hartmask coldboot_wait_hmask = { 0 }; + +static unsigned long coldboot_done; + +static void wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid) +{ + unsigned long saved_mie, cmip; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + /* Save MIE CSR */ + saved_mie = csr_read(CSR_MIE); + + /* Set MSIE bit to receive IPI */ + csr_set(CSR_MIE, MIP_MSIP); + + /* Acquire coldboot lock */ + spin_lock(&coldboot_lock); + + /* Mark current HART as waiting */ + sbi_hartmask_set_hart(hartid, &coldboot_wait_hmask); + + /* Release coldboot lock */ + spin_unlock(&coldboot_lock); + + /* Wait for coldboot to finish using WFI */ + while (!__smp_load_acquire(&coldboot_done)) { + do { + wfi(); + cmip = csr_read(CSR_MIP); + } while (!(cmip & MIP_MSIP)); + }; + + /* Acquire coldboot lock */ + spin_lock(&coldboot_lock); + + /* Unmark current HART as waiting */ + sbi_hartmask_clear_hart(hartid, &coldboot_wait_hmask); + + /* Release coldboot lock */ + spin_unlock(&coldboot_lock); + + /* Restore MIE CSR */ + csr_write(CSR_MIE, saved_mie); + + /* Clear current HART IPI */ + sbi_platform_ipi_clear(plat, hartid); +} + +static void wake_coldboot_harts(struct sbi_scratch *scratch, u32 hartid) +{ + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + /* Mark coldboot done */ + __smp_store_release(&coldboot_done, 1); + + /* Acquire coldboot lock */ + spin_lock(&coldboot_lock); + + /* Send an IPI to all HARTs waiting for coldboot */ + for (int i = 0; i <= sbi_scratch_last_hartid(); i++) { + if ((i != hartid) && + sbi_hartmask_test_hart(i, &coldboot_wait_hmask)) + sbi_platform_ipi_send(plat, i); + } + + /* Release coldboot lock */ + spin_unlock(&coldboot_lock); +} + +static unsigned long init_count_offset; + +static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) +{ + int rc; + unsigned long *init_count; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + /* Note: This has to be first thing in coldboot init sequence */ + rc = sbi_scratch_init(scratch); + if (rc) + sbi_hart_hang(); + + /* Note: This has to be second thing in coldboot init sequence */ + rc = sbi_domain_init(scratch, hartid); + if (rc) + sbi_hart_hang(); + + init_count_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__, + "INIT_COUNT"); + if (!init_count_offset) + sbi_hart_hang(); + + rc = sbi_hsm_init(scratch, hartid, TRUE); + if (rc) + sbi_hart_hang(); + + rc = sbi_platform_early_init(plat, TRUE); + if (rc) + sbi_hart_hang(); + + rc = sbi_hart_init(scratch, TRUE); + if (rc) + sbi_hart_hang(); + + rc = sbi_console_init(scratch); + if (rc) + sbi_hart_hang(); + + sbi_boot_print_banner(scratch); + + rc = sbi_platform_irqchip_init(plat, TRUE); + if (rc) { + sbi_printf("%s: platform irqchip init failed (error %d)\n", + __func__, rc); + sbi_hart_hang(); + } + + rc = sbi_ipi_init(scratch, TRUE); + if (rc) { + sbi_printf("%s: ipi init failed (error %d)\n", __func__, rc); + sbi_hart_hang(); + } + + rc = sbi_tlb_init(scratch, TRUE); + if (rc) { + sbi_printf("%s: tlb init failed (error %d)\n", __func__, rc); + sbi_hart_hang(); + } + + rc = sbi_timer_init(scratch, TRUE); + if (rc) { + sbi_printf("%s: timer init failed (error %d)\n", __func__, rc); + sbi_hart_hang(); + } + + rc = sbi_ecall_init(); + if (rc) { + sbi_printf("%s: ecall init failed (error %d)\n", __func__, rc); + sbi_hart_hang(); + } + + sbi_boot_print_general(scratch); + + /* + * Note: Finalize domains after HSM initialization so that we + * can startup non-root domains. + * Note: Finalize domains before HART PMP configuration so + * that we use correct domain for configuring PMP. + */ + rc = sbi_domain_finalize(scratch, hartid); + if (rc) { + sbi_printf("%s: domain finalize failed (error %d)\n", + __func__, rc); + sbi_hart_hang(); + } + + sbi_boot_print_domains(scratch); + + rc = sbi_hart_pmp_configure(scratch); + if (rc) { + sbi_printf("%s: PMP configure failed (error %d)\n", + __func__, rc); + sbi_hart_hang(); + } + + /* + * Note: Platform final initialization should be last so that + * it sees correct domain assignment and PMP configuration. + */ + rc = sbi_platform_final_init(plat, TRUE); + if (rc) { + sbi_printf("%s: platform final init failed (error %d)\n", + __func__, rc); + sbi_hart_hang(); + } + + sbi_boot_print_hart(scratch, hartid); + + wake_coldboot_harts(scratch, hartid); + + init_count = sbi_scratch_offset_ptr(scratch, init_count_offset); + (*init_count)++; + + sbi_hsm_prepare_next_jump(scratch, hartid); + sbi_hart_switch_mode(hartid, scratch->next_arg1, scratch->next_addr, + scratch->next_mode, FALSE); +} + +static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid) +{ + int rc; + unsigned long *init_count; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + wait_for_coldboot(scratch, hartid); + + if (!init_count_offset) + sbi_hart_hang(); + + rc = sbi_hsm_init(scratch, hartid, FALSE); + if (rc) + sbi_hart_hang(); + + rc = sbi_platform_early_init(plat, FALSE); + if (rc) + sbi_hart_hang(); + + rc = sbi_hart_init(scratch, FALSE); + if (rc) + sbi_hart_hang(); + + rc = sbi_platform_irqchip_init(plat, FALSE); + if (rc) + sbi_hart_hang(); + + rc = sbi_ipi_init(scratch, FALSE); + if (rc) + sbi_hart_hang(); + + rc = sbi_tlb_init(scratch, FALSE); + if (rc) + sbi_hart_hang(); + + rc = sbi_timer_init(scratch, FALSE); + if (rc) + sbi_hart_hang(); + + rc = sbi_hart_pmp_configure(scratch); + if (rc) + sbi_hart_hang(); + + rc = sbi_platform_final_init(plat, FALSE); + if (rc) + sbi_hart_hang(); + + init_count = sbi_scratch_offset_ptr(scratch, init_count_offset); + (*init_count)++; + + sbi_hsm_prepare_next_jump(scratch, hartid); + sbi_hart_switch_mode(hartid, scratch->next_arg1, + scratch->next_addr, + scratch->next_mode, FALSE); +} + +static atomic_t coldboot_lottery = ATOMIC_INITIALIZER(0); + +/** + * Initialize OpenSBI library for current HART and jump to next + * booting stage. + * + * The function expects following: + * 1. The 'mscratch' CSR is pointing to sbi_scratch of current HART + * 2. Stack pointer (SP) is setup for current HART + * 3. Interrupts are disabled in MSTATUS CSR + * 4. All interrupts are disabled in MIE CSR + * + * @param scratch pointer to sbi_scratch of current HART + */ +void __noreturn sbi_init(struct sbi_scratch *scratch) +{ + bool next_mode_supported = FALSE; + bool coldboot = FALSE; + u32 hartid = current_hartid(); + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + if ((SBI_HARTMASK_MAX_BITS <= hartid) || + sbi_platform_hart_invalid(plat, hartid)) + sbi_hart_hang(); + + switch (scratch->next_mode) { + case PRV_M: + next_mode_supported = TRUE; + break; + case PRV_S: + if (misa_extension('S')) + next_mode_supported = TRUE; + break; + case PRV_U: + if (misa_extension('U')) + next_mode_supported = TRUE; + break; + default: + sbi_hart_hang(); + } + + /* + * Only the HART supporting privilege mode specified in the + * scratch->next_mode should be allowed to become the coldboot + * HART because the coldboot HART will be directly jumping to + * the next booting stage. + * + * We use a lottery mechanism to select coldboot HART among + * HARTs which satisfy above condition. + */ + + if (next_mode_supported && atomic_xchg(&coldboot_lottery, 1) == 0) + coldboot = TRUE; + + if (coldboot) + init_coldboot(scratch, hartid); + else + init_warmboot(scratch, hartid); +} + +unsigned long sbi_init_count(u32 hartid) +{ + struct sbi_scratch *scratch; + unsigned long *init_count; + + if (!init_count_offset) + return 0; + + scratch = sbi_hartid_to_scratch(hartid); + if (!scratch) + return 0; + + init_count = sbi_scratch_offset_ptr(scratch, init_count_offset); + + return *init_count; +} + +/** + * Exit OpenSBI library for current HART and stop HART + * + * The function expects following: + * 1. The 'mscratch' CSR is pointing to sbi_scratch of current HART + * 2. Stack pointer (SP) is setup for current HART + * + * @param scratch pointer to sbi_scratch of current HART + */ +void __noreturn sbi_exit(struct sbi_scratch *scratch) +{ + u32 hartid = current_hartid(); + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + if (sbi_platform_hart_invalid(plat, hartid)) + sbi_hart_hang(); + + sbi_platform_early_exit(plat); + + sbi_timer_exit(scratch); + + sbi_ipi_exit(scratch); + + sbi_platform_irqchip_exit(plat); + + sbi_platform_final_exit(plat); + + sbi_hsm_exit(scratch); +} diff --git a/roms/opensbi/lib/sbi/sbi_ipi.c b/roms/opensbi/lib/sbi/sbi_ipi.c new file mode 100644 index 000000000..43478328b --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_ipi.c @@ -0,0 +1,254 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * Nick Kossifidis <mick@ics.forth.gr> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_atomic.h> +#include <sbi/riscv_barrier.h> +#include <sbi/sbi_bitops.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_hsm.h> +#include <sbi/sbi_init.h> +#include <sbi/sbi_ipi.h> +#include <sbi/sbi_platform.h> + +struct sbi_ipi_data { + unsigned long ipi_type; +}; + +static unsigned long ipi_data_off; + +static const struct sbi_ipi_event_ops *ipi_ops_array[SBI_IPI_EVENT_MAX]; + +static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid, + u32 event, void *data) +{ + int ret; + struct sbi_scratch *remote_scratch = NULL; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + struct sbi_ipi_data *ipi_data; + const struct sbi_ipi_event_ops *ipi_ops; + + if ((SBI_IPI_EVENT_MAX <= event) || + !ipi_ops_array[event]) + return SBI_EINVAL; + ipi_ops = ipi_ops_array[event]; + + remote_scratch = sbi_hartid_to_scratch(remote_hartid); + if (!remote_scratch) + return SBI_EINVAL; + + ipi_data = sbi_scratch_offset_ptr(remote_scratch, ipi_data_off); + + if (ipi_ops->update) { + ret = ipi_ops->update(scratch, remote_scratch, + remote_hartid, data); + if (ret < 0) + return ret; + } + + /* + * Set IPI type on remote hart's scratch area and + * trigger the interrupt + */ + atomic_raw_set_bit(event, &ipi_data->ipi_type); + smp_wmb(); + sbi_platform_ipi_send(plat, remote_hartid); + + if (ipi_ops->sync) + ipi_ops->sync(scratch); + + return 0; +} + +/** + * As this this function only handlers scalar values of hart mask, it must be + * set to all online harts if the intention is to send IPIs to all the harts. + * If hmask is zero, no IPIs will be sent. + */ +int sbi_ipi_send_many(ulong hmask, ulong hbase, u32 event, void *data) +{ + int rc; + ulong i, m; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + if (hbase != -1UL) { + rc = sbi_hsm_hart_started_mask(dom, hbase, &m); + if (rc) + return rc; + m &= hmask; + + /* Send IPIs */ + for (i = hbase; m; i++, m >>= 1) { + if (m & 1UL) + sbi_ipi_send(scratch, i, event, data); + } + } else { + hbase = 0; + while (!sbi_hsm_hart_started_mask(dom, hbase, &m)) { + /* Send IPIs */ + for (i = hbase; m; i++, m >>= 1) { + if (m & 1UL) + sbi_ipi_send(scratch, i, event, data); + } + hbase += BITS_PER_LONG; + } + } + + return 0; +} + +int sbi_ipi_event_create(const struct sbi_ipi_event_ops *ops) +{ + int i, ret = SBI_ENOSPC; + + if (!ops || !ops->process) + return SBI_EINVAL; + + for (i = 0; i < SBI_IPI_EVENT_MAX; i++) { + if (!ipi_ops_array[i]) { + ret = i; + ipi_ops_array[i] = ops; + break; + } + } + + return ret; +} + +void sbi_ipi_event_destroy(u32 event) +{ + if (SBI_IPI_EVENT_MAX <= event) + return; + + ipi_ops_array[event] = NULL; +} + +static void sbi_ipi_process_smode(struct sbi_scratch *scratch) +{ + csr_set(CSR_MIP, MIP_SSIP); +} + +static struct sbi_ipi_event_ops ipi_smode_ops = { + .name = "IPI_SMODE", + .process = sbi_ipi_process_smode, +}; + +static u32 ipi_smode_event = SBI_IPI_EVENT_MAX; + +int sbi_ipi_send_smode(ulong hmask, ulong hbase) +{ + return sbi_ipi_send_many(hmask, hbase, ipi_smode_event, NULL); +} + +void sbi_ipi_clear_smode(void) +{ + csr_clear(CSR_MIP, MIP_SSIP); +} + +static void sbi_ipi_process_halt(struct sbi_scratch *scratch) +{ + sbi_hsm_hart_stop(scratch, TRUE); +} + +static struct sbi_ipi_event_ops ipi_halt_ops = { + .name = "IPI_HALT", + .process = sbi_ipi_process_halt, +}; + +static u32 ipi_halt_event = SBI_IPI_EVENT_MAX; + +int sbi_ipi_send_halt(ulong hmask, ulong hbase) +{ + return sbi_ipi_send_many(hmask, hbase, ipi_halt_event, NULL); +} + +void sbi_ipi_process(void) +{ + unsigned long ipi_type; + unsigned int ipi_event; + const struct sbi_ipi_event_ops *ipi_ops; + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + struct sbi_ipi_data *ipi_data = + sbi_scratch_offset_ptr(scratch, ipi_data_off); + + u32 hartid = current_hartid(); + sbi_platform_ipi_clear(plat, hartid); + + ipi_type = atomic_raw_xchg_ulong(&ipi_data->ipi_type, 0); + ipi_event = 0; + while (ipi_type) { + if (!(ipi_type & 1UL)) + goto skip; + + ipi_ops = ipi_ops_array[ipi_event]; + if (ipi_ops && ipi_ops->process) + ipi_ops->process(scratch); + +skip: + ipi_type = ipi_type >> 1; + ipi_event++; + }; +} + +int sbi_ipi_init(struct sbi_scratch *scratch, bool cold_boot) +{ + int ret; + struct sbi_ipi_data *ipi_data; + + if (cold_boot) { + ipi_data_off = sbi_scratch_alloc_offset(sizeof(*ipi_data), + "IPI_DATA"); + if (!ipi_data_off) + return SBI_ENOMEM; + ret = sbi_ipi_event_create(&ipi_smode_ops); + if (ret < 0) + return ret; + ipi_smode_event = ret; + ret = sbi_ipi_event_create(&ipi_halt_ops); + if (ret < 0) + return ret; + ipi_halt_event = ret; + } else { + if (!ipi_data_off) + return SBI_ENOMEM; + if (SBI_IPI_EVENT_MAX <= ipi_smode_event || + SBI_IPI_EVENT_MAX <= ipi_halt_event) + return SBI_ENOSPC; + } + + ipi_data = sbi_scratch_offset_ptr(scratch, ipi_data_off); + ipi_data->ipi_type = 0x00; + + /* Platform init */ + ret = sbi_platform_ipi_init(sbi_platform_ptr(scratch), cold_boot); + if (ret) + return ret; + + /* Enable software interrupts */ + csr_set(CSR_MIE, MIP_MSIP); + + return 0; +} + +void sbi_ipi_exit(struct sbi_scratch *scratch) +{ + /* Disable software interrupts */ + csr_clear(CSR_MIE, MIP_MSIP); + + /* Process pending IPIs */ + sbi_ipi_process(); + + /* Platform exit */ + sbi_platform_ipi_exit(sbi_platform_ptr(scratch)); +} diff --git a/roms/opensbi/lib/sbi/sbi_math.c b/roms/opensbi/lib/sbi/sbi_math.c new file mode 100644 index 000000000..8ba0831d8 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_math.c @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Common helper functions used across OpenSBI project. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +unsigned long log2roundup(unsigned long x) +{ + unsigned long ret = 0; + + while (ret < __riscv_xlen) { + if (x <= (1UL << ret)) + break; + ret++; + } + + return ret; +} diff --git a/roms/opensbi/lib/sbi/sbi_misaligned_ldst.c b/roms/opensbi/lib/sbi/sbi_misaligned_ldst.c new file mode 100644 index 000000000..5057cb5ec --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_misaligned_ldst.c @@ -0,0 +1,243 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/riscv_fp.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_misaligned_ldst.h> +#include <sbi/sbi_trap.h> +#include <sbi/sbi_unpriv.h> + +union reg_data { + u8 data_bytes[8]; + ulong data_ulong; + u64 data_u64; +}; + +int sbi_misaligned_load_handler(ulong addr, ulong tval2, ulong tinst, + struct sbi_trap_regs *regs) +{ + ulong insn, insn_len; + union reg_data val; + struct sbi_trap_info uptrap; + int i, fp = 0, shift = 0, len = 0; + + if (tinst & 0x1) { + /* + * Bit[0] == 1 implies trapped instruction value is + * transformed instruction or custom instruction. + */ + insn = tinst | INSN_16BIT_MASK; + insn_len = (tinst & 0x2) ? INSN_LEN(insn) : 2; + } else { + /* + * Bit[0] == 0 implies trapped instruction value is + * zero or special value. + */ + insn = sbi_get_insn(regs->mepc, &uptrap); + if (uptrap.cause) { + uptrap.epc = regs->mepc; + return sbi_trap_redirect(regs, &uptrap); + } + insn_len = INSN_LEN(insn); + } + + if ((insn & INSN_MASK_LW) == INSN_MATCH_LW) { + len = 4; + shift = 8 * (sizeof(ulong) - len); +#if __riscv_xlen == 64 + } else if ((insn & INSN_MASK_LD) == INSN_MATCH_LD) { + len = 8; + shift = 8 * (sizeof(ulong) - len); + } else if ((insn & INSN_MASK_LWU) == INSN_MATCH_LWU) { + len = 4; +#endif +#ifdef __riscv_flen + } else if ((insn & INSN_MASK_FLD) == INSN_MATCH_FLD) { + fp = 1; + len = 8; + } else if ((insn & INSN_MASK_FLW) == INSN_MATCH_FLW) { + fp = 1; + len = 4; +#endif + } else if ((insn & INSN_MASK_LH) == INSN_MATCH_LH) { + len = 2; + shift = 8 * (sizeof(ulong) - len); + } else if ((insn & INSN_MASK_LHU) == INSN_MATCH_LHU) { + len = 2; +#if __riscv_xlen >= 64 + } else if ((insn & INSN_MASK_C_LD) == INSN_MATCH_C_LD) { + len = 8; + shift = 8 * (sizeof(ulong) - len); + insn = RVC_RS2S(insn) << SH_RD; + } else if ((insn & INSN_MASK_C_LDSP) == INSN_MATCH_C_LDSP && + ((insn >> SH_RD) & 0x1f)) { + len = 8; + shift = 8 * (sizeof(ulong) - len); +#endif + } else if ((insn & INSN_MASK_C_LW) == INSN_MATCH_C_LW) { + len = 4; + shift = 8 * (sizeof(ulong) - len); + insn = RVC_RS2S(insn) << SH_RD; + } else if ((insn & INSN_MASK_C_LWSP) == INSN_MATCH_C_LWSP && + ((insn >> SH_RD) & 0x1f)) { + len = 4; + shift = 8 * (sizeof(ulong) - len); +#ifdef __riscv_flen + } else if ((insn & INSN_MASK_C_FLD) == INSN_MATCH_C_FLD) { + fp = 1; + len = 8; + insn = RVC_RS2S(insn) << SH_RD; + } else if ((insn & INSN_MASK_C_FLDSP) == INSN_MATCH_C_FLDSP) { + fp = 1; + len = 8; +#if __riscv_xlen == 32 + } else if ((insn & INSN_MASK_C_FLW) == INSN_MATCH_C_FLW) { + fp = 1; + len = 4; + insn = RVC_RS2S(insn) << SH_RD; + } else if ((insn & INSN_MASK_C_FLWSP) == INSN_MATCH_C_FLWSP) { + fp = 1; + len = 4; +#endif +#endif + } else { + uptrap.epc = regs->mepc; + uptrap.cause = CAUSE_MISALIGNED_LOAD; + uptrap.tval = addr; + uptrap.tval2 = tval2; + uptrap.tinst = tinst; + return sbi_trap_redirect(regs, &uptrap); + } + + val.data_u64 = 0; + for (i = 0; i < len; i++) { + val.data_bytes[i] = sbi_load_u8((void *)(addr + i), + &uptrap); + if (uptrap.cause) { + uptrap.epc = regs->mepc; + return sbi_trap_redirect(regs, &uptrap); + } + } + + if (!fp) + SET_RD(insn, regs, ((long)(val.data_ulong << shift)) >> shift); +#ifdef __riscv_flen + else if (len == 8) + SET_F64_RD(insn, regs, val.data_u64); + else + SET_F32_RD(insn, regs, val.data_ulong); +#endif + + regs->mepc += insn_len; + + return 0; +} + +int sbi_misaligned_store_handler(ulong addr, ulong tval2, ulong tinst, + struct sbi_trap_regs *regs) +{ + ulong insn, insn_len; + union reg_data val; + struct sbi_trap_info uptrap; + int i, len = 0; + + if (tinst & 0x1) { + /* + * Bit[0] == 1 implies trapped instruction value is + * transformed instruction or custom instruction. + */ + insn = tinst | INSN_16BIT_MASK; + insn_len = (tinst & 0x2) ? INSN_LEN(insn) : 2; + } else { + /* + * Bit[0] == 0 implies trapped instruction value is + * zero or special value. + */ + insn = sbi_get_insn(regs->mepc, &uptrap); + if (uptrap.cause) { + uptrap.epc = regs->mepc; + return sbi_trap_redirect(regs, &uptrap); + } + insn_len = INSN_LEN(insn); + } + + val.data_ulong = GET_RS2(insn, regs); + + if ((insn & INSN_MASK_SW) == INSN_MATCH_SW) { + len = 4; +#if __riscv_xlen == 64 + } else if ((insn & INSN_MASK_SD) == INSN_MATCH_SD) { + len = 8; +#endif +#ifdef __riscv_flen + } else if ((insn & INSN_MASK_FSD) == INSN_MATCH_FSD) { + len = 8; + val.data_u64 = GET_F64_RS2(insn, regs); + } else if ((insn & INSN_MASK_FSW) == INSN_MATCH_FSW) { + len = 4; + val.data_ulong = GET_F32_RS2(insn, regs); +#endif + } else if ((insn & INSN_MASK_SH) == INSN_MATCH_SH) { + len = 2; +#if __riscv_xlen >= 64 + } else if ((insn & INSN_MASK_C_SD) == INSN_MATCH_C_SD) { + len = 8; + val.data_ulong = GET_RS2S(insn, regs); + } else if ((insn & INSN_MASK_C_SDSP) == INSN_MATCH_C_SDSP && + ((insn >> SH_RD) & 0x1f)) { + len = 8; + val.data_ulong = GET_RS2C(insn, regs); +#endif + } else if ((insn & INSN_MASK_C_SW) == INSN_MATCH_C_SW) { + len = 4; + val.data_ulong = GET_RS2S(insn, regs); + } else if ((insn & INSN_MASK_C_SWSP) == INSN_MATCH_C_SWSP && + ((insn >> SH_RD) & 0x1f)) { + len = 4; + val.data_ulong = GET_RS2C(insn, regs); +#ifdef __riscv_flen + } else if ((insn & INSN_MASK_C_FSD) == INSN_MATCH_C_FSD) { + len = 8; + val.data_u64 = GET_F64_RS2S(insn, regs); + } else if ((insn & INSN_MASK_C_FSDSP) == INSN_MATCH_C_FSDSP) { + len = 8; + val.data_u64 = GET_F64_RS2C(insn, regs); +#if __riscv_xlen == 32 + } else if ((insn & INSN_MASK_C_FSW) == INSN_MATCH_C_FSW) { + len = 4; + val.data_ulong = GET_F32_RS2S(insn, regs); + } else if ((insn & INSN_MASK_C_FSWSP) == INSN_MATCH_C_FSWSP) { + len = 4; + val.data_ulong = GET_F32_RS2C(insn, regs); +#endif +#endif + } else { + uptrap.epc = regs->mepc; + uptrap.cause = CAUSE_MISALIGNED_STORE; + uptrap.tval = addr; + uptrap.tval2 = tval2; + uptrap.tinst = tinst; + return sbi_trap_redirect(regs, &uptrap); + } + + for (i = 0; i < len; i++) { + sbi_store_u8((void *)(addr + i), val.data_bytes[i], + &uptrap); + if (uptrap.cause) { + uptrap.epc = regs->mepc; + return sbi_trap_redirect(regs, &uptrap); + } + } + + regs->mepc += insn_len; + + return 0; +} diff --git a/roms/opensbi/lib/sbi/sbi_platform.c b/roms/opensbi/lib/sbi/sbi_platform.c new file mode 100644 index 000000000..568d95661 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_platform.c @@ -0,0 +1,90 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +#include <sbi/sbi_console.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_string.h> + +static inline char *sbi_platform_feature_id2string(unsigned long feature) +{ + char *fstr = NULL; + + if (!feature) + return NULL; + + switch (feature) { + case SBI_PLATFORM_HAS_TIMER_VALUE: + fstr = "timer"; + break; + case SBI_PLATFORM_HAS_HART_HOTPLUG: + fstr = "hotplug"; + break; + case SBI_PLATFORM_HAS_MFAULTS_DELEGATION: + fstr = "mfdeleg"; + break; + case SBI_PLATFORM_HAS_HART_SECONDARY_BOOT: + fstr = "sec_boot"; + break; + default: + break; + } + + return fstr; +} + +void sbi_platform_get_features_str(const struct sbi_platform *plat, + char *features_str, int nfstr) +{ + unsigned long features, feat = 1UL; + char *temp; + int offset = 0; + + if (!plat || !features_str || !nfstr) + return; + sbi_memset(features_str, 0, nfstr); + + features = sbi_platform_get_features(plat); + if (!features) + goto done; + + do { + if (features & feat) { + temp = sbi_platform_feature_id2string(feat); + if (temp) { + sbi_snprintf(features_str + offset, nfstr, + "%s,", temp); + offset = offset + sbi_strlen(temp) + 1; + } + } + feat = feat << 1; + } while (feat <= SBI_PLATFORM_HAS_LAST_FEATURE); + +done: + if (offset) + features_str[offset - 1] = '\0'; + else + sbi_strncpy(features_str, "none", nfstr); +} + +u32 sbi_platform_hart_index(const struct sbi_platform *plat, u32 hartid) +{ + u32 i; + + if (!plat) + return -1U; + if (plat->hart_index2id) { + for (i = 0; i < plat->hart_count; i++) { + if (plat->hart_index2id[i] == hartid) + return i; + } + return -1U; + } + + return hartid; +} diff --git a/roms/opensbi/lib/sbi/sbi_scratch.c b/roms/opensbi/lib/sbi/sbi_scratch.c new file mode 100644 index 000000000..96bae5be4 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_scratch.c @@ -0,0 +1,99 @@ + /* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_locks.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_hartmask.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_string.h> + +u32 last_hartid_having_scratch = SBI_HARTMASK_MAX_BITS; +struct sbi_scratch *hartid_to_scratch_table[SBI_HARTMASK_MAX_BITS] = { 0 }; + +static spinlock_t extra_lock = SPIN_LOCK_INITIALIZER; +static unsigned long extra_offset = SBI_SCRATCH_EXTRA_SPACE_OFFSET; + +typedef struct sbi_scratch *(*hartid2scratch)(ulong hartid, ulong hartindex); + +int sbi_scratch_init(struct sbi_scratch *scratch) +{ + u32 i; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + for (i = 0; i < SBI_HARTMASK_MAX_BITS; i++) { + if (sbi_platform_hart_invalid(plat, i)) + continue; + hartid_to_scratch_table[i] = + ((hartid2scratch)scratch->hartid_to_scratch)(i, + sbi_platform_hart_index(plat, i)); + if (hartid_to_scratch_table[i]) + last_hartid_having_scratch = i; + } + + return 0; +} + +unsigned long sbi_scratch_alloc_offset(unsigned long size, const char *owner) +{ + u32 i; + void *ptr; + unsigned long ret = 0; + struct sbi_scratch *rscratch; + + /* + * We have a simple brain-dead allocator which never expects + * anything to be free-ed hence it keeps incrementing the + * next allocation offset until it runs-out of space. + * + * In future, we will have more sophisticated allocator which + * will allow us to re-claim free-ed space. + */ + + if (!size) + return 0; + + if (size & (__SIZEOF_POINTER__ - 1)) + size = (size & ~(__SIZEOF_POINTER__ - 1)) + __SIZEOF_POINTER__; + + spin_lock(&extra_lock); + + if (SBI_SCRATCH_SIZE < (extra_offset + size)) + goto done; + + ret = extra_offset; + extra_offset += size; + +done: + spin_unlock(&extra_lock); + + if (ret) { + for (i = 0; i < sbi_scratch_last_hartid(); i++) { + rscratch = sbi_hartid_to_scratch(i); + if (!rscratch) + continue; + ptr = sbi_scratch_offset_ptr(rscratch, ret); + sbi_memset(ptr, 0, size); + } + } + + return ret; +} + +void sbi_scratch_free_offset(unsigned long offset) +{ + if ((offset < SBI_SCRATCH_EXTRA_SPACE_OFFSET) || + (SBI_SCRATCH_SIZE <= offset)) + return; + + /* + * We don't actually free-up because it's a simple + * brain-dead allocator. + */ +} diff --git a/roms/opensbi/lib/sbi/sbi_string.c b/roms/opensbi/lib/sbi/sbi_string.c new file mode 100644 index 000000000..7805ba4ad --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_string.c @@ -0,0 +1,188 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +/* + * Simple libc functions. These are not optimized at all and might have some + * bugs as well. Use any optimized routines from newlib or glibc if required. + */ + +#include <sbi/sbi_string.h> + +/* + Provides sbi_strcmp for the completeness of supporting string functions. + it is not recommended to use sbi_strcmp() but use sbi_strncmp instead. +*/ +int sbi_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 sbi_strncmp(const char *a, const char *b, size_t count) +{ + /* search first diff or end of string */ + for (; count > 0 && *a == *b && *a != '\0'; a++, b++, count--) + ; + + return *a - *b; +} + +size_t sbi_strlen(const char *str) +{ + unsigned long ret = 0; + + while (*str != '\0') { + ret++; + str++; + } + + return ret; +} + +size_t sbi_strnlen(const char *str, size_t count) +{ + unsigned long ret = 0; + + while (*str != '\0' && ret < count) { + ret++; + str++; + count--; + } + + return ret; +} + +char *sbi_strcpy(char *dest, const char *src) +{ + char *ret = dest; + + while (*src != '\0') { + *dest++ = *src++; + } + + return ret; +} + +char *sbi_strncpy(char *dest, const char *src, size_t count) +{ + char *ret = dest; + + while (count-- && *src != '\0') { + *dest++ = *src++; + } + + return ret; +} + +char *sbi_strchr(const char *s, int c) +{ + while (*s != '\0' && *s != (char)c) + s++; + + if (*s == '\0') + return NULL; + else + return (char *)s; +} + +char *sbi_strrchr(const char *s, int c) +{ + const char *last = s + sbi_strlen(s); + + while (last > s && *last != (char)c) + last--; + + if (*last != (char)c) + return NULL; + else + return (char *)last; +} +void *sbi_memset(void *s, int c, size_t count) +{ + char *temp = s; + + while (count > 0) { + count--; + *temp++ = c; + } + + return s; +} + +void *sbi_memcpy(void *dest, const void *src, size_t count) +{ + char *temp1 = dest; + const char *temp2 = src; + + while (count > 0) { + *temp1++ = *temp2++; + count--; + } + + return dest; +} + +void *sbi_memmove(void *dest, const void *src, size_t count) +{ + char *temp1 = (char *)dest; + const char *temp2 = (char *)src; + + if (src == dest) + return dest; + + if (dest < src) { + while (count > 0) { + *temp1++ = *temp2++; + count--; + } + } else { + temp1 = dest + count - 1; + temp2 = src + count - 1; + + while (count > 0) { + *temp1-- = *temp2--; + count--; + } + } + + return dest; +} + +int sbi_memcmp(const void *s1, const void *s2, size_t count) +{ + const char *temp1 = s1; + const char *temp2 = s2; + + for (; count > 0 && (*temp1 == *temp2); count--) { + temp1++; + temp2++; + } + + if (count > 0) + return *(unsigned char *)temp1 - *(unsigned char *)temp2; + else + return 0; +} + +void *sbi_memchr(const void *s, int c, size_t count) +{ + const unsigned char *temp = s; + + while (count > 0) { + if ((unsigned char)c == *temp++) { + return (void *)(temp - 1); + } + count--; + } + + return NULL; +} diff --git a/roms/opensbi/lib/sbi/sbi_system.c b/roms/opensbi/lib/sbi/sbi_system.c new file mode 100644 index 000000000..08a8b47c7 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_system.c @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + * Nick Kossifidis <mick@ics.forth.gr> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/sbi_bitops.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_hsm.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_system.h> +#include <sbi/sbi_ipi.h> +#include <sbi/sbi_init.h> + +bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason) +{ + if (sbi_platform_system_reset_check(sbi_platform_thishart_ptr(), + reset_type, reset_reason)) + return TRUE; + + return FALSE; +} + +void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason) +{ + ulong hbase = 0, hmask; + u32 cur_hartid = current_hartid(); + struct sbi_domain *dom = sbi_domain_thishart_ptr(); + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + /* Send HALT IPI to every hart other than the current hart */ + while (!sbi_hsm_hart_started_mask(dom, hbase, &hmask)) { + if (hbase <= cur_hartid) + hmask &= ~(1UL << (cur_hartid - hbase)); + if (hmask) + sbi_ipi_send_halt(hmask, hbase); + hbase += BITS_PER_LONG; + } + + /* Stop current HART */ + sbi_hsm_hart_stop(scratch, FALSE); + + /* Platform specific reset if domain allowed system reset */ + if (dom->system_reset_allowed) + sbi_platform_system_reset(sbi_platform_ptr(scratch), + reset_type, reset_reason); + + /* If platform specific reset did not work then do sbi_exit() */ + sbi_exit(scratch); +} diff --git a/roms/opensbi/lib/sbi/sbi_timer.c b/roms/opensbi/lib/sbi/sbi_timer.c new file mode 100644 index 000000000..b571b1740 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_timer.c @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_timer.h> + +static unsigned long time_delta_off; +static u64 (*get_time_val)(const struct sbi_platform *plat); + +#if __riscv_xlen == 32 +static u64 get_ticks(const struct sbi_platform *plat) +{ + 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 +static u64 get_ticks(const struct sbi_platform *plat) +{ + unsigned long n; + + __asm__ __volatile__("rdtime %0" : "=r"(n)); + return n; +} +#endif + +u64 sbi_timer_value(void) +{ + return get_time_val(sbi_platform_thishart_ptr()); +} + +u64 sbi_timer_virt_value(void) +{ + u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), + time_delta_off); + + return sbi_timer_value() + *time_delta; +} + +u64 sbi_timer_get_delta(void) +{ + u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), + time_delta_off); + + return *time_delta; +} + +void sbi_timer_set_delta(ulong delta) +{ + u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), + time_delta_off); + + *time_delta = (u64)delta; +} + +void sbi_timer_set_delta_upper(ulong delta_upper) +{ + u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), + time_delta_off); + + *time_delta &= 0xffffffffULL; + *time_delta |= ((u64)delta_upper << 32); +} + +void sbi_timer_event_start(u64 next_event) +{ + sbi_platform_timer_event_start(sbi_platform_thishart_ptr(), next_event); + csr_clear(CSR_MIP, MIP_STIP); + csr_set(CSR_MIE, MIP_MTIP); +} + +void sbi_timer_process(void) +{ + csr_clear(CSR_MIE, MIP_MTIP); + csr_set(CSR_MIP, MIP_STIP); +} + +int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot) +{ + u64 *time_delta; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + int ret; + + if (cold_boot) { + time_delta_off = sbi_scratch_alloc_offset(sizeof(*time_delta), + "TIME_DELTA"); + if (!time_delta_off) + return SBI_ENOMEM; + } else { + if (!time_delta_off) + return SBI_ENOMEM; + } + + time_delta = sbi_scratch_offset_ptr(scratch, time_delta_off); + *time_delta = 0; + + ret = sbi_platform_timer_init(plat, cold_boot); + if (ret) + return ret; + + if (sbi_hart_has_feature(scratch, SBI_HART_HAS_TIME)) + get_time_val = get_ticks; + else if (sbi_platform_has_timer_value(plat)) + get_time_val = sbi_platform_timer_value; + else + /* There is no method to provide timer value */ + return SBI_ENODEV; + + return 0; +} + +void sbi_timer_exit(struct sbi_scratch *scratch) +{ + sbi_platform_timer_event_stop(sbi_platform_ptr(scratch)); + + csr_clear(CSR_MIP, MIP_STIP); + csr_clear(CSR_MIE, MIP_MTIP); + + sbi_platform_timer_exit(sbi_platform_ptr(scratch)); +} diff --git a/roms/opensbi/lib/sbi/sbi_tlb.c b/roms/opensbi/lib/sbi/sbi_tlb.c new file mode 100644 index 000000000..73f59e868 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_tlb.c @@ -0,0 +1,429 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_atomic.h> +#include <sbi/riscv_barrier.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_fifo.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_ipi.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_tlb.h> +#include <sbi/sbi_hfence.h> +#include <sbi/sbi_string.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_platform.h> + +static unsigned long tlb_sync_off; +static unsigned long tlb_fifo_off; +static unsigned long tlb_fifo_mem_off; +static unsigned long tlb_range_flush_limit; + +static void sbi_tlb_flush_all(void) +{ + __asm__ __volatile("sfence.vma"); +} + +void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo) +{ + unsigned long start = tinfo->start; + unsigned long size = tinfo->size; + unsigned long vmid = tinfo->vmid; + unsigned long i, hgatp; + + hgatp = csr_swap(CSR_HGATP, + (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK); + + if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { + __sbi_hfence_vvma_all(); + goto done; + } + + for (i = 0; i < size; i += PAGE_SIZE) { + __sbi_hfence_vvma_va(start+i); + } + +done: + csr_write(CSR_HGATP, hgatp); +} + +void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo) +{ + unsigned long start = tinfo->start; + unsigned long size = tinfo->size; + unsigned long i; + + if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { + __sbi_hfence_gvma_all(); + return; + } + + for (i = 0; i < size; i += PAGE_SIZE) { + __sbi_hfence_gvma_gpa(start+i); + } +} + +void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo) +{ + unsigned long start = tinfo->start; + unsigned long size = tinfo->size; + unsigned long i; + + if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { + sbi_tlb_flush_all(); + return; + } + + for (i = 0; i < size; i += PAGE_SIZE) { + __asm__ __volatile__("sfence.vma %0" + : + : "r"(start + i) + : "memory"); + } +} + +void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo) +{ + unsigned long start = tinfo->start; + unsigned long size = tinfo->size; + unsigned long asid = tinfo->asid; + unsigned long vmid = tinfo->vmid; + unsigned long i, hgatp; + + hgatp = csr_swap(CSR_HGATP, + (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK); + + if (start == 0 && size == 0) { + __sbi_hfence_vvma_all(); + goto done; + } + + if (size == SBI_TLB_FLUSH_ALL) { + __sbi_hfence_vvma_asid(asid); + goto done; + } + + for (i = 0; i < size; i += PAGE_SIZE) { + __sbi_hfence_vvma_asid_va(start + i, asid); + } + +done: + csr_write(CSR_HGATP, hgatp); +} + +void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo) +{ + unsigned long start = tinfo->start; + unsigned long size = tinfo->size; + unsigned long vmid = tinfo->vmid; + unsigned long i; + + if (start == 0 && size == 0) { + __sbi_hfence_gvma_all(); + return; + } + + if (size == SBI_TLB_FLUSH_ALL) { + __sbi_hfence_gvma_vmid(vmid); + return; + } + + for (i = 0; i < size; i += PAGE_SIZE) { + __sbi_hfence_gvma_vmid_gpa(start + i, vmid); + } +} + +void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo) +{ + unsigned long start = tinfo->start; + unsigned long size = tinfo->size; + unsigned long asid = tinfo->asid; + unsigned long i; + + if (start == 0 && size == 0) { + sbi_tlb_flush_all(); + return; + } + + /* Flush entire MM context for a given ASID */ + if (size == SBI_TLB_FLUSH_ALL) { + __asm__ __volatile__("sfence.vma x0, %0" + : + : "r"(asid) + : "memory"); + return; + } + + for (i = 0; i < size; i += PAGE_SIZE) { + __asm__ __volatile__("sfence.vma %0, %1" + : + : "r"(start + i), "r"(asid) + : "memory"); + } +} + +void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo) +{ + __asm__ __volatile("fence.i"); +} + +static void sbi_tlb_entry_process(struct sbi_tlb_info *tinfo) +{ + u32 rhartid; + struct sbi_scratch *rscratch = NULL; + unsigned long *rtlb_sync = NULL; + + tinfo->local_fn(tinfo); + + sbi_hartmask_for_each_hart(rhartid, &tinfo->smask) { + rscratch = sbi_hartid_to_scratch(rhartid); + if (!rscratch) + continue; + + rtlb_sync = sbi_scratch_offset_ptr(rscratch, tlb_sync_off); + while (atomic_raw_xchg_ulong(rtlb_sync, 1)) ; + } +} + +static void sbi_tlb_process_count(struct sbi_scratch *scratch, int count) +{ + struct sbi_tlb_info tinfo; + u32 deq_count = 0; + struct sbi_fifo *tlb_fifo = + sbi_scratch_offset_ptr(scratch, tlb_fifo_off); + + while (!sbi_fifo_dequeue(tlb_fifo, &tinfo)) { + sbi_tlb_entry_process(&tinfo); + deq_count++; + if (deq_count > count) + break; + + } +} + +static void sbi_tlb_process(struct sbi_scratch *scratch) +{ + struct sbi_tlb_info tinfo; + struct sbi_fifo *tlb_fifo = + sbi_scratch_offset_ptr(scratch, tlb_fifo_off); + + while (!sbi_fifo_dequeue(tlb_fifo, &tinfo)) + sbi_tlb_entry_process(&tinfo); +} + +static void sbi_tlb_sync(struct sbi_scratch *scratch) +{ + unsigned long *tlb_sync = + sbi_scratch_offset_ptr(scratch, tlb_sync_off); + + while (!atomic_raw_xchg_ulong(tlb_sync, 0)) { + /* + * While we are waiting for remote hart to set the sync, + * consume fifo requests to avoid deadlock. + */ + sbi_tlb_process_count(scratch, 1); + } + + return; +} + +static inline int __sbi_tlb_range_check(struct sbi_tlb_info *curr, + struct sbi_tlb_info *next) +{ + unsigned long curr_end; + unsigned long next_end; + int ret = SBI_FIFO_UNCHANGED; + + if (!curr || !next) + return ret; + + next_end = next->start + next->size; + curr_end = curr->start + curr->size; + if (next->start <= curr->start && next_end > curr_end) { + curr->start = next->start; + curr->size = next->size; + sbi_hartmask_or(&curr->smask, &curr->smask, &next->smask); + ret = SBI_FIFO_UPDATED; + } else if (next->start >= curr->start && next_end <= curr_end) { + sbi_hartmask_or(&curr->smask, &curr->smask, &next->smask); + ret = SBI_FIFO_SKIP; + } + + return ret; +} + +/** + * Call back to decide if an inplace fifo update is required or next entry can + * can be skipped. Here are the different cases that are being handled. + * + * Case1: + * if next flush request range lies within one of the existing entry, skip + * the next entry. + * Case2: + * if flush request range in current fifo entry lies within next flush + * request, update the current entry. + * + * Note: + * We can not issue a fifo reset anymore if a complete vma flush is requested. + * This is because we are queueing FENCE.I requests as well now. + * To ease up the pressure in enqueue/fifo sync path, try to dequeue 1 element + * before continuing the while loop. This method is preferred over wfi/ipi because + * of MMIO cost involved in later method. + */ +static int sbi_tlb_update_cb(void *in, void *data) +{ + struct sbi_tlb_info *curr; + struct sbi_tlb_info *next; + int ret = SBI_FIFO_UNCHANGED; + + if (!in || !data) + return ret; + + curr = (struct sbi_tlb_info *)data; + next = (struct sbi_tlb_info *)in; + + if (next->local_fn == sbi_tlb_local_sfence_vma_asid && + curr->local_fn == sbi_tlb_local_sfence_vma_asid) { + if (next->asid == curr->asid) + ret = __sbi_tlb_range_check(curr, next); + } else if (next->local_fn == sbi_tlb_local_sfence_vma && + curr->local_fn == sbi_tlb_local_sfence_vma) { + ret = __sbi_tlb_range_check(curr, next); + } + + return ret; +} + +static int sbi_tlb_update(struct sbi_scratch *scratch, + struct sbi_scratch *remote_scratch, + u32 remote_hartid, void *data) +{ + int ret; + struct sbi_fifo *tlb_fifo_r; + struct sbi_tlb_info *tinfo = data; + u32 curr_hartid = current_hartid(); + + /* + * If address range to flush is too big then simply + * upgrade it to flush all because we can only flush + * 4KB at a time. + */ + if (tinfo->size > tlb_range_flush_limit) { + tinfo->start = 0; + tinfo->size = SBI_TLB_FLUSH_ALL; + } + + /* + * If the request is to queue a tlb flush entry for itself + * then just do a local flush and return; + */ + if (remote_hartid == curr_hartid) { + tinfo->local_fn(tinfo); + return -1; + } + + tlb_fifo_r = sbi_scratch_offset_ptr(remote_scratch, tlb_fifo_off); + + ret = sbi_fifo_inplace_update(tlb_fifo_r, data, sbi_tlb_update_cb); + if (ret != SBI_FIFO_UNCHANGED) { + return 1; + } + + while (sbi_fifo_enqueue(tlb_fifo_r, data) < 0) { + /** + * For now, Busy loop until there is space in the fifo. + * There may be case where target hart is also + * enqueue in source hart's fifo. Both hart may busy + * loop leading to a deadlock. + * TODO: Introduce a wait/wakeup event mechanism to handle + * this properly. + */ + sbi_tlb_process_count(scratch, 1); + sbi_dprintf("hart%d: hart%d tlb fifo full\n", + curr_hartid, remote_hartid); + } + + return 0; +} + +static struct sbi_ipi_event_ops tlb_ops = { + .name = "IPI_TLB", + .update = sbi_tlb_update, + .sync = sbi_tlb_sync, + .process = sbi_tlb_process, +}; + +static u32 tlb_event = SBI_IPI_EVENT_MAX; + +int sbi_tlb_request(ulong hmask, ulong hbase, struct sbi_tlb_info *tinfo) +{ + if (!tinfo->local_fn) + return SBI_EINVAL; + + return sbi_ipi_send_many(hmask, hbase, tlb_event, tinfo); +} + +int sbi_tlb_init(struct sbi_scratch *scratch, bool cold_boot) +{ + int ret; + void *tlb_mem; + unsigned long *tlb_sync; + struct sbi_fifo *tlb_q; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + if (cold_boot) { + tlb_sync_off = sbi_scratch_alloc_offset(sizeof(*tlb_sync), + "IPI_TLB_SYNC"); + if (!tlb_sync_off) + return SBI_ENOMEM; + tlb_fifo_off = sbi_scratch_alloc_offset(sizeof(*tlb_q), + "IPI_TLB_FIFO"); + if (!tlb_fifo_off) { + sbi_scratch_free_offset(tlb_sync_off); + return SBI_ENOMEM; + } + tlb_fifo_mem_off = sbi_scratch_alloc_offset( + SBI_TLB_FIFO_NUM_ENTRIES * SBI_TLB_INFO_SIZE, + "IPI_TLB_FIFO_MEM"); + if (!tlb_fifo_mem_off) { + sbi_scratch_free_offset(tlb_fifo_off); + sbi_scratch_free_offset(tlb_sync_off); + return SBI_ENOMEM; + } + ret = sbi_ipi_event_create(&tlb_ops); + if (ret < 0) { + sbi_scratch_free_offset(tlb_fifo_mem_off); + sbi_scratch_free_offset(tlb_fifo_off); + sbi_scratch_free_offset(tlb_sync_off); + return ret; + } + tlb_event = ret; + tlb_range_flush_limit = sbi_platform_tlbr_flush_limit(plat); + } else { + if (!tlb_sync_off || + !tlb_fifo_off || + !tlb_fifo_mem_off) + return SBI_ENOMEM; + if (SBI_IPI_EVENT_MAX <= tlb_event) + return SBI_ENOSPC; + } + + tlb_sync = sbi_scratch_offset_ptr(scratch, tlb_sync_off); + tlb_q = sbi_scratch_offset_ptr(scratch, tlb_fifo_off); + tlb_mem = sbi_scratch_offset_ptr(scratch, tlb_fifo_mem_off); + + *tlb_sync = 0; + + sbi_fifo_init(tlb_q, tlb_mem, + SBI_TLB_FIFO_NUM_ENTRIES, SBI_TLB_INFO_SIZE); + + return 0; +} diff --git a/roms/opensbi/lib/sbi/sbi_trap.c b/roms/opensbi/lib/sbi/sbi_trap.c new file mode 100644 index 000000000..b7349d2c9 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_trap.c @@ -0,0 +1,293 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_ecall.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_illegal_insn.h> +#include <sbi/sbi_ipi.h> +#include <sbi/sbi_misaligned_ldst.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_timer.h> +#include <sbi/sbi_trap.h> + +static void __noreturn sbi_trap_error(const char *msg, int rc, + ulong mcause, ulong mtval, ulong mtval2, + ulong mtinst, struct sbi_trap_regs *regs) +{ + u32 hartid = current_hartid(); + + sbi_printf("%s: hart%d: %s (error %d)\n", __func__, hartid, msg, rc); + sbi_printf("%s: hart%d: mcause=0x%" PRILX " mtval=0x%" PRILX "\n", + __func__, hartid, mcause, mtval); + if (misa_extension('H')) { + sbi_printf("%s: hart%d: mtval2=0x%" PRILX + " mtinst=0x%" PRILX "\n", + __func__, hartid, mtval2, mtinst); + } + sbi_printf("%s: hart%d: mepc=0x%" PRILX " mstatus=0x%" PRILX "\n", + __func__, hartid, regs->mepc, regs->mstatus); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "ra", regs->ra, "sp", regs->sp); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "gp", regs->gp, "tp", regs->tp); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "s0", regs->s0, "s1", regs->s1); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "a0", regs->a0, "a1", regs->a1); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "a2", regs->a2, "a3", regs->a3); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "a4", regs->a4, "a5", regs->a5); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "a6", regs->a6, "a7", regs->a7); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "s2", regs->s2, "s3", regs->s3); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "s4", regs->s4, "s5", regs->s5); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "s6", regs->s6, "s7", regs->s7); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "s8", regs->s8, "s9", regs->s9); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "s10", regs->s10, "s11", regs->s11); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "t0", regs->t0, "t1", regs->t1); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "t2", regs->t2, "t3", regs->t3); + sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__, + hartid, "t4", regs->t4, "t5", regs->t5); + sbi_printf("%s: hart%d: %s=0x%" PRILX "\n", __func__, hartid, "t6", + regs->t6); + + sbi_hart_hang(); +} + +/** + * Redirect trap to lower privledge mode (S-mode or U-mode) + * + * @param regs pointer to register state + * @param trap pointer to trap details + * + * @return 0 on success and negative error code on failure + */ +int sbi_trap_redirect(struct sbi_trap_regs *regs, + struct sbi_trap_info *trap) +{ + ulong hstatus, vsstatus, prev_mode; +#if __riscv_xlen == 32 + bool prev_virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE; +#else + bool prev_virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE; +#endif + /* By default, we redirect to HS-mode */ + bool next_virt = FALSE; + + /* Sanity check on previous mode */ + prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT; + if (prev_mode != PRV_S && prev_mode != PRV_U) + return SBI_ENOTSUPP; + + /* For certain exceptions from VS/VU-mode we redirect to VS-mode */ + if (misa_extension('H') && prev_virt) { + switch (trap->cause) { + case CAUSE_FETCH_PAGE_FAULT: + case CAUSE_LOAD_PAGE_FAULT: + case CAUSE_STORE_PAGE_FAULT: + next_virt = TRUE; + break; + default: + break; + }; + } + + /* Update MSTATUS MPV bits */ +#if __riscv_xlen == 32 + regs->mstatusH &= ~MSTATUSH_MPV; + regs->mstatusH |= (next_virt) ? MSTATUSH_MPV : 0UL; +#else + regs->mstatus &= ~MSTATUS_MPV; + regs->mstatus |= (next_virt) ? MSTATUS_MPV : 0UL; +#endif + + /* Update HSTATUS for VS/VU-mode to HS-mode transition */ + if (misa_extension('H') && prev_virt && !next_virt) { + /* Update HSTATUS SPVP and SPV bits */ + hstatus = csr_read(CSR_HSTATUS); + hstatus &= ~HSTATUS_SPVP; + hstatus |= (prev_mode == PRV_S) ? HSTATUS_SPVP : 0; + hstatus &= ~HSTATUS_SPV; + hstatus |= (prev_virt) ? HSTATUS_SPV : 0; + csr_write(CSR_HSTATUS, hstatus); + csr_write(CSR_HTVAL, trap->tval2); + csr_write(CSR_HTINST, trap->tinst); + } + + /* Update exception related CSRs */ + if (next_virt) { + /* Update VS-mode exception info */ + csr_write(CSR_VSTVAL, trap->tval); + csr_write(CSR_VSEPC, trap->epc); + csr_write(CSR_VSCAUSE, trap->cause); + + /* Set MEPC to VS-mode exception vector base */ + regs->mepc = csr_read(CSR_VSTVEC); + + /* Set MPP to VS-mode */ + regs->mstatus &= ~MSTATUS_MPP; + regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT); + + /* Get VS-mode SSTATUS CSR */ + vsstatus = csr_read(CSR_VSSTATUS); + + /* Set SPP for VS-mode */ + vsstatus &= ~SSTATUS_SPP; + if (prev_mode == PRV_S) + vsstatus |= (1UL << SSTATUS_SPP_SHIFT); + + /* Set SPIE for VS-mode */ + vsstatus &= ~SSTATUS_SPIE; + if (vsstatus & SSTATUS_SIE) + vsstatus |= (1UL << SSTATUS_SPIE_SHIFT); + + /* Clear SIE for VS-mode */ + vsstatus &= ~SSTATUS_SIE; + + /* Update VS-mode SSTATUS CSR */ + csr_write(CSR_VSSTATUS, vsstatus); + } else { + /* Update S-mode exception info */ + csr_write(CSR_STVAL, trap->tval); + csr_write(CSR_SEPC, trap->epc); + csr_write(CSR_SCAUSE, trap->cause); + + /* Set MEPC to S-mode exception vector base */ + regs->mepc = csr_read(CSR_STVEC); + + /* Set MPP to S-mode */ + regs->mstatus &= ~MSTATUS_MPP; + regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT); + + /* Set SPP for S-mode */ + regs->mstatus &= ~MSTATUS_SPP; + if (prev_mode == PRV_S) + regs->mstatus |= (1UL << MSTATUS_SPP_SHIFT); + + /* Set SPIE for S-mode */ + regs->mstatus &= ~MSTATUS_SPIE; + if (regs->mstatus & MSTATUS_SIE) + regs->mstatus |= (1UL << MSTATUS_SPIE_SHIFT); + + /* Clear SIE for S-mode */ + regs->mstatus &= ~MSTATUS_SIE; + } + + return 0; +} + +/** + * Handle trap/interrupt + * + * This function is called by firmware linked to OpenSBI + * library for handling trap/interrupt. It expects the + * following: + * 1. The 'mscratch' CSR is pointing to sbi_scratch of current HART + * 2. The 'mcause' CSR is having exception/interrupt cause + * 3. The 'mtval' CSR is having additional trap information + * 4. The 'mtval2' CSR is having additional trap information + * 5. The 'mtinst' CSR is having decoded trap instruction + * 6. Stack pointer (SP) is setup for current HART + * 7. Interrupts are disabled in MSTATUS CSR + * + * @param regs pointer to register state + */ +void sbi_trap_handler(struct sbi_trap_regs *regs) +{ + int rc = SBI_ENOTSUPP; + const char *msg = "trap handler failed"; + ulong mcause = csr_read(CSR_MCAUSE); + ulong mtval = csr_read(CSR_MTVAL), mtval2 = 0, mtinst = 0; + struct sbi_trap_info trap; + + if (misa_extension('H')) { + mtval2 = csr_read(CSR_MTVAL2); + mtinst = csr_read(CSR_MTINST); + } + + if (mcause & (1UL << (__riscv_xlen - 1))) { + mcause &= ~(1UL << (__riscv_xlen - 1)); + switch (mcause) { + case IRQ_M_TIMER: + sbi_timer_process(); + break; + case IRQ_M_SOFT: + sbi_ipi_process(); + break; + default: + msg = "unhandled external interrupt"; + goto trap_error; + }; + return; + } + + switch (mcause) { + case CAUSE_ILLEGAL_INSTRUCTION: + rc = sbi_illegal_insn_handler(mtval, regs); + msg = "illegal instruction handler failed"; + break; + case CAUSE_MISALIGNED_LOAD: + rc = sbi_misaligned_load_handler(mtval, mtval2, mtinst, regs); + msg = "misaligned load handler failed"; + break; + case CAUSE_MISALIGNED_STORE: + rc = sbi_misaligned_store_handler(mtval, mtval2, mtinst, regs); + msg = "misaligned store handler failed"; + break; + case CAUSE_SUPERVISOR_ECALL: + case CAUSE_MACHINE_ECALL: + rc = sbi_ecall_handler(regs); + msg = "ecall handler failed"; + break; + default: + /* If the trap came from S or U mode, redirect it there */ + trap.epc = regs->mepc; + trap.cause = mcause; + trap.tval = mtval; + trap.tval2 = mtval2; + trap.tinst = mtinst; + rc = sbi_trap_redirect(regs, &trap); + break; + }; + +trap_error: + if (rc) + sbi_trap_error(msg, rc, mcause, mtval, mtval2, mtinst, regs); +} + +typedef void (*trap_exit_t)(const struct sbi_trap_regs *regs); + +/** + * Exit trap/interrupt handling + * + * This function is called by non-firmware code to abruptly exit + * trap/interrupt handling and resume execution at context pointed + * by given register state. + * + * @param regs pointer to register state + */ +void __noreturn sbi_trap_exit(const struct sbi_trap_regs *regs) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + ((trap_exit_t)scratch->trap_exit)(regs); + __builtin_unreachable(); +} diff --git a/roms/opensbi/lib/sbi/sbi_unpriv.c b/roms/opensbi/lib/sbi/sbi_unpriv.c new file mode 100644 index 000000000..42461241f --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_unpriv.c @@ -0,0 +1,165 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_bitops.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_trap.h> +#include <sbi/sbi_unpriv.h> + +/** + * a3 must a pointer to the sbi_trap_info and a4 is used as a temporary + * register in the trap handler. Make sure that compiler doesn't use a3 & a4. + */ +#define DEFINE_UNPRIVILEGED_LOAD_FUNCTION(type, insn) \ + type sbi_load_##type(const type *addr, \ + struct sbi_trap_info *trap) \ + { \ + register ulong tinfo asm("a3"); \ + register ulong mstatus = 0; \ + register ulong mtvec = sbi_hart_expected_trap_addr(); \ + type ret = 0; \ + trap->cause = 0; \ + asm volatile( \ + "add %[tinfo], %[taddr], zero\n" \ + "csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n" \ + "csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n" \ + ".option push\n" \ + ".option norvc\n" \ + #insn " %[ret], %[addr]\n" \ + ".option pop\n" \ + "csrw " STR(CSR_MSTATUS) ", %[mstatus]\n" \ + "csrw " STR(CSR_MTVEC) ", %[mtvec]" \ + : [mstatus] "+&r"(mstatus), [mtvec] "+&r"(mtvec), \ + [tinfo] "+&r"(tinfo), [ret] "=&r"(ret) \ + : [addr] "m"(*addr), [mprv] "r"(MSTATUS_MPRV), \ + [taddr] "r"((ulong)trap) \ + : "a4", "memory"); \ + return ret; \ + } + +#define DEFINE_UNPRIVILEGED_STORE_FUNCTION(type, insn) \ + void sbi_store_##type(type *addr, type val, \ + struct sbi_trap_info *trap) \ + { \ + register ulong tinfo asm("a3") = (ulong)trap; \ + register ulong mstatus = 0; \ + register ulong mtvec = sbi_hart_expected_trap_addr(); \ + trap->cause = 0; \ + asm volatile( \ + "add %[tinfo], %[taddr], zero\n" \ + "csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n" \ + "csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n" \ + ".option push\n" \ + ".option norvc\n" \ + #insn " %[val], %[addr]\n" \ + ".option pop\n" \ + "csrw " STR(CSR_MSTATUS) ", %[mstatus]\n" \ + "csrw " STR(CSR_MTVEC) ", %[mtvec]" \ + : [mstatus] "+&r"(mstatus), [mtvec] "+&r"(mtvec), \ + [tinfo] "+&r"(tinfo) \ + : [addr] "m"(*addr), [mprv] "r"(MSTATUS_MPRV), \ + [val] "r"(val), [taddr] "r"((ulong)trap) \ + : "a4", "memory"); \ + } + +DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u8, lbu) +DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u16, lhu) +DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s8, lb) +DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s16, lh) +DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s32, lw) +DEFINE_UNPRIVILEGED_STORE_FUNCTION(u8, sb) +DEFINE_UNPRIVILEGED_STORE_FUNCTION(u16, sh) +DEFINE_UNPRIVILEGED_STORE_FUNCTION(u32, sw) +#if __riscv_xlen == 64 +DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lwu) +DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u64, ld) +DEFINE_UNPRIVILEGED_STORE_FUNCTION(u64, sd) +DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, ld) +#else +DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lw) +DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, lw) + +u64 sbi_load_u64(const u64 *addr, + struct sbi_trap_info *trap) +{ + u64 ret = sbi_load_u32((u32 *)addr, trap); + + if (trap->cause) + return 0; + ret |= ((u64)sbi_load_u32((u32 *)addr + 1, trap) << 32); + if (trap->cause) + return 0; + + return ret; +} + +void sbi_store_u64(u64 *addr, u64 val, + struct sbi_trap_info *trap) +{ + sbi_store_u32((u32 *)addr, val, trap); + if (trap->cause) + return; + + sbi_store_u32((u32 *)addr + 1, val >> 32, trap); + if (trap->cause) + return; +} +#endif + +ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap) +{ + register ulong tinfo asm("a3"); + register ulong ttmp asm("a4"); + register ulong mstatus = 0; + register ulong mtvec = sbi_hart_expected_trap_addr(); + ulong insn = 0; + + trap->cause = 0; + + asm volatile( + "add %[tinfo], %[taddr], zero\n" + "csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n" + "csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n" + "lhu %[insn], (%[addr])\n" + "andi %[ttmp], %[insn], 3\n" + "addi %[ttmp], %[ttmp], -3\n" + "bne %[ttmp], zero, 2f\n" + "lhu %[ttmp], 2(%[addr])\n" + "sll %[ttmp], %[ttmp], 16\n" + "add %[insn], %[insn], %[ttmp]\n" + "2: csrw " STR(CSR_MSTATUS) ", %[mstatus]\n" + "csrw " STR(CSR_MTVEC) ", %[mtvec]" + : [mstatus] "+&r"(mstatus), [mtvec] "+&r"(mtvec), + [tinfo] "+&r"(tinfo), [ttmp] "+&r"(ttmp), + [insn] "=&r"(insn) + : [mprv] "r"(MSTATUS_MPRV | MSTATUS_MXR), + [taddr] "r"((ulong)trap), [addr] "r"(mepc) + : "memory"); + + switch (trap->cause) { + case CAUSE_LOAD_ACCESS: + trap->cause = CAUSE_FETCH_ACCESS; + trap->tval = mepc; + break; + case CAUSE_LOAD_PAGE_FAULT: + trap->cause = CAUSE_FETCH_PAGE_FAULT; + trap->tval = mepc; + break; + case CAUSE_LOAD_GUEST_PAGE_FAULT: + trap->cause = CAUSE_FETCH_GUEST_PAGE_FAULT; + trap->tval = mepc; + break; + default: + break; + }; + + return insn; +} diff --git a/roms/opensbi/lib/utils/fdt/fdt_domain.c b/roms/opensbi/lib/utils/fdt/fdt_domain.c new file mode 100644 index 000000000..09615e53e --- /dev/null +++ b/roms/opensbi/lib/utils/fdt/fdt_domain.c @@ -0,0 +1,455 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * fdt_domain.c - Flat Device Tree Domain helper routines + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <libfdt.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hartmask.h> +#include <sbi/sbi_scratch.h> +#include <sbi_utils/fdt/fdt_domain.h> +#include <sbi_utils/fdt/fdt_helper.h> + +int fdt_iterate_each_domain(void *fdt, void *opaque, + int (*fn)(void *fdt, int domain_offset, + void *opaque)) +{ + int rc, doffset, poffset; + + if (!fdt || !fn) + return SBI_EINVAL; + + poffset = fdt_path_offset(fdt, "/chosen"); + if (poffset < 0) + return 0; + poffset = fdt_node_offset_by_compatible(fdt, poffset, + "opensbi,domain,config"); + if (poffset < 0) + return 0; + + fdt_for_each_subnode(doffset, fdt, poffset) { + if (fdt_node_check_compatible(fdt, doffset, + "opensbi,domain,instance")) + continue; + + rc = fn(fdt, doffset, opaque); + if (rc) + return rc; + } + + return 0; +} + +int fdt_iterate_each_memregion(void *fdt, int domain_offset, void *opaque, + int (*fn)(void *fdt, int domain_offset, + int region_offset, u32 region_access, + void *opaque)) +{ + u32 i, rcount; + int rc, len, region_offset; + const u32 *regions; + + if (!fdt || (domain_offset < 0) || !fn) + return SBI_EINVAL; + + if (fdt_node_check_compatible(fdt, domain_offset, + "opensbi,domain,instance")) + return SBI_EINVAL; + + regions = fdt_getprop(fdt, domain_offset, "regions", &len); + if (!regions) + return 0; + + rcount = (u32)len / (sizeof(u32) * 2); + for (i = 0; i < rcount; i++) { + region_offset = fdt_node_offset_by_phandle(fdt, + fdt32_to_cpu(regions[2 * i])); + if (region_offset < 0) + return region_offset; + + if (fdt_node_check_compatible(fdt, region_offset, + "opensbi,domain,memregion")) + return SBI_EINVAL; + + rc = fn(fdt, domain_offset, region_offset, + fdt32_to_cpu(regions[(2 * i) + 1]), opaque); + if (rc) + return rc; + } + + return 0; +} + +struct __fixup_find_domain_offset_info { + const char *name; + int *doffset; +}; + +static int __fixup_find_domain_offset(void *fdt, int doff, void *p) +{ + struct __fixup_find_domain_offset_info *fdo = p; + + if (!sbi_strcmp(fdo->name, fdt_get_name(fdt, doff, NULL))) + *fdo->doffset = doff; + + return 0; +} + +#define DISABLE_DEVICES_MASK (SBI_DOMAIN_MEMREGION_READABLE | \ + SBI_DOMAIN_MEMREGION_WRITEABLE | \ + SBI_DOMAIN_MEMREGION_EXECUTABLE) + +static int __fixup_count_disable_devices(void *fdt, int doff, int roff, + u32 perm, void *p) +{ + int len; + u32 *dcount = p; + + if (perm & DISABLE_DEVICES_MASK) + return 0; + + len = 0; + if (fdt_getprop(fdt, roff, "devices", &len)) + *dcount += len / sizeof(u32); + + return 0; +} + +static int __fixup_disable_devices(void *fdt, int doff, int roff, + u32 raccess, void *p) +{ + int i, len, coff; + const u32 *devices; + + if (raccess & DISABLE_DEVICES_MASK) + return 0; + + len = 0; + devices = fdt_getprop(fdt, roff, "devices", &len); + if (!devices) + return 0; + len = len / sizeof(u32); + + for (i = 0; i < len; i++) { + coff = fdt_node_offset_by_phandle(fdt, + fdt32_to_cpu(devices[i])); + if (coff < 0) + return coff; + + fdt_setprop_string(fdt, coff, "status", "disabled"); + } + + return 0; +} + +void fdt_domain_fixup(void *fdt) +{ + u32 i, dcount; + int err, poffset, doffset; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); + struct __fixup_find_domain_offset_info fdo; + + /* Remove the domain assignment DT property from CPU DT nodes */ + poffset = fdt_path_offset(fdt, "/cpus"); + if (poffset < 0) + return; + fdt_for_each_subnode(doffset, fdt, poffset) { + err = fdt_parse_hart_id(fdt, doffset, &i); + if (err) + continue; + + fdt_nop_property(fdt, doffset, "opensbi-domain"); + } + + /* Skip device disable for root domain */ + if (!dom->index) + goto skip_device_disable; + + /* Find current domain DT node */ + doffset = -1; + fdo.name = dom->name; + fdo.doffset = &doffset; + fdt_iterate_each_domain(fdt, &fdo, __fixup_find_domain_offset); + if (doffset < 0) + goto skip_device_disable; + + /* Count current domain device DT nodes to be disabled */ + dcount = 0; + fdt_iterate_each_memregion(fdt, doffset, &dcount, + __fixup_count_disable_devices); + if (!dcount) + goto skip_device_disable; + + /* Expand FDT based on device DT nodes to be disabled */ + err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + dcount * 32); + if (err < 0) + return; + + /* Again find current domain DT node */ + doffset = -1; + fdo.name = dom->name; + fdo.doffset = &doffset; + fdt_iterate_each_domain(fdt, &fdo, __fixup_find_domain_offset); + if (doffset < 0) + goto skip_device_disable; + + /* Disable device DT nodes for current domain */ + fdt_iterate_each_memregion(fdt, doffset, NULL, + __fixup_disable_devices); +skip_device_disable: + + /* Remove the OpenSBI domain config DT node */ + poffset = fdt_path_offset(fdt, "/chosen"); + if (poffset < 0) + return; + poffset = fdt_node_offset_by_compatible(fdt, poffset, + "opensbi,domain,config"); + if (poffset < 0) + return; + fdt_nop_node(fdt, poffset); +} + +#define FDT_DOMAIN_MAX_COUNT 8 +#define FDT_DOMAIN_REGION_MAX_COUNT 16 + +static u32 fdt_domains_count; +static struct sbi_domain fdt_domains[FDT_DOMAIN_MAX_COUNT]; +static struct sbi_hartmask fdt_masks[FDT_DOMAIN_MAX_COUNT]; +static struct sbi_domain_memregion + fdt_regions[FDT_DOMAIN_MAX_COUNT][FDT_DOMAIN_REGION_MAX_COUNT + 2]; + +static int __fdt_parse_region(void *fdt, int domain_offset, + int region_offset, u32 region_access, + void *opaque) +{ + int len; + u32 val32; + u64 val64; + const u32 *val; + u32 *region_count = opaque; + struct sbi_domain_memregion *region; + + /* Find next region of the domain */ + if (FDT_DOMAIN_REGION_MAX_COUNT <= *region_count) + return SBI_EINVAL; + region = &fdt_regions[fdt_domains_count][*region_count]; + + /* Read "base" DT property */ + val = fdt_getprop(fdt, region_offset, "base", &len); + if (!val && len >= 8) + return SBI_EINVAL; + val64 = fdt32_to_cpu(val[0]); + val64 = (val64 << 32) | fdt32_to_cpu(val[1]); + region->base = val64; + + /* Read "order" DT property */ + val = fdt_getprop(fdt, region_offset, "order", &len); + if (!val && len >= 4) + return SBI_EINVAL; + val32 = fdt32_to_cpu(*val); + if (val32 < 3 || __riscv_xlen < val32) + return SBI_EINVAL; + region->order = val32; + + /* Read "mmio" DT property */ + region->flags = region_access & SBI_DOMAIN_MEMREGION_ACCESS_MASK; + if (fdt_get_property(fdt, region_offset, "mmio", NULL)) + region->flags |= SBI_DOMAIN_MEMREGION_MMIO; + + (*region_count)++; + + return 0; +} + +static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque) +{ + u32 val32; + u64 val64; + const u32 *val; + struct sbi_domain *dom; + struct sbi_hartmask *mask; + struct sbi_hartmask assign_mask; + int *cold_domain_offset = opaque; + struct sbi_domain_memregion *regions; + int i, err, len, cpus_offset, cpu_offset, doffset; + + /* Sanity check on maximum domains we can handle */ + if (FDT_DOMAIN_MAX_COUNT <= fdt_domains_count) + return SBI_EINVAL; + dom = &fdt_domains[fdt_domains_count]; + mask = &fdt_masks[fdt_domains_count]; + regions = &fdt_regions[fdt_domains_count][0]; + + /* Read DT node name */ + sbi_strncpy(dom->name, fdt_get_name(fdt, domain_offset, NULL), + sizeof(dom->name)); + dom->name[sizeof(dom->name) - 1] = '\0'; + + /* Setup possible HARTs mask */ + SBI_HARTMASK_INIT(mask); + dom->possible_harts = mask; + val = fdt_getprop(fdt, domain_offset, "possible-harts", &len); + len = len / sizeof(u32); + if (val && len) { + for (i = 0; i < len; i++) { + cpu_offset = fdt_node_offset_by_phandle(fdt, + fdt32_to_cpu(val[i])); + if (cpu_offset < 0) + return cpu_offset; + + err = fdt_parse_hart_id(fdt, cpu_offset, &val32); + if (err) + return err; + + sbi_hartmask_set_hart(val32, mask); + } + } + + /* Setup memregions from DT */ + val32 = 0; + sbi_memset(regions, 0, + sizeof(*regions) * (FDT_DOMAIN_REGION_MAX_COUNT + 2)); + dom->regions = regions; + err = fdt_iterate_each_memregion(fdt, domain_offset, &val32, + __fdt_parse_region); + if (err) + return err; + sbi_domain_memregion_initfw(®ions[val32]); + + /* Read "boot-hart" DT property */ + val32 = -1U; + val = fdt_getprop(fdt, domain_offset, "boot-hart", &len); + if (val && len >= 4) { + cpu_offset = fdt_node_offset_by_phandle(fdt, + fdt32_to_cpu(*val)); + if (cpu_offset >= 0) + fdt_parse_hart_id(fdt, cpu_offset, &val32); + } else { + if (domain_offset == *cold_domain_offset) + val32 = current_hartid(); + } + dom->boot_hartid = val32; + + /* Read "next-arg1" DT property */ + val64 = 0; + val = fdt_getprop(fdt, domain_offset, "next-arg1", &len); + if (val && len >= 8) { + val64 = fdt32_to_cpu(val[0]); + val64 = (val64 << 32) | fdt32_to_cpu(val[1]); + } else { + if (domain_offset == *cold_domain_offset) + val64 = sbi_scratch_thishart_ptr()->next_arg1; + } + dom->next_arg1 = val64; + + /* Read "next-addr" DT property */ + val64 = 0; + val = fdt_getprop(fdt, domain_offset, "next-addr", &len); + if (val && len >= 8) { + val64 = fdt32_to_cpu(val[0]); + val64 = (val64 << 32) | fdt32_to_cpu(val[1]); + } else { + if (domain_offset == *cold_domain_offset) + val64 = sbi_scratch_thishart_ptr()->next_addr; + } + dom->next_addr = val64; + + /* Read "next-mode" DT property */ + val32 = 0x1; + val = fdt_getprop(fdt, domain_offset, "next-mode", &len); + if (val && len >= 4) { + val32 = fdt32_to_cpu(val[0]); + if (val32 != 0x0 && val32 != 0x1) + val32 = 0x1; + } else { + if (domain_offset == *cold_domain_offset) + val32 = sbi_scratch_thishart_ptr()->next_mode; + } + dom->next_mode = val32; + + /* Read "system-reset-allowed" DT property */ + if (fdt_get_property(fdt, domain_offset, + "system-reset-allowed", NULL)) + dom->system_reset_allowed = TRUE; + else + dom->system_reset_allowed = FALSE; + + /* Find /cpus DT node */ + cpus_offset = fdt_path_offset(fdt, "/cpus"); + if (cpus_offset < 0) + return cpus_offset; + + /* HART to domain assignment mask based on CPU DT nodes */ + sbi_hartmask_clear_all(&assign_mask); + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { + err = fdt_parse_hart_id(fdt, cpu_offset, &val32); + if (err) + continue; + + if (SBI_HARTMASK_MAX_BITS <= val32) + continue; + + val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len); + if (!val || len < 4) + return SBI_EINVAL; + + doffset = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val)); + if (doffset < 0) + return doffset; + + if (doffset == domain_offset) + sbi_hartmask_set_hart(val32, &assign_mask); + } + + /* Increment domains count */ + fdt_domains_count++; + + /* Register the domain */ + return sbi_domain_register(dom, &assign_mask); +} + +int fdt_domains_populate(void *fdt) +{ + const u32 *val; + int cold_domain_offset; + u32 hartid, cold_hartid; + int err, len, cpus_offset, cpu_offset; + + /* Sanity checks */ + if (!fdt) + return SBI_EINVAL; + + /* Find /cpus DT node */ + cpus_offset = fdt_path_offset(fdt, "/cpus"); + if (cpus_offset < 0) + return cpus_offset; + + /* Find coldboot HART domain DT node offset */ + cold_domain_offset = -1; + cold_hartid = current_hartid(); + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { + err = fdt_parse_hart_id(fdt, cpu_offset, &hartid); + if (err) + continue; + + if (hartid != cold_hartid) + continue; + + val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len); + if (val && len >= 4) + cold_domain_offset = fdt_node_offset_by_phandle(fdt, + fdt32_to_cpu(*val)); + + break; + } + + /* Iterate over each domain in FDT and populate details */ + return fdt_iterate_each_domain(fdt, &cold_domain_offset, + __fdt_parse_domain); +} diff --git a/roms/opensbi/lib/utils/fdt/fdt_fixup.c b/roms/opensbi/lib/utils/fdt/fdt_fixup.c new file mode 100644 index 000000000..eea450d80 --- /dev/null +++ b/roms/opensbi/lib/utils/fdt/fdt_fixup.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * fdt_fixup.c - Flat Device Tree parsing helper routines + * Implement helper routines to parse FDT nodes on top of + * libfdt for OpenSBI usage + * + * Copyright (C) 2020 Bin Meng <bmeng.cn@gmail.com> + */ + +#include <libfdt.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_math.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_string.h> +#include <sbi_utils/fdt/fdt_fixup.h> +#include <sbi_utils/fdt/fdt_helper.h> + +void fdt_cpu_fixup(void *fdt) +{ + struct sbi_domain *dom = sbi_domain_thishart_ptr(); + int err, cpu_offset, cpus_offset, len; + const char *mmu_type; + u32 hartid; + + err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 32); + if (err < 0) + return; + + cpus_offset = fdt_path_offset(fdt, "/cpus"); + if (cpus_offset < 0) + return; + + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { + err = fdt_parse_hart_id(fdt, cpu_offset, &hartid); + if (err) + continue; + + /* + * Disable a HART DT node if one of the following is true: + * 1. The HART is not assigned to the current domain + * 2. MMU is not available for the HART + */ + + mmu_type = fdt_getprop(fdt, cpu_offset, "mmu-type", &len); + if (!sbi_domain_is_assigned_hart(dom, hartid) || + !mmu_type || !len) + fdt_setprop_string(fdt, cpu_offset, "status", + "disabled"); + } +} + +void fdt_plic_fixup(void *fdt, const char *compat) +{ + u32 *cells; + int i, cells_count; + int plic_off; + + plic_off = fdt_node_offset_by_compatible(fdt, 0, compat); + if (plic_off < 0) + return; + + cells = (u32 *)fdt_getprop(fdt, plic_off, + "interrupts-extended", &cells_count); + if (!cells) + return; + + cells_count = cells_count / sizeof(u32); + if (!cells_count) + return; + + for (i = 0; i < (cells_count / 2); i++) { + if (fdt32_to_cpu(cells[2 * i + 1]) == IRQ_M_EXT) + cells[2 * i + 1] = cpu_to_fdt32(0xffffffff); + } +} + +static int fdt_resv_memory_update_node(void *fdt, unsigned long addr, + unsigned long size, int index, + int parent, bool no_map) +{ + int na = fdt_address_cells(fdt, 0); + int ns = fdt_size_cells(fdt, 0); + fdt32_t addr_high, addr_low; + fdt32_t size_high, size_low; + int subnode, err; + fdt32_t reg[4]; + fdt32_t *val; + char name[32]; + + addr_high = (u64)addr >> 32; + addr_low = addr; + size_high = (u64)size >> 32; + size_low = size; + + if (na > 1 && addr_high) + sbi_snprintf(name, sizeof(name), + "mmode_resv%d@%x,%x", index, + addr_high, addr_low); + else + sbi_snprintf(name, sizeof(name), + "mmode_resv%d@%x", index, + addr_low); + + subnode = fdt_add_subnode(fdt, parent, name); + if (subnode < 0) + return subnode; + + if (no_map) { + /* + * Tell operating system not to create a virtual + * mapping of the region as part of its standard + * mapping of system memory. + */ + err = fdt_setprop_empty(fdt, subnode, "no-map"); + if (err < 0) + return err; + } + + /* encode the <reg> property value */ + val = reg; + if (na > 1) + *val++ = cpu_to_fdt32(addr_high); + *val++ = cpu_to_fdt32(addr_low); + if (ns > 1) + *val++ = cpu_to_fdt32(size_high); + *val++ = cpu_to_fdt32(size_low); + + err = fdt_setprop(fdt, subnode, "reg", reg, + (na + ns) * sizeof(fdt32_t)); + if (err < 0) + return err; + + return 0; +} + +/** + * We use PMP to protect OpenSBI firmware to safe-guard it from buggy S-mode + * software, see pmp_init() in lib/sbi/sbi_hart.c. The protected memory region + * information needs to be conveyed to S-mode software (e.g.: operating system) + * via some well-known method. + * + * With device tree, this can be done by inserting a child node of the reserved + * memory node which is used to specify one or more regions of reserved memory. + * + * For the reserved memory node bindings, see Linux kernel documentation at + * Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt + * + * Some additional memory spaces may be protected by platform codes via PMP as + * well, and corresponding child nodes will be inserted. + */ +int fdt_reserved_memory_fixup(void *fdt) +{ + struct sbi_domain_memregion *reg; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + unsigned long addr, size; + int err, parent, i; + int na = fdt_address_cells(fdt, 0); + int ns = fdt_size_cells(fdt, 0); + + /* + * Expand the device tree to accommodate new node + * by the following estimated size: + * + * Each PMP memory region entry occupies 64 bytes. + * With 16 PMP memory regions we need 64 * 16 = 1024 bytes. + */ + err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 1024); + if (err < 0) + return err; + + /* try to locate the reserved memory node */ + parent = fdt_path_offset(fdt, "/reserved-memory"); + if (parent < 0) { + /* if such node does not exist, create one */ + parent = fdt_add_subnode(fdt, 0, "reserved-memory"); + if (parent < 0) + return parent; + + /* + * reserved-memory node has 3 required properties: + * - #address-cells: the same value as the root node + * - #size-cells: the same value as the root node + * - ranges: should be empty + */ + + err = fdt_setprop_empty(fdt, parent, "ranges"); + if (err < 0) + return err; + + err = fdt_setprop_u32(fdt, parent, "#size-cells", ns); + if (err < 0) + return err; + + err = fdt_setprop_u32(fdt, parent, "#address-cells", na); + if (err < 0) + return err; + } + + /* + * We assume the given device tree does not contain any memory region + * child node protected by PMP. Normally PMP programming happens at + * M-mode firmware. The memory space used by OpenSBI is protected. + * Some additional memory spaces may be protected by domain memory + * regions. + * + * With above assumption, we create child nodes directly. + */ + + i = 0; + sbi_domain_for_each_memregion(dom, reg) { + /* Ignore MMIO or READABLE or WRITABLE or EXECUTABLE regions */ + if (reg->flags & SBI_DOMAIN_MEMREGION_MMIO) + continue; + if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE) + continue; + if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE) + continue; + if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE) + continue; + + addr = reg->base; + size = 1UL << reg->order; + fdt_resv_memory_update_node(fdt, addr, size, i, parent, + (sbi_hart_pmp_count(scratch)) ? false : true); + i++; + } + + return 0; +} + +int fdt_reserved_memory_nomap_fixup(void *fdt) +{ + int parent, subnode; + int err; + + /* Locate the reserved memory node */ + parent = fdt_path_offset(fdt, "/reserved-memory"); + if (parent < 0) + return parent; + + fdt_for_each_subnode(subnode, fdt, parent) { + /* + * Tell operating system not to create a virtual + * mapping of the region as part of its standard + * mapping of system memory. + */ + err = fdt_setprop_empty(fdt, subnode, "no-map"); + if (err < 0) + return err; + } + + return 0; +} + +void fdt_fixups(void *fdt) +{ + fdt_plic_fixup(fdt, "riscv,plic0"); + + fdt_reserved_memory_fixup(fdt); +} + + diff --git a/roms/opensbi/lib/utils/fdt/fdt_helper.c b/roms/opensbi/lib/utils/fdt/fdt_helper.c new file mode 100644 index 000000000..bf19ff92d --- /dev/null +++ b/roms/opensbi/lib/utils/fdt/fdt_helper.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * fdt_helper.c - Flat Device Tree manipulation helper routines + * Implement helper routines on top of libfdt for OpenSBI usage + * + * Copyright (C) 2020 Bin Meng <bmeng.cn@gmail.com> + */ + +#include <libfdt.h> +#include <sbi/riscv_asm.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_hartmask.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_scratch.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/irqchip/plic.h> +#include <sbi_utils/sys/clint.h> + +#define DEFAULT_UART_FREQ 0 +#define DEFAULT_UART_BAUD 115200 +#define DEFAULT_UART_REG_SHIFT 0 +#define DEFAULT_UART_REG_IO_WIDTH 1 + +#define DEFAULT_SIFIVE_UART_FREQ 0 +#define DEFAULT_SIFIVE_UART_BAUD 115200 +#define DEFAULT_SIFIVE_UART_REG_SHIFT 0 +#define DEFAULT_SIFIVE_UART_REG_IO_WIDTH 4 + +#define DEFAULT_SHAKTI_UART_FREQ 50000000 +#define DEFAULT_SHAKTI_UART_BAUD 115200 + +const struct fdt_match *fdt_match_node(void *fdt, int nodeoff, + const struct fdt_match *match_table) +{ + int ret; + + if (!fdt || nodeoff < 0 || !match_table) + return NULL; + + while (match_table->compatible) { + ret = fdt_node_check_compatible(fdt, nodeoff, + match_table->compatible); + if (!ret) + return match_table; + match_table++; + } + + return NULL; +} + +int fdt_find_match(void *fdt, int startoff, + const struct fdt_match *match_table, + const struct fdt_match **out_match) +{ + int nodeoff; + + if (!fdt || !match_table) + return SBI_ENODEV; + + while (match_table->compatible) { + nodeoff = fdt_node_offset_by_compatible(fdt, startoff, + match_table->compatible); + if (nodeoff >= 0) { + if (out_match) + *out_match = match_table; + return nodeoff; + } + match_table++; + } + + return SBI_ENODEV; +} + +static int fdt_translate_address(void *fdt, uint64_t reg, int parent, + unsigned long *addr) +{ + int i, rlen; + int cell_addr, cell_size; + const fdt32_t *ranges; + uint64_t offset = 0, caddr = 0, paddr = 0, rsize = 0; + + cell_addr = fdt_address_cells(fdt, parent); + if (cell_addr < 1) + return SBI_ENODEV; + + cell_size = fdt_size_cells(fdt, parent); + if (cell_size < 0) + return SBI_ENODEV; + + ranges = fdt_getprop(fdt, parent, "ranges", &rlen); + if (ranges && rlen > 0) { + for (i = 0; i < cell_addr; i++) + caddr = (caddr << 32) | fdt32_to_cpu(*ranges++); + for (i = 0; i < cell_addr; i++) + paddr = (paddr << 32) | fdt32_to_cpu(*ranges++); + for (i = 0; i < cell_size; i++) + rsize = (rsize << 32) | fdt32_to_cpu(*ranges++); + if (reg < caddr || caddr >= (reg + rsize )) { + sbi_printf("invalid address translation\n"); + return SBI_ENODEV; + } + offset = reg - caddr; + *addr = paddr + offset; + } else { + /* No translation required */ + *addr = reg; + } + + return 0; +} + +int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr, + unsigned long *size) +{ + int parent, len, i, rc; + int cell_addr, cell_size; + const fdt32_t *prop_addr, *prop_size; + uint64_t temp = 0; + + parent = fdt_parent_offset(fdt, node); + if (parent < 0) + return parent; + cell_addr = fdt_address_cells(fdt, parent); + if (cell_addr < 1) + return SBI_ENODEV; + + cell_size = fdt_size_cells(fdt, parent); + if (cell_size < 0) + return SBI_ENODEV; + + prop_addr = fdt_getprop(fdt, node, "reg", &len); + if (!prop_addr) + return SBI_ENODEV; + prop_size = prop_addr + cell_addr; + + if (addr) { + for (i = 0; i < cell_addr; i++) + temp = (temp << 32) | fdt32_to_cpu(*prop_addr++); + do { + if (parent < 0) + break; + rc = fdt_translate_address(fdt, temp, parent, addr); + if (rc) + break; + parent = fdt_parent_offset(fdt, parent); + temp = *addr; + } while (1); + } + temp = 0; + + if (size) { + for (i = 0; i < cell_size; i++) + temp = (temp << 32) | fdt32_to_cpu(*prop_size++); + *size = temp; + } + + return 0; +} + +int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid) +{ + int len; + const void *prop; + const fdt32_t *val; + + if (!fdt || cpu_offset < 0) + return SBI_EINVAL; + + prop = fdt_getprop(fdt, cpu_offset, "device_type", &len); + if (!prop || !len) + return SBI_EINVAL; + if (strncmp (prop, "cpu", strlen ("cpu"))) + return SBI_EINVAL; + + val = fdt_getprop(fdt, cpu_offset, "reg", &len); + if (!val || len < sizeof(fdt32_t)) + return SBI_EINVAL; + + if (len > sizeof(fdt32_t)) + val++; + + if (hartid) + *hartid = fdt32_to_cpu(*val); + + return 0; +} + +int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid) +{ + u32 hartid; + int err, cpu_offset, cpus_offset; + + if (!fdt) + return SBI_EINVAL; + if (!max_hartid) + return 0; + + *max_hartid = 0; + + cpus_offset = fdt_path_offset(fdt, "/cpus"); + if (cpus_offset < 0) + return cpus_offset; + + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { + err = fdt_parse_hart_id(fdt, cpu_offset, &hartid); + if (err) + continue; + + if (hartid > *max_hartid) + *max_hartid = hartid; + } + + return 0; +} + +int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset, + struct platform_uart_data *uart) +{ + int len, rc; + const fdt32_t *val; + unsigned long reg_addr, reg_size; + + if (nodeoffset < 0 || !uart || !fdt) + return SBI_ENODEV; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size); + if (rc < 0 || !reg_addr || !reg_size) + return SBI_ENODEV; + uart->addr = reg_addr; + + /** + * UART address is mandaotry. clock-frequency and current-speed + * may not be present. Don't return error. + */ + val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len); + if (len > 0 && val) + uart->freq = fdt32_to_cpu(*val); + else + uart->freq = DEFAULT_SHAKTI_UART_FREQ; + + val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len); + if (len > 0 && val) + uart->baud = fdt32_to_cpu(*val); + else + uart->baud = DEFAULT_SHAKTI_UART_BAUD; + + return 0; +} + +int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset, + struct platform_uart_data *uart) +{ + int len, rc; + const fdt32_t *val; + unsigned long reg_addr, reg_size; + + if (nodeoffset < 0 || !uart || !fdt) + return SBI_ENODEV; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size); + if (rc < 0 || !reg_addr || !reg_size) + return SBI_ENODEV; + uart->addr = reg_addr; + + /** + * UART address is mandaotry. clock-frequency and current-speed + * may not be present. Don't return error. + */ + val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len); + if (len > 0 && val) + uart->freq = fdt32_to_cpu(*val); + else + uart->freq = DEFAULT_SIFIVE_UART_FREQ; + + val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len); + if (len > 0 && val) + uart->baud = fdt32_to_cpu(*val); + else + uart->baud = DEFAULT_SIFIVE_UART_BAUD; + + /* For SiFive UART, the reg-shift and reg-io-width are fixed .*/ + uart->reg_shift = DEFAULT_SIFIVE_UART_REG_SHIFT; + uart->reg_io_width = DEFAULT_SIFIVE_UART_REG_IO_WIDTH; + + return 0; +} + +int fdt_parse_uart8250_node(void *fdt, int nodeoffset, + struct platform_uart_data *uart) +{ + int len, rc; + const fdt32_t *val; + unsigned long reg_addr, reg_size; + + if (nodeoffset < 0 || !uart || !fdt) + return SBI_ENODEV; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size); + if (rc < 0 || !reg_addr || !reg_size) + return SBI_ENODEV; + uart->addr = reg_addr; + + /** + * UART address is mandaotry. clock-frequency and current-speed + * may not be present. Don't return error. + */ + val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len); + if (len > 0 && val) + uart->freq = fdt32_to_cpu(*val); + else + uart->freq = DEFAULT_UART_FREQ; + + val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len); + if (len > 0 && val) + uart->baud = fdt32_to_cpu(*val); + else + uart->baud = DEFAULT_UART_BAUD; + + val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-shift", &len); + if (len > 0 && val) + uart->reg_shift = fdt32_to_cpu(*val); + else + uart->reg_shift = DEFAULT_UART_REG_SHIFT; + + val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-io-width", &len); + if (len > 0 && val) + uart->reg_io_width = fdt32_to_cpu(*val); + else + uart->reg_io_width = DEFAULT_UART_REG_IO_WIDTH; + + return 0; +} + +int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart, + const char *compatible) +{ + int nodeoffset; + + if (!compatible || !uart || !fdt) + return SBI_ENODEV; + + nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); + if (nodeoffset < 0) + return nodeoffset; + + return fdt_parse_uart8250_node(fdt, nodeoffset, uart); +} + +int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic) +{ + int len, rc; + const fdt32_t *val; + unsigned long reg_addr, reg_size; + + if (nodeoffset < 0 || !plic || !fdt) + return SBI_ENODEV; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size); + if (rc < 0 || !reg_addr || !reg_size) + return SBI_ENODEV; + plic->addr = reg_addr; + + val = fdt_getprop(fdt, nodeoffset, "riscv,ndev", &len); + if (len > 0) + plic->num_src = fdt32_to_cpu(*val); + + return 0; +} + +int fdt_parse_plic(void *fdt, struct plic_data *plic, const char *compat) +{ + int nodeoffset; + + if (!compat || !plic || !fdt) + return SBI_ENODEV; + + nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compat); + if (nodeoffset < 0) + return nodeoffset; + + return fdt_parse_plic_node(fdt, nodeoffset, plic); +} + +int fdt_parse_clint_node(void *fdt, int nodeoffset, bool for_timer, + struct clint_data *clint) +{ + const fdt32_t *val; + unsigned long reg_addr, reg_size; + int i, rc, count, cpu_offset, cpu_intc_offset; + u32 phandle, hwirq, hartid, first_hartid, last_hartid; + u32 match_hwirq = (for_timer) ? IRQ_M_TIMER : IRQ_M_SOFT; + + if (nodeoffset < 0 || !clint || !fdt) + return SBI_ENODEV; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size); + if (rc < 0 || !reg_addr || !reg_size) + return SBI_ENODEV; + clint->addr = reg_addr; + + val = fdt_getprop(fdt, nodeoffset, "interrupts-extended", &count); + if (!val || count < sizeof(fdt32_t)) + return SBI_EINVAL; + count = count / sizeof(fdt32_t); + + first_hartid = -1U; + last_hartid = 0; + clint->hart_count = 0; + for (i = 0; i < count; i += 2) { + phandle = fdt32_to_cpu(val[i]); + hwirq = fdt32_to_cpu(val[i + 1]); + + cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle); + if (cpu_intc_offset < 0) + continue; + + cpu_offset = fdt_parent_offset(fdt, cpu_intc_offset); + if (cpu_intc_offset < 0) + continue; + + rc = fdt_parse_hart_id(fdt, cpu_offset, &hartid); + if (rc) + continue; + + if (SBI_HARTMASK_MAX_BITS <= hartid) + continue; + + if (match_hwirq == hwirq) { + if (hartid < first_hartid) + first_hartid = hartid; + if (hartid > last_hartid) + last_hartid = hartid; + clint->hart_count++; + } + } + + if ((last_hartid < first_hartid) || first_hartid == -1U) + return SBI_ENODEV; + + clint->first_hartid = first_hartid; + count = last_hartid - first_hartid + 1; + if (clint->hart_count < count) + clint->hart_count = count; + + /* TODO: We should figure-out CLINT has_64bit_mmio from DT node */ + clint->has_64bit_mmio = TRUE; + + return 0; +} + +int fdt_parse_compat_addr(void *fdt, unsigned long *addr, + const char *compatible) +{ + int nodeoffset, rc; + + nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); + if (nodeoffset < 0) + return nodeoffset; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, addr, NULL); + if (rc < 0 || !addr) + return SBI_ENODEV; + + return 0; +} diff --git a/roms/opensbi/lib/utils/fdt/objects.mk b/roms/opensbi/lib/utils/fdt/objects.mk new file mode 100644 index 000000000..d9f1eae19 --- /dev/null +++ b/roms/opensbi/lib/utils/fdt/objects.mk @@ -0,0 +1,9 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (C) 2020 Bin Meng <bmeng.cn@gmail.com> +# + +libsbiutils-objs-y += fdt/fdt_domain.o +libsbiutils-objs-y += fdt/fdt_helper.o +libsbiutils-objs-y += fdt/fdt_fixup.o diff --git a/roms/opensbi/lib/utils/ipi/fdt_ipi.c b/roms/opensbi/lib/utils/ipi/fdt_ipi.c new file mode 100644 index 000000000..6562469da --- /dev/null +++ b/roms/opensbi/lib/utils/ipi/fdt_ipi.c @@ -0,0 +1,101 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_scratch.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/ipi/fdt_ipi.h> + +extern struct fdt_ipi fdt_ipi_clint; + +static struct fdt_ipi *ipi_drivers[] = { + &fdt_ipi_clint +}; + +static void dummy_send(u32 target_hart) +{ +} + +static void dummy_clear(u32 target_hart) +{ +} + +static struct fdt_ipi dummy = { + .match_table = NULL, + .cold_init = NULL, + .warm_init = NULL, + .exit = NULL, + .send = dummy_send, + .clear = dummy_clear +}; + +static struct fdt_ipi *current_driver = &dummy; + +void fdt_ipi_send(u32 target_hart) +{ + current_driver->send(target_hart); +} + +void fdt_ipi_clear(u32 target_hart) +{ + current_driver->clear(target_hart); +} + +void fdt_ipi_exit(void) +{ + if (current_driver->exit) + current_driver->exit(); +} + +static int fdt_ipi_warm_init(void) +{ + if (current_driver->warm_init) + return current_driver->warm_init(); + return 0; +} + +static int fdt_ipi_cold_init(void) +{ + int pos, noff, rc; + struct fdt_ipi *drv; + const struct fdt_match *match; + void *fdt = sbi_scratch_thishart_arg1_ptr(); + + for (pos = 0; pos < array_size(ipi_drivers); pos++) { + drv = ipi_drivers[pos]; + + noff = -1; + while ((noff = fdt_find_match(fdt, noff, + drv->match_table, &match)) >= 0) { + if (drv->cold_init) { + rc = drv->cold_init(fdt, noff, match); + if (rc) + return rc; + } + current_driver = drv; + } + + if (current_driver != &dummy) + break; + } + + return 0; +} + +int fdt_ipi_init(bool cold_boot) +{ + int rc; + + if (cold_boot) { + rc = fdt_ipi_cold_init(); + if (rc) + return rc; + } + + return fdt_ipi_warm_init(); +} diff --git a/roms/opensbi/lib/utils/ipi/fdt_ipi_clint.c b/roms/opensbi/lib/utils/ipi/fdt_ipi_clint.c new file mode 100644 index 000000000..e99244064 --- /dev/null +++ b/roms/opensbi/lib/utils/ipi/fdt_ipi_clint.c @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_error.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/ipi/fdt_ipi.h> +#include <sbi_utils/sys/clint.h> + +#define CLINT_IPI_MAX_NR 16 + +static unsigned long clint_ipi_count = 0; +static struct clint_data clint_ipi[CLINT_IPI_MAX_NR]; + +static int ipi_clint_cold_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + struct clint_data *ci; + + if (CLINT_IPI_MAX_NR <= clint_ipi_count) + return SBI_ENOSPC; + ci = &clint_ipi[clint_ipi_count++]; + + rc = fdt_parse_clint_node(fdt, nodeoff, FALSE, ci); + if (rc) + return rc; + + return clint_cold_ipi_init(ci); +} + +static const struct fdt_match ipi_clint_match[] = { + { .compatible = "riscv,clint0" }, + { }, +}; + +struct fdt_ipi fdt_ipi_clint = { + .match_table = ipi_clint_match, + .cold_init = ipi_clint_cold_init, + .warm_init = clint_warm_ipi_init, + .exit = NULL, + .send = clint_ipi_send, + .clear = clint_ipi_clear, +}; diff --git a/roms/opensbi/lib/utils/ipi/objects.mk b/roms/opensbi/lib/utils/ipi/objects.mk new file mode 100644 index 000000000..00719579d --- /dev/null +++ b/roms/opensbi/lib/utils/ipi/objects.mk @@ -0,0 +1,11 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +libsbiutils-objs-y += ipi/fdt_ipi.o +libsbiutils-objs-y += ipi/fdt_ipi_clint.o diff --git a/roms/opensbi/lib/utils/irqchip/fdt_irqchip.c b/roms/opensbi/lib/utils/irqchip/fdt_irqchip.c new file mode 100644 index 000000000..3630be657 --- /dev/null +++ b/roms/opensbi/lib/utils/irqchip/fdt_irqchip.c @@ -0,0 +1,74 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_scratch.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/irqchip/fdt_irqchip.h> + +extern struct fdt_irqchip fdt_irqchip_plic; + +static struct fdt_irqchip *irqchip_drivers[] = { + &fdt_irqchip_plic +}; + +static struct fdt_irqchip *current_driver = NULL; + +void fdt_irqchip_exit(void) +{ + if (current_driver && current_driver->exit) + current_driver->exit(); +} + +static int fdt_irqchip_warm_init(void) +{ + if (current_driver && current_driver->warm_init) + return current_driver->warm_init(); + return 0; +} + +static int fdt_irqchip_cold_init(void) +{ + int pos, noff, rc; + struct fdt_irqchip *drv; + const struct fdt_match *match; + void *fdt = sbi_scratch_thishart_arg1_ptr(); + + for (pos = 0; pos < array_size(irqchip_drivers); pos++) { + drv = irqchip_drivers[pos]; + + noff = -1; + while ((noff = fdt_find_match(fdt, noff, + drv->match_table, &match)) >= 0) { + if (drv->cold_init) { + rc = drv->cold_init(fdt, noff, match); + if (rc) + return rc; + } + current_driver = drv; + } + + if (current_driver) + break; + } + + return 0; +} + +int fdt_irqchip_init(bool cold_boot) +{ + int rc; + + if (cold_boot) { + rc = fdt_irqchip_cold_init(); + if (rc) + return rc; + } + + return fdt_irqchip_warm_init(); +} diff --git a/roms/opensbi/lib/utils/irqchip/fdt_irqchip_plic.c b/roms/opensbi/lib/utils/irqchip/fdt_irqchip_plic.c new file mode 100644 index 000000000..18d2797c9 --- /dev/null +++ b/roms/opensbi/lib/utils/irqchip/fdt_irqchip_plic.c @@ -0,0 +1,120 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <libfdt.h> +#include <sbi/riscv_asm.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hartmask.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/irqchip/fdt_irqchip.h> +#include <sbi_utils/irqchip/plic.h> + +#define PLIC_MAX_NR 16 + +static unsigned long plic_count = 0; +static struct plic_data plic[PLIC_MAX_NR]; + +static struct plic_data *plic_hartid2data[SBI_HARTMASK_MAX_BITS]; +static int plic_hartid2context[SBI_HARTMASK_MAX_BITS][2]; + +static int irqchip_plic_warm_init(void) +{ + u32 hartid = current_hartid(); + + return plic_warm_irqchip_init(plic_hartid2data[hartid], + plic_hartid2context[hartid][0], + plic_hartid2context[hartid][1]); +} + +static int irqchip_plic_update_hartid_table(void *fdt, int nodeoff, + struct plic_data *pd) +{ + const fdt32_t *val; + u32 phandle, hwirq, hartid; + int i, err, count, cpu_offset, cpu_intc_offset; + + val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &count); + if (!val || count < sizeof(fdt32_t)) + return SBI_EINVAL; + count = count / sizeof(fdt32_t); + + for (i = 0; i < count; i += 2) { + phandle = fdt32_to_cpu(val[i]); + hwirq = fdt32_to_cpu(val[i + 1]); + + cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle); + if (cpu_intc_offset < 0) + continue; + + cpu_offset = fdt_parent_offset(fdt, cpu_intc_offset); + if (cpu_intc_offset < 0) + continue; + + err = fdt_parse_hart_id(fdt, cpu_offset, &hartid); + if (err) + continue; + + if (SBI_HARTMASK_MAX_BITS <= hartid) + continue; + + plic_hartid2data[hartid] = pd; + switch (hwirq) { + case IRQ_M_EXT: + plic_hartid2context[hartid][0] = i / 2; + break; + case IRQ_S_EXT: + plic_hartid2context[hartid][1] = i / 2; + break; + } + } + + return 0; +} + +static int irqchip_plic_cold_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int i, rc; + struct plic_data *pd; + + if (PLIC_MAX_NR <= plic_count) + return SBI_ENOSPC; + pd = &plic[plic_count++]; + + rc = fdt_parse_plic_node(fdt, nodeoff, pd); + if (rc) + return rc; + + rc = plic_cold_irqchip_init(pd); + if (rc) + return rc; + + if (plic_count == 1) { + for (i = 0; i < SBI_HARTMASK_MAX_BITS; i++) { + plic_hartid2data[i] = NULL; + plic_hartid2context[i][0] = -1; + plic_hartid2context[i][1] = -1; + } + } + + return irqchip_plic_update_hartid_table(fdt, nodeoff, pd); +} + +static const struct fdt_match irqchip_plic_match[] = { + { .compatible = "riscv,plic0" }, + { .compatible = "sifive,plic-1.0.0" }, + { }, +}; + +struct fdt_irqchip fdt_irqchip_plic = { + .match_table = irqchip_plic_match, + .cold_init = irqchip_plic_cold_init, + .warm_init = irqchip_plic_warm_init, + .exit = NULL, +}; diff --git a/roms/opensbi/lib/utils/irqchip/objects.mk b/roms/opensbi/lib/utils/irqchip/objects.mk new file mode 100644 index 000000000..934f706b6 --- /dev/null +++ b/roms/opensbi/lib/utils/irqchip/objects.mk @@ -0,0 +1,12 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +libsbiutils-objs-y += irqchip/fdt_irqchip.o +libsbiutils-objs-y += irqchip/fdt_irqchip_plic.o +libsbiutils-objs-y += irqchip/plic.o diff --git a/roms/opensbi/lib/utils/irqchip/plic.c b/roms/opensbi/lib/utils/irqchip/plic.c new file mode 100644 index 000000000..7665c62ee --- /dev/null +++ b/roms/opensbi/lib/utils/irqchip/plic.c @@ -0,0 +1,100 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_io.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_string.h> +#include <sbi_utils/irqchip/plic.h> + +#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 void plic_set_priority(struct plic_data *plic, u32 source, u32 val) +{ + volatile void *plic_priority = (void *)plic->addr + + PLIC_PRIORITY_BASE + 4 * source; + writel(val, plic_priority); +} + +void plic_set_thresh(struct plic_data *plic, u32 cntxid, u32 val) +{ + volatile void *plic_thresh; + + if (!plic) + return; + + plic_thresh = (void *)plic->addr + + PLIC_CONTEXT_BASE + PLIC_CONTEXT_STRIDE * cntxid; + writel(val, plic_thresh); +} + +void plic_set_ie(struct plic_data *plic, u32 cntxid, u32 word_index, u32 val) +{ + volatile void *plic_ie; + + if (!plic) + return; + + plic_ie = (void *)plic->addr + + PLIC_ENABLE_BASE + PLIC_ENABLE_STRIDE * cntxid; + writel(val, plic_ie + word_index * 4); +} + +int plic_warm_irqchip_init(struct plic_data *plic, + int m_cntx_id, int s_cntx_id) +{ + size_t i, ie_words; + + if (!plic) + return SBI_EINVAL; + + ie_words = plic->num_src / 32 + 1; + + /* By default, disable all IRQs for M-mode of target HART */ + if (m_cntx_id > -1) { + for (i = 0; i < ie_words; i++) + plic_set_ie(plic, m_cntx_id, i, 0); + } + + /* By default, disable all IRQs for S-mode of target HART */ + if (s_cntx_id > -1) { + for (i = 0; i < ie_words; i++) + plic_set_ie(plic, s_cntx_id, i, 0); + } + + /* By default, disable M-mode threshold */ + if (m_cntx_id > -1) + plic_set_thresh(plic, m_cntx_id, 0x7); + + /* By default, disable S-mode threshold */ + if (s_cntx_id > -1) + plic_set_thresh(plic, s_cntx_id, 0x7); + + return 0; +} + +int plic_cold_irqchip_init(struct plic_data *plic) +{ + int i; + + if (!plic) + return SBI_EINVAL; + + /* Configure default priorities of all IRQs */ + for (i = 1; i <= plic->num_src; i++) + plic_set_priority(plic, i, 0); + + return 0; +} diff --git a/roms/opensbi/lib/utils/libfdt/.clang-format b/roms/opensbi/lib/utils/libfdt/.clang-format new file mode 100644 index 000000000..e3845288a --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/.clang-format @@ -0,0 +1 @@ +DisableFormat: true diff --git a/roms/opensbi/lib/utils/libfdt/Makefile.libfdt b/roms/opensbi/lib/utils/libfdt/Makefile.libfdt new file mode 100644 index 000000000..b6d8fc02d --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/Makefile.libfdt @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +# Makefile.libfdt +# +# This is not a complete Makefile of itself. Instead, it is designed to +# be easily embeddable into other systems of Makefiles. +# +LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1 +LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h +LIBFDT_VERSION = version.lds +LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c \ + fdt_addresses.c fdt_overlay.c fdt_check.c +LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o) +LIBFDT_LIB = libfdt-$(DTC_VERSION).$(SHAREDLIB_EXT) + +libfdt_clean: + @$(VECHO) CLEAN "(libfdt)" + rm -f $(STD_CLEANFILES:%=$(LIBFDT_dir)/%) + rm -f $(LIBFDT_dir)/$(LIBFDT_soname) diff --git a/roms/opensbi/lib/utils/libfdt/TODO b/roms/opensbi/lib/utils/libfdt/TODO new file mode 100644 index 000000000..288437e39 --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/TODO @@ -0,0 +1,3 @@ +- Tree traversal functions +- Graft function +- Complete libfdt.h documenting comments diff --git a/roms/opensbi/lib/utils/libfdt/fdt.c b/roms/opensbi/lib/utils/libfdt/fdt.c new file mode 100644 index 000000000..c28fcc115 --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +/* + * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks + * that the given buffer contains what appears to be a flattened + * device tree with sane information in its header. + */ +int32_t fdt_ro_probe_(const void *fdt) +{ + uint32_t totalsize = fdt_totalsize(fdt); + + if (can_assume(VALID_DTB)) + return totalsize; + + if (fdt_magic(fdt) == FDT_MAGIC) { + /* Complete tree */ + if (!can_assume(LATEST)) { + if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + if (fdt_last_comp_version(fdt) > + FDT_LAST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + } + } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { + /* Unfinished sequential-write blob */ + if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0) + return -FDT_ERR_BADSTATE; + } else { + return -FDT_ERR_BADMAGIC; + } + + if (totalsize < INT32_MAX) + return totalsize; + else + return -FDT_ERR_TRUNCATED; +} + +static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) +{ + return (off >= hdrsize) && (off <= totalsize); +} + +static int check_block_(uint32_t hdrsize, uint32_t totalsize, + uint32_t base, uint32_t size) +{ + if (!check_off_(hdrsize, totalsize, base)) + return 0; /* block start out of bounds */ + if ((base + size) < base) + return 0; /* overflow */ + if (!check_off_(hdrsize, totalsize, base + size)) + return 0; /* block end out of bounds */ + return 1; +} + +size_t fdt_header_size_(uint32_t version) +{ + if (version <= 1) + return FDT_V1_SIZE; + else if (version <= 2) + return FDT_V2_SIZE; + else if (version <= 3) + return FDT_V3_SIZE; + else if (version <= 16) + return FDT_V16_SIZE; + else + return FDT_V17_SIZE; +} + +size_t fdt_header_size(const void *fdt) +{ + return can_assume(LATEST) ? FDT_V17_SIZE : + fdt_header_size_(fdt_version(fdt)); +} + +int fdt_check_header(const void *fdt) +{ + size_t hdrsize; + + if (fdt_magic(fdt) != FDT_MAGIC) + return -FDT_ERR_BADMAGIC; + if (!can_assume(LATEST)) { + if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) + || (fdt_last_comp_version(fdt) > + FDT_LAST_SUPPORTED_VERSION)) + return -FDT_ERR_BADVERSION; + if (fdt_version(fdt) < fdt_last_comp_version(fdt)) + return -FDT_ERR_BADVERSION; + } + hdrsize = fdt_header_size(fdt); + if (!can_assume(VALID_DTB)) { + + if ((fdt_totalsize(fdt) < hdrsize) + || (fdt_totalsize(fdt) > INT_MAX)) + return -FDT_ERR_TRUNCATED; + + /* Bounds check memrsv block */ + if (!check_off_(hdrsize, fdt_totalsize(fdt), + fdt_off_mem_rsvmap(fdt))) + return -FDT_ERR_TRUNCATED; + } + + if (!can_assume(VALID_DTB)) { + /* Bounds check structure block */ + if (!can_assume(LATEST) && fdt_version(fdt) < 17) { + if (!check_off_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_struct(fdt))) + return -FDT_ERR_TRUNCATED; + } else { + if (!check_block_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_struct(fdt), + fdt_size_dt_struct(fdt))) + return -FDT_ERR_TRUNCATED; + } + + /* Bounds check strings block */ + if (!check_block_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_strings(fdt), + fdt_size_dt_strings(fdt))) + return -FDT_ERR_TRUNCATED; + } + + return 0; +} + +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) +{ + unsigned absoffset = offset + fdt_off_dt_struct(fdt); + + if (!can_assume(VALID_INPUT)) + if ((absoffset < offset) + || ((absoffset + len) < absoffset) + || (absoffset + len) > fdt_totalsize(fdt)) + return NULL; + + if (can_assume(LATEST) || fdt_version(fdt) >= 0x11) + if (((offset + len) < offset) + || ((offset + len) > fdt_size_dt_struct(fdt))) + return NULL; + + return fdt_offset_ptr_(fdt, offset); +} + +uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) +{ + const fdt32_t *tagp, *lenp; + uint32_t tag; + int offset = startoffset; + const char *p; + + *nextoffset = -FDT_ERR_TRUNCATED; + tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); + if (!can_assume(VALID_DTB) && !tagp) + return FDT_END; /* premature end */ + tag = fdt32_to_cpu(*tagp); + offset += FDT_TAGSIZE; + + *nextoffset = -FDT_ERR_BADSTRUCTURE; + switch (tag) { + case FDT_BEGIN_NODE: + /* skip name */ + do { + p = fdt_offset_ptr(fdt, offset++, 1); + } while (p && (*p != '\0')); + if (!can_assume(VALID_DTB) && !p) + return FDT_END; /* premature end */ + break; + + case FDT_PROP: + lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); + if (!can_assume(VALID_DTB) && !lenp) + return FDT_END; /* premature end */ + /* skip-name offset, length and value */ + offset += sizeof(struct fdt_property) - FDT_TAGSIZE + + fdt32_to_cpu(*lenp); + if (!can_assume(LATEST) && + fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && + ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) + offset += 4; + break; + + case FDT_END: + case FDT_END_NODE: + case FDT_NOP: + break; + + default: + return FDT_END; + } + + if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) + return FDT_END; /* premature end */ + + *nextoffset = FDT_TAGALIGN(offset); + return tag; +} + +int fdt_check_node_offset_(const void *fdt, int offset) +{ + if (can_assume(VALID_INPUT)) + return offset; + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int fdt_check_prop_offset_(const void *fdt, int offset) +{ + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int fdt_next_node(const void *fdt, int offset, int *depth) +{ + int nextoffset = 0; + uint32_t tag; + + if (offset >= 0) + if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) + return nextoffset; + + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_PROP: + case FDT_NOP: + break; + + case FDT_BEGIN_NODE: + if (depth) + (*depth)++; + break; + + case FDT_END_NODE: + if (depth && ((--(*depth)) < 0)) + return nextoffset; + break; + + case FDT_END: + if ((nextoffset >= 0) + || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) + return -FDT_ERR_NOTFOUND; + else + return nextoffset; + } + } while (tag != FDT_BEGIN_NODE); + + return offset; +} + +int fdt_first_subnode(const void *fdt, int offset) +{ + int depth = 0; + + offset = fdt_next_node(fdt, offset, &depth); + if (offset < 0 || depth != 1) + return -FDT_ERR_NOTFOUND; + + return offset; +} + +int fdt_next_subnode(const void *fdt, int offset) +{ + int depth = 1; + + /* + * With respect to the parent, the depth of the next subnode will be + * the same as the last. + */ + do { + offset = fdt_next_node(fdt, offset, &depth); + if (offset < 0 || depth < 1) + return -FDT_ERR_NOTFOUND; + } while (depth > 1); + + return offset; +} + +const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) +{ + int len = strlen(s) + 1; + const char *last = strtab + tabsize - len; + const char *p; + + for (p = strtab; p <= last; p++) + if (memcmp(p, s, len) == 0) + return p; + return NULL; +} + +int fdt_move(const void *fdt, void *buf, int bufsize) +{ + FDT_RO_PROBE(fdt); + + if (fdt_totalsize(fdt) > bufsize) + return -FDT_ERR_NOSPACE; + + memmove(buf, fdt, fdt_totalsize(fdt)); + return 0; +} diff --git a/roms/opensbi/lib/utils/libfdt/fdt.h b/roms/opensbi/lib/utils/libfdt/fdt.h new file mode 100644 index 000000000..f2e68807f --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef FDT_H +#define FDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2012 Kim Phillips, Freescale Semiconductor. + */ + +#ifndef __ASSEMBLY__ + +struct fdt_header { + fdt32_t magic; /* magic word FDT_MAGIC */ + fdt32_t totalsize; /* total size of DT block */ + fdt32_t off_dt_struct; /* offset to structure */ + fdt32_t off_dt_strings; /* offset to strings */ + fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ + fdt32_t version; /* format version */ + fdt32_t last_comp_version; /* last compatible version */ + + /* version 2 fields below */ + fdt32_t boot_cpuid_phys; /* Which physical CPU id we're + booting on */ + /* version 3 fields below */ + fdt32_t size_dt_strings; /* size of the strings block */ + + /* version 17 fields below */ + fdt32_t size_dt_struct; /* size of the structure block */ +}; + +struct fdt_reserve_entry { + fdt64_t address; + fdt64_t size; +}; + +struct fdt_node_header { + fdt32_t tag; + char name[0]; +}; + +struct fdt_property { + fdt32_t tag; + fdt32_t len; + fdt32_t nameoff; + char data[0]; +}; + +#endif /* !__ASSEMBLY */ + +#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ +#define FDT_TAGSIZE sizeof(fdt32_t) + +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, + size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 + +#define FDT_V1_SIZE (7*sizeof(fdt32_t)) +#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) +#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t)) +#define FDT_V16_SIZE FDT_V3_SIZE +#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) + +#endif /* FDT_H */ diff --git a/roms/opensbi/lib/utils/libfdt/fdt_addresses.c b/roms/opensbi/lib/utils/libfdt/fdt_addresses.c new file mode 100644 index 000000000..9a82cd0ba --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt_addresses.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au> + * Copyright (C) 2018 embedded brains GmbH + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int fdt_cells(const void *fdt, int nodeoffset, const char *name) +{ + const fdt32_t *c; + uint32_t val; + int len; + + c = fdt_getprop(fdt, nodeoffset, name, &len); + if (!c) + return len; + + if (len != sizeof(*c)) + return -FDT_ERR_BADNCELLS; + + val = fdt32_to_cpu(*c); + if (val > FDT_MAX_NCELLS) + return -FDT_ERR_BADNCELLS; + + return (int)val; +} + +int fdt_address_cells(const void *fdt, int nodeoffset) +{ + int val; + + val = fdt_cells(fdt, nodeoffset, "#address-cells"); + if (val == 0) + return -FDT_ERR_BADNCELLS; + if (val == -FDT_ERR_NOTFOUND) + return 2; + return val; +} + +int fdt_size_cells(const void *fdt, int nodeoffset) +{ + int val; + + val = fdt_cells(fdt, nodeoffset, "#size-cells"); + if (val == -FDT_ERR_NOTFOUND) + return 1; + return val; +} + +/* This function assumes that [address|size]_cells is 1 or 2 */ +int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, + const char *name, uint64_t addr, uint64_t size) +{ + int addr_cells, size_cells, ret; + uint8_t data[sizeof(fdt64_t) * 2], *prop; + + ret = fdt_address_cells(fdt, parent); + if (ret < 0) + return ret; + addr_cells = ret; + + ret = fdt_size_cells(fdt, parent); + if (ret < 0) + return ret; + size_cells = ret; + + /* check validity of address */ + prop = data; + if (addr_cells == 1) { + if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size)) + return -FDT_ERR_BADVALUE; + + fdt32_st(prop, (uint32_t)addr); + } else if (addr_cells == 2) { + fdt64_st(prop, addr); + } else { + return -FDT_ERR_BADNCELLS; + } + + /* check validity of size */ + prop += addr_cells * sizeof(fdt32_t); + if (size_cells == 1) { + if (size > UINT32_MAX) + return -FDT_ERR_BADVALUE; + + fdt32_st(prop, (uint32_t)size); + } else if (size_cells == 2) { + fdt64_st(prop, size); + } else { + return -FDT_ERR_BADNCELLS; + } + + return fdt_appendprop(fdt, nodeoffset, name, data, + (addr_cells + size_cells) * sizeof(fdt32_t)); +} diff --git a/roms/opensbi/lib/utils/libfdt/fdt_check.c b/roms/opensbi/lib/utils/libfdt/fdt_check.c new file mode 100644 index 000000000..7f6a96c6b --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt_check.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +int fdt_check_full(const void *fdt, size_t bufsize) +{ + int err; + int num_memrsv; + int offset, nextoffset = 0; + uint32_t tag; + unsigned int depth = 0; + const void *prop; + const char *propname; + + if (bufsize < FDT_V1_SIZE) + return -FDT_ERR_TRUNCATED; + err = fdt_check_header(fdt); + if (err != 0) + return err; + if (bufsize < fdt_totalsize(fdt)) + return -FDT_ERR_TRUNCATED; + + num_memrsv = fdt_num_mem_rsv(fdt); + if (num_memrsv < 0) + return num_memrsv; + + while (1) { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + if (nextoffset < 0) + return nextoffset; + + switch (tag) { + case FDT_NOP: + break; + + case FDT_END: + if (depth != 0) + return -FDT_ERR_BADSTRUCTURE; + return 0; + + case FDT_BEGIN_NODE: + depth++; + if (depth > INT_MAX) + return -FDT_ERR_BADSTRUCTURE; + break; + + case FDT_END_NODE: + if (depth == 0) + return -FDT_ERR_BADSTRUCTURE; + depth--; + break; + + case FDT_PROP: + prop = fdt_getprop_by_offset(fdt, offset, &propname, + &err); + if (!prop) + return err; + break; + + default: + return -FDT_ERR_INTERNAL; + } + } +} diff --git a/roms/opensbi/lib/utils/libfdt/fdt_empty_tree.c b/roms/opensbi/lib/utils/libfdt/fdt_empty_tree.c new file mode 100644 index 000000000..49d54d44b --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt_empty_tree.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2012 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +int fdt_create_empty_tree(void *buf, int bufsize) +{ + int err; + + err = fdt_create(buf, bufsize); + if (err) + return err; + + err = fdt_finish_reservemap(buf); + if (err) + return err; + + err = fdt_begin_node(buf, ""); + if (err) + return err; + + err = fdt_end_node(buf); + if (err) + return err; + + err = fdt_finish(buf); + if (err) + return err; + + return fdt_open_into(buf, buf, bufsize); +} diff --git a/roms/opensbi/lib/utils/libfdt/fdt_overlay.c b/roms/opensbi/lib/utils/libfdt/fdt_overlay.c new file mode 100644 index 000000000..b310e49a6 --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt_overlay.c @@ -0,0 +1,881 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2016 Free Electrons + * Copyright (C) 2016 NextThing Co. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +/** + * overlay_get_target_phandle - retrieves the target phandle of a fragment + * @fdto: pointer to the device tree overlay blob + * @fragment: node offset of the fragment in the overlay + * + * overlay_get_target_phandle() retrieves the target phandle of an + * overlay fragment when that fragment uses a phandle (target + * property) instead of a path (target-path property). + * + * returns: + * the phandle pointed by the target property + * 0, if the phandle was not found + * -1, if the phandle was malformed + */ +static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) +{ + const fdt32_t *val; + int len; + + val = fdt_getprop(fdto, fragment, "target", &len); + if (!val) + return 0; + + if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) + return (uint32_t)-1; + + return fdt32_to_cpu(*val); +} + +/** + * overlay_get_target - retrieves the offset of a fragment's target + * @fdt: Base device tree blob + * @fdto: Device tree overlay blob + * @fragment: node offset of the fragment in the overlay + * @pathp: pointer which receives the path of the target (or NULL) + * + * overlay_get_target() retrieves the target offset in the base + * device tree of a fragment, no matter how the actual targeting is + * done (through a phandle or a path) + * + * returns: + * the targeted node offset in the base device tree + * Negative error code on error + */ +static int overlay_get_target(const void *fdt, const void *fdto, + int fragment, char const **pathp) +{ + uint32_t phandle; + const char *path = NULL; + int path_len = 0, ret; + + /* Try first to do a phandle based lookup */ + phandle = overlay_get_target_phandle(fdto, fragment); + if (phandle == (uint32_t)-1) + return -FDT_ERR_BADPHANDLE; + + /* no phandle, try path */ + if (!phandle) { + /* And then a path based lookup */ + path = fdt_getprop(fdto, fragment, "target-path", &path_len); + if (path) + ret = fdt_path_offset(fdt, path); + else + ret = path_len; + } else + ret = fdt_node_offset_by_phandle(fdt, phandle); + + /* + * If we haven't found either a target or a + * target-path property in a node that contains a + * __overlay__ subnode (we wouldn't be called + * otherwise), consider it a improperly written + * overlay + */ + if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) + ret = -FDT_ERR_BADOVERLAY; + + /* return on error */ + if (ret < 0) + return ret; + + /* return pointer to path (if available) */ + if (pathp) + *pathp = path ? path : NULL; + + return ret; +} + +/** + * overlay_phandle_add_offset - Increases a phandle by an offset + * @fdt: Base device tree blob + * @node: Device tree overlay blob + * @name: Name of the property to modify (phandle or linux,phandle) + * @delta: offset to apply + * + * overlay_phandle_add_offset() increments a node phandle by a given + * offset. + * + * returns: + * 0 on success. + * Negative error code on error + */ +static int overlay_phandle_add_offset(void *fdt, int node, + const char *name, uint32_t delta) +{ + const fdt32_t *val; + uint32_t adj_val; + int len; + + val = fdt_getprop(fdt, node, name, &len); + if (!val) + return len; + + if (len != sizeof(*val)) + return -FDT_ERR_BADPHANDLE; + + adj_val = fdt32_to_cpu(*val); + if ((adj_val + delta) < adj_val) + return -FDT_ERR_NOPHANDLES; + + adj_val += delta; + if (adj_val == (uint32_t)-1) + return -FDT_ERR_NOPHANDLES; + + return fdt_setprop_inplace_u32(fdt, node, name, adj_val); +} + +/** + * overlay_adjust_node_phandles - Offsets the phandles of a node + * @fdto: Device tree overlay blob + * @node: Offset of the node we want to adjust + * @delta: Offset to shift the phandles of + * + * overlay_adjust_node_phandles() adds a constant to all the phandles + * of a given node. This is mainly use as part of the overlay + * application process, when we want to update all the overlay + * phandles to not conflict with the overlays of the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_adjust_node_phandles(void *fdto, int node, + uint32_t delta) +{ + int child; + int ret; + + ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + + ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + + fdt_for_each_subnode(child, fdto, node) { + ret = overlay_adjust_node_phandles(fdto, child, delta); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay + * @fdto: Device tree overlay blob + * @delta: Offset to shift the phandles of + * + * overlay_adjust_local_phandles() adds a constant to all the + * phandles of an overlay. This is mainly use as part of the overlay + * application process, when we want to update all the overlay + * phandles to not conflict with the overlays of the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) +{ + /* + * Start adjusting the phandles from the overlay root + */ + return overlay_adjust_node_phandles(fdto, 0, delta); +} + +/** + * overlay_update_local_node_references - Adjust the overlay references + * @fdto: Device tree overlay blob + * @tree_node: Node offset of the node to operate on + * @fixup_node: Node offset of the matching local fixups node + * @delta: Offset to shift the phandles of + * + * overlay_update_local_nodes_references() update the phandles + * pointing to a node within the device tree overlay by adding a + * constant delta. + * + * This is mainly used as part of a device tree application process, + * where you want the device tree overlays phandles to not conflict + * with the ones from the base device tree before merging them. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_update_local_node_references(void *fdto, + int tree_node, + int fixup_node, + uint32_t delta) +{ + int fixup_prop; + int fixup_child; + int ret; + + fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { + const fdt32_t *fixup_val; + const char *tree_val; + const char *name; + int fixup_len; + int tree_len; + int i; + + fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, + &name, &fixup_len); + if (!fixup_val) + return fixup_len; + + if (fixup_len % sizeof(uint32_t)) + return -FDT_ERR_BADOVERLAY; + + tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); + if (!tree_val) { + if (tree_len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + + return tree_len; + } + + for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) { + fdt32_t adj_val; + uint32_t poffset; + + poffset = fdt32_to_cpu(fixup_val[i]); + + /* + * phandles to fixup can be unaligned. + * + * Use a memcpy for the architectures that do + * not support unaligned accesses. + */ + memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); + + adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); + + ret = fdt_setprop_inplace_namelen_partial(fdto, + tree_node, + name, + strlen(name), + poffset, + &adj_val, + sizeof(adj_val)); + if (ret == -FDT_ERR_NOSPACE) + return -FDT_ERR_BADOVERLAY; + + if (ret) + return ret; + } + } + + fdt_for_each_subnode(fixup_child, fdto, fixup_node) { + const char *fixup_child_name = fdt_get_name(fdto, fixup_child, + NULL); + int tree_child; + + tree_child = fdt_subnode_offset(fdto, tree_node, + fixup_child_name); + if (tree_child == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + if (tree_child < 0) + return tree_child; + + ret = overlay_update_local_node_references(fdto, + tree_child, + fixup_child, + delta); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_update_local_references - Adjust the overlay references + * @fdto: Device tree overlay blob + * @delta: Offset to shift the phandles of + * + * overlay_update_local_references() update all the phandles pointing + * to a node within the device tree overlay by adding a constant + * delta to not conflict with the base overlay. + * + * This is mainly used as part of a device tree application process, + * where you want the device tree overlays phandles to not conflict + * with the ones from the base device tree before merging them. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_update_local_references(void *fdto, uint32_t delta) +{ + int fixups; + + fixups = fdt_path_offset(fdto, "/__local_fixups__"); + if (fixups < 0) { + /* There's no local phandles to adjust, bail out */ + if (fixups == -FDT_ERR_NOTFOUND) + return 0; + + return fixups; + } + + /* + * Update our local references from the root of the tree + */ + return overlay_update_local_node_references(fdto, 0, fixups, + delta); +} + +/** + * overlay_fixup_one_phandle - Set an overlay phandle to the base one + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * @symbols_off: Node offset of the symbols node in the base device tree + * @path: Path to a node holding a phandle in the overlay + * @path_len: number of path characters to consider + * @name: Name of the property holding the phandle reference in the overlay + * @name_len: number of name characters to consider + * @poffset: Offset within the overlay property where the phandle is stored + * @label: Label of the node referenced by the phandle + * + * overlay_fixup_one_phandle() resolves an overlay phandle pointing to + * a node in the base device tree. + * + * This is part of the device tree overlay application process, when + * you want all the phandles in the overlay to point to the actual + * base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_one_phandle(void *fdt, void *fdto, + int symbols_off, + const char *path, uint32_t path_len, + const char *name, uint32_t name_len, + int poffset, const char *label) +{ + const char *symbol_path; + uint32_t phandle; + fdt32_t phandle_prop; + int symbol_off, fixup_off; + int prop_len; + + if (symbols_off < 0) + return symbols_off; + + symbol_path = fdt_getprop(fdt, symbols_off, label, + &prop_len); + if (!symbol_path) + return prop_len; + + symbol_off = fdt_path_offset(fdt, symbol_path); + if (symbol_off < 0) + return symbol_off; + + phandle = fdt_get_phandle(fdt, symbol_off); + if (!phandle) + return -FDT_ERR_NOTFOUND; + + fixup_off = fdt_path_offset_namelen(fdto, path, path_len); + if (fixup_off == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + if (fixup_off < 0) + return fixup_off; + + phandle_prop = cpu_to_fdt32(phandle); + return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, + name, name_len, poffset, + &phandle_prop, + sizeof(phandle_prop)); +}; + +/** + * overlay_fixup_phandle - Set an overlay phandle to the base one + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * @symbols_off: Node offset of the symbols node in the base device tree + * @property: Property offset in the overlay holding the list of fixups + * + * overlay_fixup_phandle() resolves all the overlay phandles pointed + * to in a __fixups__ property, and updates them to match the phandles + * in use in the base device tree. + * + * This is part of the device tree overlay application process, when + * you want all the phandles in the overlay to point to the actual + * base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, + int property) +{ + const char *value; + const char *label; + int len; + + value = fdt_getprop_by_offset(fdto, property, + &label, &len); + if (!value) { + if (len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + + return len; + } + + do { + const char *path, *name, *fixup_end; + const char *fixup_str = value; + uint32_t path_len, name_len; + uint32_t fixup_len; + char *sep, *endptr; + int poffset, ret; + + fixup_end = memchr(value, '\0', len); + if (!fixup_end) + return -FDT_ERR_BADOVERLAY; + fixup_len = fixup_end - fixup_str; + + len -= fixup_len + 1; + value += fixup_len + 1; + + path = fixup_str; + sep = memchr(fixup_str, ':', fixup_len); + if (!sep || *sep != ':') + return -FDT_ERR_BADOVERLAY; + + path_len = sep - path; + if (path_len == (fixup_len - 1)) + return -FDT_ERR_BADOVERLAY; + + fixup_len -= path_len + 1; + name = sep + 1; + sep = memchr(name, ':', fixup_len); + if (!sep || *sep != ':') + return -FDT_ERR_BADOVERLAY; + + name_len = sep - name; + if (!name_len) + return -FDT_ERR_BADOVERLAY; + + poffset = strtoul(sep + 1, &endptr, 10); + if ((*endptr != '\0') || (endptr <= (sep + 1))) + return -FDT_ERR_BADOVERLAY; + + ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, + path, path_len, name, name_len, + poffset, label); + if (ret) + return ret; + } while (len > 0); + + return 0; +} + +/** + * overlay_fixup_phandles - Resolve the overlay phandles to the base + * device tree + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_fixup_phandles() resolves all the overlay phandles pointing + * to nodes in the base device tree. + * + * This is one of the steps of the device tree overlay application + * process, when you want all the phandles in the overlay to point to + * the actual base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_phandles(void *fdt, void *fdto) +{ + int fixups_off, symbols_off; + int property; + + /* We can have overlays without any fixups */ + fixups_off = fdt_path_offset(fdto, "/__fixups__"); + if (fixups_off == -FDT_ERR_NOTFOUND) + return 0; /* nothing to do */ + if (fixups_off < 0) + return fixups_off; + + /* And base DTs without symbols */ + symbols_off = fdt_path_offset(fdt, "/__symbols__"); + if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) + return symbols_off; + + fdt_for_each_property_offset(property, fdto, fixups_off) { + int ret; + + ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_apply_node - Merges a node into the base device tree + * @fdt: Base Device Tree blob + * @target: Node offset in the base device tree to apply the fragment to + * @fdto: Device tree overlay blob + * @node: Node offset in the overlay holding the changes to merge + * + * overlay_apply_node() merges a node into a target base device tree + * node pointed. + * + * This is part of the final step in the device tree overlay + * application process, when all the phandles have been adjusted and + * resolved and you just have to merge overlay into the base device + * tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_apply_node(void *fdt, int target, + void *fdto, int node) +{ + int property; + int subnode; + + fdt_for_each_property_offset(property, fdto, node) { + const char *name; + const void *prop; + int prop_len; + int ret; + + prop = fdt_getprop_by_offset(fdto, property, &name, + &prop_len); + if (prop_len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + if (prop_len < 0) + return prop_len; + + ret = fdt_setprop(fdt, target, name, prop, prop_len); + if (ret) + return ret; + } + + fdt_for_each_subnode(subnode, fdto, node) { + const char *name = fdt_get_name(fdto, subnode, NULL); + int nnode; + int ret; + + nnode = fdt_add_subnode(fdt, target, name); + if (nnode == -FDT_ERR_EXISTS) { + nnode = fdt_subnode_offset(fdt, target, name); + if (nnode == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + } + + if (nnode < 0) + return nnode; + + ret = overlay_apply_node(fdt, nnode, fdto, subnode); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_merge - Merge an overlay into its base device tree + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_merge() merges an overlay into its base device tree. + * + * This is the next to last step in the device tree overlay application + * process, when all the phandles have been adjusted and resolved and + * you just have to merge overlay into the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_merge(void *fdt, void *fdto) +{ + int fragment; + + fdt_for_each_subnode(fragment, fdto, 0) { + int overlay; + int target; + int ret; + + /* + * Each fragments will have an __overlay__ node. If + * they don't, it's not supposed to be merged + */ + overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); + if (overlay == -FDT_ERR_NOTFOUND) + continue; + + if (overlay < 0) + return overlay; + + target = overlay_get_target(fdt, fdto, fragment, NULL); + if (target < 0) + return target; + + ret = overlay_apply_node(fdt, target, fdto, overlay); + if (ret) + return ret; + } + + return 0; +} + +static int get_path_len(const void *fdt, int nodeoffset) +{ + int len = 0, namelen; + const char *name; + + FDT_RO_PROBE(fdt); + + for (;;) { + name = fdt_get_name(fdt, nodeoffset, &namelen); + if (!name) + return namelen; + + /* root? we're done */ + if (namelen == 0) + break; + + nodeoffset = fdt_parent_offset(fdt, nodeoffset); + if (nodeoffset < 0) + return nodeoffset; + len += namelen + 1; + } + + /* in case of root pretend it's "/" */ + if (len == 0) + len++; + return len; +} + +/** + * overlay_symbol_update - Update the symbols of base tree after a merge + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_symbol_update() updates the symbols of the base tree with the + * symbols of the applied overlay + * + * This is the last step in the device tree overlay application + * process, allowing the reference of overlay symbols by subsequent + * overlay operations. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_symbol_update(void *fdt, void *fdto) +{ + int root_sym, ov_sym, prop, path_len, fragment, target; + int len, frag_name_len, ret, rel_path_len; + const char *s, *e; + const char *path; + const char *name; + const char *frag_name; + const char *rel_path; + const char *target_path; + char *buf; + void *p; + + ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); + + /* if no overlay symbols exist no problem */ + if (ov_sym < 0) + return 0; + + root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); + + /* it no root symbols exist we should create them */ + if (root_sym == -FDT_ERR_NOTFOUND) + root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); + + /* any error is fatal now */ + if (root_sym < 0) + return root_sym; + + /* iterate over each overlay symbol */ + fdt_for_each_property_offset(prop, fdto, ov_sym) { + path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); + if (!path) + return path_len; + + /* verify it's a string property (terminated by a single \0) */ + if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) + return -FDT_ERR_BADVALUE; + + /* keep end marker to avoid strlen() */ + e = path + path_len; + + if (*path != '/') + return -FDT_ERR_BADVALUE; + + /* get fragment name first */ + s = strchr(path + 1, '/'); + if (!s) { + /* Symbol refers to something that won't end + * up in the target tree */ + continue; + } + + frag_name = path + 1; + frag_name_len = s - path - 1; + + /* verify format; safe since "s" lies in \0 terminated prop */ + len = sizeof("/__overlay__/") - 1; + if ((e - s) > len && (memcmp(s, "/__overlay__/", len) == 0)) { + /* /<fragment-name>/__overlay__/<relative-subnode-path> */ + rel_path = s + len; + rel_path_len = e - rel_path - 1; + } else if ((e - s) == len + && (memcmp(s, "/__overlay__", len - 1) == 0)) { + /* /<fragment-name>/__overlay__ */ + rel_path = ""; + rel_path_len = 0; + } else { + /* Symbol refers to something that won't end + * up in the target tree */ + continue; + } + + /* find the fragment index in which the symbol lies */ + ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, + frag_name_len); + /* not found? */ + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + fragment = ret; + + /* an __overlay__ subnode must exist */ + ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + + /* get the target of the fragment */ + ret = overlay_get_target(fdt, fdto, fragment, &target_path); + if (ret < 0) + return ret; + target = ret; + + /* if we have a target path use */ + if (!target_path) { + ret = get_path_len(fdt, target); + if (ret < 0) + return ret; + len = ret; + } else { + len = strlen(target_path); + } + + ret = fdt_setprop_placeholder(fdt, root_sym, name, + len + (len > 1) + rel_path_len + 1, &p); + if (ret < 0) + return ret; + + if (!target_path) { + /* again in case setprop_placeholder changed it */ + ret = overlay_get_target(fdt, fdto, fragment, &target_path); + if (ret < 0) + return ret; + target = ret; + } + + buf = p; + if (len > 1) { /* target is not root */ + if (!target_path) { + ret = fdt_get_path(fdt, target, buf, len + 1); + if (ret < 0) + return ret; + } else + memcpy(buf, target_path, len + 1); + + } else + len--; + + buf[len] = '/'; + memcpy(buf + len + 1, rel_path, rel_path_len); + buf[len + 1 + rel_path_len] = '\0'; + } + + return 0; +} + +int fdt_overlay_apply(void *fdt, void *fdto) +{ + uint32_t delta; + int ret; + + FDT_RO_PROBE(fdt); + FDT_RO_PROBE(fdto); + + ret = fdt_find_max_phandle(fdt, &delta); + if (ret) + goto err; + + ret = overlay_adjust_local_phandles(fdto, delta); + if (ret) + goto err; + + ret = overlay_update_local_references(fdto, delta); + if (ret) + goto err; + + ret = overlay_fixup_phandles(fdt, fdto); + if (ret) + goto err; + + ret = overlay_merge(fdt, fdto); + if (ret) + goto err; + + ret = overlay_symbol_update(fdt, fdto); + if (ret) + goto err; + + /* + * The overlay has been damaged, erase its magic. + */ + fdt_set_magic(fdto, ~0); + + return 0; + +err: + /* + * The overlay might have been damaged, erase its magic. + */ + fdt_set_magic(fdto, ~0); + + /* + * The base device tree might have been damaged, erase its + * magic. + */ + fdt_set_magic(fdt, ~0); + + return ret; +} diff --git a/roms/opensbi/lib/utils/libfdt/fdt_ro.c b/roms/opensbi/lib/utils/libfdt/fdt_ro.c new file mode 100644 index 000000000..e03570a56 --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt_ro.c @@ -0,0 +1,857 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int fdt_nodename_eq_(const void *fdt, int offset, + const char *s, int len) +{ + int olen; + const char *p = fdt_get_name(fdt, offset, &olen); + + if (!p || olen < len) + /* short match */ + return 0; + + if (memcmp(p, s, len) != 0) + return 0; + + if (p[len] == '\0') + return 1; + else if (!memchr(s, '@', len) && (p[len] == '@')) + return 1; + else + return 0; +} + +const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) +{ + int32_t totalsize; + uint32_t absoffset; + size_t len; + int err; + const char *s, *n; + + if (can_assume(VALID_INPUT)) { + s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; + + if (lenp) + *lenp = strlen(s); + return s; + } + totalsize = fdt_ro_probe_(fdt); + err = totalsize; + if (totalsize < 0) + goto fail; + + err = -FDT_ERR_BADOFFSET; + absoffset = stroffset + fdt_off_dt_strings(fdt); + if (absoffset >= totalsize) + goto fail; + len = totalsize - absoffset; + + if (fdt_magic(fdt) == FDT_MAGIC) { + if (stroffset < 0) + goto fail; + if (can_assume(LATEST) || fdt_version(fdt) >= 17) { + if (stroffset >= fdt_size_dt_strings(fdt)) + goto fail; + if ((fdt_size_dt_strings(fdt) - stroffset) < len) + len = fdt_size_dt_strings(fdt) - stroffset; + } + } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { + if ((stroffset >= 0) + || (stroffset < -fdt_size_dt_strings(fdt))) + goto fail; + if ((-stroffset) < len) + len = -stroffset; + } else { + err = -FDT_ERR_INTERNAL; + goto fail; + } + + s = (const char *)fdt + absoffset; + n = memchr(s, '\0', len); + if (!n) { + /* missing terminating NULL */ + err = -FDT_ERR_TRUNCATED; + goto fail; + } + + if (lenp) + *lenp = n - s; + return s; + +fail: + if (lenp) + *lenp = err; + return NULL; +} + +const char *fdt_string(const void *fdt, int stroffset) +{ + return fdt_get_string(fdt, stroffset, NULL); +} + +static int fdt_string_eq_(const void *fdt, int stroffset, + const char *s, int len) +{ + int slen; + const char *p = fdt_get_string(fdt, stroffset, &slen); + + return p && (slen == len) && (memcmp(p, s, len) == 0); +} + +int fdt_find_max_phandle(const void *fdt, uint32_t *phandle) +{ + uint32_t max = 0; + int offset = -1; + + while (true) { + uint32_t value; + + offset = fdt_next_node(fdt, offset, NULL); + if (offset < 0) { + if (offset == -FDT_ERR_NOTFOUND) + break; + + return offset; + } + + value = fdt_get_phandle(fdt, offset); + + if (value > max) + max = value; + } + + if (phandle) + *phandle = max; + + return 0; +} + +int fdt_generate_phandle(const void *fdt, uint32_t *phandle) +{ + uint32_t max; + int err; + + err = fdt_find_max_phandle(fdt, &max); + if (err < 0) + return err; + + if (max == FDT_MAX_PHANDLE) + return -FDT_ERR_NOPHANDLES; + + if (phandle) + *phandle = max + 1; + + return 0; +} + +static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) +{ + int offset = n * sizeof(struct fdt_reserve_entry); + int absoffset = fdt_off_mem_rsvmap(fdt) + offset; + + if (!can_assume(VALID_INPUT)) { + if (absoffset < fdt_off_mem_rsvmap(fdt)) + return NULL; + if (absoffset > fdt_totalsize(fdt) - + sizeof(struct fdt_reserve_entry)) + return NULL; + } + return fdt_mem_rsv_(fdt, n); +} + +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) +{ + const struct fdt_reserve_entry *re; + + FDT_RO_PROBE(fdt); + re = fdt_mem_rsv(fdt, n); + if (!can_assume(VALID_INPUT) && !re) + return -FDT_ERR_BADOFFSET; + + *address = fdt64_ld(&re->address); + *size = fdt64_ld(&re->size); + return 0; +} + +int fdt_num_mem_rsv(const void *fdt) +{ + int i; + const struct fdt_reserve_entry *re; + + for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { + if (fdt64_ld(&re->size) == 0) + return i; + } + return -FDT_ERR_TRUNCATED; +} + +static int nextprop_(const void *fdt, int offset) +{ + uint32_t tag; + int nextoffset; + + do { + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_END: + if (nextoffset >= 0) + return -FDT_ERR_BADSTRUCTURE; + else + return nextoffset; + + case FDT_PROP: + return offset; + } + offset = nextoffset; + } while (tag == FDT_NOP); + + return -FDT_ERR_NOTFOUND; +} + +int fdt_subnode_offset_namelen(const void *fdt, int offset, + const char *name, int namelen) +{ + int depth; + + FDT_RO_PROBE(fdt); + + for (depth = 0; + (offset >= 0) && (depth >= 0); + offset = fdt_next_node(fdt, offset, &depth)) + if ((depth == 1) + && fdt_nodename_eq_(fdt, offset, name, namelen)) + return offset; + + if (depth < 0) + return -FDT_ERR_NOTFOUND; + return offset; /* error */ +} + +int fdt_subnode_offset(const void *fdt, int parentoffset, + const char *name) +{ + return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) +{ + const char *end = path + namelen; + const char *p = path; + int offset = 0; + + FDT_RO_PROBE(fdt); + + /* see if we have an alias */ + if (*path != '/') { + const char *q = memchr(path, '/', end - p); + + if (!q) + q = end; + + p = fdt_get_alias_namelen(fdt, p, q - p); + if (!p) + return -FDT_ERR_BADPATH; + offset = fdt_path_offset(fdt, p); + + p = q; + } + + while (p < end) { + const char *q; + + while (*p == '/') { + p++; + if (p == end) + return offset; + } + q = memchr(p, '/', end - p); + if (! q) + q = end; + + offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); + if (offset < 0) + return offset; + + p = q; + } + + return offset; +} + +int fdt_path_offset(const void *fdt, const char *path) +{ + return fdt_path_offset_namelen(fdt, path, strlen(path)); +} + +const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) +{ + const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); + const char *nameptr; + int err; + + if (((err = fdt_ro_probe_(fdt)) < 0) + || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) + goto fail; + + nameptr = nh->name; + + if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { + /* + * For old FDT versions, match the naming conventions of V16: + * give only the leaf name (after all /). The actual tree + * contents are loosely checked. + */ + const char *leaf; + leaf = strrchr(nameptr, '/'); + if (leaf == NULL) { + err = -FDT_ERR_BADSTRUCTURE; + goto fail; + } + nameptr = leaf+1; + } + + if (len) + *len = strlen(nameptr); + + return nameptr; + + fail: + if (len) + *len = err; + return NULL; +} + +int fdt_first_property_offset(const void *fdt, int nodeoffset) +{ + int offset; + + if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) + return offset; + + return nextprop_(fdt, offset); +} + +int fdt_next_property_offset(const void *fdt, int offset) +{ + if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) + return offset; + + return nextprop_(fdt, offset); +} + +static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, + int offset, + int *lenp) +{ + int err; + const struct fdt_property *prop; + + if (!can_assume(VALID_INPUT) && + (err = fdt_check_prop_offset_(fdt, offset)) < 0) { + if (lenp) + *lenp = err; + return NULL; + } + + prop = fdt_offset_ptr_(fdt, offset); + + if (lenp) + *lenp = fdt32_ld(&prop->len); + + return prop; +} + +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *lenp) +{ + /* Prior to version 16, properties may need realignment + * and this API does not work. fdt_getprop_*() will, however. */ + + if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { + if (lenp) + *lenp = -FDT_ERR_BADVERSION; + return NULL; + } + + return fdt_get_property_by_offset_(fdt, offset, lenp); +} + +static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, + int offset, + const char *name, + int namelen, + int *lenp, + int *poffset) +{ + for (offset = fdt_first_property_offset(fdt, offset); + (offset >= 0); + (offset = fdt_next_property_offset(fdt, offset))) { + const struct fdt_property *prop; + + prop = fdt_get_property_by_offset_(fdt, offset, lenp); + if (!can_assume(LIBFDT_FLAWLESS) && !prop) { + offset = -FDT_ERR_INTERNAL; + break; + } + if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff), + name, namelen)) { + if (poffset) + *poffset = offset; + return prop; + } + } + + if (lenp) + *lenp = offset; + return NULL; +} + + +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int offset, + const char *name, + int namelen, int *lenp) +{ + /* Prior to version 16, properties may need realignment + * and this API does not work. fdt_getprop_*() will, however. */ + if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { + if (lenp) + *lenp = -FDT_ERR_BADVERSION; + return NULL; + } + + return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, + NULL); +} + + +const struct fdt_property *fdt_get_property(const void *fdt, + int nodeoffset, + const char *name, int *lenp) +{ + return fdt_get_property_namelen(fdt, nodeoffset, name, + strlen(name), lenp); +} + +const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp) +{ + int poffset; + const struct fdt_property *prop; + + prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, + &poffset); + if (!prop) + return NULL; + + /* Handle realignment */ + if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && + (poffset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8) + return prop->data + 4; + return prop->data; +} + +const void *fdt_getprop_by_offset(const void *fdt, int offset, + const char **namep, int *lenp) +{ + const struct fdt_property *prop; + + prop = fdt_get_property_by_offset_(fdt, offset, lenp); + if (!prop) + return NULL; + if (namep) { + const char *name; + int namelen; + + if (!can_assume(VALID_INPUT)) { + name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff), + &namelen); + if (!name) { + if (lenp) + *lenp = namelen; + return NULL; + } + *namep = name; + } else { + *namep = fdt_string(fdt, fdt32_ld(&prop->nameoff)); + } + } + + /* Handle realignment */ + if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && + (offset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8) + return prop->data + 4; + return prop->data; +} + +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); +} + +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) +{ + const fdt32_t *php; + int len; + + /* FIXME: This is a bit sub-optimal, since we potentially scan + * over all the properties twice. */ + php = fdt_getprop(fdt, nodeoffset, "phandle", &len); + if (!php || (len != sizeof(*php))) { + php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); + if (!php || (len != sizeof(*php))) + return 0; + } + + return fdt32_ld(php); +} + +const char *fdt_get_alias_namelen(const void *fdt, + const char *name, int namelen) +{ + int aliasoffset; + + aliasoffset = fdt_path_offset(fdt, "/aliases"); + if (aliasoffset < 0) + return NULL; + + return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); +} + +const char *fdt_get_alias(const void *fdt, const char *name) +{ + return fdt_get_alias_namelen(fdt, name, strlen(name)); +} + +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) +{ + int pdepth = 0, p = 0; + int offset, depth, namelen; + const char *name; + + FDT_RO_PROBE(fdt); + + if (buflen < 2) + return -FDT_ERR_NOSPACE; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + while (pdepth > depth) { + do { + p--; + } while (buf[p-1] != '/'); + pdepth--; + } + + if (pdepth >= depth) { + name = fdt_get_name(fdt, offset, &namelen); + if (!name) + return namelen; + if ((p + namelen + 1) <= buflen) { + memcpy(buf + p, name, namelen); + p += namelen; + buf[p++] = '/'; + pdepth++; + } + } + + if (offset == nodeoffset) { + if (pdepth < (depth + 1)) + return -FDT_ERR_NOSPACE; + + if (p > 1) /* special case so that root path is "/", not "" */ + p--; + buf[p] = '\0'; + return 0; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth) +{ + int offset, depth; + int supernodeoffset = -FDT_ERR_INTERNAL; + + FDT_RO_PROBE(fdt); + + if (supernodedepth < 0) + return -FDT_ERR_NOTFOUND; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + if (depth == supernodedepth) + supernodeoffset = offset; + + if (offset == nodeoffset) { + if (nodedepth) + *nodedepth = depth; + + if (supernodedepth > depth) + return -FDT_ERR_NOTFOUND; + else + return supernodeoffset; + } + } + + if (!can_assume(VALID_INPUT)) { + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_depth(const void *fdt, int nodeoffset) +{ + int nodedepth; + int err; + + err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); + if (err) + return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err : + -FDT_ERR_INTERNAL; + return nodedepth; +} + +int fdt_parent_offset(const void *fdt, int nodeoffset) +{ + int nodedepth = fdt_node_depth(fdt, nodeoffset); + + if (nodedepth < 0) + return nodedepth; + return fdt_supernode_atdepth_offset(fdt, nodeoffset, + nodedepth - 1, NULL); +} + +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen) +{ + int offset; + const void *val; + int len; + + FDT_RO_PROBE(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_getprop(), then if that didn't + * find what we want, we scan over them again making our way + * to the next node. Still it's the easiest to implement + * approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + val = fdt_getprop(fdt, offset, propname, &len); + if (val && (len == proplen) + && (memcmp(val, propval, len) == 0)) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) +{ + int offset; + + if ((phandle == 0) || (phandle == -1)) + return -FDT_ERR_BADPHANDLE; + + FDT_RO_PROBE(fdt); + + /* FIXME: The algorithm here is pretty horrible: we + * potentially scan each property of a node in + * fdt_get_phandle(), then if that didn't find what + * we want, we scan over them again making our way to the next + * node. Still it's the easiest to implement approach; + * performance can come later. */ + for (offset = fdt_next_node(fdt, -1, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + if (fdt_get_phandle(fdt, offset) == phandle) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) +{ + int len = strlen(str); + const char *p; + + while (listlen >= len) { + if (memcmp(str, strlist, len+1) == 0) + return 1; + p = memchr(strlist, '\0', listlen); + if (!p) + return 0; /* malformed strlist.. */ + listlen -= (p-strlist) + 1; + strlist = p + 1; + } + return 0; +} + +int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) +{ + const char *list, *end; + int length, count = 0; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) + return length; + + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) + return -FDT_ERR_BADVALUE; + + list += length; + count++; + } + + return count; +} + +int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, + const char *string) +{ + int length, len, idx = 0; + const char *list, *end; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) + return length; + + len = strlen(string) + 1; + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) + return -FDT_ERR_BADVALUE; + + if (length == len && memcmp(list, string, length) == 0) + return idx; + + list += length; + idx++; + } + + return -FDT_ERR_NOTFOUND; +} + +const char *fdt_stringlist_get(const void *fdt, int nodeoffset, + const char *property, int idx, + int *lenp) +{ + const char *list, *end; + int length; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) { + if (lenp) + *lenp = length; + + return NULL; + } + + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) { + if (lenp) + *lenp = -FDT_ERR_BADVALUE; + + return NULL; + } + + if (idx == 0) { + if (lenp) + *lenp = length - 1; + + return list; + } + + list += length; + idx--; + } + + if (lenp) + *lenp = -FDT_ERR_NOTFOUND; + + return NULL; +} + +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible) +{ + const void *prop; + int len; + + prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); + if (!prop) + return len; + + return !fdt_stringlist_contains(prop, len, compatible); +} + +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible) +{ + int offset, err; + + FDT_RO_PROBE(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_node_check_compatible(), then if + * that didn't find what we want, we scan over them again + * making our way to the next node. Still it's the easiest to + * implement approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + err = fdt_node_check_compatible(fdt, offset, compatible); + if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) + return err; + else if (err == 0) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} diff --git a/roms/opensbi/lib/utils/libfdt/fdt_rw.c b/roms/opensbi/lib/utils/libfdt/fdt_rw.c new file mode 100644 index 000000000..13854253f --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt_rw.c @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int fdt_blocks_misordered_(const void *fdt, + int mem_rsv_size, int struct_size) +{ + return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) + || (fdt_off_dt_struct(fdt) < + (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) + || (fdt_off_dt_strings(fdt) < + (fdt_off_dt_struct(fdt) + struct_size)) + || (fdt_totalsize(fdt) < + (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); +} + +static int fdt_rw_probe_(void *fdt) +{ + if (can_assume(VALID_DTB)) + return 0; + FDT_RO_PROBE(fdt); + + if (!can_assume(LATEST) && fdt_version(fdt) < 17) + return -FDT_ERR_BADVERSION; + if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry), + fdt_size_dt_struct(fdt))) + return -FDT_ERR_BADLAYOUT; + if (!can_assume(LATEST) && fdt_version(fdt) > 17) + fdt_set_version(fdt, 17); + + return 0; +} + +#define FDT_RW_PROBE(fdt) \ + { \ + int err_; \ + if ((err_ = fdt_rw_probe_(fdt)) != 0) \ + return err_; \ + } + +static inline int fdt_data_size_(void *fdt) +{ + return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); +} + +static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen) +{ + char *p = splicepoint; + char *end = (char *)fdt + fdt_data_size_(fdt); + + if (((p + oldlen) < p) || ((p + oldlen) > end)) + return -FDT_ERR_BADOFFSET; + if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt)) + return -FDT_ERR_BADOFFSET; + if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt))) + return -FDT_ERR_NOSPACE; + memmove(p + newlen, p + oldlen, end - p - oldlen); + return 0; +} + +static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p, + int oldn, int newn) +{ + int delta = (newn - oldn) * sizeof(*p); + int err; + err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); + if (err) + return err; + fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int fdt_splice_struct_(void *fdt, void *p, + int oldlen, int newlen) +{ + int delta = newlen - oldlen; + int err; + + if ((err = fdt_splice_(fdt, p, oldlen, newlen))) + return err; + + fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +/* Must only be used to roll back in case of error */ +static void fdt_del_last_string_(void *fdt, const char *s) +{ + int newlen = strlen(s) + 1; + + fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen); +} + +static int fdt_splice_string_(void *fdt, int newlen) +{ + void *p = (char *)fdt + + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); + int err; + + if ((err = fdt_splice_(fdt, p, 0, newlen))) + return err; + + fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); + return 0; +} + +/** + * fdt_find_add_string_() - Find or allocate a string + * + * @fdt: pointer to the device tree to check/adjust + * @s: string to find/add + * @allocated: Set to 0 if the string was found, 1 if not found and so + * allocated. Ignored if can_assume(NO_ROLLBACK) + * @return offset of string in the string table (whether found or added) + */ +static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) +{ + char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); + const char *p; + char *new; + int len = strlen(s) + 1; + int err; + + if (!can_assume(NO_ROLLBACK)) + *allocated = 0; + + p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s); + if (p) + /* found it */ + return (p - strtab); + + new = strtab + fdt_size_dt_strings(fdt); + err = fdt_splice_string_(fdt, len); + if (err) + return err; + + if (!can_assume(NO_ROLLBACK)) + *allocated = 1; + + memcpy(new, s, len); + return (new - strtab); +} + +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) +{ + struct fdt_reserve_entry *re; + int err; + + FDT_RW_PROBE(fdt); + + re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt)); + err = fdt_splice_mem_rsv_(fdt, re, 0, 1); + if (err) + return err; + + re->address = cpu_to_fdt64(address); + re->size = cpu_to_fdt64(size); + return 0; +} + +int fdt_del_mem_rsv(void *fdt, int n) +{ + struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); + + FDT_RW_PROBE(fdt); + + if (n >= fdt_num_mem_rsv(fdt)) + return -FDT_ERR_NOTFOUND; + + return fdt_splice_mem_rsv_(fdt, re, 1, 0); +} + +static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int oldlen; + int err; + + *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + if (!*prop) + return oldlen; + + if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), + FDT_TAGALIGN(len)))) + return err; + + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int proplen; + int nextoffset; + int namestroff; + int err; + int allocated; + + if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) + return nextoffset; + + namestroff = fdt_find_add_string_(fdt, name, &allocated); + if (namestroff < 0) + return namestroff; + + *prop = fdt_offset_ptr_w_(fdt, nextoffset); + proplen = sizeof(**prop) + FDT_TAGALIGN(len); + + err = fdt_splice_struct_(fdt, *prop, 0, proplen); + if (err) { + /* Delete the string if we failed to add it */ + if (!can_assume(NO_ROLLBACK) && allocated) + fdt_del_last_string_(fdt, name); + return err; + } + + (*prop)->tag = cpu_to_fdt32(FDT_PROP); + (*prop)->nameoff = cpu_to_fdt32(namestroff); + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +int fdt_set_name(void *fdt, int nodeoffset, const char *name) +{ + char *namep; + int oldlen, newlen; + int err; + + FDT_RW_PROBE(fdt); + + namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); + if (!namep) + return oldlen; + + newlen = strlen(name); + + err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1), + FDT_TAGALIGN(newlen+1)); + if (err) + return err; + + memcpy(namep, name, newlen+1); + return 0; +} + +int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, + int len, void **prop_data) +{ + struct fdt_property *prop; + int err; + + FDT_RW_PROBE(fdt); + + err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop); + if (err == -FDT_ERR_NOTFOUND) + err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); + if (err) + return err; + + *prop_data = prop->data; + return 0; +} + +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + void *prop_data; + int err; + + err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); + if (err) + return err; + + if (len) + memcpy(prop_data, val, len); + return 0; +} + +int fdt_appendprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err, oldlen, newlen; + + FDT_RW_PROBE(fdt); + + prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + if (prop) { + newlen = len + oldlen; + err = fdt_splice_struct_(fdt, prop->data, + FDT_TAGALIGN(oldlen), + FDT_TAGALIGN(newlen)); + if (err) + return err; + prop->len = cpu_to_fdt32(newlen); + memcpy(prop->data + oldlen, val, len); + } else { + err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); + if (err) + return err; + memcpy(prop->data, val, len); + } + return 0; +} + +int fdt_delprop(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len, proplen; + + FDT_RW_PROBE(fdt); + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (!prop) + return len; + + proplen = sizeof(*prop) + FDT_TAGALIGN(len); + return fdt_splice_struct_(fdt, prop, proplen, 0); +} + +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen) +{ + struct fdt_node_header *nh; + int offset, nextoffset; + int nodelen; + int err; + uint32_t tag; + fdt32_t *endtag; + + FDT_RW_PROBE(fdt); + + offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); + if (offset >= 0) + return -FDT_ERR_EXISTS; + else if (offset != -FDT_ERR_NOTFOUND) + return offset; + + /* Try to place the new node after the parent's properties */ + fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + } while ((tag == FDT_PROP) || (tag == FDT_NOP)); + + nh = fdt_offset_ptr_w_(fdt, offset); + nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; + + err = fdt_splice_struct_(fdt, nh, 0, nodelen); + if (err) + return err; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); + memcpy(nh->name, name, namelen); + endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); + *endtag = cpu_to_fdt32(FDT_END_NODE); + + return offset; +} + +int fdt_add_subnode(void *fdt, int parentoffset, const char *name) +{ + return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_del_node(void *fdt, int nodeoffset) +{ + int endoffset; + + FDT_RW_PROBE(fdt); + + endoffset = fdt_node_end_offset_(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset), + endoffset - nodeoffset, 0); +} + +static void fdt_packblocks_(const char *old, char *new, + int mem_rsv_size, int struct_size) +{ + int mem_rsv_off, struct_off, strings_off; + + mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); + struct_off = mem_rsv_off + mem_rsv_size; + strings_off = struct_off + struct_size; + + memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); + fdt_set_off_mem_rsvmap(new, mem_rsv_off); + + memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); + fdt_set_off_dt_struct(new, struct_off); + fdt_set_size_dt_struct(new, struct_size); + + memmove(new + strings_off, old + fdt_off_dt_strings(old), + fdt_size_dt_strings(old)); + fdt_set_off_dt_strings(new, strings_off); + fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); +} + +int fdt_open_into(const void *fdt, void *buf, int bufsize) +{ + int err; + int mem_rsv_size, struct_size; + int newsize; + const char *fdtstart = fdt; + const char *fdtend = fdtstart + fdt_totalsize(fdt); + char *tmp; + + FDT_RO_PROBE(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + + if (can_assume(LATEST) || fdt_version(fdt) >= 17) { + struct_size = fdt_size_dt_struct(fdt); + } else { + struct_size = 0; + while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) + ; + if (struct_size < 0) + return struct_size; + } + + if (can_assume(LIBFDT_ORDER) | + !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) { + /* no further work necessary */ + err = fdt_move(fdt, buf, bufsize); + if (err) + return err; + fdt_set_version(buf, 17); + fdt_set_size_dt_struct(buf, struct_size); + fdt_set_totalsize(buf, bufsize); + return 0; + } + + /* Need to reorder */ + newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size + + struct_size + fdt_size_dt_strings(fdt); + + if (bufsize < newsize) + return -FDT_ERR_NOSPACE; + + /* First attempt to build converted tree at beginning of buffer */ + tmp = buf; + /* But if that overlaps with the old tree... */ + if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { + /* Try right after the old tree instead */ + tmp = (char *)(uintptr_t)fdtend; + if ((tmp + newsize) > ((char *)buf + bufsize)) + return -FDT_ERR_NOSPACE; + } + + fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size); + memmove(buf, tmp, newsize); + + fdt_set_magic(buf, FDT_MAGIC); + fdt_set_totalsize(buf, bufsize); + fdt_set_version(buf, 17); + fdt_set_last_comp_version(buf, 16); + fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); + + return 0; +} + +int fdt_pack(void *fdt) +{ + int mem_rsv_size; + + FDT_RW_PROBE(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); + fdt_set_totalsize(fdt, fdt_data_size_(fdt)); + + return 0; +} diff --git a/roms/opensbi/lib/utils/libfdt/fdt_strerror.c b/roms/opensbi/lib/utils/libfdt/fdt_strerror.c new file mode 100644 index 000000000..768db66ea --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt_strerror.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +struct fdt_errtabent { + const char *str; +}; + +#define FDT_ERRTABENT(val) \ + [(val)] = { .str = #val, } + +static struct fdt_errtabent fdt_errtable[] = { + FDT_ERRTABENT(FDT_ERR_NOTFOUND), + FDT_ERRTABENT(FDT_ERR_EXISTS), + FDT_ERRTABENT(FDT_ERR_NOSPACE), + + FDT_ERRTABENT(FDT_ERR_BADOFFSET), + FDT_ERRTABENT(FDT_ERR_BADPATH), + FDT_ERRTABENT(FDT_ERR_BADPHANDLE), + FDT_ERRTABENT(FDT_ERR_BADSTATE), + + FDT_ERRTABENT(FDT_ERR_TRUNCATED), + FDT_ERRTABENT(FDT_ERR_BADMAGIC), + FDT_ERRTABENT(FDT_ERR_BADVERSION), + FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), + FDT_ERRTABENT(FDT_ERR_BADLAYOUT), + FDT_ERRTABENT(FDT_ERR_INTERNAL), + FDT_ERRTABENT(FDT_ERR_BADNCELLS), + FDT_ERRTABENT(FDT_ERR_BADVALUE), + FDT_ERRTABENT(FDT_ERR_BADOVERLAY), + FDT_ERRTABENT(FDT_ERR_NOPHANDLES), + FDT_ERRTABENT(FDT_ERR_BADFLAGS), +}; +#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) + +const char *fdt_strerror(int errval) +{ + if (errval > 0) + return "<valid offset/length>"; + else if (errval == 0) + return "<no error>"; + else if (errval > -FDT_ERRTABSIZE) { + const char *s = fdt_errtable[-errval].str; + + if (s) + return s; + } + + return "<unknown error>"; +} diff --git a/roms/opensbi/lib/utils/libfdt/fdt_sw.c b/roms/opensbi/lib/utils/libfdt/fdt_sw.c new file mode 100644 index 000000000..26759d5df --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt_sw.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int fdt_sw_probe_(void *fdt) +{ + if (!can_assume(VALID_INPUT)) { + if (fdt_magic(fdt) == FDT_MAGIC) + return -FDT_ERR_BADSTATE; + else if (fdt_magic(fdt) != FDT_SW_MAGIC) + return -FDT_ERR_BADMAGIC; + } + + return 0; +} + +#define FDT_SW_PROBE(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_(fdt)) != 0) \ + return err; \ + } + +/* 'memrsv' state: Initial state after fdt_create() + * + * Allowed functions: + * fdt_add_reservmap_entry() + * fdt_finish_reservemap() [moves to 'struct' state] + */ +static int fdt_sw_probe_memrsv_(void *fdt) +{ + int err = fdt_sw_probe_(fdt); + if (err) + return err; + + if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0) + return -FDT_ERR_BADSTATE; + return 0; +} + +#define FDT_SW_PROBE_MEMRSV(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ + return err; \ + } + +/* 'struct' state: Enter this state after fdt_finish_reservemap() + * + * Allowed functions: + * fdt_begin_node() + * fdt_end_node() + * fdt_property*() + * fdt_finish() [moves to 'complete' state] + */ +static int fdt_sw_probe_struct_(void *fdt) +{ + int err = fdt_sw_probe_(fdt); + if (err) + return err; + + if (!can_assume(VALID_INPUT) && + fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) + return -FDT_ERR_BADSTATE; + return 0; +} + +#define FDT_SW_PROBE_STRUCT(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ + return err; \ + } + +static inline uint32_t sw_flags(void *fdt) +{ + /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */ + return fdt_last_comp_version(fdt); +} + +/* 'complete' state: Enter this state after fdt_finish() + * + * Allowed functions: none + */ + +static void *fdt_grab_space_(void *fdt, size_t len) +{ + int offset = fdt_size_dt_struct(fdt); + int spaceleft; + + spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) + - fdt_size_dt_strings(fdt); + + if ((offset + len < offset) || (offset + len > spaceleft)) + return NULL; + + fdt_set_size_dt_struct(fdt, offset + len); + return fdt_offset_ptr_w_(fdt, offset); +} + +int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags) +{ + const size_t hdrsize = FDT_ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry)); + void *fdt = buf; + + if (bufsize < hdrsize) + return -FDT_ERR_NOSPACE; + + if (flags & ~FDT_CREATE_FLAGS_ALL) + return -FDT_ERR_BADFLAGS; + + memset(buf, 0, bufsize); + + /* + * magic and last_comp_version keep intermediate state during the fdt + * creation process, which is replaced with the proper FDT format by + * fdt_finish(). + * + * flags should be accessed with sw_flags(). + */ + fdt_set_magic(fdt, FDT_SW_MAGIC); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, flags); + + fdt_set_totalsize(fdt, bufsize); + + fdt_set_off_mem_rsvmap(fdt, hdrsize); + fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); + fdt_set_off_dt_strings(fdt, 0); + + return 0; +} + +int fdt_create(void *buf, int bufsize) +{ + return fdt_create_with_flags(buf, bufsize, 0); +} + +int fdt_resize(void *fdt, void *buf, int bufsize) +{ + size_t headsize, tailsize; + char *oldtail, *newtail; + + FDT_SW_PROBE(fdt); + + headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + tailsize = fdt_size_dt_strings(fdt); + + if (!can_assume(VALID_DTB) && + headsize + tailsize > fdt_totalsize(fdt)) + return -FDT_ERR_INTERNAL; + + if ((headsize + tailsize) > bufsize) + return -FDT_ERR_NOSPACE; + + oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; + newtail = (char *)buf + bufsize - tailsize; + + /* Two cases to avoid clobbering data if the old and new + * buffers partially overlap */ + if (buf <= fdt) { + memmove(buf, fdt, headsize); + memmove(newtail, oldtail, tailsize); + } else { + memmove(newtail, oldtail, tailsize); + memmove(buf, fdt, headsize); + } + + fdt_set_totalsize(buf, bufsize); + if (fdt_off_dt_strings(buf)) + fdt_set_off_dt_strings(buf, bufsize); + + return 0; +} + +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) +{ + struct fdt_reserve_entry *re; + int offset; + + FDT_SW_PROBE_MEMRSV(fdt); + + offset = fdt_off_dt_struct(fdt); + if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) + return -FDT_ERR_NOSPACE; + + re = (struct fdt_reserve_entry *)((char *)fdt + offset); + re->address = cpu_to_fdt64(addr); + re->size = cpu_to_fdt64(size); + + fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); + + return 0; +} + +int fdt_finish_reservemap(void *fdt) +{ + int err = fdt_add_reservemap_entry(fdt, 0, 0); + + if (err) + return err; + + fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); + return 0; +} + +int fdt_begin_node(void *fdt, const char *name) +{ + struct fdt_node_header *nh; + int namelen; + + FDT_SW_PROBE_STRUCT(fdt); + + namelen = strlen(name) + 1; + nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); + if (! nh) + return -FDT_ERR_NOSPACE; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memcpy(nh->name, name, namelen); + return 0; +} + +int fdt_end_node(void *fdt) +{ + fdt32_t *en; + + FDT_SW_PROBE_STRUCT(fdt); + + en = fdt_grab_space_(fdt, FDT_TAGSIZE); + if (! en) + return -FDT_ERR_NOSPACE; + + *en = cpu_to_fdt32(FDT_END_NODE); + return 0; +} + +static int fdt_add_string_(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + int strtabsize = fdt_size_dt_strings(fdt); + int len = strlen(s) + 1; + int struct_top, offset; + + offset = -strtabsize - len; + struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + if (fdt_totalsize(fdt) + offset < struct_top) + return 0; /* no more room :( */ + + memcpy(strtab + offset, s, len); + fdt_set_size_dt_strings(fdt, strtabsize + len); + return offset; +} + +/* Must only be used to roll back in case of error */ +static void fdt_del_last_string_(void *fdt, const char *s) +{ + int strtabsize = fdt_size_dt_strings(fdt); + int len = strlen(s) + 1; + + fdt_set_size_dt_strings(fdt, strtabsize - len); +} + +static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + int strtabsize = fdt_size_dt_strings(fdt); + const char *p; + + *allocated = 0; + + p = fdt_find_string_(strtab - strtabsize, strtabsize, s); + if (p) + return p - strtab; + + *allocated = 1; + + return fdt_add_string_(fdt, s); +} + +int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) +{ + struct fdt_property *prop; + int nameoff; + int allocated; + + FDT_SW_PROBE_STRUCT(fdt); + + /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */ + if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) { + allocated = 1; + nameoff = fdt_add_string_(fdt, name); + } else { + nameoff = fdt_find_add_string_(fdt, name, &allocated); + } + if (nameoff == 0) + return -FDT_ERR_NOSPACE; + + prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); + if (! prop) { + if (allocated) + fdt_del_last_string_(fdt, name); + return -FDT_ERR_NOSPACE; + } + + prop->tag = cpu_to_fdt32(FDT_PROP); + prop->nameoff = cpu_to_fdt32(nameoff); + prop->len = cpu_to_fdt32(len); + *valp = prop->data; + return 0; +} + +int fdt_property(void *fdt, const char *name, const void *val, int len) +{ + void *ptr; + int ret; + + ret = fdt_property_placeholder(fdt, name, len, &ptr); + if (ret) + return ret; + memcpy(ptr, val, len); + return 0; +} + +int fdt_finish(void *fdt) +{ + char *p = (char *)fdt; + fdt32_t *end; + int oldstroffset, newstroffset; + uint32_t tag; + int offset, nextoffset; + + FDT_SW_PROBE_STRUCT(fdt); + + /* Add terminator */ + end = fdt_grab_space_(fdt, sizeof(*end)); + if (! end) + return -FDT_ERR_NOSPACE; + *end = cpu_to_fdt32(FDT_END); + + /* Relocate the string table */ + oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); + newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); + fdt_set_off_dt_strings(fdt, newstroffset); + + /* Walk the structure, correcting string offsets */ + offset = 0; + while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { + if (tag == FDT_PROP) { + struct fdt_property *prop = + fdt_offset_ptr_w_(fdt, offset); + int nameoff; + + nameoff = fdt32_to_cpu(prop->nameoff); + nameoff += fdt_size_dt_strings(fdt); + prop->nameoff = cpu_to_fdt32(nameoff); + } + offset = nextoffset; + } + if (nextoffset < 0) + return nextoffset; + + /* Finally, adjust the header */ + fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); + + /* And fix up fields that were keeping intermediate state. */ + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + fdt_set_magic(fdt, FDT_MAGIC); + + return 0; +} diff --git a/roms/opensbi/lib/utils/libfdt/fdt_wip.c b/roms/opensbi/lib/utils/libfdt/fdt_wip.c new file mode 100644 index 000000000..f64139e0b --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/fdt_wip.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, + const char *name, int namelen, + uint32_t idx, const void *val, + int len) +{ + void *propval; + int proplen; + + propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, + &proplen); + if (!propval) + return proplen; + + if (proplen < (len + idx)) + return -FDT_ERR_NOSPACE; + + memcpy((char *)propval + idx, val, len); + return 0; +} + +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + const void *propval; + int proplen; + + propval = fdt_getprop(fdt, nodeoffset, name, &proplen); + if (!propval) + return proplen; + + if (proplen != len) + return -FDT_ERR_NOSPACE; + + return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name, + strlen(name), 0, + val, len); +} + +static void fdt_nop_region_(void *start, int len) +{ + fdt32_t *p; + + for (p = start; (char *)p < ((char *)start + len); p++) + *p = cpu_to_fdt32(FDT_NOP); +} + +int fdt_nop_property(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len; + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (!prop) + return len; + + fdt_nop_region_(prop, len + sizeof(*prop)); + + return 0; +} + +int fdt_node_end_offset_(void *fdt, int offset) +{ + int depth = 0; + + while ((offset >= 0) && (depth >= 0)) + offset = fdt_next_node(fdt, offset, &depth); + + return offset; +} + +int fdt_nop_node(void *fdt, int nodeoffset) +{ + int endoffset; + + endoffset = fdt_node_end_offset_(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0), + endoffset - nodeoffset); + return 0; +} diff --git a/roms/opensbi/lib/utils/libfdt/libfdt.h b/roms/opensbi/lib/utils/libfdt/libfdt.h new file mode 100644 index 000000000..48f375c9c --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/libfdt.h @@ -0,0 +1,2072 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef LIBFDT_H +#define LIBFDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ + +#include <libfdt_env.h> +#include <fdt.h> + +#define FDT_FIRST_SUPPORTED_VERSION 0x02 +#define FDT_LAST_SUPPORTED_VERSION 0x11 + +/* Error codes: informative error codes */ +#define FDT_ERR_NOTFOUND 1 + /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ +#define FDT_ERR_EXISTS 2 + /* FDT_ERR_EXISTS: Attempted to create a node or property which + * already exists */ +#define FDT_ERR_NOSPACE 3 + /* FDT_ERR_NOSPACE: Operation needed to expand the device + * tree, but its buffer did not have sufficient space to + * contain the expanded tree. Use fdt_open_into() to move the + * device tree to a buffer with more space. */ + +/* Error codes: codes for bad parameters */ +#define FDT_ERR_BADOFFSET 4 + /* FDT_ERR_BADOFFSET: Function was passed a structure block + * offset which is out-of-bounds, or which points to an + * unsuitable part of the structure for the operation. */ +#define FDT_ERR_BADPATH 5 + /* FDT_ERR_BADPATH: Function was passed a badly formatted path + * (e.g. missing a leading / for a function which requires an + * absolute path) */ +#define FDT_ERR_BADPHANDLE 6 + /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle. + * This can be caused either by an invalid phandle property + * length, or the phandle value was either 0 or -1, which are + * not permitted. */ +#define FDT_ERR_BADSTATE 7 + /* FDT_ERR_BADSTATE: Function was passed an incomplete device + * tree created by the sequential-write functions, which is + * not sufficiently complete for the requested operation. */ + +/* Error codes: codes for bad device tree blobs */ +#define FDT_ERR_TRUNCATED 8 + /* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly + * terminated (overflows, goes outside allowed bounds, or + * isn't properly terminated). */ +#define FDT_ERR_BADMAGIC 9 + /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a + * device tree at all - it is missing the flattened device + * tree magic number. */ +#define FDT_ERR_BADVERSION 10 + /* FDT_ERR_BADVERSION: Given device tree has a version which + * can't be handled by the requested operation. For + * read-write functions, this may mean that fdt_open_into() is + * required to convert the tree to the expected version. */ +#define FDT_ERR_BADSTRUCTURE 11 + /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt + * structure block or other serious error (e.g. misnested + * nodes, or subnodes preceding properties). */ +#define FDT_ERR_BADLAYOUT 12 + /* FDT_ERR_BADLAYOUT: For read-write functions, the given + * device tree has it's sub-blocks in an order that the + * function can't handle (memory reserve map, then structure, + * then strings). Use fdt_open_into() to reorganize the tree + * into a form suitable for the read-write operations. */ + +/* "Can't happen" error indicating a bug in libfdt */ +#define FDT_ERR_INTERNAL 13 + /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. + * Should never be returned, if it is, it indicates a bug in + * libfdt itself. */ + +/* Errors in device tree content */ +#define FDT_ERR_BADNCELLS 14 + /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells + * or similar property with a bad format or value */ + +#define FDT_ERR_BADVALUE 15 + /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected + * value. For example: a property expected to contain a string list + * is not NUL-terminated within the length of its value. */ + +#define FDT_ERR_BADOVERLAY 16 + /* FDT_ERR_BADOVERLAY: The device tree overlay, while + * correctly structured, cannot be applied due to some + * unexpected or missing value, property or node. */ + +#define FDT_ERR_NOPHANDLES 17 + /* FDT_ERR_NOPHANDLES: The device tree doesn't have any + * phandle available anymore without causing an overflow */ + +#define FDT_ERR_BADFLAGS 18 + /* FDT_ERR_BADFLAGS: The function was passed a flags field that + * contains invalid flags or an invalid combination of flags. */ + +#define FDT_ERR_MAX 18 + +/* constants */ +#define FDT_MAX_PHANDLE 0xfffffffe + /* Valid values for phandles range from 1 to 2^32-2. */ + +/**********************************************************************/ +/* Low-level functions (you probably don't need these) */ +/**********************************************************************/ + +#ifndef SWIG /* This function is not useful in Python */ +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); +#endif +static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) +{ + return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); +} + +uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); + +/* + * Alignment helpers: + * These helpers access words from a device tree blob. They're + * built to work even with unaligned pointers on platforms (ike + * ARM) that don't like unaligned loads and stores + */ + +static inline uint32_t fdt32_ld(const fdt32_t *p) +{ + const uint8_t *bp = (const uint8_t *)p; + + return ((uint32_t)bp[0] << 24) + | ((uint32_t)bp[1] << 16) + | ((uint32_t)bp[2] << 8) + | bp[3]; +} + +static inline void fdt32_st(void *property, uint32_t value) +{ + uint8_t *bp = (uint8_t *)property; + + bp[0] = value >> 24; + bp[1] = (value >> 16) & 0xff; + bp[2] = (value >> 8) & 0xff; + bp[3] = value & 0xff; +} + +static inline uint64_t fdt64_ld(const fdt64_t *p) +{ + const uint8_t *bp = (const uint8_t *)p; + + return ((uint64_t)bp[0] << 56) + | ((uint64_t)bp[1] << 48) + | ((uint64_t)bp[2] << 40) + | ((uint64_t)bp[3] << 32) + | ((uint64_t)bp[4] << 24) + | ((uint64_t)bp[5] << 16) + | ((uint64_t)bp[6] << 8) + | bp[7]; +} + +static inline void fdt64_st(void *property, uint64_t value) +{ + uint8_t *bp = (uint8_t *)property; + + bp[0] = value >> 56; + bp[1] = (value >> 48) & 0xff; + bp[2] = (value >> 40) & 0xff; + bp[3] = (value >> 32) & 0xff; + bp[4] = (value >> 24) & 0xff; + bp[5] = (value >> 16) & 0xff; + bp[6] = (value >> 8) & 0xff; + bp[7] = value & 0xff; +} + +/**********************************************************************/ +/* Traversal functions */ +/**********************************************************************/ + +int fdt_next_node(const void *fdt, int offset, int *depth); + +/** + * fdt_first_subnode() - get offset of first direct subnode + * + * @fdt: FDT blob + * @offset: Offset of node to check + * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none + */ +int fdt_first_subnode(const void *fdt, int offset); + +/** + * fdt_next_subnode() - get offset of next direct subnode + * + * After first calling fdt_first_subnode(), call this function repeatedly to + * get direct subnodes of a parent node. + * + * @fdt: FDT blob + * @offset: Offset of previous subnode + * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more + * subnodes + */ +int fdt_next_subnode(const void *fdt, int offset); + +/** + * fdt_for_each_subnode - iterate over all subnodes of a parent + * + * @node: child node (int, lvalue) + * @fdt: FDT blob (const void *) + * @parent: parent node (int) + * + * This is actually a wrapper around a for loop and would be used like so: + * + * fdt_for_each_subnode(node, fdt, parent) { + * Use node + * ... + * } + * + * if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) { + * Error handling + * } + * + * Note that this is implemented as a macro and @node is used as + * iterator in the loop. The parent variable be constant or even a + * literal. + * + */ +#define fdt_for_each_subnode(node, fdt, parent) \ + for (node = fdt_first_subnode(fdt, parent); \ + node >= 0; \ + node = fdt_next_subnode(fdt, node)) + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ +#define fdt_get_header(fdt, field) \ + (fdt32_ld(&((const struct fdt_header *)(fdt))->field)) +#define fdt_magic(fdt) (fdt_get_header(fdt, magic)) +#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) +#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) +#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) +#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) +#define fdt_version(fdt) (fdt_get_header(fdt, version)) +#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) +#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) +#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) +#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) + +#define fdt_set_hdr_(name) \ + static inline void fdt_set_##name(void *fdt, uint32_t val) \ + { \ + struct fdt_header *fdth = (struct fdt_header *)fdt; \ + fdth->name = cpu_to_fdt32(val); \ + } +fdt_set_hdr_(magic); +fdt_set_hdr_(totalsize); +fdt_set_hdr_(off_dt_struct); +fdt_set_hdr_(off_dt_strings); +fdt_set_hdr_(off_mem_rsvmap); +fdt_set_hdr_(version); +fdt_set_hdr_(last_comp_version); +fdt_set_hdr_(boot_cpuid_phys); +fdt_set_hdr_(size_dt_strings); +fdt_set_hdr_(size_dt_struct); +#undef fdt_set_hdr_ + +/** + * fdt_header_size - return the size of the tree's header + * @fdt: pointer to a flattened device tree + */ +size_t fdt_header_size(const void *fdt); + +/** + * fdt_header_size_ - internal function which takes a version number + */ +size_t fdt_header_size_(uint32_t version); + +/** + * fdt_check_header - sanity check a device tree header + + * @fdt: pointer to data which might be a flattened device tree + * + * fdt_check_header() checks that the given buffer contains what + * appears to be a flattened device tree, and that the header contains + * valid information (to the extent that can be determined from the + * header alone). + * + * returns: + * 0, if the buffer appears to contain a valid device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_TRUNCATED, standard meanings, as above + */ +int fdt_check_header(const void *fdt); + +/** + * fdt_move - move a device tree around in memory + * @fdt: pointer to the device tree to move + * @buf: pointer to memory where the device is to be moved + * @bufsize: size of the memory space at buf + * + * fdt_move() relocates, if possible, the device tree blob located at + * fdt to the buffer at buf of size bufsize. The buffer may overlap + * with the existing device tree blob at fdt. Therefore, + * fdt_move(fdt, fdt, fdt_totalsize(fdt)) + * should always succeed. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_move(const void *fdt, void *buf, int bufsize); + +/**********************************************************************/ +/* Read-only functions */ +/**********************************************************************/ + +int fdt_check_full(const void *fdt, size_t bufsize); + +/** + * fdt_get_string - retrieve a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * @lenp: optional pointer to return the string's length + * + * fdt_get_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt, and optionally also + * returns the string's length in *lenp. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds, or doesn't point to a valid string + */ +const char *fdt_get_string(const void *fdt, int stroffset, int *lenp); + +/** + * fdt_string - retrieve a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * + * fdt_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds, or doesn't point to a valid string + */ +const char *fdt_string(const void *fdt, int stroffset); + +/** + * fdt_find_max_phandle - find and return the highest phandle in a tree + * @fdt: pointer to the device tree blob + * @phandle: return location for the highest phandle value found in the tree + * + * fdt_find_max_phandle() finds the highest phandle value in the given device + * tree. The value returned in @phandle is only valid if the function returns + * success. + * + * returns: + * 0 on success or a negative error code on failure + */ +int fdt_find_max_phandle(const void *fdt, uint32_t *phandle); + +/** + * fdt_get_max_phandle - retrieves the highest phandle in a tree + * @fdt: pointer to the device tree blob + * + * fdt_get_max_phandle retrieves the highest phandle in the given + * device tree. This will ignore badly formatted phandles, or phandles + * with a value of 0 or -1. + * + * This function is deprecated in favour of fdt_find_max_phandle(). + * + * returns: + * the highest phandle on success + * 0, if no phandle was found in the device tree + * -1, if an error occurred + */ +static inline uint32_t fdt_get_max_phandle(const void *fdt) +{ + uint32_t phandle; + int err; + + err = fdt_find_max_phandle(fdt, &phandle); + if (err < 0) + return (uint32_t)-1; + + return phandle; +} + +/** + * fdt_generate_phandle - return a new, unused phandle for a device tree blob + * @fdt: pointer to the device tree blob + * @phandle: return location for the new phandle + * + * Walks the device tree blob and looks for the highest phandle value. On + * success, the new, unused phandle value (one higher than the previously + * highest phandle value in the device tree blob) will be returned in the + * @phandle parameter. + * + * Returns: + * 0 on success or a negative error-code on failure + */ +int fdt_generate_phandle(const void *fdt, uint32_t *phandle); + +/** + * fdt_num_mem_rsv - retrieve the number of memory reserve map entries + * @fdt: pointer to the device tree blob + * + * Returns the number of entries in the device tree blob's memory + * reservation map. This does not include the terminating 0,0 entry + * or any other (0,0) entries reserved for expansion. + * + * returns: + * the number of entries + */ +int fdt_num_mem_rsv(const void *fdt); + +/** + * fdt_get_mem_rsv - retrieve one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: pointers to 64-bit variables + * + * On success, *address and *size will contain the address and size of + * the n-th reserve map entry from the device tree blob, in + * native-endian format. + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); + +/** + * fdt_subnode_offset_namelen - find a subnode based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_subnode_offset(), but only examine the first + * namelen characters of name for matching the subnode name. This is + * useful for finding subnodes based on a portion of a larger string, + * such as a full path. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, + const char *name, int namelen); +#endif +/** + * fdt_subnode_offset - find a subnode of a given node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_subnode_offset() finds a subnode of the node at structure block + * offset parentoffset with the given name. name may include a unit + * address, in which case fdt_subnode_offset() will find the subnode + * with that unit address, or the unit address may be omitted, in + * which case fdt_subnode_offset() will find an arbitrary subnode + * whose name excluding unit address matches the given name. + * + * returns: + * structure block offset of the requested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); + +/** + * fdt_path_offset_namelen - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * @namelen: number of characters of path to consider + * + * Identical to fdt_path_offset(), but only consider the first namelen + * characters of path as the path name. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); +#endif + +/** + * fdt_path_offset - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * + * fdt_path_offset() finds a node of a given path in the device tree. + * Each path component may omit the unit address portion, but the + * results of this are undefined if any such path component is + * ambiguous (that is if there are multiple nodes at the relevant + * level matching the given component, differentiated only by unit + * address). + * + * returns: + * structure block offset of the node with the requested path (>=0), on + * success + * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid + * -FDT_ERR_NOTFOUND, if the requested node does not exist + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_path_offset(const void *fdt, const char *path); + +/** + * fdt_get_name - retrieve the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the starting node + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_name() retrieves the name (including unit address) of the + * device tree node at structure block offset nodeoffset. If lenp is + * non-NULL, the length of this name is also returned, in the integer + * pointed to by lenp. + * + * returns: + * pointer to the node's name, on success + * If lenp is non-NULL, *lenp contains the length of that name + * (>=0) + * NULL, on error + * if lenp is non-NULL *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); + +/** + * fdt_first_property_offset - find the offset of a node's first property + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * + * fdt_first_property_offset() finds the first property of the node at + * the given structure block offset. + * + * returns: + * structure block offset of the property (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested node has no properties + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_first_property_offset(const void *fdt, int nodeoffset); + +/** + * fdt_next_property_offset - step through a node's properties + * @fdt: pointer to the device tree blob + * @offset: structure block offset of a property + * + * fdt_next_property_offset() finds the property immediately after the + * one at the given structure block offset. This will be a property + * of the same node as the given property. + * + * returns: + * structure block offset of the next property (>=0), on success + * -FDT_ERR_NOTFOUND, if the given property is the last in its node + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_next_property_offset(const void *fdt, int offset); + +/** + * fdt_for_each_property_offset - iterate over all properties of a node + * + * @property_offset: property offset (int, lvalue) + * @fdt: FDT blob (const void *) + * @node: node offset (int) + * + * This is actually a wrapper around a for loop and would be used like so: + * + * fdt_for_each_property_offset(property, fdt, node) { + * Use property + * ... + * } + * + * if ((property < 0) && (property != -FDT_ERR_NOTFOUND)) { + * Error handling + * } + * + * Note that this is implemented as a macro and property is used as + * iterator in the loop. The node variable can be constant or even a + * literal. + */ +#define fdt_for_each_property_offset(property, fdt, node) \ + for (property = fdt_first_property_offset(fdt, node); \ + property >= 0; \ + property = fdt_next_property_offset(fdt, property)) + +/** + * fdt_get_property_by_offset - retrieve the property at a given offset + * @fdt: pointer to the device tree blob + * @offset: offset of the property to retrieve + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property_by_offset() retrieves a pointer to the + * fdt_property structure within the device tree blob at the given + * offset. If lenp is non-NULL, the length of the property value is + * also returned, in the integer pointed to by lenp. + * + * Note that this code only works on device tree versions >= 16. fdt_getprop() + * works on all versions. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *lenp); + +/** + * fdt_get_property_namelen - find a property based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_get_property(), but only examine the first namelen + * characters of name for matching the property name. + */ +#ifndef SWIG /* Not available in Python */ +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int nodeoffset, + const char *name, + int namelen, int *lenp); +#endif + +/** + * fdt_get_property - find a given property in a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property() retrieves a pointer to the fdt_property + * structure within the device tree blob corresponding to the property + * named 'name' of the node at offset nodeoffset. If lenp is + * non-NULL, the length of the property value is also returned, in the + * integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, + const char *name, + int *lenp) +{ + return (struct fdt_property *)(uintptr_t) + fdt_get_property(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_getprop_by_offset - retrieve the value of a property at a given offset + * @fdt: pointer to the device tree blob + * @offset: offset of the property to read + * @namep: pointer to a string variable (will be overwritten) or NULL + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop_by_offset() retrieves a pointer to the value of the + * property at structure block offset 'offset' (this will be a pointer + * to within the device blob itself, not a copy of the value). If + * lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. If namep is non-NULL, + * the property's namne will also be returned in the char * pointed to + * by namep (this will be a pointer to within the device tree's string + * block, not a new copy of the name). + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * if namep is non-NULL *namep contiains a pointer to the property + * name. + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#ifndef SWIG /* This function is not useful in Python */ +const void *fdt_getprop_by_offset(const void *fdt, int offset, + const char **namep, int *lenp); +#endif + +/** + * fdt_getprop_namelen - get property value based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_getprop(), but only examine the first namelen + * characters of name for matching the property name. + */ +#ifndef SWIG /* Not available in Python */ +const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp); +static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset, + const char *name, int namelen, + int *lenp) +{ + return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name, + namelen, lenp); +} +#endif + +/** + * fdt_getprop - retrieve the value of a given property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop() retrieves a pointer to the value of the property + * named 'name' of the node at offset nodeoffset (this will be a + * pointer to within the device blob itself, not a copy of the value). + * If lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline void *fdt_getprop_w(void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_get_phandle - retrieve the phandle of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the node + * + * fdt_get_phandle() retrieves the phandle of the device tree node at + * structure block offset nodeoffset. + * + * returns: + * the phandle of the node at nodeoffset, on success (!= 0, != -1) + * 0, if the node has no phandle, or another error occurs + */ +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); + +/** + * fdt_get_alias_namelen - get alias based on substring + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * @namelen: number of characters of name to consider + * + * Identical to fdt_get_alias(), but only examine the first namelen + * characters of name for matching the alias name. + */ +#ifndef SWIG /* Not available in Python */ +const char *fdt_get_alias_namelen(const void *fdt, + const char *name, int namelen); +#endif + +/** + * fdt_get_alias - retrieve the path referenced by a given alias + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * + * fdt_get_alias() retrieves the value of a given alias. That is, the + * value of the property named 'name' in the node /aliases. + * + * returns: + * a pointer to the expansion of the alias named 'name', if it exists + * NULL, if the given alias or the /aliases node does not exist + */ +const char *fdt_get_alias(const void *fdt, const char *name); + +/** + * fdt_get_path - determine the full path of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose path to find + * @buf: character buffer to contain the returned path (will be overwritten) + * @buflen: size of the character buffer at buf + * + * fdt_get_path() computes the full path of the node at offset + * nodeoffset, and records that path in the buffer at buf. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * 0, on success + * buf contains the absolute path of the node at + * nodeoffset, as a NUL-terminated string. + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) + * characters and will not fit in the given buffer. + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); + +/** + * fdt_supernode_atdepth_offset - find a specific ancestor of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * @supernodedepth: depth of the ancestor to find + * @nodedepth: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_supernode_atdepth_offset() finds an ancestor of the given node + * at a specific depth from the root (where the root itself has depth + * 0, its immediate subnodes depth 1 and so forth). So + * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); + * will always return 0, the offset of the root node. If the node at + * nodeoffset has depth D, then: + * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); + * will return nodeoffset itself. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * structure block offset of the node at node offset's ancestor + * of depth supernodedepth (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of + * nodeoffset + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth); + +/** + * fdt_node_depth - find the depth of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_node_depth() finds the depth of a given node. The root node + * has depth 0, its immediate subnodes depth 1 and so forth. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * depth of the node at nodeoffset (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_depth(const void *fdt, int nodeoffset); + +/** + * fdt_parent_offset - find the parent of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_parent_offset() locates the parent node of a given node (that + * is, it finds the offset of the node which contains the node at + * nodeoffset as a subnode). + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset, *twice*. + * + * returns: + * structure block offset of the parent of the node at nodeoffset + * (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_parent_offset(const void *fdt, int nodeoffset); + +/** + * fdt_node_offset_by_prop_value - find nodes with a given property value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @propname: property name to check + * @propval: property value to search for + * @proplen: length of the value in propval + * + * fdt_node_offset_by_prop_value() returns the offset of the first + * node after startoffset, which has a property named propname whose + * value is of length proplen and has value equal to propval; or if + * startoffset is -1, the very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, + * propval, proplen); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, + * propval, proplen); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen); + +/** + * fdt_node_offset_by_phandle - find the node with a given phandle + * @fdt: pointer to the device tree blob + * @phandle: phandle value + * + * fdt_node_offset_by_phandle() returns the offset of the node + * which has the given phandle value. If there is more than one node + * in the tree with the given phandle (an invalid tree), results are + * undefined. + * + * returns: + * structure block offset of the located node (>= 0), on success + * -FDT_ERR_NOTFOUND, no node with that phandle exists + * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); + +/** + * fdt_node_check_compatible: check a node's compatible property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @compatible: string to match against + * + * + * fdt_node_check_compatible() returns 0 if the given node contains a + * 'compatible' property with the given string as one of its elements, + * it returns non-zero otherwise, or on error. + * + * returns: + * 0, if the node has a 'compatible' property listing the given string + * 1, if the node has a 'compatible' property, but it does not list + * the given string + * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property + * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible); + +/** + * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @compatible: 'compatible' string to match against + * + * fdt_node_offset_by_compatible() returns the offset of the first + * node after startoffset, which has a 'compatible' property which + * lists the given compatible string; or if startoffset is -1, the + * very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible); + +/** + * fdt_stringlist_contains - check a string list property for a string + * @strlist: Property containing a list of strings to check + * @listlen: Length of property + * @str: String to search for + * + * This is a utility function provided for convenience. The list contains + * one or more strings, each terminated by \0, as is found in a device tree + * "compatible" property. + * + * @return: 1 if the string is found in the list, 0 not found, or invalid list + */ +int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); + +/** + * fdt_stringlist_count - count the number of strings in a string list + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @return: + * the number of strings in the given property + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist + */ +int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property); + +/** + * fdt_stringlist_search - find a string in a string list and return its index + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @string: string to look up in the string list + * + * Note that it is possible for this function to succeed on property values + * that are not NUL-terminated. That's because the function will stop after + * finding the first occurrence of @string. This can for example happen with + * small-valued cell properties, such as #address-cells, when searching for + * the empty string. + * + * @return: + * the index of the string in the list of strings + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist or does not contain + * the given string + */ +int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, + const char *string); + +/** + * fdt_stringlist_get() - obtain the string at a given index in a string list + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @index: index of the string to return + * @lenp: return location for the string length or an error code on failure + * + * Note that this will successfully extract strings from properties with + * non-NUL-terminated values. For example on small-valued cell properties + * this function will return the empty string. + * + * If non-NULL, the length of the string (on success) or a negative error-code + * (on failure) will be stored in the integer pointer to by lenp. + * + * @return: + * A pointer to the string at the given index in the string list or NULL on + * failure. On success the length of the string will be stored in the memory + * location pointed to by the lenp parameter, if non-NULL. On failure one of + * the following negative error codes will be returned in the lenp parameter + * (if non-NULL): + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist + */ +const char *fdt_stringlist_get(const void *fdt, int nodeoffset, + const char *property, int index, + int *lenp); + +/**********************************************************************/ +/* Read-only functions (addressing related) */ +/**********************************************************************/ + +/** + * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells + * + * This is the maximum value for #address-cells, #size-cells and + * similar properties that will be processed by libfdt. IEE1275 + * requires that OF implementations handle values up to 4. + * Implementations may support larger values, but in practice higher + * values aren't used. + */ +#define FDT_MAX_NCELLS 4 + +/** + * fdt_address_cells - retrieve address size for a bus represented in the tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address size for + * + * When the node has a valid #address-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 2, if the node has no #address-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #address-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_address_cells(const void *fdt, int nodeoffset); + +/** + * fdt_size_cells - retrieve address range size for a bus represented in the + * tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address range size for + * + * When the node has a valid #size-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 1, if the node has no #size-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #size-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_size_cells(const void *fdt, int nodeoffset); + + +/**********************************************************************/ +/* Write-in-place functions */ +/**********************************************************************/ + +/** + * fdt_setprop_inplace_namelen_partial - change a property's value, + * but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @namelen: number of characters of name to consider + * @idx: index of the property to change in the array + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * Identical to fdt_setprop_inplace(), but modifies the given property + * starting from the given index, and using only the first characters + * of the name. It is useful when you want to manipulate only one value of + * an array and you have a string that doesn't end with \0. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, + const char *name, int namelen, + uint32_t idx, const void *val, + int len); +#endif + +/** + * fdt_setprop_inplace - change a property's value, but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * fdt_setprop_inplace() replaces the value of a given property with + * the data in val, of length len. This function cannot change the + * size of a property, and so will only work if len is equal to the + * current length of the property. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if len is not equal to the property's current length + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#ifndef SWIG /* Not available in Python */ +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len); +#endif + +/** + * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to replace the property with + * + * fdt_setprop_inplace_u32() replaces the value of a given property + * with the 32-bit integer value in val, converting val to big-endian + * if necessary. This function cannot change the size of a property, + * and so will only work if the property already exists and has length + * 4. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value to replace the property with + * + * fdt_setprop_inplace_u64() replaces the value of a given property + * with the 64-bit integer value in val, converting val to big-endian + * if necessary. This function cannot change the size of a property, + * and so will only work if the property already exists and has length + * 8. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 8 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset, + const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_inplace_cell - change the value of a single-cell property + * + * This is an alternative name for fdt_setprop_inplace_u32() + */ +static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_nop_property - replace a property with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_nop_property() will replace a given property's representation + * in the blob with FDT_NOP tags, effectively removing it from the + * tree. + * + * This function will alter only the bytes in the blob which contain + * the property, and will not alter or move any other part of the + * tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_property(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_nop_node - replace a node (subtree) with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_nop_node() will replace a given node's representation in the + * blob, including all its subnodes, if any, with FDT_NOP tags, + * effectively removing it from the tree. + * + * This function will alter only the bytes in the blob which contain + * the node and its properties and subnodes, and will not alter or + * move any other part of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_node(void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Sequential write functions */ +/**********************************************************************/ + +/* fdt_create_with_flags flags */ +#define FDT_CREATE_FLAG_NO_NAME_DEDUP 0x1 + /* FDT_CREATE_FLAG_NO_NAME_DEDUP: Do not try to de-duplicate property + * names in the fdt. This can result in faster creation times, but + * a larger fdt. */ + +#define FDT_CREATE_FLAGS_ALL (FDT_CREATE_FLAG_NO_NAME_DEDUP) + +/** + * fdt_create_with_flags - begin creation of a new fdt + * @fdt: pointer to memory allocated where fdt will be created + * @bufsize: size of the memory space at fdt + * @flags: a valid combination of FDT_CREATE_FLAG_ flags, or 0. + * + * fdt_create_with_flags() begins the process of creating a new fdt with + * the sequential write interface. + * + * fdt creation process must end with fdt_finished() to produce a valid fdt. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt + * -FDT_ERR_BADFLAGS, flags is not valid + */ +int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags); + +/** + * fdt_create - begin creation of a new fdt + * @fdt: pointer to memory allocated where fdt will be created + * @bufsize: size of the memory space at fdt + * + * fdt_create() is equivalent to fdt_create_with_flags() with flags=0. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt + */ +int fdt_create(void *buf, int bufsize); + +int fdt_resize(void *fdt, void *buf, int bufsize); +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); +int fdt_finish_reservemap(void *fdt); +int fdt_begin_node(void *fdt, const char *name); +int fdt_property(void *fdt, const char *name, const void *val, int len); +static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_property(fdt, name, &tmp, sizeof(tmp)); +} +static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_property(fdt, name, &tmp, sizeof(tmp)); +} + +#ifndef SWIG /* Not available in Python */ +static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) +{ + return fdt_property_u32(fdt, name, val); +} +#endif + +/** + * fdt_property_placeholder - add a new property and return a ptr to its value + * + * @fdt: pointer to the device tree blob + * @name: name of property to add + * @len: length of property value in bytes + * @valp: returns a pointer to where where the value should be placed + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_NOSPACE, standard meanings + */ +int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp); + +#define fdt_property_string(fdt, name, str) \ + fdt_property(fdt, name, str, strlen(str)+1) +int fdt_end_node(void *fdt); +int fdt_finish(void *fdt); + +/**********************************************************************/ +/* Read-write functions */ +/**********************************************************************/ + +int fdt_create_empty_tree(void *buf, int bufsize); +int fdt_open_into(const void *fdt, void *buf, int bufsize); +int fdt_pack(void *fdt); + +/** + * fdt_add_mem_rsv - add one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: 64-bit values (native endian) + * + * Adds a reserve map entry to the given blob reserving a region at + * address address of length size. + * + * This function will insert data into the reserve map and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new reservation entry + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); + +/** + * fdt_del_mem_rsv - remove a memory reserve map entry + * @fdt: pointer to the device tree blob + * @n: entry to remove + * + * fdt_del_mem_rsv() removes the n-th memory reserve map entry from + * the blob. + * + * This function will delete data from the reservation table and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there + * are less than n+1 reserve map entries) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_mem_rsv(void *fdt, int n); + +/** + * fdt_set_name - change the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * @name: name to give the node + * + * fdt_set_name() replaces the name (including unit address, if any) + * of the given node with the given string. NOTE: this function can't + * efficiently check if the new name is unique amongst the given + * node's siblings; results are undefined if this function is invoked + * with a name equal to one of the given node's siblings. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob + * to contain the new name + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_set_name(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_setprop - create or change a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to set the property value to + * @len: length of the property value + * + * fdt_setprop() sets the value of the named property in the given + * node to the given value and length, creating the property if it + * does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_placeholder - allocate space for a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @len: length of the property value + * @prop_data: return pointer to property data + * + * fdt_setprop_placeholer() allocates the named property in the given node. + * If the property exists it is resized. In either case a pointer to the + * property data is returned. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, + int len, void **prop_data); + +/** + * fdt_setprop_u32 - set a property to a 32-bit integer + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value for the property (native endian) + * + * fdt_setprop_u32() sets the value of the named property in the given + * node to the given 32-bit integer value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_u64 - set a property to a 64-bit integer + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value for the property (native endian) + * + * fdt_setprop_u64() sets the value of the named property in the given + * node to the given 64-bit integer value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name, + uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_cell - set a property to a single cell value + * + * This is an alternative name for fdt_setprop_u32() + */ +static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + return fdt_setprop_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_setprop_string - set a property to a string value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value for the property + * + * fdt_setprop_string() sets the value of the named property in the + * given node to the given string value (using the length of the + * string to determine the new length of the property), or creates a + * new property with that value if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_string(fdt, nodeoffset, name, str) \ + fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + + +/** + * fdt_setprop_empty - set a property to an empty value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * + * fdt_setprop_empty() sets the value of the named property in the + * given node to an empty (zero length) value, or creates a new empty + * property if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_empty(fdt, nodeoffset, name) \ + fdt_setprop((fdt), (nodeoffset), (name), NULL, 0) + +/** + * fdt_appendprop - append to or create a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to append to + * @val: pointer to data to append to the property value + * @len: length of the data to append to the property value + * + * fdt_appendprop() appends the value to the named property in the + * given node, creating the property if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_appendprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_appendprop_u32 - append a 32-bit integer value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to append to the property (native endian) + * + * fdt_appendprop_u32() appends the given 32-bit integer value + * (converting to big-endian if necessary) to the value of the named + * property in the given node, or creates a new property with that + * value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_appendprop_u32(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_appendprop_u64 - append a 64-bit integer value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value to append to the property (native endian) + * + * fdt_appendprop_u64() appends the given 64-bit integer value + * (converting to big-endian if necessary) to the value of the named + * property in the given node, or creates a new property with that + * value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_appendprop_u64(void *fdt, int nodeoffset, + const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_appendprop_cell - append a single cell value to a property + * + * This is an alternative name for fdt_appendprop_u32() + */ +static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + return fdt_appendprop_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_appendprop_string - append a string to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value to append to the property + * + * fdt_appendprop_string() appends the given string to the value of + * the named property in the given node, or creates a new property + * with that value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_appendprop_string(fdt, nodeoffset, name, str) \ + fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + +/** + * fdt_appendprop_addrrange - append a address range property + * @fdt: pointer to the device tree blob + * @parent: offset of the parent node + * @nodeoffset: offset of the node to add a property at + * @name: name of property + * @addr: start address of a given range + * @size: size of a given range + * + * fdt_appendprop_addrrange() appends an address range value (start + * address and size) to the value of the named property in the given + * node, or creates a new property with that value if it does not + * already exist. + * If "name" is not specified, a default "reg" is used. + * Cell sizes are determined by parent's #address-cells and #size-cells. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #address-cells property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADVALUE, addr or size doesn't fit to respective cells size + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain a new property + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, + const char *name, uint64_t addr, uint64_t size); + +/** + * fdt_delprop - delete a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_del_property() will delete the given property. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_delprop(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_add_subnode_namelen - creates a new node based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_add_subnode(), but use only the first namelen + * characters of name as the name of the new node. This is useful for + * creating subnodes based on a portion of a larger string, such as a + * full path. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen); +#endif + +/** + * fdt_add_subnode - creates a new node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_add_subnode() creates a new node as a subnode of the node at + * structure block offset parentoffset, with the given name (which + * should include the unit address, if any). + * + * This function will insert data into the blob, and will therefore + * change the offsets of some existing nodes. + + * returns: + * structure block offset of the created nodeequested subnode (>=0), on + * success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE + * tag + * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of + * the given name + * -FDT_ERR_NOSPACE, if there is insufficient free space in the + * blob to contain the new node + * -FDT_ERR_NOSPACE + * -FDT_ERR_BADLAYOUT + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_add_subnode(void *fdt, int parentoffset, const char *name); + +/** + * fdt_del_node - delete a node (subtree) + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_del_node() will remove the given node, including all its + * subnodes if any, from the blob. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_node(void *fdt, int nodeoffset); + +/** + * fdt_overlay_apply - Applies a DT overlay on a base DT + * @fdt: pointer to the base device tree blob + * @fdto: pointer to the device tree overlay blob + * + * fdt_overlay_apply() will apply the given device tree overlay on the + * given base device tree. + * + * Expect the base device tree to be modified, even if the function + * returns an error. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there's not enough space in the base device tree + * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or + * properties in the base DT + * -FDT_ERR_BADPHANDLE, + * -FDT_ERR_BADOVERLAY, + * -FDT_ERR_NOPHANDLES, + * -FDT_ERR_INTERNAL, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADOFFSET, + * -FDT_ERR_BADPATH, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_overlay_apply(void *fdt, void *fdto); + +/**********************************************************************/ +/* Debugging / informational functions */ +/**********************************************************************/ + +const char *fdt_strerror(int errval); + +#endif /* LIBFDT_H */ diff --git a/roms/opensbi/lib/utils/libfdt/libfdt_env.h b/roms/opensbi/lib/utils/libfdt/libfdt_env.h new file mode 100644 index 000000000..bc3e75886 --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/libfdt_env.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef LIBFDT_ENV_H +#define LIBFDT_ENV_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2012 Kim Phillips, Freescale Semiconductor. + */ + +#include <sbi/sbi_string.h> +#include <sbi/sbi_types.h> + +#define INT_MAX ((int)(~0U >> 1)) +#define UINT_MAX ((unsigned int)~0U) +#define INT32_MAX INT_MAX +#define UINT32_MAX UINT_MAX + +#ifdef __CHECKER__ +#define FDT_FORCE __attribute__((force)) +#define FDT_BITWISE __attribute__((bitwise)) +#else +#define FDT_FORCE +#define FDT_BITWISE +#endif + +#define memmove sbi_memmove +#define memcpy sbi_memcpy +#define memcmp sbi_memcmp +#define memchr sbi_memchr +#define memset sbi_memset +#define strchr sbi_strchr +#define strrchr sbi_strrchr +#define strcpy sbi_strcpy +#define strcmp sbi_strcmp +#define strncmp sbi_strncmp +#define strlen sbi_strlen +#define strnlen sbi_strnlen + +typedef uint16_t FDT_BITWISE fdt16_t; +typedef uint32_t FDT_BITWISE fdt32_t; +typedef uint64_t FDT_BITWISE fdt64_t; + +#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) +#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) +#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ + (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) +#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ + (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ + (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ + (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) + +static inline uint16_t fdt16_to_cpu(fdt16_t x) +{ + return (FDT_FORCE uint16_t)CPU_TO_FDT16(x); +} +static inline fdt16_t cpu_to_fdt16(uint16_t x) +{ + return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x); +} + +static inline uint32_t fdt32_to_cpu(fdt32_t x) +{ + return (FDT_FORCE uint32_t)CPU_TO_FDT32(x); +} +static inline fdt32_t cpu_to_fdt32(uint32_t x) +{ + return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x); +} + +static inline uint64_t fdt64_to_cpu(fdt64_t x) +{ + return (FDT_FORCE uint64_t)CPU_TO_FDT64(x); +} +static inline fdt64_t cpu_to_fdt64(uint64_t x) +{ + return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x); +} +#undef CPU_TO_FDT64 +#undef CPU_TO_FDT32 +#undef CPU_TO_FDT16 +#undef EXTRACT_BYTE + +#ifdef __APPLE__ +#include <AvailabilityMacros.h> + +/* strnlen() is not available on Mac OS < 10.7 */ +# if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \ + MAC_OS_X_VERSION_10_7) + +#define strnlen fdt_strnlen + +/* + * fdt_strnlen: returns the length of a string or max_count - which ever is + * smallest. + * Input 1 string: the string whose size is to be determined + * Input 2 max_count: the maximum value returned by this function + * Output: length of the string or max_count (the smallest of the two) + */ +static inline size_t fdt_strnlen(const char *string, size_t max_count) +{ + const char *p = memchr(string, 0, max_count); + return p ? p - string : max_count; +} + +#endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < + MAC_OS_X_VERSION_10_7) */ + +#endif /* __APPLE__ */ + +#endif /* LIBFDT_ENV_H */ diff --git a/roms/opensbi/lib/utils/libfdt/libfdt_internal.h b/roms/opensbi/lib/utils/libfdt/libfdt_internal.h new file mode 100644 index 000000000..d4e0bd49c --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/libfdt_internal.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef LIBFDT_INTERNAL_H +#define LIBFDT_INTERNAL_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include <fdt.h> + +#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) + +int32_t fdt_ro_probe_(const void *fdt); +#define FDT_RO_PROBE(fdt) \ + { \ + int32_t totalsize_; \ + if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \ + return totalsize_; \ + } + +int fdt_check_node_offset_(const void *fdt, int offset); +int fdt_check_prop_offset_(const void *fdt, int offset); +const char *fdt_find_string_(const char *strtab, int tabsize, const char *s); +int fdt_node_end_offset_(void *fdt, int nodeoffset); + +static inline const void *fdt_offset_ptr_(const void *fdt, int offset) +{ + return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; +} + +static inline void *fdt_offset_ptr_w_(void *fdt, int offset) +{ + return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset); +} + +static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n) +{ + const struct fdt_reserve_entry *rsv_table = + (const struct fdt_reserve_entry *) + ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); + + return rsv_table + n; +} +static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n) +{ + return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n); +} + +#define FDT_SW_MAGIC (~FDT_MAGIC) + +/**********************************************************************/ +/* Checking controls */ +/**********************************************************************/ + +#ifndef FDT_ASSUME_MASK +#define FDT_ASSUME_MASK 0 +#endif + +/* + * Defines assumptions which can be enabled. Each of these can be enabled + * individually. For maximum safety, don't enable any assumptions! + * + * For minimal code size and no safety, use ASSUME_PERFECT at your own risk. + * You should have another method of validating the device tree, such as a + * signature or hash check before using libfdt. + * + * For situations where security is not a concern it may be safe to enable + * ASSUME_SANE. + */ +enum { + /* + * This does essentially no checks. Only the latest device-tree + * version is correctly handled. Inconsistencies or errors in the device + * tree may cause undefined behaviour or crashes. Invalid parameters + * passed to libfdt may do the same. + * + * If an error occurs when modifying the tree it may leave the tree in + * an intermediate (but valid) state. As an example, adding a property + * where there is insufficient space may result in the property name + * being added to the string table even though the property itself is + * not added to the struct section. + * + * Only use this if you have a fully validated device tree with + * the latest supported version and wish to minimise code size. + */ + ASSUME_PERFECT = 0xff, + + /* + * This assumes that the device tree is sane. i.e. header metadata + * and basic hierarchy are correct. + * + * With this assumption enabled, normal device trees produced by libfdt + * and the compiler should be handled safely. Malicious device trees and + * complete garbage may cause libfdt to behave badly or crash. Truncated + * device trees (e.g. those only partially loaded) can also cause + * problems. + * + * Note: Only checks that relate exclusively to the device tree itself + * (not the parameters passed to libfdt) are disabled by this + * assumption. This includes checking headers, tags and the like. + */ + ASSUME_VALID_DTB = 1 << 0, + + /* + * This builds on ASSUME_VALID_DTB and further assumes that libfdt + * functions are called with valid parameters, i.e. not trigger + * FDT_ERR_BADOFFSET or offsets that are out of bounds. It disables any + * extensive checking of parameters and the device tree, making various + * assumptions about correctness. + * + * It doesn't make sense to enable this assumption unless + * ASSUME_VALID_DTB is also enabled. + */ + ASSUME_VALID_INPUT = 1 << 1, + + /* + * This disables checks for device-tree version and removes all code + * which handles older versions. + * + * Only enable this if you know you have a device tree with the latest + * version. + */ + ASSUME_LATEST = 1 << 2, + + /* + * This assumes that it is OK for a failed addition to the device tree, + * due to lack of space or some other problem, to skip any rollback + * steps (such as dropping the property name from the string table). + * This is safe to enable in most circumstances, even though it may + * leave the tree in a sub-optimal state. + */ + ASSUME_NO_ROLLBACK = 1 << 3, + + /* + * This assumes that the device tree components appear in a 'convenient' + * order, i.e. the memory reservation block first, then the structure + * block and finally the string block. + * + * This order is not specified by the device-tree specification, + * but is expected by libfdt. The device-tree compiler always created + * device trees with this order. + * + * This assumption disables a check in fdt_open_into() and removes the + * ability to fix the problem there. This is safe if you know that the + * device tree is correctly ordered. See fdt_blocks_misordered_(). + */ + ASSUME_LIBFDT_ORDER = 1 << 4, + + /* + * This assumes that libfdt itself does not have any internal bugs. It + * drops certain checks that should never be needed unless libfdt has an + * undiscovered bug. + * + * This can generally be considered safe to enable. + */ + ASSUME_LIBFDT_FLAWLESS = 1 << 5, +}; + +/** + * can_assume_() - check if a particular assumption is enabled + * + * @mask: Mask to check (ASSUME_...) + * @return true if that assumption is enabled, else false + */ +static inline bool can_assume_(int mask) +{ + return FDT_ASSUME_MASK & mask; +} + +/** helper macros for checking assumptions */ +#define can_assume(_assume) can_assume_(ASSUME_ ## _assume) + +#endif /* LIBFDT_INTERNAL_H */ diff --git a/roms/opensbi/lib/utils/libfdt/objects.mk b/roms/opensbi/lib/utils/libfdt/objects.mk new file mode 100644 index 000000000..8c060df9d --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/objects.mk @@ -0,0 +1,16 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Atish Patra<atish.patra@wdc.com> +# + +libfdt_files = fdt.o fdt_addresses.o fdt_check.o fdt_empty_tree.o fdt_ro.o fdt_rw.o \ + fdt_strerror.o fdt_sw.o fdt_wip.o +$(foreach file, $(libfdt_files), \ + $(eval CFLAGS_$(file) = -I$(src)/../../utils/libfdt)) + +libsbiutils-objs-y += $(addprefix libfdt/,$(libfdt_files)) +libsbiutils-genflags-y += -I$(libsbiutils_dir)/libfdt/ diff --git a/roms/opensbi/lib/utils/libfdt/version.lds b/roms/opensbi/lib/utils/libfdt/version.lds new file mode 100644 index 000000000..7ab85f1d9 --- /dev/null +++ b/roms/opensbi/lib/utils/libfdt/version.lds @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +LIBFDT_1.2 { + global: + fdt_next_node; + fdt_check_header; + fdt_move; + fdt_string; + fdt_num_mem_rsv; + fdt_get_mem_rsv; + fdt_subnode_offset_namelen; + fdt_subnode_offset; + fdt_path_offset_namelen; + fdt_path_offset; + fdt_get_name; + fdt_get_property_namelen; + fdt_get_property; + fdt_getprop_namelen; + fdt_getprop; + fdt_get_phandle; + fdt_get_alias_namelen; + fdt_get_alias; + fdt_get_path; + fdt_header_size; + fdt_supernode_atdepth_offset; + fdt_node_depth; + fdt_parent_offset; + fdt_node_offset_by_prop_value; + fdt_node_offset_by_phandle; + fdt_node_check_compatible; + fdt_node_offset_by_compatible; + fdt_setprop_inplace; + fdt_nop_property; + fdt_nop_node; + fdt_create; + fdt_add_reservemap_entry; + fdt_finish_reservemap; + fdt_begin_node; + fdt_property; + fdt_end_node; + fdt_finish; + fdt_open_into; + fdt_pack; + fdt_add_mem_rsv; + fdt_del_mem_rsv; + fdt_set_name; + fdt_setprop; + fdt_delprop; + fdt_add_subnode_namelen; + fdt_add_subnode; + fdt_del_node; + fdt_strerror; + fdt_offset_ptr; + fdt_next_tag; + fdt_appendprop; + fdt_create_empty_tree; + fdt_first_property_offset; + fdt_get_property_by_offset; + fdt_getprop_by_offset; + fdt_next_property_offset; + fdt_first_subnode; + fdt_next_subnode; + fdt_address_cells; + fdt_size_cells; + fdt_stringlist_contains; + fdt_stringlist_count; + fdt_stringlist_search; + fdt_stringlist_get; + fdt_resize; + fdt_overlay_apply; + fdt_get_string; + fdt_find_max_phandle; + fdt_generate_phandle; + fdt_check_full; + fdt_setprop_placeholder; + fdt_property_placeholder; + fdt_header_size_; + fdt_appendprop_addrrange; + fdt_setprop_inplace_namelen_partial; + fdt_create_with_flags; + local: + *; +}; diff --git a/roms/opensbi/lib/utils/reset/fdt_reset.c b/roms/opensbi/lib/utils/reset/fdt_reset.c new file mode 100644 index 000000000..dead8a342 --- /dev/null +++ b/roms/opensbi/lib/utils/reset/fdt_reset.c @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_scratch.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/reset/fdt_reset.h> + +extern struct fdt_reset fdt_reset_sifive; +extern struct fdt_reset fdt_reset_htif; + +static struct fdt_reset *reset_drivers[] = { + &fdt_reset_sifive, + &fdt_reset_htif, +}; + +static struct fdt_reset *current_driver = NULL; + +int fdt_system_reset_check(u32 reset_type, u32 reset_reason) +{ + if (current_driver && current_driver->system_reset_check) + return current_driver->system_reset_check(reset_type, + reset_reason); + return 0; +} + +void fdt_system_reset(u32 reset_type, u32 reset_reason) +{ + if (current_driver && current_driver->system_reset) + current_driver->system_reset(reset_type, reset_reason); +} + +int fdt_reset_init(void) +{ + int pos, noff, rc; + struct fdt_reset *drv; + const struct fdt_match *match; + void *fdt = sbi_scratch_thishart_arg1_ptr(); + + for (pos = 0; pos < array_size(reset_drivers); pos++) { + drv = reset_drivers[pos]; + + noff = fdt_find_match(fdt, -1, drv->match_table, &match); + if (noff < 0) + continue; + + if (drv->init) { + rc = drv->init(fdt, noff, match); + if (rc) + return rc; + } + current_driver = drv; + break; + } + + return 0; +} diff --git a/roms/opensbi/lib/utils/reset/fdt_reset_htif.c b/roms/opensbi/lib/utils/reset/fdt_reset_htif.c new file mode 100644 index 000000000..587e7d65e --- /dev/null +++ b/roms/opensbi/lib/utils/reset/fdt_reset_htif.c @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi_utils/reset/fdt_reset.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/sys/htif.h> + +static const struct fdt_match htif_reset_match[] = { + { .compatible = "ucb,htif0" }, + { }, +}; + +struct fdt_reset fdt_reset_htif = { + .match_table = htif_reset_match, + .system_reset_check = htif_system_reset_check, + .system_reset = htif_system_reset +}; diff --git a/roms/opensbi/lib/utils/reset/fdt_reset_sifive.c b/roms/opensbi/lib/utils/reset/fdt_reset_sifive.c new file mode 100644 index 000000000..38b520c48 --- /dev/null +++ b/roms/opensbi/lib/utils/reset/fdt_reset_sifive.c @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_scratch.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/reset/fdt_reset.h> +#include <sbi_utils/sys/sifive_test.h> + +static int sifive_test_reset_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + unsigned long addr; + + rc = fdt_get_node_addr_size(fdt, nodeoff, &addr, NULL); + if (rc) + return rc; + + return sifive_test_init(addr); +} + +static const struct fdt_match sifive_test_reset_match[] = { + { .compatible = "sifive,test1" }, + { }, +}; + +struct fdt_reset fdt_reset_sifive = { + .match_table = sifive_test_reset_match, + .init = sifive_test_reset_init, + .system_reset_check = sifive_test_system_reset_check, + .system_reset = sifive_test_system_reset +}; diff --git a/roms/opensbi/lib/utils/reset/objects.mk b/roms/opensbi/lib/utils/reset/objects.mk new file mode 100644 index 000000000..b447261f8 --- /dev/null +++ b/roms/opensbi/lib/utils/reset/objects.mk @@ -0,0 +1,12 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +libsbiutils-objs-y += reset/fdt_reset.o +libsbiutils-objs-y += reset/fdt_reset_htif.o +libsbiutils-objs-y += reset/fdt_reset_sifive.o diff --git a/roms/opensbi/lib/utils/serial/fdt_serial.c b/roms/opensbi/lib/utils/serial/fdt_serial.c new file mode 100644 index 000000000..b9ce67ef1 --- /dev/null +++ b/roms/opensbi/lib/utils/serial/fdt_serial.c @@ -0,0 +1,111 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <libfdt.h> +#include <sbi/sbi_scratch.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/serial/fdt_serial.h> + +extern struct fdt_serial fdt_serial_uart8250; +extern struct fdt_serial fdt_serial_sifive; +extern struct fdt_serial fdt_serial_htif; +extern struct fdt_serial fdt_serial_shakti; + +static struct fdt_serial *serial_drivers[] = { + &fdt_serial_uart8250, + &fdt_serial_sifive, + &fdt_serial_htif, + &fdt_serial_shakti, +}; + +static void dummy_putc(char ch) +{ +} + +static int dummy_getc(void) +{ + return -1; +} + +static struct fdt_serial dummy = { + .match_table = NULL, + .init = NULL, + .putc = dummy_putc, + .getc = dummy_getc, +}; + +static struct fdt_serial *current_driver = &dummy; + +void fdt_serial_putc(char ch) +{ + current_driver->putc(ch); +} + +int fdt_serial_getc(void) +{ + return current_driver->getc(); +} + +int fdt_serial_init(void) +{ + const void *prop; + struct fdt_serial *drv; + const struct fdt_match *match; + int pos, noff = -1, len, coff, rc; + void *fdt = sbi_scratch_thishart_arg1_ptr(); + + /* Find offset of node pointed by stdout-path */ + coff = fdt_path_offset(fdt, "/chosen"); + if (-1 < coff) { + prop = fdt_getprop(fdt, coff, "stdout-path", &len); + if (prop && len) + noff = fdt_path_offset(fdt, prop); + } + + /* First check DT node pointed by stdout-path */ + for (pos = 0; pos < array_size(serial_drivers) && -1 < noff; pos++) { + drv = serial_drivers[pos]; + + match = fdt_match_node(fdt, noff, drv->match_table); + if (!match) + continue; + + if (drv->init) { + rc = drv->init(fdt, noff, match); + if (rc) + return rc; + } + current_driver = drv; + break; + } + + /* Check if we found desired driver */ + if (current_driver != &dummy) + goto done; + + /* Lastly check all DT nodes */ + for (pos = 0; pos < array_size(serial_drivers); pos++) { + drv = serial_drivers[pos]; + + noff = fdt_find_match(fdt, -1, drv->match_table, &match); + if (noff < 0) + continue; + + if (drv->init) { + rc = drv->init(fdt, noff, match); + if (rc) + return rc; + } + current_driver = drv; + break; + } + +done: + return 0; +} diff --git a/roms/opensbi/lib/utils/serial/fdt_serial_htif.c b/roms/opensbi/lib/utils/serial/fdt_serial_htif.c new file mode 100644 index 000000000..32d695317 --- /dev/null +++ b/roms/opensbi/lib/utils/serial/fdt_serial_htif.c @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/serial/fdt_serial.h> +#include <sbi_utils/sys/htif.h> + +static const struct fdt_match serial_htif_match[] = { + { .compatible = "ucb,htif0" }, + { }, +}; + +struct fdt_serial fdt_serial_htif = { + .match_table = serial_htif_match, + .init = NULL, + .getc = htif_getc, + .putc = htif_putc +}; diff --git a/roms/opensbi/lib/utils/serial/fdt_serial_shakti.c b/roms/opensbi/lib/utils/serial/fdt_serial_shakti.c new file mode 100644 index 000000000..c6385a577 --- /dev/null +++ b/roms/opensbi/lib/utils/serial/fdt_serial_shakti.c @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Vijai Kumar K <vijai@behindbytes.com> + * + */ + +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/serial/fdt_serial.h> +#include <sbi_utils/serial/shakti-uart.h> + +static int serial_shakti_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + struct platform_uart_data uart; + + rc = fdt_parse_shakti_uart_node(fdt, nodeoff, &uart); + if (rc) + return rc; + + return shakti_uart_init(uart.addr, uart.freq, uart.baud); +} + +static const struct fdt_match serial_shakti_match[] = { + { .compatible = "shakti,uart0" }, + { }, +}; + +struct fdt_serial fdt_serial_shakti = { + .match_table = serial_shakti_match, + .init = serial_shakti_init, + .getc = shakti_uart_getc, + .putc = shakti_uart_putc +}; diff --git a/roms/opensbi/lib/utils/serial/fdt_serial_sifive.c b/roms/opensbi/lib/utils/serial/fdt_serial_sifive.c new file mode 100644 index 000000000..9e487a2b5 --- /dev/null +++ b/roms/opensbi/lib/utils/serial/fdt_serial_sifive.c @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/serial/fdt_serial.h> +#include <sbi_utils/serial/sifive-uart.h> + +static int serial_sifive_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + struct platform_uart_data uart; + + rc = fdt_parse_sifive_uart_node(fdt, nodeoff, &uart); + if (rc) + return rc; + + return sifive_uart_init(uart.addr, uart.freq, uart.baud); +} + +static const struct fdt_match serial_sifive_match[] = { + { .compatible = "sifive,fu540-c000-uart" }, + { .compatible = "sifive,uart0" }, + { }, +}; + +struct fdt_serial fdt_serial_sifive = { + .match_table = serial_sifive_match, + .init = serial_sifive_init, + .getc = sifive_uart_getc, + .putc = sifive_uart_putc +}; diff --git a/roms/opensbi/lib/utils/serial/fdt_serial_uart8250.c b/roms/opensbi/lib/utils/serial/fdt_serial_uart8250.c new file mode 100644 index 000000000..5030b823a --- /dev/null +++ b/roms/opensbi/lib/utils/serial/fdt_serial_uart8250.c @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/serial/fdt_serial.h> +#include <sbi_utils/serial/uart8250.h> + +static int serial_uart8250_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + struct platform_uart_data uart; + + rc = fdt_parse_uart8250_node(fdt, nodeoff, &uart); + if (rc) + return rc; + + return uart8250_init(uart.addr, uart.freq, uart.baud, + uart.reg_shift, uart.reg_io_width); +} + +static const struct fdt_match serial_uart8250_match[] = { + { .compatible = "ns16550" }, + { .compatible = "ns16550a" }, + { }, +}; + +struct fdt_serial fdt_serial_uart8250 = { + .match_table = serial_uart8250_match, + .init = serial_uart8250_init, + .getc = uart8250_getc, + .putc = uart8250_putc +}; diff --git a/roms/opensbi/lib/utils/serial/objects.mk b/roms/opensbi/lib/utils/serial/objects.mk new file mode 100644 index 000000000..c0746f07e --- /dev/null +++ b/roms/opensbi/lib/utils/serial/objects.mk @@ -0,0 +1,17 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +libsbiutils-objs-y += serial/fdt_serial.o +libsbiutils-objs-y += serial/fdt_serial_htif.o +libsbiutils-objs-y += serial/fdt_serial_shakti.o +libsbiutils-objs-y += serial/fdt_serial_sifive.o +libsbiutils-objs-y += serial/fdt_serial_uart8250.o +libsbiutils-objs-y += serial/shakti-uart.o +libsbiutils-objs-y += serial/sifive-uart.o +libsbiutils-objs-y += serial/uart8250.o diff --git a/roms/opensbi/lib/utils/serial/shakti-uart.c b/roms/opensbi/lib/utils/serial/shakti-uart.c new file mode 100644 index 000000000..7c1148ee1 --- /dev/null +++ b/roms/opensbi/lib/utils/serial/shakti-uart.c @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Vijai Kumar K <vijai@behindbytes.com> + */ + +#include <sbi/riscv_io.h> +#include <sbi/sbi_console.h> +#include <sbi_utils/serial/shakti-uart.h> + +#define REG_BAUD 0x00 +#define REG_TX 0x04 +#define REG_RX 0x08 +#define REG_STATUS 0x0C +#define REG_DELAY 0x10 +#define REG_CONTROL 0x14 +#define REG_INT_EN 0x18 +#define REG_IQ_CYCLES 0x1C +#define REG_RX_THRES 0x20 + +#define UART_TX_FULL 0x2 +#define UART_RX_FULL 0x8 + +static volatile void *uart_base; + +void shakti_uart_putc(char ch) +{ + while((readw(uart_base + REG_STATUS) & UART_TX_FULL)) + ; + writeb(ch, uart_base + REG_TX); +} + +int shakti_uart_getc(void) +{ + u16 status = readw(uart_base + REG_STATUS); + if (status & UART_RX_FULL) + return readb(uart_base + REG_RX); + return -1; +} + +int shakti_uart_init(unsigned long base, u32 in_freq, u32 baudrate) +{ + uart_base = (volatile void *)base; + u16 baud = (u16)(in_freq/(16 * baudrate)); + writew(baud, uart_base + REG_BAUD); + + return 0; +} diff --git a/roms/opensbi/lib/utils/serial/sifive-uart.c b/roms/opensbi/lib/utils/serial/sifive-uart.c new file mode 100644 index 000000000..72c8a6249 --- /dev/null +++ b/roms/opensbi/lib/utils/serial/sifive-uart.c @@ -0,0 +1,102 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_io.h> +#include <sbi/sbi_console.h> +#include <sbi_utils/serial/sifive-uart.h> + +/* clang-format off */ + +#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 + +/* clang-format on */ + +static volatile void *uart_base; +static u32 uart_in_freq; +static u32 uart_baudrate; + +/** + * Find minimum divisor divides in_freq to max_target_hz; + * Based on uart driver n SiFive FSBL. + * + * f_baud = f_in / (div + 1) => div = (f_in / f_baud) - 1 + * The nearest integer solution requires rounding up as to not exceed max_target_hz. + * div = ceil(f_in / f_baud) - 1 + * = floor((f_in - 1 + f_baud) / f_baud) - 1 + * This should not overflow as long as (f_in - 1 + f_baud) does not exceed + * 2^32 - 1, which is unlikely since we represent frequencies in kHz. + */ +static inline unsigned int uart_min_clk_divisor(uint64_t in_freq, + uint64_t max_target_hz) +{ + uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz); + /* Avoid underflow */ + if (quotient == 0) { + return 0; + } else { + return quotient - 1; + } +} + +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); +} + +int sifive_uart_getc(void) +{ + u32 ret = get_reg(UART_REG_RXFIFO); + if (!(ret & UART_RXFIFO_EMPTY)) + return ret & UART_RXFIFO_DATA; + return -1; +} + +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 */ + if (in_freq) + set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate)); + /* 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/roms/opensbi/lib/utils/serial/uart8250.c b/roms/opensbi/lib/utils/serial/uart8250.c new file mode 100644 index 000000000..9635ba863 --- /dev/null +++ b/roms/opensbi/lib/utils/serial/uart8250.c @@ -0,0 +1,125 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_io.h> +#include <sbi_utils/serial/uart8250.h> + +/* clang-format off */ + +#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 */ + +/* clang-format on */ + +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); +} + +int uart8250_getc(void) +{ + if (get_reg(UART_LSR_OFFSET) & UART_LSR_DR) + return get_reg(UART_RBR_OFFSET); + return -1; +} + +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); + + if (bdiv) { + /* 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/roms/opensbi/lib/utils/sys/clint.c b/roms/opensbi/lib/utils/sys/clint.c new file mode 100644 index 000000000..7a392aad8 --- /dev/null +++ b/roms/opensbi/lib/utils/sys/clint.c @@ -0,0 +1,203 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_atomic.h> +#include <sbi/riscv_io.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hartmask.h> +#include <sbi_utils/sys/clint.h> + +#define CLINT_IPI_OFF 0 +#define CLINT_TIME_CMP_OFF 0x4000 +#define CLINT_TIME_VAL_OFF 0xbff8 + +static struct clint_data *clint_ipi_hartid2data[SBI_HARTMASK_MAX_BITS]; + +void clint_ipi_send(u32 target_hart) +{ + struct clint_data *clint; + + if (SBI_HARTMASK_MAX_BITS <= target_hart) + return; + clint = clint_ipi_hartid2data[target_hart]; + if (!clint) + return; + + /* Set CLINT IPI */ + writel(1, &clint->ipi[target_hart - clint->first_hartid]); +} + +void clint_ipi_clear(u32 target_hart) +{ + struct clint_data *clint; + + if (SBI_HARTMASK_MAX_BITS <= target_hart) + return; + clint = clint_ipi_hartid2data[target_hart]; + if (!clint) + return; + + /* Clear CLINT IPI */ + writel(0, &clint->ipi[target_hart - clint->first_hartid]); +} + +int clint_warm_ipi_init(void) +{ + /* Clear CLINT IPI for current HART */ + clint_ipi_clear(current_hartid()); + + return 0; +} + +int clint_cold_ipi_init(struct clint_data *clint) +{ + u32 i; + + if (!clint) + return SBI_EINVAL; + + /* Initialize private data */ + clint->ipi = (void *)clint->addr; + + /* Update IPI hartid table */ + for (i = 0; i < clint->hart_count; i++) + clint_ipi_hartid2data[clint->first_hartid + i] = clint; + + return 0; +} + +static struct clint_data *clint_timer_hartid2data[SBI_HARTMASK_MAX_BITS]; + +#if __riscv_xlen != 32 +static u64 clint_time_rd64(volatile u64 *addr) +{ + return readq_relaxed(addr); +} + +static void clint_time_wr64(u64 value, volatile u64 *addr) +{ + writeq_relaxed(value, addr); +} +#endif + +static u64 clint_time_rd32(volatile u64 *addr) +{ + u32 lo, hi; + + do { + hi = readl_relaxed((u32 *)addr + 1); + lo = readl_relaxed((u32 *)addr); + } while (hi != readl_relaxed((u32 *)addr + 1)); + + return ((u64)hi << 32) | (u64)lo; +} + +static void clint_time_wr32(u64 value, volatile u64 *addr) +{ + u32 mask = -1U; + + writel_relaxed(value & mask, (void *)(addr)); + writel_relaxed(value >> 32, (void *)(addr) + 0x04); +} + +u64 clint_timer_value(void) +{ + struct clint_data *clint = clint_timer_hartid2data[current_hartid()]; + + /* Read CLINT Time Value */ + return clint->time_rd(clint->time_val) + clint->time_delta; +} + +void clint_timer_event_stop(void) +{ + u32 target_hart = current_hartid(); + struct clint_data *clint = clint_timer_hartid2data[target_hart]; + + /* Clear CLINT Time Compare */ + clint->time_wr(-1ULL, + &clint->time_cmp[target_hart - clint->first_hartid]); +} + +void clint_timer_event_start(u64 next_event) +{ + u32 target_hart = current_hartid(); + struct clint_data *clint = clint_timer_hartid2data[target_hart]; + + /* Program CLINT Time Compare */ + clint->time_wr(next_event - clint->time_delta, + &clint->time_cmp[target_hart - clint->first_hartid]); +} + +int clint_warm_timer_init(void) +{ + u64 v1, v2, mv; + u32 target_hart = current_hartid(); + struct clint_data *reference; + struct clint_data *clint = clint_timer_hartid2data[target_hart]; + + if (!clint) + return SBI_ENODEV; + + /* + * Compute delta if reference available + * + * We deliberately compute time_delta in warm init so that time_delta + * is computed on a HART which is going to use given CLINT. We use + * atomic flag timer_delta_computed to ensure that only one HART does + * time_delta computation. + */ + if (clint->time_delta_reference) { + reference = clint->time_delta_reference; + if (!atomic_raw_xchg_ulong(&clint->time_delta_computed, 1)) { + v1 = clint->time_rd(clint->time_val); + mv = reference->time_rd(reference->time_val); + v2 = clint->time_rd(clint->time_val); + clint->time_delta = mv - ((v1 / 2) + (v2 / 2)); + } + } + + /* Clear CLINT Time Compare */ + clint->time_wr(-1ULL, + &clint->time_cmp[target_hart - clint->first_hartid]); + + return 0; +} + +int clint_cold_timer_init(struct clint_data *clint, + struct clint_data *reference) +{ + u32 i; + + if (!clint) + return SBI_EINVAL; + + /* Initialize private data */ + clint->time_delta_reference = reference; + clint->time_delta_computed = 0; + clint->time_delta = 0; + clint->time_val = (u64 *)((void *)clint->addr + CLINT_TIME_VAL_OFF); + clint->time_cmp = (u64 *)((void *)clint->addr + CLINT_TIME_CMP_OFF); + clint->time_rd = clint_time_rd32; + clint->time_wr = clint_time_wr32; + + /* Override read/write accessors for 64bit MMIO */ +#if __riscv_xlen != 32 + if (clint->has_64bit_mmio) { + clint->time_rd = clint_time_rd64; + clint->time_wr = clint_time_wr64; + } +#endif + + /* Update timer hartid table */ + for (i = 0; i < clint->hart_count; i++) + clint_timer_hartid2data[clint->first_hartid + i] = clint; + + return 0; +} diff --git a/roms/opensbi/lib/utils/sys/htif.c b/roms/opensbi/lib/utils/sys/htif.c new file mode 100644 index 000000000..fd70fb981 --- /dev/null +++ b/roms/opensbi/lib/utils/sys/htif.c @@ -0,0 +1,154 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2010-2020, The Regents of the University of California + * (Regents). All Rights Reserved. + */ + +#include <sbi/riscv_locks.h> +#include <sbi_utils/sys/htif.h> + +#define HTIF_DATA_BITS 48 +#define HTIF_DATA_MASK ((1ULL << HTIF_DATA_BITS) - 1) +#define HTIF_DATA_SHIFT 0 +#define HTIF_CMD_BITS 8 +#define HTIF_CMD_MASK ((1ULL << HTIF_CMD_BITS) - 1) +#define HTIF_CMD_SHIFT 48 +#define HTIF_DEV_BITS 8 +#define HTIF_DEV_MASK ((1ULL << HTIF_DEV_BITS) - 1) +#define HTIF_DEV_SHIFT 56 + +#define HTIF_DEV_SYSTEM 0 +#define HTIF_DEV_CONSOLE 1 + +#define HTIF_CONSOLE_CMD_GETC 0 +#define HTIF_CONSOLE_CMD_PUTC 1 + +#if __riscv_xlen == 64 +# define TOHOST_CMD(dev, cmd, payload) \ + (((uint64_t)(dev) << HTIF_DEV_SHIFT) | \ + ((uint64_t)(cmd) << HTIF_CMD_SHIFT) | \ + (uint64_t)(payload)) +#else +# define TOHOST_CMD(dev, cmd, payload) ({ \ + if ((dev) || (cmd)) __builtin_trap(); \ + (payload); }) +#endif +#define FROMHOST_DEV(fromhost_value) \ + ((uint64_t)((fromhost_value) >> HTIF_DEV_SHIFT) & HTIF_DEV_MASK) +#define FROMHOST_CMD(fromhost_value) \ + ((uint64_t)((fromhost_value) >> HTIF_CMD_SHIFT) & HTIF_CMD_MASK) +#define FROMHOST_DATA(fromhost_value) \ + ((uint64_t)((fromhost_value) >> HTIF_DATA_SHIFT) & HTIF_DATA_MASK) + +#define PK_SYS_write 64 + +volatile uint64_t tohost __attribute__((section(".htif"))); +volatile uint64_t fromhost __attribute__((section(".htif"))); +static int htif_console_buf; +static spinlock_t htif_lock = SPIN_LOCK_INITIALIZER; + +static void __check_fromhost() +{ + uint64_t fh = fromhost; + if (!fh) + return; + fromhost = 0; + + /* this should be from the console */ + if (FROMHOST_DEV(fh) != HTIF_DEV_CONSOLE) + __builtin_trap(); + switch (FROMHOST_CMD(fh)) { + case HTIF_CONSOLE_CMD_GETC: + htif_console_buf = 1 + (uint8_t)FROMHOST_DATA(fh); + break; + case HTIF_CONSOLE_CMD_PUTC: + break; + default: + __builtin_trap(); + } +} + +static void __set_tohost(uint64_t dev, uint64_t cmd, uint64_t data) +{ + while (tohost) + __check_fromhost(); + tohost = TOHOST_CMD(dev, cmd, data); +} + +#if __riscv_xlen == 32 +static void do_tohost_fromhost(uint64_t dev, uint64_t cmd, uint64_t data) +{ + spin_lock(&htif_lock); + + __set_tohost(HTIF_DEV_SYSTEM, cmd, data); + + while (1) { + uint64_t fh = fromhost; + if (fh) { + if (FROMHOST_DEV(fh) == HTIF_DEV_SYSTEM && + FROMHOST_CMD(fh) == cmd) { + fromhost = 0; + break; + } + __check_fromhost(); + } + } + + spin_unlock(&htif_lock); +} + +void htif_putc(char ch) +{ + /* HTIF devices are not supported on RV32, so do a proxy write call */ + volatile uint64_t magic_mem[8]; + magic_mem[0] = PK_SYS_write; + magic_mem[1] = HTIF_DEV_CONSOLE; + magic_mem[2] = (uint64_t)(uintptr_t)&ch; + magic_mem[3] = HTIF_CONSOLE_CMD_PUTC; + do_tohost_fromhost(HTIF_DEV_SYSTEM, 0, (uint64_t)(uintptr_t)magic_mem); +} +#else +void htif_putc(char ch) +{ + spin_lock(&htif_lock); + __set_tohost(HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_PUTC, ch); + spin_unlock(&htif_lock); +} +#endif + +int htif_getc(void) +{ + int ch; + +#if __riscv_xlen == 32 + /* HTIF devices are not supported on RV32 */ + return -1; +#endif + + spin_lock(&htif_lock); + + __check_fromhost(); + ch = htif_console_buf; + if (ch >= 0) { + htif_console_buf = -1; + __set_tohost(HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_GETC, 0); + } + + spin_unlock(&htif_lock); + + return ch - 1; +} + +int htif_system_reset_check(u32 type, u32 reason) +{ + return 1; +} + +void htif_system_reset(u32 type, u32 reason) +{ + while (1) { + fromhost = 0; + tohost = 1; + } +} diff --git a/roms/opensbi/lib/utils/sys/objects.mk b/roms/opensbi/lib/utils/sys/objects.mk new file mode 100644 index 000000000..7878ca8b4 --- /dev/null +++ b/roms/opensbi/lib/utils/sys/objects.mk @@ -0,0 +1,12 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +libsbiutils-objs-y += sys/clint.o +libsbiutils-objs-y += sys/htif.o +libsbiutils-objs-y += sys/sifive_test.o diff --git a/roms/opensbi/lib/utils/sys/sifive_test.c b/roms/opensbi/lib/utils/sys/sifive_test.c new file mode 100644 index 000000000..fdf31690c --- /dev/null +++ b/roms/opensbi/lib/utils/sys/sifive_test.c @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_io.h> +#include <sbi/sbi_ecall_interface.h> +#include <sbi_utils/sys/sifive_test.h> + +#define FINISHER_FAIL 0x3333 +#define FINISHER_PASS 0x5555 +#define FINISHER_RESET 0x7777 + +static void *sifive_test_base; + +int sifive_test_system_reset_check(u32 type, u32 reason) +{ + switch (type) { + case SBI_SRST_RESET_TYPE_SHUTDOWN: + case SBI_SRST_RESET_TYPE_COLD_REBOOT: + case SBI_SRST_RESET_TYPE_WARM_REBOOT: + return 1; + } + + return 0; +} + +void sifive_test_system_reset(u32 type, u32 reason) +{ + /* + * Tell the "finisher" that the simulation + * was successful so that QEMU exits + */ + switch (type) { + case SBI_SRST_RESET_TYPE_SHUTDOWN: + if (reason == SBI_SRST_RESET_REASON_NONE) + writew(FINISHER_PASS, sifive_test_base); + else + writew(FINISHER_FAIL, sifive_test_base); + break; + case SBI_SRST_RESET_TYPE_COLD_REBOOT: + case SBI_SRST_RESET_TYPE_WARM_REBOOT: + writew(FINISHER_RESET, sifive_test_base); + break; + } +} + +int sifive_test_init(unsigned long base) +{ + sifive_test_base = (void *)base; + + return 0; +} diff --git a/roms/opensbi/lib/utils/timer/fdt_timer.c b/roms/opensbi/lib/utils/timer/fdt_timer.c new file mode 100644 index 000000000..d1d0e0ce1 --- /dev/null +++ b/roms/opensbi/lib/utils/timer/fdt_timer.c @@ -0,0 +1,112 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_scratch.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/timer/fdt_timer.h> + +extern struct fdt_timer fdt_timer_clint; + +static struct fdt_timer *timer_drivers[] = { + &fdt_timer_clint +}; + +static u64 dummy_value(void) +{ + return 0; +} + +static void dummy_event_stop(void) +{ +} + +static void dummy_event_start(u64 next_event) +{ +} + +static struct fdt_timer dummy = { + .match_table = NULL, + .cold_init = NULL, + .warm_init = NULL, + .exit = NULL, + .value = dummy_value, + .event_stop = dummy_event_stop, + .event_start = dummy_event_start +}; + +static struct fdt_timer *current_driver = &dummy; + +u64 fdt_timer_value(void) +{ + return current_driver->value(); +} + +void fdt_timer_event_stop(void) +{ + current_driver->event_stop(); +} + +void fdt_timer_event_start(u64 next_event) +{ + current_driver->event_start(next_event); +} + +void fdt_timer_exit(void) +{ + if (current_driver->exit) + current_driver->exit(); +} + +static int fdt_timer_warm_init(void) +{ + if (current_driver->warm_init) + return current_driver->warm_init(); + return 0; +} + +static int fdt_timer_cold_init(void) +{ + int pos, noff, rc; + struct fdt_timer *drv; + const struct fdt_match *match; + void *fdt = sbi_scratch_thishart_arg1_ptr(); + + for (pos = 0; pos < array_size(timer_drivers); pos++) { + drv = timer_drivers[pos]; + + noff = -1; + while ((noff = fdt_find_match(fdt, noff, + drv->match_table, &match)) >= 0) { + if (drv->cold_init) { + rc = drv->cold_init(fdt, noff, match); + if (rc) + return rc; + } + current_driver = drv; + } + + if (current_driver != &dummy) + break; + } + + return 0; +} + +int fdt_timer_init(bool cold_boot) +{ + int rc; + + if (cold_boot) { + rc = fdt_timer_cold_init(); + if (rc) + return rc; + } + + return fdt_timer_warm_init(); +} diff --git a/roms/opensbi/lib/utils/timer/fdt_timer_clint.c b/roms/opensbi/lib/utils/timer/fdt_timer_clint.c new file mode 100644 index 000000000..fe6670866 --- /dev/null +++ b/roms/opensbi/lib/utils/timer/fdt_timer_clint.c @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/sbi_error.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/timer/fdt_timer.h> +#include <sbi_utils/sys/clint.h> + +#define CLINT_TIMER_MAX_NR 16 + +static unsigned long clint_timer_count = 0; +static struct clint_data clint_timer[CLINT_TIMER_MAX_NR]; + +static int timer_clint_cold_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + struct clint_data *ct, *ctmaster = NULL; + + if (CLINT_TIMER_MAX_NR <= clint_timer_count) + return SBI_ENOSPC; + ct = &clint_timer[clint_timer_count++]; + if (1 < clint_timer_count) + ctmaster = &clint_timer[0]; + + rc = fdt_parse_clint_node(fdt, nodeoff, TRUE, ct); + if (rc) + return rc; + + return clint_cold_timer_init(ct, ctmaster); +} + +static const struct fdt_match timer_clint_match[] = { + { .compatible = "riscv,clint0" }, + { }, +}; + +struct fdt_timer fdt_timer_clint = { + .match_table = timer_clint_match, + .cold_init = timer_clint_cold_init, + .warm_init = clint_warm_timer_init, + .exit = NULL, + .value = clint_timer_value, + .event_stop = clint_timer_event_stop, + .event_start = clint_timer_event_start, +}; diff --git a/roms/opensbi/lib/utils/timer/objects.mk b/roms/opensbi/lib/utils/timer/objects.mk new file mode 100644 index 000000000..1b84e926c --- /dev/null +++ b/roms/opensbi/lib/utils/timer/objects.mk @@ -0,0 +1,11 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +libsbiutils-objs-y += timer/fdt_timer.o +libsbiutils-objs-y += timer/fdt_timer_clint.o diff --git a/roms/opensbi/platform/andes/ae350/cache.c b/roms/opensbi/platform/andes/ae350/cache.c new file mode 100644 index 000000000..af724c5c7 --- /dev/null +++ b/roms/opensbi/platform/andes/ae350/cache.c @@ -0,0 +1,89 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Andes Technology Corporation + * + * Authors: + * Nylon Chen <nylon7@andestech.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_io.h> +#include <sbi/sbi_types.h> +#include "platform.h" + +uintptr_t mcall_set_mcache_ctl(unsigned long input) +{ + csr_clear(CSR_MCACHECTL, V5_MCACHE_CTL_MASK); + csr_write(CSR_MCACHECTL, input); + return 0; +} + +uintptr_t mcall_set_mmisc_ctl(unsigned long input) +{ + csr_clear(CSR_MMISCCTL, V5_MMISC_CTL_MASK); + csr_write(CSR_MMISCCTL, input); + return 0; +} + +uintptr_t mcall_icache_op(unsigned int enable) +{ + if (enable) { + csr_set(CSR_MCACHECTL, V5_MCACHE_CTL_IC_EN); + } else { + csr_clear(CSR_MCACHECTL, V5_MCACHE_CTL_IC_EN); + asm volatile("fence.i\n\t"); + } + return 0; +} + +uintptr_t mcall_dcache_op(unsigned int enable) +{ + if (enable) { + csr_set(CSR_MCACHECTL, V5_MCACHE_CTL_DC_EN); + } else { + csr_clear(CSR_MCACHECTL, V5_MCACHE_CTL_DC_EN); + csr_write(CSR_MCCTLCOMMAND, V5_UCCTL_L1D_WBINVAL_ALL); + } + return 0; +} + +uintptr_t mcall_l1_cache_i_prefetch_op(unsigned long enable) +{ + if (enable) { + csr_set(CSR_MCACHECTL, V5_MCACHE_CTL_L1I_PREFETCH_EN); + } else { + csr_clear(CSR_MCACHECTL, V5_MCACHE_CTL_L1I_PREFETCH_EN); + } + return 0; +} + +uintptr_t mcall_l1_cache_d_prefetch_op(unsigned long enable) +{ + if (enable) { + csr_set(CSR_MCACHECTL, V5_MCACHE_CTL_L1D_PREFETCH_EN); + } else { + csr_clear(CSR_MCACHECTL, V5_MCACHE_CTL_L1D_PREFETCH_EN); + } + return 0; +} + +uintptr_t mcall_non_blocking_load_store(unsigned long enable) +{ + if (enable) { + csr_set(CSR_MCACHECTL, V5_MMISC_CTL_NON_BLOCKING_EN); + } else { + csr_clear(CSR_MCACHECTL, V5_MMISC_CTL_NON_BLOCKING_EN); + } + return 0; +} + +uintptr_t mcall_write_around(unsigned long enable) +{ + if (enable) { + csr_set(CSR_MCACHECTL, V5_MCACHE_CTL_DC_WAROUND_1_EN); + } else { + csr_clear(CSR_MCACHECTL, V5_MCACHE_CTL_DC_WAROUND_1_EN); + } + return 0; +} diff --git a/roms/opensbi/platform/andes/ae350/cache.h b/roms/opensbi/platform/andes/ae350/cache.h new file mode 100644 index 000000000..e1c1826f3 --- /dev/null +++ b/roms/opensbi/platform/andes/ae350/cache.h @@ -0,0 +1,17 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Andes Technology Corporation + * + * Authors: + * Nylon Chen <nylon7@andestech.com> + */ + +uintptr_t mcall_set_mcache_ctl(unsigned long input); +uintptr_t mcall_set_mmisc_ctl(unsigned long input); +uintptr_t mcall_icache_op(unsigned int enable); +uintptr_t mcall_dcache_op(unsigned int enable); +uintptr_t mcall_l1_cache_i_prefetch_op(unsigned long enable); +uintptr_t mcall_l1_cache_d_prefetch_op(unsigned long enable); +uintptr_t mcall_non_blocking_load_store(unsigned long enable); +uintptr_t mcall_write_around(unsigned long enable); diff --git a/roms/opensbi/platform/andes/ae350/config.mk b/roms/opensbi/platform/andes/ae350/config.mk new file mode 100644 index 000000000..f555ef5be --- /dev/null +++ b/roms/opensbi/platform/andes/ae350/config.mk @@ -0,0 +1,36 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Andes Technology Corporation +# +# Authors: +# Zong Li <zong@andestech.com> +# Nylon Chen <nylon7@andestech.com> + +# Compiler flags +platform-cppflags-y = +platform-cflags-y = +platform-asflags-y = +platform-ldflags-y = + +# Blobs to build +FW_TEXT_START=0x00000000 + +FW_DYNAMIC=y + +FW_JUMP=y +ifeq ($(PLATFORM_RISCV_XLEN), 32) + FW_JUMP_ADDR=0x400000 +else + FW_JUMP_ADDR=0x200000 +endif +FW_JUMP_FDT_ADDR=0x2000000 + +FW_PAYLOAD=y +ifeq ($(PLATFORM_RISCV_XLEN), 32) + FW_PAYLOAD_OFFSET=0x400000 +else + FW_PAYLOAD_OFFSET=0x200000 +endif + +FW_PAYLOAD_FDT_ADDR=0x2000000 diff --git a/roms/opensbi/platform/andes/ae350/objects.mk b/roms/opensbi/platform/andes/ae350/objects.mk new file mode 100644 index 000000000..5369677c3 --- /dev/null +++ b/roms/opensbi/platform/andes/ae350/objects.mk @@ -0,0 +1,11 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Andes Technology Corporation +# +# Authors: +# Zong Li <zong@andestech.com> +# Nylon Chen <nylon7@andestech.com> +# + +platform-objs-y += cache.o platform.o plicsw.o plmt.o diff --git a/roms/opensbi/platform/andes/ae350/platform.c b/roms/opensbi/platform/andes/ae350/platform.c new file mode 100644 index 000000000..aec91cdff --- /dev/null +++ b/roms/opensbi/platform/andes/ae350/platform.c @@ -0,0 +1,192 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + * Nylon Chen <nylon7@andestech.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_const.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_trap.h> +#include <sbi_utils/fdt/fdt_fixup.h> +#include <sbi_utils/irqchip/plic.h> +#include <sbi_utils/serial/uart8250.h> +#include "platform.h" +#include "plicsw.h" +#include "plmt.h" +#include "cache.h" + +static struct plic_data plic = { + .addr = AE350_PLIC_ADDR, + .num_src = AE350_PLIC_NUM_SOURCES, +}; + +/* Platform final initialization. */ +static int ae350_final_init(bool cold_boot) +{ + void *fdt; + + /* enable L1 cache */ + uintptr_t mcache_ctl_val = csr_read(CSR_MCACHECTL); + + if (!(mcache_ctl_val & V5_MCACHE_CTL_IC_EN)) + mcache_ctl_val |= V5_MCACHE_CTL_IC_EN; + if (!(mcache_ctl_val & V5_MCACHE_CTL_DC_EN)) + mcache_ctl_val |= V5_MCACHE_CTL_DC_EN; + if (!(mcache_ctl_val & V5_MCACHE_CTL_CCTL_SUEN)) + mcache_ctl_val |= V5_MCACHE_CTL_CCTL_SUEN; + csr_write(CSR_MCACHECTL, mcache_ctl_val); + + /* enable L2 cache */ + uint32_t *l2c_ctl_base = (void *)AE350_L2C_ADDR + V5_L2C_CTL_OFFSET; + uint32_t l2c_ctl_val = *l2c_ctl_base; + + if (!(l2c_ctl_val & V5_L2C_CTL_ENABLE_MASK)) + l2c_ctl_val |= V5_L2C_CTL_ENABLE_MASK; + *l2c_ctl_base = l2c_ctl_val; + + if (!cold_boot) + return 0; + + fdt = sbi_scratch_thishart_arg1_ptr(); + fdt_fixups(fdt); + + return 0; +} + +/* Initialize the platform console. */ +static int ae350_console_init(void) +{ + return uart8250_init(AE350_UART_ADDR, + AE350_UART_FREQUENCY, + AE350_UART_BAUDRATE, + AE350_UART_REG_SHIFT, + AE350_UART_REG_WIDTH); +} + +/* Initialize the platform interrupt controller for current HART. */ +static int ae350_irqchip_init(bool cold_boot) +{ + u32 hartid = current_hartid(); + int ret; + + if (cold_boot) { + ret = plic_cold_irqchip_init(&plic); + if (ret) + return ret; + } + + return plic_warm_irqchip_init(&plic, 2 * hartid, 2 * hartid + 1); +} + +/* Initialize IPI for current HART. */ +static int ae350_ipi_init(bool cold_boot) +{ + int ret; + + if (cold_boot) { + ret = plicsw_cold_ipi_init(AE350_PLICSW_ADDR, + AE350_HART_COUNT); + if (ret) + return ret; + } + + return plicsw_warm_ipi_init(); +} + +/* Initialize platform timer for current HART. */ +static int ae350_timer_init(bool cold_boot) +{ + int ret; + + if (cold_boot) { + ret = plmt_cold_timer_init(AE350_PLMT_ADDR, + AE350_HART_COUNT); + if (ret) + return ret; + } + + return plmt_warm_timer_init(); +} + +/* Vendor-Specific SBI handler */ +static int ae350_vendor_ext_provider(long extid, long funcid, + const struct sbi_trap_regs *regs, unsigned long *out_value, + struct sbi_trap_info *out_trap) +{ + int ret = 0; + switch (funcid) { + case SBI_EXT_ANDES_GET_MCACHE_CTL_STATUS: + *out_value = csr_read(CSR_MCACHECTL); + break; + case SBI_EXT_ANDES_GET_MMISC_CTL_STATUS: + *out_value = csr_read(CSR_MMISCCTL); + break; + case SBI_EXT_ANDES_SET_MCACHE_CTL: + ret = mcall_set_mcache_ctl(regs->a0); + break; + case SBI_EXT_ANDES_SET_MMISC_CTL: + ret = mcall_set_mmisc_ctl(regs->a0); + break; + case SBI_EXT_ANDES_ICACHE_OP: + ret = mcall_icache_op(regs->a0); + break; + case SBI_EXT_ANDES_DCACHE_OP: + ret = mcall_dcache_op(regs->a0); + break; + case SBI_EXT_ANDES_L1CACHE_I_PREFETCH: + ret = mcall_l1_cache_i_prefetch_op(regs->a0); + break; + case SBI_EXT_ANDES_L1CACHE_D_PREFETCH: + ret = mcall_l1_cache_d_prefetch_op(regs->a0); + break; + case SBI_EXT_ANDES_NON_BLOCKING_LOAD_STORE: + ret = mcall_non_blocking_load_store(regs->a0); + break; + case SBI_EXT_ANDES_WRITE_AROUND: + ret = mcall_write_around(regs->a0); + break; + default: + sbi_printf("Unsupported vendor sbi call : %ld\n", funcid); + asm volatile("ebreak"); + } + return ret; +} + +/* Platform descriptor. */ +const struct sbi_platform_operations platform_ops = { + .final_init = ae350_final_init, + + .console_init = ae350_console_init, + .console_putc = uart8250_putc, + .console_getc = uart8250_getc, + + .irqchip_init = ae350_irqchip_init, + + .ipi_init = ae350_ipi_init, + .ipi_send = plicsw_ipi_send, + .ipi_clear = plicsw_ipi_clear, + + .timer_init = ae350_timer_init, + .timer_value = plmt_timer_value, + .timer_event_start = plmt_timer_event_start, + .timer_event_stop = plmt_timer_event_stop, + + .vendor_ext_provider = ae350_vendor_ext_provider +}; + +const struct sbi_platform platform = { + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01), + .name = "Andes AE350", + .features = SBI_PLATFORM_DEFAULT_FEATURES, + .hart_count = AE350_HART_COUNT, + .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .platform_ops_addr = (unsigned long)&platform_ops +}; diff --git a/roms/opensbi/platform/andes/ae350/platform.h b/roms/opensbi/platform/andes/ae350/platform.h new file mode 100644 index 000000000..f34ca0fcc --- /dev/null +++ b/roms/opensbi/platform/andes/ae350/platform.h @@ -0,0 +1,125 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + * Nylon Chen <nylon7@andestech.com> + */ + +#ifndef _AE350_PLATFORM_H_ +#define _AE350_PLATFORM_H_ + +#define AE350_HART_COUNT 4 + +#define AE350_PLIC_ADDR 0xe4000000 +#define AE350_PLIC_NUM_SOURCES 71 + +#define AE350_PLICSW_ADDR 0xe6400000 + +#define AE350_PLMT_ADDR 0xe6000000 + +#define AE350_L2C_ADDR 0xe0500000 + +#define AE350_UART_ADDR_OFFSET 0x20 +#define AE350_UART_ADDR (0xf0300000 + AE350_UART_ADDR_OFFSET) +#define AE350_UART_FREQUENCY 19660800 +#define AE350_UART_BAUDRATE 38400 +#define AE350_UART_REG_SHIFT 2 +#define AE350_UART_REG_WIDTH 0 + +/*Memory and Miscellaneous Registers*/ +#define CSR_MILMB 0x7c0 +#define CSR_MDLMB 0x7c1 +#define CSR_MECC_CDOE 0x7c2 +#define CSR_MNVEC 0x7c3 +#define CSR_MPFTCTL 0x7c5 +#define CSR_MCACHECTL 0x7ca +#define CSR_MCCTLBEGINADDR 0x7cb +#define CSR_MCCTLCOMMAND 0x7cc +#define CSR_MCCTLDATA 0x7cc +#define CSR_SCCTLDATA 0x9cd +#define CSR_UCCTLBEGINADDR 0x80c +#define CSR_MMISCCTL 0x7d0 + +enum sbi_ext_andes_fid { + SBI_EXT_ANDES_GET_MCACHE_CTL_STATUS = 0, + SBI_EXT_ANDES_GET_MMISC_CTL_STATUS, + SBI_EXT_ANDES_SET_MCACHE_CTL, + SBI_EXT_ANDES_SET_MMISC_CTL, + SBI_EXT_ANDES_ICACHE_OP, + SBI_EXT_ANDES_DCACHE_OP, + SBI_EXT_ANDES_L1CACHE_I_PREFETCH, + SBI_EXT_ANDES_L1CACHE_D_PREFETCH, + SBI_EXT_ANDES_NON_BLOCKING_LOAD_STORE, + SBI_EXT_ANDES_WRITE_AROUND, +}; + +/* nds v5 mmisc_ctl register*/ +#define V5_MMISC_CTL_VEC_PLIC_OFFSET 1 +#define V5_MMISC_CTL_RVCOMPM_OFFSET 2 +#define V5_MMISC_CTL_BRPE_OFFSET 3 +#define V5_MMISC_CTL_MSA_OR_UNA_OFFSET 6 +#define V5_MMISC_CTL_NON_BLOCKING_OFFSET 8 +#define V5_MCACHE_CTL_L1I_PREFETCH_OFFSET 9 +#define V5_MCACHE_CTL_L1D_PREFETCH_OFFSET 10 +#define V5_MCACHE_CTL_DC_WAROUND_OFFSET_1 13 +#define V5_MCACHE_CTL_DC_WAROUND_OFFSET_2 14 + +#define V5_MMISC_CTL_VEC_PLIC_EN (1UL << V5_MMISC_CTL_VEC_PLIC_OFFSET) +#define V5_MMISC_CTL_RVCOMPM_EN (1UL << V5_MMISC_CTL_RVCOMPM_OFFSET) +#define V5_MMISC_CTL_BRPE_EN (1UL << V5_MMISC_CTL_BRPE_OFFSET) +#define V5_MMISC_CTL_MSA_OR_UNA_EN (1UL << V5_MMISC_CTL_MSA_OR_UNA_OFFSET) +#define V5_MMISC_CTL_NON_BLOCKING_EN (1UL << V5_MMISC_CTL_NON_BLOCKING_OFFSET) +#define V5_MCACHE_CTL_L1I_PREFETCH_EN (1UL << V5_MCACHE_CTL_L1I_PREFETCH_OFFSET) +#define V5_MCACHE_CTL_L1D_PREFETCH_EN (1UL << V5_MCACHE_CTL_L1D_PREFETCH_OFFSET) +#define V5_MCACHE_CTL_DC_WAROUND_1_EN (1UL << V5_MCACHE_CTL_DC_WAROUND_OFFSET_1) +#define V5_MCACHE_CTL_DC_WAROUND_2_EN (1UL << V5_MCACHE_CTL_DC_WAROUND_OFFSET_2) + +#define V5_MMISC_CTL_MASK (V5_MMISC_CTL_VEC_PLIC_EN | V5_MMISC_CTL_RVCOMPM_EN \ + | V5_MMISC_CTL_BRPE_EN | V5_MMISC_CTL_MSA_OR_UNA_EN | V5_MMISC_CTL_NON_BLOCKING_EN) + +/* nds mcache_ctl register*/ +#define V5_MCACHE_CTL_IC_EN_OFFSET 0 +#define V5_MCACHE_CTL_DC_EN_OFFSET 1 +#define V5_MCACHE_CTL_IC_ECCEN_OFFSET 2 +#define V5_MCACHE_CTL_DC_ECCEN_OFFSET 4 +#define V5_MCACHE_CTL_IC_RWECC_OFFSET 6 +#define V5_MCACHE_CTL_DC_RWECC_OFFSET 7 +#define V5_MCACHE_CTL_CCTL_SUEN_OFFSET 8 + +/*nds cctl command*/ +#define V5_UCCTL_L1D_WBINVAL_ALL 6 +#define V5_UCCTL_L1D_WB_ALL 7 + +#define V5_MCACHE_CTL_IC_EN (1UL << V5_MCACHE_CTL_IC_EN_OFFSET) +#define V5_MCACHE_CTL_DC_EN (1UL << V5_MCACHE_CTL_DC_EN_OFFSET) +#define V5_MCACHE_CTL_IC_RWECC (1UL << V5_MCACHE_CTL_IC_RWECC_OFFSET) +#define V5_MCACHE_CTL_DC_RWECC (1UL << V5_MCACHE_CTL_DC_RWECC_OFFSET) +#define V5_MCACHE_CTL_CCTL_SUEN (1UL << V5_MCACHE_CTL_CCTL_SUEN_OFFSET) + +#define V5_MCACHE_CTL_MASK (V5_MCACHE_CTL_IC_EN | V5_MCACHE_CTL_DC_EN \ + | V5_MCACHE_CTL_IC_RWECC | V5_MCACHE_CTL_DC_RWECC \ + | V5_MCACHE_CTL_CCTL_SUEN | V5_MCACHE_CTL_L1I_PREFETCH_EN \ + | V5_MCACHE_CTL_L1D_PREFETCH_EN | V5_MCACHE_CTL_DC_WAROUND_1_EN \ + | V5_MCACHE_CTL_DC_WAROUND_2_EN) + +#define V5_L2C_CTL_OFFSET 0x8 +#define V5_L2C_CTL_ENABLE_OFFSET 0 +#define V5_L2C_CTL_IPFDPT_OFFSET 3 +#define V5_L2C_CTL_DPFDPT_OFFSET 5 +#define V5_L2C_CTL_TRAMOCTL_OFFSET 8 +#define V5_L2C_CTL_TRAMICTL_OFFSET 10 +#define V5_L2C_CTL_DRAMOCTL_OFFSET 11 +#define V5_L2C_CTL_DRAMICTL_OFFSET 13 + +#define V5_L2C_CTL_ENABLE_MASK (1UL << V5_L2C_CTL_ENABLE_OFFSET) +#define V5_L2C_CTL_IPFDPT_MASK (3UL << V5_L2C_CTL_IPFDPT_OFFSET) +#define V5_L2C_CTL_DPFDPT_MASK (3UL << V5_L2C_CTL_DPFDPT_OFFSET) +#define V5_L2C_CTL_TRAMOCTL_MASK (3UL << V5_L2C_CTL_TRAMOCTL_OFFSET) +#define V5_L2C_CTL_TRAMICTL_MASK (1UL << V5_L2C_CTL_TRAMICTL_OFFSET) +#define V5_L2C_CTL_DRAMOCTL_MASK (3UL << V5_L2C_CTL_DRAMOCTL_OFFSET) +#define V5_L2C_CTL_DRAMICTL_MASK (1UL << V5_L2C_CTL_DRAMICTL_OFFSET) + +#endif /* _AE350_PLATFORM_H_ */ diff --git a/roms/opensbi/platform/andes/ae350/plicsw.c b/roms/opensbi/platform/andes/ae350/plicsw.c new file mode 100644 index 000000000..d07df28c8 --- /dev/null +++ b/roms/opensbi/platform/andes/ae350/plicsw.c @@ -0,0 +1,145 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + * Nylon Chen <nylon7@andestech.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_io.h> +#include <sbi/sbi_types.h> +#include "plicsw.h" +#include "platform.h" + +static u32 plicsw_ipi_hart_count; +static struct plicsw plicsw_dev[AE350_HART_COUNT]; + +static inline void plicsw_claim(void) +{ + u32 source_hart = current_hartid(); + + plicsw_dev[source_hart].source_id = + readl(plicsw_dev[source_hart].plicsw_claim); +} + +static inline void plicsw_complete(void) +{ + u32 source_hart = current_hartid(); + u32 source = plicsw_dev[source_hart].source_id; + + writel(source, plicsw_dev[source_hart].plicsw_claim); +} + +static inline u32 plicsw_get_pending(u32 source_hart, u32 target_hart) +{ + return readl(plicsw_dev[source_hart].plicsw_pending) + & (PLICSW_HART_MASK >> target_hart); +} + +static inline void plic_sw_pending(u32 target_hart) +{ + /* + * The pending array registers are w1s type. + * IPI pending array mapping as following: + * + * Pending array start address: base + 0x1000 + * ------------------------------------- + * | hart 3 | hart 2 | hart 1 | hart 0 | + * ------------------------------------- + * Each hart X can send IPI to another hart by setting the + * corresponding bit in hart X own region(see the below). + * + * In each hart region: + * ----------------------------------------------- + * | bit 7 | bit 6 | bit 5 | bit 4 | ... | bit 0 | + * ----------------------------------------------- + * The bit 7 is used to send IPI to hart 0 + * The bit 6 is used to send IPI to hart 1 + * The bit 5 is used to send IPI to hart 2 + * The bit 4 is used to send IPI to hart 3 + */ + u32 source_hart = current_hartid(); + u32 target_offset = (PLICSW_PENDING_PER_HART - 1) - target_hart; + u32 per_hart_offset = PLICSW_PENDING_PER_HART * source_hart; + u32 val = 1 << target_offset << per_hart_offset; + + writel(val, plicsw_dev[source_hart].plicsw_pending); +} + +void plicsw_ipi_send(u32 target_hart) +{ + if (plicsw_ipi_hart_count <= target_hart) + return; + + /* Set PLICSW IPI */ + plic_sw_pending(target_hart); +} + +void plicsw_ipi_clear(u32 target_hart) +{ + if (plicsw_ipi_hart_count <= target_hart) + return; + + /* Clear CLINT IPI */ + plicsw_claim(); + plicsw_complete(); +} + +int plicsw_warm_ipi_init(void) +{ + u32 hartid = current_hartid(); + + if (!plicsw_dev[hartid].plicsw_pending + && !plicsw_dev[hartid].plicsw_enable + && !plicsw_dev[hartid].plicsw_claim) + return -1; + + /* Clear PLICSW IPI */ + plicsw_ipi_clear(hartid); + + return 0; +} + +int plicsw_cold_ipi_init(unsigned long base, u32 hart_count) +{ + /* Setup source priority */ + uint32_t *priority = (void *)base + PLICSW_PRIORITY_BASE; + + for (int i = 0; i < AE350_HART_COUNT*PLICSW_PENDING_PER_HART; i++) + writel(1, &priority[i]); + + /* Setup target enable.*/ + uint32_t enable_mask = PLICSW_HART_MASK; + + for (int i = 0; i < AE350_HART_COUNT; i++) { + uint32_t *enable = (void *)base + PLICSW_ENABLE_BASE + + PLICSW_ENABLE_PER_HART * i; + writel(enable_mask, &enable[0]); + enable_mask >>= 1; + } + + /* Figure-out PLICSW IPI register address */ + plicsw_ipi_hart_count = hart_count; + + for (u32 hartid = 0; hartid < AE350_HART_COUNT; hartid++) { + plicsw_dev[hartid].source_id = 0; + plicsw_dev[hartid].plicsw_pending = + (void *)base + + PLICSW_PENDING_BASE + + ((hartid / 4) * 4); + plicsw_dev[hartid].plicsw_enable = + (void *)base + + PLICSW_ENABLE_BASE + + PLICSW_ENABLE_PER_HART * hartid; + plicsw_dev[hartid].plicsw_claim = + (void *)base + + PLICSW_CONTEXT_BASE + + PLICSW_CONTEXT_CLAIM + + PLICSW_CONTEXT_PER_HART * hartid; + } + + return 0; +} diff --git a/roms/opensbi/platform/andes/ae350/plicsw.h b/roms/opensbi/platform/andes/ae350/plicsw.h new file mode 100644 index 000000000..8be61945b --- /dev/null +++ b/roms/opensbi/platform/andes/ae350/plicsw.h @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + * Nylon Chen <nylon7@andestech.com> + */ + +#ifndef _AE350_PLICSW_H_ +#define _AE350_PLICSW_H_ + +#define PLICSW_PRIORITY_BASE 0x4 + +#define PLICSW_PENDING_BASE 0x1000 +#define PLICSW_PENDING_PER_HART 0x8 + +#define PLICSW_ENABLE_BASE 0x2000 +#define PLICSW_ENABLE_PER_HART 0x80 + +#define PLICSW_CONTEXT_BASE 0x200000 +#define PLICSW_CONTEXT_PER_HART 0x1000 +#define PLICSW_CONTEXT_CLAIM 0x4 + +#define PLICSW_HART_MASK 0x80808080 + +struct plicsw { + u32 source_id; + + volatile uint32_t *plicsw_pending; + volatile uint32_t *plicsw_enable; + volatile uint32_t *plicsw_claim; +}; + +void plicsw_ipi_send(u32 target_hart); + +void plicsw_ipi_sync(u32 target_hart); + +void plicsw_ipi_clear(u32 target_hart); + +int plicsw_warm_ipi_init(void); + +int plicsw_cold_ipi_init(unsigned long base, u32 hart_count); + +#endif /* _AE350_PLICSW_H_ */ diff --git a/roms/opensbi/platform/andes/ae350/plmt.c b/roms/opensbi/platform/andes/ae350/plmt.c new file mode 100644 index 000000000..3848e158d --- /dev/null +++ b/roms/opensbi/platform/andes/ae350/plmt.c @@ -0,0 +1,97 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + * Nylon Chen <nylon7@andestech.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_io.h> + +static u32 plmt_time_hart_count; +static volatile void *plmt_time_base; +static volatile u64 *plmt_time_val; +static volatile u64 *plmt_time_cmp; + +u64 plmt_timer_value(void) +{ +#if __riscv_xlen == 64 + return readq_relaxed(plmt_time_val); +#else + u32 lo, hi; + + do { + hi = readl_relaxed((void *)plmt_time_val + 0x04); + lo = readl_relaxed(plmt_time_val); + } while (hi != readl_relaxed((void *)plmt_time_val + 0x04)); + + return ((u64)hi << 32) | (u64)lo; +#endif +} + +void plmt_timer_event_stop(void) +{ + u32 target_hart = current_hartid(); + + if (plmt_time_hart_count <= target_hart) + return; + + /* Clear PLMT Time Compare */ +#if __riscv_xlen == 64 + writeq_relaxed(-1ULL, &plmt_time_cmp[target_hart]); +#else + writel_relaxed(-1UL, &plmt_time_cmp[target_hart]); + writel_relaxed(-1UL, (void *)(&plmt_time_cmp[target_hart]) + 0x04); +#endif +} + +void plmt_timer_event_start(u64 next_event) +{ + u32 target_hart = current_hartid(); + + if (plmt_time_hart_count <= target_hart) + return; + + /* Program PLMT Time Compare */ +#if __riscv_xlen == 64 + writeq_relaxed(next_event, &plmt_time_cmp[target_hart]); +#else + u32 mask = -1UL; + + writel_relaxed(next_event & mask, &plmt_time_cmp[target_hart]); + writel_relaxed(next_event >> 32, + (void *)(&plmt_time_cmp[target_hart]) + 0x04); +#endif + +} + +int plmt_warm_timer_init(void) +{ + u32 target_hart = current_hartid(); + + if (plmt_time_hart_count <= target_hart || !plmt_time_base) + return -1; + + /* Clear PLMT Time Compare */ +#if __riscv_xlen == 64 + writeq_relaxed(-1ULL, &plmt_time_cmp[target_hart]); +#else + writel_relaxed(-1UL, &plmt_time_cmp[target_hart]); + writel_relaxed(-1UL, (void *)(&plmt_time_cmp[target_hart]) + 0x04); +#endif + + return 0; +} + +int plmt_cold_timer_init(unsigned long base, u32 hart_count) +{ + plmt_time_hart_count = hart_count; + plmt_time_base = (void *)base; + plmt_time_val = (u64 *)(plmt_time_base); + plmt_time_cmp = (u64 *)(plmt_time_base + 0x8); + + return 0; +} diff --git a/roms/opensbi/platform/andes/ae350/plmt.h b/roms/opensbi/platform/andes/ae350/plmt.h new file mode 100644 index 000000000..129fcf8d1 --- /dev/null +++ b/roms/opensbi/platform/andes/ae350/plmt.h @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + */ + +#ifndef _AE350_PLMT_H_ +#define _AE350_PLMT_H_ + +u64 plmt_timer_value(void); + +void plmt_timer_event_stop(void); + +void plmt_timer_event_start(u64 next_event); + +int plmt_warm_timer_init(void); + +int plmt_cold_timer_init(unsigned long base, u32 hart_count); + +#endif /* _AE350_PLMT_H_ */ diff --git a/roms/opensbi/platform/fpga/ariane/config.mk b/roms/opensbi/platform/fpga/ariane/config.mk new file mode 100644 index 000000000..3556461a1 --- /dev/null +++ b/roms/opensbi/platform/fpga/ariane/config.mk @@ -0,0 +1,36 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (C) 2019 FORTH-ICS/CARV +# Panagiotis Peristerakis <perister@ics.forth.gr> +# + +#for more infos, check out /platform/template/config.mk + +PLATFORM_RISCV_XLEN = 64 + +# Blobs to build +FW_TEXT_START=0x80000000 +FW_JUMP=n + +ifeq ($(PLATFORM_RISCV_XLEN), 32) + # This needs to be 4MB aligned for 32-bit support + FW_JUMP_ADDR=0x80400000 + else + # This needs to be 2MB aligned for 64-bit support + FW_JUMP_ADDR=0x80200000 + endif +FW_JUMP_FDT_ADDR=0x82200000 + +# Firmware with payload configuration. +FW_PAYLOAD=y + +ifeq ($(PLATFORM_RISCV_XLEN), 32) +# This needs to be 4MB aligned for 32-bit support + FW_PAYLOAD_OFFSET=0x400000 +else +# This needs to be 2MB aligned for 64-bit support + FW_PAYLOAD_OFFSET=0x200000 +endif +FW_PAYLOAD_FDT_ADDR=0x82200000 +FW_PAYLOAD_ALIGN=0x1000 diff --git a/roms/opensbi/platform/fpga/ariane/objects.mk b/roms/opensbi/platform/fpga/ariane/objects.mk new file mode 100644 index 000000000..814e6da63 --- /dev/null +++ b/roms/opensbi/platform/fpga/ariane/objects.mk @@ -0,0 +1,8 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (C) 2019 FORTH-ICS/CARV +# Panagiotis Peristerakis <perister@ics.forth.gr> +# + +platform-objs-y += platform.o diff --git a/roms/opensbi/platform/fpga/ariane/platform.c b/roms/opensbi/platform/fpga/ariane/platform.c new file mode 100644 index 000000000..ea179e52d --- /dev/null +++ b/roms/opensbi/platform/fpga/ariane/platform.c @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019 FORTH-ICS/CARV + * Panagiotis Peristerakis <perister@ics.forth.gr> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/riscv_io.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_const.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_platform.h> +#include <sbi_utils/fdt/fdt_fixup.h> +#include <sbi_utils/irqchip/plic.h> +#include <sbi_utils/serial/uart8250.h> +#include <sbi_utils/sys/clint.h> + +#define ARIANE_UART_ADDR 0x10000000 +#define ARIANE_UART_FREQ 50000000 +#define ARIANE_UART_BAUDRATE 115200 +#define ARIANE_UART_REG_SHIFT 2 +#define ARIANE_UART_REG_WIDTH 4 +#define ARIANE_PLIC_ADDR 0xc000000 +#define ARIANE_PLIC_NUM_SOURCES 3 +#define ARIANE_HART_COUNT 1 +#define ARIANE_CLINT_ADDR 0x2000000 + +static struct plic_data plic = { + .addr = ARIANE_PLIC_ADDR, + .num_src = ARIANE_PLIC_NUM_SOURCES, +}; + +static struct clint_data clint = { + .addr = ARIANE_CLINT_ADDR, + .first_hartid = 0, + .hart_count = ARIANE_HART_COUNT, + .has_64bit_mmio = TRUE, +}; + +/* + * Ariane platform early initialization. + */ +static int ariane_early_init(bool cold_boot) +{ + /* For now nothing to do. */ + return 0; +} + +/* + * Ariane platform final initialization. + */ +static int ariane_final_init(bool cold_boot) +{ + void *fdt; + + if (!cold_boot) + return 0; + + fdt = sbi_scratch_thishart_arg1_ptr(); + fdt_fixups(fdt); + + return 0; +} + +/* + * Initialize the ariane console. + */ +static int ariane_console_init(void) +{ + return uart8250_init(ARIANE_UART_ADDR, + ARIANE_UART_FREQ, + ARIANE_UART_BAUDRATE, + ARIANE_UART_REG_SHIFT, + ARIANE_UART_REG_WIDTH); +} + +static int plic_ariane_warm_irqchip_init(int m_cntx_id, int s_cntx_id) +{ + size_t i, ie_words = ARIANE_PLIC_NUM_SOURCES / 32 + 1; + + /* By default, enable all IRQs for M-mode of target HART */ + if (m_cntx_id > -1) { + for (i = 0; i < ie_words; i++) + plic_set_ie(&plic, m_cntx_id, i, 1); + } + /* Enable all IRQs for S-mode of target HART */ + if (s_cntx_id > -1) { + for (i = 0; i < ie_words; i++) + plic_set_ie(&plic, s_cntx_id, i, 1); + } + /* By default, enable M-mode threshold */ + if (m_cntx_id > -1) + plic_set_thresh(&plic, m_cntx_id, 1); + /* By default, disable S-mode threshold */ + if (s_cntx_id > -1) + plic_set_thresh(&plic, s_cntx_id, 0); + + return 0; +} + +/* + * Initialize the ariane interrupt controller for current HART. + */ +static int ariane_irqchip_init(bool cold_boot) +{ + u32 hartid = current_hartid(); + int ret; + + if (cold_boot) { + ret = plic_cold_irqchip_init(&plic); + if (ret) + return ret; + } + return plic_ariane_warm_irqchip_init(2 * hartid, 2 * hartid + 1); +} + +/* + * Initialize IPI for current HART. + */ +static int ariane_ipi_init(bool cold_boot) +{ + int ret; + + if (cold_boot) { + ret = clint_cold_ipi_init(&clint); + if (ret) + return ret; + } + + return clint_warm_ipi_init(); +} + +/* + * Initialize ariane timer for current HART. + */ +static int ariane_timer_init(bool cold_boot) +{ + int ret; + + if (cold_boot) { + ret = clint_cold_timer_init(&clint, NULL); + if (ret) + return ret; + } + + return clint_warm_timer_init(); +} + +/* + * Platform descriptor. + */ +const struct sbi_platform_operations platform_ops = { + .early_init = ariane_early_init, + .final_init = ariane_final_init, + .console_init = ariane_console_init, + .console_putc = uart8250_putc, + .console_getc = uart8250_getc, + .irqchip_init = ariane_irqchip_init, + .ipi_init = ariane_ipi_init, + .ipi_send = clint_ipi_send, + .ipi_clear = clint_ipi_clear, + .timer_init = ariane_timer_init, + .timer_value = clint_timer_value, + .timer_event_start = clint_timer_event_start, + .timer_event_stop = clint_timer_event_stop, +}; + +const struct sbi_platform platform = { + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01), + .name = "ARIANE RISC-V", + .features = SBI_PLATFORM_DEFAULT_FEATURES, + .hart_count = ARIANE_HART_COUNT, + .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .platform_ops_addr = (unsigned long)&platform_ops +}; diff --git a/roms/opensbi/platform/fpga/openpiton/config.mk b/roms/opensbi/platform/fpga/openpiton/config.mk new file mode 100644 index 000000000..a969b250a --- /dev/null +++ b/roms/opensbi/platform/fpga/openpiton/config.mk @@ -0,0 +1,35 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Western Digital Corporation or its affiliates. +# + +#for more infos, check out /platform/template/config.mk + +PLATFORM_RISCV_XLEN = 64 + +# Blobs to build +FW_TEXT_START=0x80000000 +FW_JUMP=n + +ifeq ($(PLATFORM_RISCV_XLEN), 32) + # This needs to be 4MB aligned for 32-bit support + FW_JUMP_ADDR=0x80400000 + else + # This needs to be 2MB aligned for 64-bit support + FW_JUMP_ADDR=0x80200000 + endif +FW_JUMP_FDT_ADDR=0x82200000 + +# Firmware with payload configuration. +FW_PAYLOAD=y + +ifeq ($(PLATFORM_RISCV_XLEN), 32) +# This needs to be 4MB aligned for 32-bit support + FW_PAYLOAD_OFFSET=0x400000 +else +# This needs to be 2MB aligned for 64-bit support + FW_PAYLOAD_OFFSET=0x200000 +endif +FW_PAYLOAD_FDT_ADDR=0x82200000 +FW_PAYLOAD_ALIGN=0x1000 diff --git a/roms/opensbi/platform/fpga/openpiton/objects.mk b/roms/opensbi/platform/fpga/openpiton/objects.mk new file mode 100644 index 000000000..30a3c4fb8 --- /dev/null +++ b/roms/opensbi/platform/fpga/openpiton/objects.mk @@ -0,0 +1,7 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Western Digital Corporation or its affiliates. +# + +platform-objs-y += platform.o diff --git a/roms/opensbi/platform/fpga/openpiton/platform.c b/roms/opensbi/platform/fpga/openpiton/platform.c new file mode 100644 index 000000000..5eae47799 --- /dev/null +++ b/roms/opensbi/platform/fpga/openpiton/platform.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/riscv_io.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_const.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_platform.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/fdt/fdt_fixup.h> +#include <sbi_utils/irqchip/plic.h> +#include <sbi_utils/serial/uart8250.h> +#include <sbi_utils/sys/clint.h> + +#define OPENPITON_DEFAULT_UART_ADDR 0xfff0c2c000 +#define OPENPITON_DEFAULT_UART_FREQ 60000000 +#define OPENPITON_DEFAULT_UART_BAUDRATE 115200 +#define OPENPITON_DEFAULT_UART_REG_SHIFT 0 +#define OPENPITON_DEFAULT_UART_REG_WIDTH 1 +#define OPENPITON_DEFAULT_PLIC_ADDR 0xfff1100000 +#define OPENPITON_DEFAULT_PLIC_NUM_SOURCES 2 +#define OPENPITON_DEFAULT_HART_COUNT 3 +#define OPENPITON_DEFAULT_CLINT_ADDR 0xfff1020000 + +static struct platform_uart_data uart = { + OPENPITON_DEFAULT_UART_ADDR, + OPENPITON_DEFAULT_UART_FREQ, + OPENPITON_DEFAULT_UART_BAUDRATE, +}; +static struct plic_data plic = { + .addr = OPENPITON_DEFAULT_PLIC_ADDR, + .num_src = OPENPITON_DEFAULT_PLIC_NUM_SOURCES, +}; + +static struct clint_data clint = { + .addr = OPENPITON_DEFAULT_CLINT_ADDR, + .first_hartid = 0, + .hart_count = OPENPITON_DEFAULT_HART_COUNT, + .has_64bit_mmio = TRUE, +}; + +/* + * OpenPiton platform early initialization. + */ +static int openpiton_early_init(bool cold_boot) +{ + void *fdt; + struct platform_uart_data uart_data; + struct plic_data plic_data; + unsigned long clint_addr; + int rc; + + if (!cold_boot) + return 0; + fdt = sbi_scratch_thishart_arg1_ptr(); + + rc = fdt_parse_uart8250(fdt, &uart_data, "ns16550"); + if (!rc) + uart = uart_data; + + rc = fdt_parse_plic(fdt, &plic_data, "riscv,plic0"); + if (!rc) + plic = plic_data; + + rc = fdt_parse_compat_addr(fdt, &clint_addr, "riscv,clint0"); + if (!rc) + clint.addr = clint_addr; + + return 0; +} + +/* + * OpenPiton platform final initialization. + */ +static int openpiton_final_init(bool cold_boot) +{ + void *fdt; + + if (!cold_boot) + return 0; + + fdt = sbi_scratch_thishart_arg1_ptr(); + fdt_fixups(fdt); + + return 0; +} + +/* + * Initialize the openpiton console. + */ +static int openpiton_console_init(void) +{ + return uart8250_init(uart.addr, + uart.freq, + uart.baud, + OPENPITON_DEFAULT_UART_REG_SHIFT, + OPENPITON_DEFAULT_UART_REG_WIDTH); +} + +static int plic_openpiton_warm_irqchip_init(int m_cntx_id, int s_cntx_id) +{ + size_t i, ie_words = plic.num_src / 32 + 1; + + /* By default, enable all IRQs for M-mode of target HART */ + if (m_cntx_id > -1) { + for (i = 0; i < ie_words; i++) + plic_set_ie(&plic, m_cntx_id, i, 1); + } + /* Enable all IRQs for S-mode of target HART */ + if (s_cntx_id > -1) { + for (i = 0; i < ie_words; i++) + plic_set_ie(&plic, s_cntx_id, i, 1); + } + /* By default, enable M-mode threshold */ + if (m_cntx_id > -1) + plic_set_thresh(&plic, m_cntx_id, 1); + /* By default, disable S-mode threshold */ + if (s_cntx_id > -1) + plic_set_thresh(&plic, s_cntx_id, 0); + + return 0; +} + +/* + * Initialize the openpiton interrupt controller for current HART. + */ +static int openpiton_irqchip_init(bool cold_boot) +{ + u32 hartid = current_hartid(); + int ret; + + if (cold_boot) { + ret = plic_cold_irqchip_init(&plic); + if (ret) + return ret; + } + return plic_openpiton_warm_irqchip_init(2 * hartid, 2 * hartid + 1); +} + +/* + * Initialize IPI for current HART. + */ +static int openpiton_ipi_init(bool cold_boot) +{ + int ret; + + if (cold_boot) { + ret = clint_cold_ipi_init(&clint); + if (ret) + return ret; + } + + return clint_warm_ipi_init(); +} + +/* + * Initialize openpiton timer for current HART. + */ +static int openpiton_timer_init(bool cold_boot) +{ + int ret; + + if (cold_boot) { + ret = clint_cold_timer_init(&clint, NULL); + if (ret) + return ret; + } + + return clint_warm_timer_init(); +} + +/* + * Platform descriptor. + */ +const struct sbi_platform_operations platform_ops = { + .early_init = openpiton_early_init, + .final_init = openpiton_final_init, + .console_init = openpiton_console_init, + .console_putc = uart8250_putc, + .console_getc = uart8250_getc, + .irqchip_init = openpiton_irqchip_init, + .ipi_init = openpiton_ipi_init, + .ipi_send = clint_ipi_send, + .ipi_clear = clint_ipi_clear, + .timer_init = openpiton_timer_init, + .timer_value = clint_timer_value, + .timer_event_start = clint_timer_event_start, + .timer_event_stop = clint_timer_event_stop, +}; + +const struct sbi_platform platform = { + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01), + .name = "OPENPITON RISC-V", + .features = SBI_PLATFORM_DEFAULT_FEATURES, + .hart_count = OPENPITON_DEFAULT_HART_COUNT, + .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .platform_ops_addr = (unsigned long)&platform_ops +}; diff --git a/roms/opensbi/platform/generic/config.mk b/roms/opensbi/platform/generic/config.mk new file mode 100644 index 000000000..8151974f2 --- /dev/null +++ b/roms/opensbi/platform/generic/config.mk @@ -0,0 +1,40 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +# Compiler flags +platform-cppflags-y = +platform-cflags-y = +platform-asflags-y = +platform-ldflags-y = + +# Command for platform specific "make run" +platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \ + -nographic -bios $(build_dir)/platform/generic/firmware/fw_payload.elf + +# Blobs to build +FW_TEXT_START=0x80000000 +FW_DYNAMIC=y +FW_JUMP=y +ifeq ($(PLATFORM_RISCV_XLEN), 32) + # This needs to be 4MB aligned for 32-bit system + FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x400000))) +else + # This needs to be 2MB aligned for 64-bit system + FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x200000))) +endif +FW_JUMP_FDT_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x2200000))) +FW_PAYLOAD=y +ifeq ($(PLATFORM_RISCV_XLEN), 32) + # This needs to be 4MB aligned for 32-bit system + FW_PAYLOAD_OFFSET=0x400000 +else + # This needs to be 2MB aligned for 64-bit system + FW_PAYLOAD_OFFSET=0x200000 +endif +FW_PAYLOAD_FDT_ADDR=$(FW_JUMP_FDT_ADDR) diff --git a/roms/opensbi/platform/generic/include/platform_override.h b/roms/opensbi/platform/generic/include/platform_override.h new file mode 100644 index 000000000..77a90d645 --- /dev/null +++ b/roms/opensbi/platform/generic/include/platform_override.h @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#ifndef __PLATFORM_OVERRIDE_H__ +#define __PLATFORM_OVERRIDE_H__ + +#include <sbi/sbi_types.h> + +struct platform_override { + const struct fdt_match *match_table; + u64 (*features)(const struct fdt_match *match); + u64 (*tlbr_flush_limit)(const struct fdt_match *match); + int (*early_init)(bool cold_boot, const struct fdt_match *match); + int (*final_init)(bool cold_boot, const struct fdt_match *match); + void (*early_exit)(const struct fdt_match *match); + void (*final_exit)(const struct fdt_match *match); + int (*system_reset_check)(u32 reset_type, u32 reset_reason, + const struct fdt_match *match); + void (*system_reset)(u32 reset_type, u32 reset_reason, + const struct fdt_match *match); + int (*fdt_fixup)(void *fdt, const struct fdt_match *match); +}; + +#endif diff --git a/roms/opensbi/platform/generic/objects.mk b/roms/opensbi/platform/generic/objects.mk new file mode 100644 index 000000000..d6c8a42d6 --- /dev/null +++ b/roms/opensbi/platform/generic/objects.mk @@ -0,0 +1,11 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Western Digital Corporation or its affiliates. +# +# Authors: +# Anup Patel <anup.patel@wdc.com> +# + +platform-objs-y += platform.o +platform-objs-y += sifive_fu540.o diff --git a/roms/opensbi/platform/generic/platform.c b/roms/opensbi/platform/generic/platform.c new file mode 100644 index 000000000..8c1e06f12 --- /dev/null +++ b/roms/opensbi/platform/generic/platform.c @@ -0,0 +1,241 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <libfdt.h> +#include <platform_override.h> +#include <sbi/riscv_asm.h> +#include <sbi/sbi_hartmask.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_string.h> +#include <sbi_utils/fdt/fdt_domain.h> +#include <sbi_utils/fdt/fdt_fixup.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/irqchip/fdt_irqchip.h> +#include <sbi_utils/serial/fdt_serial.h> +#include <sbi_utils/timer/fdt_timer.h> +#include <sbi_utils/ipi/fdt_ipi.h> +#include <sbi_utils/reset/fdt_reset.h> + +extern const struct platform_override sifive_fu540; + +static const struct platform_override *special_platforms[] = { + &sifive_fu540, +}; + +static const struct platform_override *generic_plat = NULL; +static const struct fdt_match *generic_plat_match = NULL; + +static void fw_platform_lookup_special(void *fdt, int root_offset) +{ + int pos, noff; + const struct platform_override *plat; + const struct fdt_match *match; + + for (pos = 0; pos < array_size(special_platforms); pos++) { + plat = special_platforms[pos]; + if (!plat->match_table) + continue; + + noff = fdt_find_match(fdt, -1, plat->match_table, &match); + if (noff < 0) + continue; + + generic_plat = plat; + generic_plat_match = match; + break; + } +} + +extern struct sbi_platform platform; +static u32 generic_hart_index2id[SBI_HARTMASK_MAX_BITS] = { 0 }; + +/* + * The fw_platform_init() function is called very early on the boot HART + * OpenSBI reference firmwares so that platform specific code get chance + * to update "platform" instance before it is used. + * + * The arguments passed to fw_platform_init() function are boot time state + * of A0 to A4 register. The "arg0" will be boot HART id and "arg1" will + * be address of FDT passed by previous booting stage. + * + * The return value of fw_platform_init() function is the FDT location. If + * FDT is unchanged (or FDT is modified in-place) then fw_platform_init() + * can always return the original FDT location (i.e. 'arg1') unmodified. + */ +unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1, + unsigned long arg2, unsigned long arg3, + unsigned long arg4) +{ + const char *model; + void *fdt = (void *)arg1; + u32 hartid, hart_count = 0; + int rc, root_offset, cpus_offset, cpu_offset, len; + + root_offset = fdt_path_offset(fdt, "/"); + if (root_offset < 0) + goto fail; + + fw_platform_lookup_special(fdt, root_offset); + + model = fdt_getprop(fdt, root_offset, "model", &len); + if (model) + sbi_strncpy(platform.name, model, sizeof(platform.name)); + + if (generic_plat && generic_plat->features) + platform.features = generic_plat->features(generic_plat_match); + + cpus_offset = fdt_path_offset(fdt, "/cpus"); + if (cpus_offset < 0) + goto fail; + + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { + rc = fdt_parse_hart_id(fdt, cpu_offset, &hartid); + if (rc) + continue; + + if (SBI_HARTMASK_MAX_BITS <= hartid) + continue; + + generic_hart_index2id[hart_count++] = hartid; + } + + platform.hart_count = hart_count; + + /* Return original FDT pointer */ + return arg1; + +fail: + while (1) + wfi(); +} + +static int generic_early_init(bool cold_boot) +{ + int rc; + + if (generic_plat && generic_plat->early_init) { + rc = generic_plat->early_init(cold_boot, generic_plat_match); + if (rc) + return rc; + } + + if (!cold_boot) + return 0; + + return fdt_reset_init(); +} + +static int generic_final_init(bool cold_boot) +{ + void *fdt; + int rc; + + if (generic_plat && generic_plat->final_init) { + rc = generic_plat->final_init(cold_boot, generic_plat_match); + if (rc) + return rc; + } + + if (!cold_boot) + return 0; + + fdt = sbi_scratch_thishart_arg1_ptr(); + + fdt_cpu_fixup(fdt); + fdt_fixups(fdt); + fdt_domain_fixup(fdt); + + if (generic_plat && generic_plat->fdt_fixup) { + rc = generic_plat->fdt_fixup(fdt, generic_plat_match); + if (rc) + return rc; + } + + return 0; +} + +static void generic_early_exit(void) +{ + if (generic_plat && generic_plat->early_exit) + generic_plat->early_exit(generic_plat_match); +} + +static void generic_final_exit(void) +{ + if (generic_plat && generic_plat->final_exit) + generic_plat->final_exit(generic_plat_match); +} + +static int generic_domains_init(void) +{ + return fdt_domains_populate(sbi_scratch_thishart_arg1_ptr()); +} + +static u64 generic_tlbr_flush_limit(void) +{ + if (generic_plat && generic_plat->tlbr_flush_limit) + return generic_plat->tlbr_flush_limit(generic_plat_match); + return SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_DEFAULT; +} + +static int generic_system_reset_check(u32 reset_type, u32 reset_reason) +{ + if (generic_plat && generic_plat->system_reset_check) + return generic_plat->system_reset_check(reset_type, + reset_reason, + generic_plat_match); + return fdt_system_reset_check(reset_type, reset_reason); +} + +static void generic_system_reset(u32 reset_type, u32 reset_reason) +{ + if (generic_plat && generic_plat->system_reset) { + generic_plat->system_reset(reset_type, reset_reason, + generic_plat_match); + return; + } + + fdt_system_reset(reset_type, reset_reason); +} + +const struct sbi_platform_operations platform_ops = { + .early_init = generic_early_init, + .final_init = generic_final_init, + .early_exit = generic_early_exit, + .final_exit = generic_final_exit, + .domains_init = generic_domains_init, + .console_putc = fdt_serial_putc, + .console_getc = fdt_serial_getc, + .console_init = fdt_serial_init, + .irqchip_init = fdt_irqchip_init, + .irqchip_exit = fdt_irqchip_exit, + .ipi_send = fdt_ipi_send, + .ipi_clear = fdt_ipi_clear, + .ipi_init = fdt_ipi_init, + .ipi_exit = fdt_ipi_exit, + .get_tlbr_flush_limit = generic_tlbr_flush_limit, + .timer_value = fdt_timer_value, + .timer_event_stop = fdt_timer_event_stop, + .timer_event_start = fdt_timer_event_start, + .timer_init = fdt_timer_init, + .timer_exit = fdt_timer_exit, + .system_reset_check = generic_system_reset_check, + .system_reset = generic_system_reset, +}; + +struct sbi_platform platform = { + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01), + .name = "Generic", + .features = SBI_PLATFORM_DEFAULT_FEATURES, + .hart_count = SBI_HARTMASK_MAX_BITS, + .hart_index2id = generic_hart_index2id, + .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .platform_ops_addr = (unsigned long)&platform_ops +}; diff --git a/roms/opensbi/platform/generic/sifive_fu540.c b/roms/opensbi/platform/generic/sifive_fu540.c new file mode 100644 index 000000000..08f7bfc40 --- /dev/null +++ b/roms/opensbi/platform/generic/sifive_fu540.c @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <platform_override.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/fdt/fdt_fixup.h> + +static u64 sifive_fu540_tlbr_flush_limit(const struct fdt_match *match) +{ + /* + * The sfence.vma by virtual address does not work on + * SiFive FU540 so we return remote TLB flush limit as zero. + */ + return 0; +} + +static int sifive_fu540_fdt_fixup(void *fdt, const struct fdt_match *match) +{ + /* + * SiFive Freedom U540 has an erratum that prevents S-mode software + * to access a PMP protected region using 1GB page table mapping, so + * always add the no-map attribute on this platform. + */ + fdt_reserved_memory_nomap_fixup(fdt); + + return 0; +} + +static const struct fdt_match sifive_fu540_match[] = { + { .compatible = "sifive,fu540" }, + { .compatible = "sifive,fu540g" }, + { .compatible = "sifive,fu540-c000" }, + { .compatible = "sifive,hifive-unleashed-a00" }, + { }, +}; + +const struct platform_override sifive_fu540 = { + .match_table = sifive_fu540_match, + .tlbr_flush_limit = sifive_fu540_tlbr_flush_limit, + .fdt_fixup = sifive_fu540_fdt_fixup, +}; diff --git a/roms/opensbi/platform/kendryte/k210/config.mk b/roms/opensbi/platform/kendryte/k210/config.mk new file mode 100644 index 000000000..8a9b81418 --- /dev/null +++ b/roms/opensbi/platform/kendryte/k210/config.mk @@ -0,0 +1,19 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Damien Le Moal <damien.lemoal@wdc.com> +# + +# Compiler flags +platform-cppflags-y = +platform-cflags-y = +platform-asflags-y = +platform-ldflags-y = + +# Blobs to build +FW_TEXT_START=0x80000000 +FW_PAYLOAD=y +FW_PAYLOAD_ALIGN=0x1000 diff --git a/roms/opensbi/platform/kendryte/k210/k210.dts b/roms/opensbi/platform/kendryte/k210/k210.dts new file mode 100644 index 000000000..bcd075bf5 --- /dev/null +++ b/roms/opensbi/platform/kendryte/k210/k210.dts @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Damien Le Moal <damien.lemoal@wdc.com> + */ + +/dts-v1/; +/ { + #address-cells = <2>; + #size-cells = <2>; + compatible = "kendryte,k210"; + + chosen { + bootargs = "console=hvc0 earlycon=sbi"; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu0: cpu@0 { + device_type = "cpu"; + clock-frequency = <390000000>; + i-cache-size = <32768>; + d-cache-size = <32768>; + mmu-type = "none"; + reg = <0>; + riscv,isa = "rv64imafdc"; + status = "okay"; + cpu0_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + cpu1: cpu@1 { + device_type = "cpu"; + clock-frequency = <390000000>; + d-cache-size = <32768>; + i-cache-size = <32768>; + mmu-type = "none"; + reg = <1>; + riscv,isa = "rv64imafdc"; + status = "okay"; + cpu1_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + }; + + memory@80000000 { + /* Bank 0: 4 MB, Bank 1: 2 MB, AI chip SRAM: 2MB */ + device_type = "memory"; + reg = <0x00000000 0x80000000 0x00000000 0x00800000>; + }; + + plic0: interrupt-controller@C000000 { + #interrupt-cells = <1>; + compatible = "riscv,plic0"; + interrupt-controller; + interrupts-extended = + <&cpu0_intc 11 &cpu0_intc 9 + &cpu1_intc 11 &cpu1_intc 9>; + reg = <0x0 0xc000000 0x0 0x4000000>; + }; +}; diff --git a/roms/opensbi/platform/kendryte/k210/objects.mk b/roms/opensbi/platform/kendryte/k210/objects.mk new file mode 100644 index 000000000..b74da7407 --- /dev/null +++ b/roms/opensbi/platform/kendryte/k210/objects.mk @@ -0,0 +1,14 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Damien Le Moal <damien.lemoal@wdc.com> +# + +platform-objs-y += platform.o + +platform-objs-y += k210.o +platform-varprefix-k210.o = dt_k210 +platform-padding-k210.o = 2048 diff --git a/roms/opensbi/platform/kendryte/k210/platform.c b/roms/opensbi/platform/kendryte/k210/platform.c new file mode 100644 index 000000000..944b38885 --- /dev/null +++ b/roms/opensbi/platform/kendryte/k210/platform.c @@ -0,0 +1,159 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Damien Le Moal <damien.lemoal@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_const.h> +#include <sbi/sbi_platform.h> +#include <sbi_utils/fdt/fdt_fixup.h> +#include <sbi_utils/irqchip/plic.h> +#include <sbi_utils/serial/sifive-uart.h> +#include <sbi_utils/sys/clint.h> +#include "platform.h" + +extern const char dt_k210_start[]; + +unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1, + unsigned long arg2, unsigned long arg3, + unsigned long arg4) +{ + return (unsigned long)&dt_k210_start[0]; +} + +static struct plic_data plic = { + .addr = K210_PLIC_BASE_ADDR, + .num_src = K210_PLIC_NUM_SOURCES, +}; + +static struct clint_data clint = { + .addr = K210_CLINT_BASE_ADDR, + .first_hartid = 0, + .hart_count = K210_HART_COUNT, + .has_64bit_mmio = TRUE, +}; + +static u32 k210_get_clk_freq(void) +{ + u32 clksel0, pll0; + u64 pll0_freq, clkr0, clkf0, clkod0, div; + + /* + * If the clock selector is not set, use the base frequency. + * Otherwise, use PLL0 frequency with a frequency divisor. + */ + clksel0 = k210_read_sysreg(K210_CLKSEL0); + if (!(clksel0 & 0x1)) + return K210_CLK0_FREQ; + + /* + * Get PLL0 frequency: + * freq = base frequency * clkf0 / (clkr0 * clkod0) + */ + pll0 = k210_read_sysreg(K210_PLL0); + clkr0 = 1 + (pll0 & 0x0000000f); + clkf0 = 1 + ((pll0 & 0x000003f0) >> 4); + clkod0 = 1 + ((pll0 & 0x00003c00) >> 10); + pll0_freq = clkf0 * K210_CLK0_FREQ / (clkr0 * clkod0); + + /* Get the frequency divisor from the clock selector */ + div = 2ULL << ((clksel0 & 0x00000006) >> 1); + + return pll0_freq / div; +} + +static int k210_final_init(bool cold_boot) +{ + void *fdt; + + if (!cold_boot) + return 0; + + fdt = sbi_scratch_thishart_arg1_ptr(); + + fdt_cpu_fixup(fdt); + fdt_fixups(fdt); + + return 0; +} + +static int k210_console_init(void) +{ + return sifive_uart_init(K210_UART_BASE_ADDR, k210_get_clk_freq(), + K210_UART_BAUDRATE); +} + +static int k210_irqchip_init(bool cold_boot) +{ + int rc; + u32 hartid = current_hartid(); + + if (cold_boot) { + rc = plic_cold_irqchip_init(&plic); + if (rc) + return rc; + } + + return plic_warm_irqchip_init(&plic, hartid * 2, hartid * 2 + 1); +} + +static int k210_ipi_init(bool cold_boot) +{ + int rc; + + if (cold_boot) { + rc = clint_cold_ipi_init(&clint); + if (rc) + return rc; + } + + return clint_warm_ipi_init(); +} + +static int k210_timer_init(bool cold_boot) +{ + int rc; + + if (cold_boot) { + rc = clint_cold_timer_init(&clint, NULL); + if (rc) + return rc; + } + + return clint_warm_timer_init(); +} + +const struct sbi_platform_operations platform_ops = { + .final_init = k210_final_init, + + .console_init = k210_console_init, + .console_putc = sifive_uart_putc, + .console_getc = sifive_uart_getc, + + .irqchip_init = k210_irqchip_init, + + .ipi_init = k210_ipi_init, + .ipi_send = clint_ipi_send, + .ipi_clear = clint_ipi_clear, + + .timer_init = k210_timer_init, + .timer_value = clint_timer_value, + .timer_event_stop = clint_timer_event_stop, + .timer_event_start = clint_timer_event_start, +}; + +const struct sbi_platform platform = { + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01), + .name = "Kendryte K210", + .features = SBI_PLATFORM_HAS_TIMER_VALUE, + .hart_count = K210_HART_COUNT, + .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .platform_ops_addr = (unsigned long)&platform_ops +}; diff --git a/roms/opensbi/platform/kendryte/k210/platform.h b/roms/opensbi/platform/kendryte/k210/platform.h new file mode 100644 index 000000000..5269bc4fd --- /dev/null +++ b/roms/opensbi/platform/kendryte/k210/platform.h @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Damien Le Moal <damien.lemoal@wdc.com> + */ +#ifndef _K210_PLATFORM_H_ +#define _K210_PLATFORM_H_ + +#include <sbi/riscv_io.h> + +#define K210_HART_COUNT 2 + +#define K210_UART_BAUDRATE 115200 + +#define K210_CLK0_FREQ 26000000UL +#define K210_PLIC_NUM_SOURCES 65 + +/* Registers base address */ +#define K210_SYSCTL_BASE_ADDR 0x50440000ULL +#define K210_UART_BASE_ADDR 0x38000000ULL +#define K210_CLINT_BASE_ADDR 0x02000000ULL +#define K210_PLIC_BASE_ADDR 0x0C000000ULL + +/* Registers */ +#define K210_PLL0 0x08 +#define K210_CLKSEL0 0x20 + +static inline u32 k210_read_sysreg(u32 reg) +{ + return readl((volatile void *)(K210_SYSCTL_BASE_ADDR + reg)); +} + +#endif /* _K210_PLATFORM_H_ */ diff --git a/roms/opensbi/platform/nuclei/ux600/config.mk b/roms/opensbi/platform/nuclei/ux600/config.mk new file mode 100644 index 000000000..dddcc4ea5 --- /dev/null +++ b/roms/opensbi/platform/nuclei/ux600/config.mk @@ -0,0 +1,30 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Nuclei Corporation or its affiliates. +# +# Authors: +# lujun <lujun@nucleisys.com> +# hqfang <578567190@qq.com> +# + +# Compiler flags +platform-cppflags-y = +platform-cflags-y = +platform-asflags-y = +platform-ldflags-y = + +# Command for platform specific "make run" +platform-runcmd = xl_spike \ + $(build_dir)/platform/nuclei/ux600/firmware/fw_payload.elf + +# Blobs to build +FW_TEXT_START=0xA0000000 +FW_DYNAMIC=y +FW_JUMP=y + +FW_JUMP_ADDR=0xA0200000 +FW_JUMP_FDT_ADDR=0xA8000000 +FW_PAYLOAD=y +FW_PAYLOAD_OFFSET=0x200000 +FW_PAYLOAD_FDT_ADDR=0xA8000000 diff --git a/roms/opensbi/platform/nuclei/ux600/objects.mk b/roms/opensbi/platform/nuclei/ux600/objects.mk new file mode 100644 index 000000000..8c36c90a7 --- /dev/null +++ b/roms/opensbi/platform/nuclei/ux600/objects.mk @@ -0,0 +1,11 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Nuclei Corporation or its affiliates. +# +# Authors: +# lujun <lujun@nuclesys.com> +# hqfang <578567190@qq.com> +# + +platform-objs-y += platform.o diff --git a/roms/opensbi/platform/nuclei/ux600/platform.c b/roms/opensbi/platform/nuclei/ux600/platform.c new file mode 100644 index 000000000..d0a45a2ef --- /dev/null +++ b/roms/opensbi/platform/nuclei/ux600/platform.c @@ -0,0 +1,228 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) Nuclei Corporation or its affiliates. + * + * Authors: + * lujun <lujun@nucleisys.com> + * hqfang <578567190@qq.com> + */ + +#include <libfdt.h> +#include <sbi/riscv_asm.h> +#include <sbi/riscv_io.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_const.h> +#include <sbi/sbi_platform.h> +#include <sbi_utils/fdt/fdt_fixup.h> +#include <sbi_utils/irqchip/plic.h> +#include <sbi_utils/serial/sifive-uart.h> +#include <sbi_utils/sys/clint.h> + +/* clang-format off */ + +#define UX600_HART_COUNT 1 +#define UX600_TIMER_FREQ 32768 + +/* Nuclei timer base address */ +#define UX600_NUCLEI_TIMER_ADDR 0x2000000 +#define UX600_NUCLEI_TIMER_MSFTRST_OFS 0xFF0 +#define UX600_NUCLEI_TIMER_MSFTRST_KEY 0x80000A5F +/* The clint compatiable timer offset is 0x1000 against nuclei timer */ +#define UX600_CLINT_TIMER_ADDR (UX600_NUCLEI_TIMER_ADDR + 0x1000) + +#define UX600_PLIC_ADDR 0x8000000 +#define UX600_PLIC_NUM_SOURCES 0x35 +#define UX600_PLIC_NUM_PRIORITIES 7 + +#define UX600_UART0_ADDR 0x10013000 +#define UX600_UART1_ADDR 0x10023000 + +#define UX600_DEBUG_UART UX600_UART0_ADDR + +#ifndef UX600_UART_BAUDRATE +#define UX600_UART_BAUDRATE 57600 +#endif + +#define UX600_GPIO_ADDR 0x10012000 +#define UX600_GPIO_IOF_EN_OFS 0x38 +#define UX600_GPIO_IOF_SEL_OFS 0x3C +#define UX600_GPIO_IOF_UART0_MASK 0x00030000 + +#define UX600_TIMER_VALUE() readl((void *)UX600_NUCLEI_TIMER_ADDR) + +/* clang-format on */ +static u32 ux600_clk_freq = 8000000; + +static struct plic_data plic = { + .addr = UX600_PLIC_ADDR, + .num_src = UX600_PLIC_NUM_SOURCES, +}; + +static struct clint_data clint = { + .addr = UX600_CLINT_TIMER_ADDR, + .first_hartid = 0, + .hart_count = UX600_HART_COUNT, + .has_64bit_mmio = TRUE, +}; +static u32 measure_cpu_freq(u32 n) +{ + u32 start_mtime, delta_mtime; + u32 mtime_freq = UX600_TIMER_FREQ; + u32 tmp = (u32)UX600_TIMER_VALUE(); + u32 start_mcycle, delta_mcycle, freq; + + /* Don't start measuring until we see an mtime tick */ + do { + start_mtime = (u32)UX600_TIMER_VALUE(); + } while (start_mtime == tmp); + + start_mcycle = csr_read(mcycle); + + do { + delta_mtime = (u32)UX600_TIMER_VALUE() - start_mtime; + } while (delta_mtime < n); + + delta_mcycle = csr_read(mcycle) - start_mcycle; + + freq = (delta_mcycle / delta_mtime) * mtime_freq + + ((delta_mcycle % delta_mtime) * mtime_freq) / delta_mtime; + + return freq; +} + +static u32 ux600_get_clk_freq(void) +{ + u32 cpu_freq; + + /* warm up */ + measure_cpu_freq(1); + /* measure for real */ + cpu_freq = measure_cpu_freq(100); + + return cpu_freq; +} + +static int ux600_early_init(bool cold_boot) +{ + u32 regval; + + /* Measure CPU Frequency using Timer */ + ux600_clk_freq = ux600_get_clk_freq(); + + /* Init GPIO UART pinmux */ + regval = readl((void *)(UX600_GPIO_ADDR + UX600_GPIO_IOF_SEL_OFS)) & + ~UX600_GPIO_IOF_UART0_MASK; + writel(regval, (void *)(UX600_GPIO_ADDR + UX600_GPIO_IOF_SEL_OFS)); + regval = readl((void *)(UX600_GPIO_ADDR + UX600_GPIO_IOF_EN_OFS)) | + UX600_GPIO_IOF_UART0_MASK; + writel(regval, (void *)(UX600_GPIO_ADDR + UX600_GPIO_IOF_EN_OFS)); + return 0; +} + +static void ux600_modify_dt(void *fdt) +{ + fdt_fixups(fdt); +} + +static int ux600_final_init(bool cold_boot) +{ + void *fdt; + + if (!cold_boot) + return 0; + + fdt = sbi_scratch_thishart_arg1_ptr(); + ux600_modify_dt(fdt); + + return 0; +} + +static int ux600_console_init(void) +{ + return sifive_uart_init(UX600_DEBUG_UART, ux600_clk_freq, + UX600_UART_BAUDRATE); +} + +static int ux600_irqchip_init(bool cold_boot) +{ + int rc; + u32 hartid = current_hartid(); + + if (cold_boot) { + rc = plic_cold_irqchip_init(&plic); + if (rc) + return rc; + } + + return plic_warm_irqchip_init(&plic, (hartid) ? (2 * hartid - 1) : 0, + (hartid) ? (2 * hartid) : -1); +} + +static int ux600_ipi_init(bool cold_boot) +{ + int rc; + + if (cold_boot) { + rc = clint_cold_ipi_init(&clint); + if (rc) + return rc; + } + + return clint_warm_ipi_init(); +} + +static int ux600_timer_init(bool cold_boot) +{ + int rc; + + if (cold_boot) { + rc = clint_cold_timer_init(&clint, NULL); + if (rc) + return rc; + } + + return clint_warm_timer_init(); +} + +static int ux600_system_reset_check(u32 type, u32 reason) +{ + return 1; +} + +static void ux600_system_reset(u32 type, u32 reason) +{ + /* Reset system using MSFTRST register in Nuclei Timer. */ + writel(UX600_NUCLEI_TIMER_MSFTRST_KEY, (void *)(UX600_NUCLEI_TIMER_ADDR + + UX600_NUCLEI_TIMER_MSFTRST_OFS)); + while(1); +} + +const struct sbi_platform_operations platform_ops = { + .early_init = ux600_early_init, + .final_init = ux600_final_init, + .console_putc = sifive_uart_putc, + .console_getc = sifive_uart_getc, + .console_init = ux600_console_init, + .irqchip_init = ux600_irqchip_init, + .ipi_send = clint_ipi_send, + .ipi_clear = clint_ipi_clear, + .ipi_init = ux600_ipi_init, + .timer_value = clint_timer_value, + .timer_event_stop = clint_timer_event_stop, + .timer_event_start = clint_timer_event_start, + .timer_init = ux600_timer_init, + .system_reset_check = ux600_system_reset_check, + .system_reset = ux600_system_reset +}; + +const struct sbi_platform platform = { + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0U, 0x01U), + .name = "Nuclei UX600", + .features = SBI_PLATFORM_DEFAULT_FEATURES, + .hart_count = UX600_HART_COUNT, + .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .platform_ops_addr = (unsigned long)&platform_ops +}; diff --git a/roms/opensbi/platform/sifive/fu540/config.mk b/roms/opensbi/platform/sifive/fu540/config.mk new file mode 100644 index 000000000..23169b1b9 --- /dev/null +++ b/roms/opensbi/platform/sifive/fu540/config.mk @@ -0,0 +1,40 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Atish Patra <atish.patra@wdc.com> +# + +# Compiler flags +platform-cppflags-y = +platform-cflags-y = +platform-asflags-y = +platform-ldflags-y = + +# Command for platform specific "make run" +platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M sifive_u -m 256M \ + -nographic -bios $(build_dir)/platform/sifive/fu540/firmware/fw_payload.elf + +# Blobs to build +FW_TEXT_START=0x80000000 +FW_DYNAMIC=y +FW_JUMP=y +ifeq ($(PLATFORM_RISCV_XLEN), 32) + # This needs to be 4MB aligned for 32-bit system + FW_JUMP_ADDR=0x80400000 +else + # This needs to be 2MB aligned for 64-bit system + FW_JUMP_ADDR=0x80200000 +endif +FW_JUMP_FDT_ADDR=0x88000000 +FW_PAYLOAD=y +ifeq ($(PLATFORM_RISCV_XLEN), 32) + # This needs to be 4MB aligned for 32-bit system + FW_PAYLOAD_OFFSET=0x400000 +else + # This needs to be 2MB aligned for 64-bit system + FW_PAYLOAD_OFFSET=0x200000 +endif +FW_PAYLOAD_FDT_ADDR=0x88000000 diff --git a/roms/opensbi/platform/sifive/fu540/objects.mk b/roms/opensbi/platform/sifive/fu540/objects.mk new file mode 100644 index 000000000..e0d058538 --- /dev/null +++ b/roms/opensbi/platform/sifive/fu540/objects.mk @@ -0,0 +1,10 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# +# Authors: +# Atish Patra <atish.patra@wdc.com> +# + +platform-objs-y += platform.o diff --git a/roms/opensbi/platform/sifive/fu540/platform.c b/roms/opensbi/platform/sifive/fu540/platform.c new file mode 100644 index 000000000..cdd829363 --- /dev/null +++ b/roms/opensbi/platform/sifive/fu540/platform.c @@ -0,0 +1,182 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra <atish.patra@wdc.com> + */ + +#include <libfdt.h> +#include <sbi/riscv_asm.h> +#include <sbi/riscv_io.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_const.h> +#include <sbi/sbi_platform.h> +#include <sbi_utils/fdt/fdt_fixup.h> +#include <sbi_utils/irqchip/plic.h> +#include <sbi_utils/serial/sifive-uart.h> +#include <sbi_utils/sys/clint.h> + +/* clang-format off */ + +#define FU540_HART_COUNT 5 + +#define FU540_SYS_CLK 1000000000 + +#define FU540_CLINT_ADDR 0x2000000 + +#define FU540_PLIC_ADDR 0xc000000 +#define FU540_PLIC_NUM_SOURCES 0x35 +#define FU540_PLIC_NUM_PRIORITIES 7 + +#define FU540_UART0_ADDR 0x10010000 +#define FU540_UART1_ADDR 0x10011000 +#define FU540_UART_BAUDRATE 115200 + +/* PRCI clock related macros */ +//TODO: Do we need a separate driver for this ? +#define FU540_PRCI_BASE_ADDR 0x10000000 +#define FU540_PRCI_CLKMUXSTATUSREG 0x002C +#define FU540_PRCI_CLKMUX_STATUS_TLCLKSEL (0x1 << 1) + +/* Full tlb flush always */ +#define FU540_TLB_RANGE_FLUSH_LIMIT 0 + +/* clang-format on */ + +static struct plic_data plic = { + .addr = FU540_PLIC_ADDR, + .num_src = FU540_PLIC_NUM_SOURCES, +}; + +static struct clint_data clint = { + .addr = FU540_CLINT_ADDR, + .first_hartid = 0, + .hart_count = FU540_HART_COUNT, + .has_64bit_mmio = TRUE, +}; + +static void fu540_modify_dt(void *fdt) +{ + fdt_cpu_fixup(fdt); + + fdt_fixups(fdt); + + /* + * SiFive Freedom U540 has an erratum that prevents S-mode software + * to access a PMP protected region using 1GB page table mapping, so + * always add the no-map attribute on this platform. + */ + fdt_reserved_memory_nomap_fixup(fdt); +} + +static int fu540_final_init(bool cold_boot) +{ + void *fdt; + + if (!cold_boot) + return 0; + + fdt = sbi_scratch_thishart_arg1_ptr(); + fu540_modify_dt(fdt); + + return 0; +} + +static int fu540_console_init(void) +{ + unsigned long peri_in_freq; + + if (readl((volatile void *)FU540_PRCI_BASE_ADDR + + FU540_PRCI_CLKMUXSTATUSREG) & + FU540_PRCI_CLKMUX_STATUS_TLCLKSEL) { + peri_in_freq = FU540_SYS_CLK; + } else { + peri_in_freq = FU540_SYS_CLK / 2; + } + + return sifive_uart_init(FU540_UART0_ADDR, peri_in_freq, + FU540_UART_BAUDRATE); +} + +static int fu540_irqchip_init(bool cold_boot) +{ + int rc; + u32 hartid = current_hartid(); + + if (cold_boot) { + rc = plic_cold_irqchip_init(&plic); + if (rc) + return rc; + } + + return plic_warm_irqchip_init(&plic, (hartid) ? (2 * hartid - 1) : 0, + (hartid) ? (2 * hartid) : -1); +} + +static int fu540_ipi_init(bool cold_boot) +{ + int rc; + + if (cold_boot) { + rc = clint_cold_ipi_init(&clint); + if (rc) + return rc; + } + + return clint_warm_ipi_init(); +} + +static u64 fu540_get_tlbr_flush_limit(void) +{ + return FU540_TLB_RANGE_FLUSH_LIMIT; +} + +static int fu540_timer_init(bool cold_boot) +{ + int rc; + + if (cold_boot) { + rc = clint_cold_timer_init(&clint, NULL); + if (rc) + return rc; + } + + return clint_warm_timer_init(); +} + +static u32 fu540_hart_index2id[FU540_HART_COUNT - 1] = { + [0] = 1, + [1] = 2, + [2] = 3, + [3] = 4, +}; + +const struct sbi_platform_operations platform_ops = { + .final_init = fu540_final_init, + .console_putc = sifive_uart_putc, + .console_getc = sifive_uart_getc, + .console_init = fu540_console_init, + .irqchip_init = fu540_irqchip_init, + .ipi_send = clint_ipi_send, + .ipi_clear = clint_ipi_clear, + .ipi_init = fu540_ipi_init, + .get_tlbr_flush_limit = fu540_get_tlbr_flush_limit, + .timer_value = clint_timer_value, + .timer_event_stop = clint_timer_event_stop, + .timer_event_start = clint_timer_event_start, + .timer_init = fu540_timer_init, +}; + +const struct sbi_platform platform = { + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01), + .name = "SiFive Freedom U540", + .features = SBI_PLATFORM_DEFAULT_FEATURES, + .hart_count = (FU540_HART_COUNT - 1), + .hart_index2id = fu540_hart_index2id, + .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .platform_ops_addr = (unsigned long)&platform_ops +}; diff --git a/roms/opensbi/platform/template/config.mk b/roms/opensbi/platform/template/config.mk new file mode 100644 index 000000000..f817342c2 --- /dev/null +++ b/roms/opensbi/platform/template/config.mk @@ -0,0 +1,77 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# + +# Compiler pre-processor flags +platform-cppflags-y = + +# C Compiler and assembler flags. +platform-cflags-y = +platform-asflags-y = + +# Linker flags: additional libraries and object files that the platform +# code needs can be added here +platform-ldflags-y = + +# +# Command for platform specific "make run" +# Useful for development and debugging on plaftform simulator (such as QEMU) +# +# platform-runcmd = your_platform_run.sh + +# +# Platform RISC-V XLEN, ABI, ISA and Code Model configuration. +# These are optional parameters but platforms can optionaly provide it. +# Some of these are guessed based on GCC compiler capabilities +# +# PLATFORM_RISCV_XLEN = 64 +# PLATFORM_RISCV_ABI = lp64 +# PLATFORM_RISCV_ISA = rv64imafdc +# PLATFORM_RISCV_CODE_MODEL = medany + +# Firmware load address configuration. This is mandatory. +FW_TEXT_START=0x80000000 + +# Optional parameter for path to external FDT +# FW_FDT_PATH="path to platform flattened device tree file" + +# +# Dynamic firmware configuration. +# Optional parameters are commented out. Uncomment and define these parameters +# as needed. +# +FW_DYNAMIC=<y|n> + +# +# Jump firmware configuration. +# Optional parameters are commented out. Uncomment and define these parameters +# as needed. +# +FW_JUMP=<y|n> +# This needs to be 4MB aligned for 32-bit support +# This needs to be 2MB aligned for 64-bit support +# ifeq ($(PLATFORM_RISCV_XLEN), 32) +# FW_JUMP_ADDR=0x80400000 +# else +# FW_JUMP_ADDR=0x80200000 +# endif +# FW_JUMP_FDT_ADDR=0x82200000 + +# +# Firmware with payload configuration. +# Optional parameters are commented out. Uncomment and define these parameters +# as needed. +# +FW_PAYLOAD=<y|n> +# This needs to be 4MB aligned for 32-bit support +# This needs to be 2MB aligned for 64-bit support +ifeq ($(PLATFORM_RISCV_XLEN), 32) +FW_PAYLOAD_OFFSET=0x400000 +else +FW_PAYLOAD_OFFSET=0x200000 +endif +# FW_PAYLOAD_ALIGN=0x1000 +# FW_PAYLOAD_PATH="path to next boot stage binary image file" +# FW_PAYLOAD_FDT_ADDR=0x82200000 diff --git a/roms/opensbi/platform/template/objects.mk b/roms/opensbi/platform/template/objects.mk new file mode 100644 index 000000000..caaec2bee --- /dev/null +++ b/roms/opensbi/platform/template/objects.mk @@ -0,0 +1,15 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# + +# Space separated list of object file names to be compiled for the platform +platform-objs-y += platform.o + +# +# If the platform support requires a builtin device tree file, the name of +# the device tree compiled file should be specified here. The device tree +# source file be in the form <dt file name>.dts +# +# platform-objs-y += <dt file name>.o diff --git a/roms/opensbi/platform/template/platform.c b/roms/opensbi/platform/template/platform.c new file mode 100644 index 000000000..5bdb186c1 --- /dev/null +++ b/roms/opensbi/platform/template/platform.c @@ -0,0 +1,223 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_const.h> +#include <sbi/sbi_platform.h> + +/* + * Include these files as needed. + * See config.mk PLATFORM_xxx configuration parameters. + */ +#include <sbi_utils/irqchip/plic.h> +#include <sbi_utils/serial/uart8250.h> +#include <sbi_utils/sys/clint.h> + +#define PLATFORM_PLIC_ADDR 0xc000000 +#define PLATFORM_PLIC_NUM_SOURCES 128 +#define PLATFORM_HART_COUNT 4 +#define PLATFORM_CLINT_ADDR 0x2000000 +#define PLATFORM_UART_ADDR 0x09000000 +#define PLATFORM_UART_INPUT_FREQ 10000000 +#define PLATFORM_UART_BAUDRATE 115200 + +static struct plic_data plic = { + .addr = PLATFORM_PLIC_ADDR, + .num_src = PLATFORM_PLIC_NUM_SOURCES, +}; + +static struct clint_data clint = { + .addr = PLATFORM_CLINT_ADDR, + .first_hartid = 0, + .hart_count = PLATFORM_HART_COUNT, + .has_64bit_mmio = TRUE, +}; + +/* + * Platform early initialization. + */ +static int platform_early_init(bool cold_boot) +{ + return 0; +} + +/* + * Platform final initialization. + */ +static int platform_final_init(bool cold_boot) +{ + return 0; +} + +/* + * Initialize the platform console. + */ +static int platform_console_init(void) +{ + /* Example if the generic UART8250 driver is used */ + return uart8250_init(PLATFORM_UART_ADDR, PLATFORM_UART_INPUT_FREQ, + PLATFORM_UART_BAUDRATE, 0, 1); +} + +/* + * Write a character to the platform console output. + */ +static void platform_console_putc(char ch) +{ + /* Example if the generic UART8250 driver is used */ + uart8250_putc(ch); +} + +/* + * Read a character from the platform console input. + */ +static int platform_console_getc(void) +{ + return uart8250_getc(); +} + +/* + * Initialize the platform interrupt controller for current HART. + */ +static int platform_irqchip_init(bool cold_boot) +{ + u32 hartid = current_hartid(); + int ret; + + /* Example if the generic PLIC driver is used */ + if (cold_boot) { + ret = plic_cold_irqchip_init(&plic); + if (ret) + return ret; + } + + return plic_warm_irqchip_init(&plic, 2 * hartid, 2 * hartid + 1); +} + +/* + * Initialize IPI for current HART. + */ +static int platform_ipi_init(bool cold_boot) +{ + int ret; + + /* Example if the generic CLINT driver is used */ + if (cold_boot) { + ret = clint_cold_ipi_init(&clint); + if (ret) + return ret; + } + + return clint_warm_ipi_init(); +} + +/* + * Send IPI to a target HART + */ +static void platform_ipi_send(u32 target_hart) +{ + /* Example if the generic CLINT driver is used */ + clint_ipi_send(target_hart); +} + +/* + * Clear IPI for a target HART. + */ +static void platform_ipi_clear(u32 target_hart) +{ + /* Example if the generic CLINT driver is used */ + clint_ipi_clear(target_hart); +} + +/* + * Initialize platform timer for current HART. + */ +static int platform_timer_init(bool cold_boot) +{ + int ret; + + /* Example if the generic CLINT driver is used */ + if (cold_boot) { + ret = clint_cold_timer_init(&clint, NULL); + if (ret) + return ret; + } + + return clint_warm_timer_init(); +} + +/* + * Get platform timer value. + */ +static u64 platform_timer_value(void) +{ + /* Example if the generic CLINT driver is used */ + return clint_timer_value(); +} + +/* + * Start platform timer event for current HART. + */ +static void platform_timer_event_start(u64 next_event) +{ + /* Example if the generic CLINT driver is used */ + clint_timer_event_start(next_event); +} + +/* + * Stop platform timer event for current HART. + */ +static void platform_timer_event_stop(void) +{ + /* Example if the generic CLINT driver is used */ + clint_timer_event_stop(); +} + +/* + * Check reset type and reason supported by the platform. + */ +static int platform_system_reset_check(u32 type, u32 reason) +{ + return 0; +} + +/* + * Reset the platform. + */ +static void platform_system_reset(u32 type, u32 reason) +{ +} + +/* + * Platform descriptor. + */ +const struct sbi_platform_operations platform_ops = { + .early_init = platform_early_init, + .final_init = platform_final_init, + .console_putc = platform_console_putc, + .console_getc = platform_console_getc, + .console_init = platform_console_init, + .irqchip_init = platform_irqchip_init, + .ipi_send = platform_ipi_send, + .ipi_clear = platform_ipi_clear, + .ipi_init = platform_ipi_init, + .timer_value = platform_timer_value, + .timer_event_stop = platform_timer_event_stop, + .timer_event_start = platform_timer_event_start, + .timer_init = platform_timer_init, + .system_reset_check = platform_system_reset_check, + .system_reset = platform_system_reset +}; +const struct sbi_platform platform = { + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0, 0x00), + .name = "platform-name", + .features = SBI_PLATFORM_DEFAULT_FEATURES, + .hart_count = 1, + .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .platform_ops_addr = (unsigned long)&platform_ops +}; diff --git a/roms/opensbi/platform/thead/c910/config.mk b/roms/opensbi/platform/thead/c910/config.mk new file mode 100644 index 000000000..bd5eab716 --- /dev/null +++ b/roms/opensbi/platform/thead/c910/config.mk @@ -0,0 +1,14 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# + +# Compiler flags +platform-cppflags-y = +platform-cflags-y = +platform-asflags-y = +platform-ldflags-y = + +# Blobs to build +FW_TEXT_START?=0x0 +FW_JUMP=y +FW_JUMP_ADDR?=0x00200000 diff --git a/roms/opensbi/platform/thead/c910/objects.mk b/roms/opensbi/platform/thead/c910/objects.mk new file mode 100644 index 000000000..d025a3642 --- /dev/null +++ b/roms/opensbi/platform/thead/c910/objects.mk @@ -0,0 +1,5 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# + +platform-objs-y += platform.o diff --git a/roms/opensbi/platform/thead/c910/platform.c b/roms/opensbi/platform/thead/c910/platform.c new file mode 100644 index 000000000..dfa484ad2 --- /dev/null +++ b/roms/opensbi/platform/thead/c910/platform.c @@ -0,0 +1,156 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sbi/riscv_encoding.h> +#include <sbi/riscv_io.h> +#include <sbi/sbi_console.h> +#include <sbi/sbi_const.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_platform.h> +#include <sbi_utils/irqchip/plic.h> +#include <sbi_utils/serial/uart8250.h> +#include <sbi_utils/sys/clint.h> +#include "platform.h" + +static struct c910_regs_struct c910_regs; + +static struct clint_data clint = { + .addr = 0, /* Updated at cold boot time */ + .first_hartid = 0, + .hart_count = C910_HART_COUNT, + .has_64bit_mmio = FALSE, +}; + +static int c910_early_init(bool cold_boot) +{ + if (cold_boot) { + /* Load from boot core */ + c910_regs.pmpaddr0 = csr_read(CSR_PMPADDR0); + c910_regs.pmpaddr1 = csr_read(CSR_PMPADDR1); + c910_regs.pmpaddr2 = csr_read(CSR_PMPADDR2); + c910_regs.pmpaddr3 = csr_read(CSR_PMPADDR3); + c910_regs.pmpaddr4 = csr_read(CSR_PMPADDR4); + c910_regs.pmpaddr5 = csr_read(CSR_PMPADDR5); + c910_regs.pmpaddr6 = csr_read(CSR_PMPADDR6); + c910_regs.pmpaddr7 = csr_read(CSR_PMPADDR7); + c910_regs.pmpcfg0 = csr_read(CSR_PMPCFG0); + + c910_regs.mcor = csr_read(CSR_MCOR); + c910_regs.mhcr = csr_read(CSR_MHCR); + c910_regs.mccr2 = csr_read(CSR_MCCR2); + c910_regs.mhint = csr_read(CSR_MHINT); + c910_regs.mxstatus = csr_read(CSR_MXSTATUS); + + c910_regs.plic_base_addr = csr_read(CSR_PLIC_BASE); + c910_regs.clint_base_addr = + c910_regs.plic_base_addr + C910_PLIC_CLINT_OFFSET; + } else { + /* Store to other core */ + csr_write(CSR_PMPADDR0, c910_regs.pmpaddr0); + csr_write(CSR_PMPADDR1, c910_regs.pmpaddr1); + csr_write(CSR_PMPADDR2, c910_regs.pmpaddr2); + csr_write(CSR_PMPADDR3, c910_regs.pmpaddr3); + csr_write(CSR_PMPADDR4, c910_regs.pmpaddr4); + csr_write(CSR_PMPADDR5, c910_regs.pmpaddr5); + csr_write(CSR_PMPADDR6, c910_regs.pmpaddr6); + csr_write(CSR_PMPADDR7, c910_regs.pmpaddr7); + csr_write(CSR_PMPCFG0, c910_regs.pmpcfg0); + + csr_write(CSR_MCOR, c910_regs.mcor); + csr_write(CSR_MHCR, c910_regs.mhcr); + csr_write(CSR_MHINT, c910_regs.mhint); + csr_write(CSR_MXSTATUS, c910_regs.mxstatus); + } + + return 0; +} + +static int c910_final_init(bool cold_boot) +{ + return 0; +} + +static int c910_irqchip_init(bool cold_boot) +{ + /* Delegate plic enable into S-mode */ + writel(C910_PLIC_DELEG_ENABLE, + (void *)c910_regs.plic_base_addr + C910_PLIC_DELEG_OFFSET); + + return 0; +} + +static int c910_ipi_init(bool cold_boot) +{ + int rc; + + if (cold_boot) { + clint.addr = c910_regs.clint_base_addr; + rc = clint_cold_ipi_init(&clint); + if (rc) + return rc; + } + + return clint_warm_ipi_init(); +} + +static int c910_timer_init(bool cold_boot) +{ + int ret; + + if (cold_boot) { + clint.addr = c910_regs.clint_base_addr; + ret = clint_cold_timer_init(&clint, NULL); + if (ret) + return ret; + } + + return clint_warm_timer_init(); +} + +static int c910_system_reset_check(u32 type, u32 reason) +{ + return 1; +} + +static void c910_system_reset(u32 type, u32 reason) +{ + asm volatile ("ebreak"); +} + +int c910_hart_start(u32 hartid, ulong saddr) +{ + csr_write(CSR_MRVBR, saddr); + csr_write(CSR_MRMR, csr_read(CSR_MRMR) | (1 << hartid)); + + return 0; +} + +const struct sbi_platform_operations platform_ops = { + .early_init = c910_early_init, + .final_init = c910_final_init, + + .irqchip_init = c910_irqchip_init, + + .ipi_init = c910_ipi_init, + .ipi_send = clint_ipi_send, + .ipi_clear = clint_ipi_clear, + + .timer_init = c910_timer_init, + .timer_event_start = clint_timer_event_start, + + .system_reset_check = c910_system_reset_check, + .system_reset = c910_system_reset, + + .hart_start = c910_hart_start, +}; + +const struct sbi_platform platform = { + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01), + .name = "T-HEAD Xuantie c910", + .features = SBI_THEAD_FEATURES, + .hart_count = C910_HART_COUNT, + .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .platform_ops_addr = (unsigned long)&platform_ops +}; diff --git a/roms/opensbi/platform/thead/c910/platform.h b/roms/opensbi/platform/thead/c910/platform.h new file mode 100644 index 000000000..354404ef7 --- /dev/null +++ b/roms/opensbi/platform/thead/c910/platform.h @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _C910_PLATFORM_H_ +#define _C910_PLATFORM_H_ + +#define C910_HART_COUNT 16 + +#define SBI_THEAD_FEATURES \ + (SBI_PLATFORM_HAS_MFAULTS_DELEGATION | \ + SBI_PLATFORM_HAS_HART_SECONDARY_BOOT) + +#define CSR_MCOR 0x7c2 +#define CSR_MHCR 0x7c1 +#define CSR_MCCR2 0x7c3 +#define CSR_MHINT 0x7c5 +#define CSR_MXSTATUS 0x7c0 +#define CSR_PLIC_BASE 0xfc1 +#define CSR_MRMR 0x7c6 +#define CSR_MRVBR 0x7c7 + +#define C910_PLIC_CLINT_OFFSET 0x04000000 /* 64M */ +#define C910_PLIC_DELEG_OFFSET 0x001ffffc +#define C910_PLIC_DELEG_ENABLE 0x1 + +struct c910_regs_struct { + u64 pmpaddr0; + u64 pmpaddr1; + u64 pmpaddr2; + u64 pmpaddr3; + u64 pmpaddr4; + u64 pmpaddr5; + u64 pmpaddr6; + u64 pmpaddr7; + u64 pmpcfg0; + u64 mcor; + u64 mhcr; + u64 mccr2; + u64 mhint; + u64 mxstatus; + u64 plic_base_addr; + u64 clint_base_addr; +}; + +#endif /* _C910_PLATFORM_H_ */ diff --git a/roms/opensbi/scripts/create-binary-archive.sh b/roms/opensbi/scripts/create-binary-archive.sh new file mode 100755 index 000000000..c19af2698 --- /dev/null +++ b/roms/opensbi/scripts/create-binary-archive.sh @@ -0,0 +1,168 @@ +#!/bin/bash + +function usage() +{ + echo "Usage:" + echo " $0 [options]" + echo "Options:" + echo " -h Display help or usage" + echo " -p <opensbi_source_path> OpenSBI source path" + echo " -o <output_path> Build output path" + echo " -d Build and install documentation" + echo " -t Build only with no archive created" + echo " -j <num_threads> Number of threads for Make (Default: 1)" + echo " -s <archive_suffix> Archive name suffix (Default: unknown)" + echo " -x <riscv_xlen> RISC-V XLENs for Build (Default: 0)" + echo " 0: RV32 and RV64" + echo " 32: RV32 only" + echo " 64: RV64 only" + exit 1; +} + +# Command line options +NUM_THREADS=1 +OUTPUT_PATH="$(pwd)/build" +OPENSBI_SOURCE_PATH="$(pwd)" +NEED_DOCS="no" +COMPILE_ONLY="no" +ARCHIVE_SUFFIX="unknown" +RISCV_XLEN=0 + +while getopts "hdtj:o:p:s:x:" o; do + case "${o}" in + h) + usage + ;; + d) + NEED_DOCS="yes" + ;; + t) + COMPILE_ONLY="yes" + ;; + j) + NUM_THREADS=${OPTARG} + ;; + o) + OUTPUT_PATH=${OPTARG} + ;; + p) + OPENSBI_SOURCE_PATH=${OPTARG} + ;; + s) + ARCHIVE_SUFFIX=${OPTARG} + ;; + x) + RISCV_XLEN=${OPTARG} + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${OPENSBI_SOURCE_PATH}" ]; then + echo "Must specify OpenSBI source path" + usage +fi + +if [ ! -d "${OPENSBI_SOURCE_PATH}" ]; then + echo "OpenSBI source path does not exist" + usage +fi + +if [ -z "${ARCHIVE_SUFFIX}" ]; then + echo "Archive suffice cannot be empty" + usage +fi + +build_opensbi() { + # Setup parameters + BUILD_NUM_THREADS=$1 + BUILD_OUTPUT_PATH=$2 + BUILD_OPENSBI_SOURCE_PATH=$3 + BUILD_DOCS=$4 + BUILD_ONLY=$5 + BUILD_RISCV_XLEN=$6 + BUILD_ARCHIVE_RISCV_XLEN=$7 + BUILD_ARCHIVE_SUFFIX=$8 + + # Setup derived parameters + BUILD_VERSION_MAJOR=$(grep "define OPENSBI_VERSION_MAJOR" "${BUILD_OPENSBI_SOURCE_PATH}/include/sbi/sbi_version.h" | sed 's/.*MAJOR.*\([0-9][0-9]*\)/\1/') + BUILD_VERSION_MINOR=$(grep "define OPENSBI_VERSION_MINOR" "${BUILD_OPENSBI_SOURCE_PATH}/include/sbi/sbi_version.h" | sed 's/.*MINOR.*\([0-9][0-9]*\)/\1/') + BUILD_NAME="opensbi-${BUILD_VERSION_MAJOR}.${BUILD_VERSION_MINOR}-rv${BUILD_RISCV_XLEN}" + BUILD_ARCHIVE_NAME="opensbi-${BUILD_VERSION_MAJOR}.${BUILD_VERSION_MINOR}-rv${BUILD_ARCHIVE_RISCV_XLEN}-${BUILD_ARCHIVE_SUFFIX}" + case "${BUILD_RISCV_XLEN}" in + 32) + # Setup 32-bit platform list + BUILD_PLATFORM_SUBDIR=("sifive/fu540") + BUILD_PLATFORM_SUBDIR+=("generic") + ;; + 64) + # Setup 64-bit platform list + BUILD_PLATFORM_SUBDIR=("sifive/fu540") + BUILD_PLATFORM_SUBDIR+=("nuclei/ux600") + BUILD_PLATFORM_SUBDIR+=("kendryte/k210") + BUILD_PLATFORM_SUBDIR+=("fpga/ariane") + BUILD_PLATFORM_SUBDIR+=("fpga/openpiton") + BUILD_PLATFORM_SUBDIR+=("andes/ae350") + BUILD_PLATFORM_SUBDIR+=("thead/c910") + BUILD_PLATFORM_SUBDIR+=("generic") + ;; + *) + echo "Invalid Build RISC-V XLEN" + usage + ;; + esac + + # Ensure output directory is present + mkdir -p "${BUILD_OUTPUT_PATH}/${BUILD_NAME}" + + # Build and install generic library + echo "Build and install generic library XLEN=${BUILD_RISCV_XLEN}" + echo "" + make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" PLATFORM_RISCV_XLEN="${BUILD_RISCV_XLEN}" install_libsbi install_libsbiutils -j "${BUILD_NUM_THREADS}" + echo "" + + # Build and install relevant platforms + for INDEX in $(seq 0 1 "$(expr ${#BUILD_PLATFORM_SUBDIR[*]} - 1)") + do + echo "Build and install PLATFORM=${BUILD_PLATFORM_SUBDIR[${INDEX}]} XLEN=${BUILD_RISCV_XLEN}" + echo "" + make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" PLATFORM="${BUILD_PLATFORM_SUBDIR[${INDEX}]}" PLATFORM_RISCV_XLEN="${BUILD_RISCV_XLEN}" install_libplatsbi install_firmwares -j "${BUILD_NUM_THREADS}" + echo "" + done + + # Build and install docs + if [ "${BUILD_DOCS}" == "yes" ]; then + echo "Build and install docs" + echo "" + make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" install_docs + echo "" + fi + + # Create archive file + if [ "${BUILD_ONLY}" == "no" ]; then + echo "Create archive ${BUILD_ARCHIVE_NAME}.tar.xz" + echo "" + tar -C "${BUILD_OUTPUT_PATH}" -cJvf "${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}.tar.xz" "${BUILD_ARCHIVE_NAME}" + echo "" + fi +} + +case "${RISCV_XLEN}" in +0) + build_opensbi ${NUM_THREADS} ${OUTPUT_PATH} ${OPENSBI_SOURCE_PATH} "no" "yes" "32" "" ${ARCHIVE_SUFFIX} + build_opensbi ${NUM_THREADS} ${OUTPUT_PATH} ${OPENSBI_SOURCE_PATH} ${NEED_DOCS} ${COMPILE_ONLY} "64" "" ${ARCHIVE_SUFFIX} + ;; +32) + build_opensbi ${NUM_THREADS} ${OUTPUT_PATH} ${OPENSBI_SOURCE_PATH} ${NEED_DOCS} ${COMPILE_ONLY} "32" "32" ${ARCHIVE_SUFFIX} + ;; +64) + build_opensbi ${NUM_THREADS} ${OUTPUT_PATH} ${OPENSBI_SOURCE_PATH} ${NEED_DOCS} ${COMPILE_ONLY} "64" "64" ${ARCHIVE_SUFFIX} + ;; +*) + echo "Invalid RISC-V XLEN" + usage + ;; +esac diff --git a/roms/opensbi/scripts/d2c.sh b/roms/opensbi/scripts/d2c.sh new file mode 100755 index 000000000..33a3ccf76 --- /dev/null +++ b/roms/opensbi/scripts/d2c.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +function usage() +{ + echo "Usage:" + echo " $0 [options]" + echo "Options:" + echo " -h Display help or usage" + echo " -i <input_file_path> Input binary file path" + echo " -a <c_align> Output C array alignment" + echo " -p <c_prefix> Output C array name prefix" + echo " -t <num_zero_bytes> Output padding zero bytes" + exit 1; +} + +# Command line options +INPUT_PATH="" +OUTPUT_C_ALIGN="" +OUTPUT_C_PREFIX="" +NUM_ZERO_BYTES=0 + +while getopts "hi:a:p:t:" o; do + case "${o}" in + h) + usage + ;; + i) + INPUT_PATH=${OPTARG} + ;; + a) + OUTPUT_C_ALIGN=${OPTARG} + ;; + p) + OUTPUT_C_PREFIX=${OPTARG} + ;; + t) + NUM_ZERO_BYTES=${OPTARG} + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${INPUT_PATH}" ]; then + echo "Must specify input file path" + usage +fi + +if [ ! -f "${INPUT_PATH}" ]; then + echo "The input path should be a file" + usage +fi + +if [ -z "${OUTPUT_C_ALIGN}" ]; then + echo "Must provide output C array alignment" + usage +fi + +if [ -z "${OUTPUT_C_PREFIX}" ]; then + echo "Must provide output C array name prefix" + usage +fi + +printf "const char __attribute__((aligned(%s))) %s_start[] = {\n" "${OUTPUT_C_ALIGN}" "${OUTPUT_C_PREFIX}" + +od -v -t x1 -An ${INPUT_PATH} | awk '{for (i=1; i<=NF; i++) printf " 0x%s,", $i; printf "\n"; }' + +echo __dummy__ | awk "{for (i=1; i<=${NUM_ZERO_BYTES}; i++) { printf \" 0x00,\"; if (i % 16 == 0) printf \"\n\"; } }" + +printf "};\n" + +printf "const unsigned long %s_size = sizeof(%s_start);\n" "${OUTPUT_C_PREFIX}" "${OUTPUT_C_PREFIX}" |