mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2025-08-24 23:41:23 +01:00
Compare commits
143 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
be92da280d | ||
![]() |
30f09fbfd1 | ||
![]() |
0790be0f2c | ||
![]() |
848ed4f644 | ||
![]() |
26aec6afed | ||
![]() |
3d335bc54b | ||
![]() |
8925e3865c | ||
![]() |
e561c63036 | ||
![]() |
2c7bab76a2 | ||
![]() |
1e9f88889f | ||
![]() |
7d4420bd69 | ||
![]() |
a14e7ee82c | ||
![]() |
bbeb8e619d | ||
![]() |
1a5614e971 | ||
![]() |
0089897d41 | ||
![]() |
1ed9eb255d | ||
![]() |
0a482e2edb | ||
![]() |
190e3f4bd9 | ||
![]() |
8853758268 | ||
![]() |
98ee15ca3a | ||
![]() |
f2e82c3d79 | ||
![]() |
144acef684 | ||
![]() |
9dfe720579 | ||
![]() |
fca8c3be01 | ||
![]() |
6ed2bc154f | ||
![]() |
7b7690ed9c | ||
![]() |
6bd1512024 | ||
![]() |
2e5cc9051b | ||
![]() |
ffa6c5f457 | ||
![]() |
3cbb419def | ||
![]() |
a2a7763ac7 | ||
![]() |
75229705a0 | ||
![]() |
897a97a6af | ||
![]() |
f6e13e0dd3 | ||
![]() |
a88e424f6c | ||
![]() |
2e0f3ac758 | ||
![]() |
6c24193293 | ||
![]() |
5e4021a2f5 | ||
![]() |
6ddf71e6e9 | ||
![]() |
3f738f5897 | ||
![]() |
88c87f0af4 | ||
![]() |
dbff3e9f12 | ||
![]() |
b1318e578b | ||
![]() |
446b6f30a4 | ||
![]() |
c1b9dd3ab5 | ||
![]() |
0f18b3fe0a | ||
![]() |
85546a5477 | ||
![]() |
c90009aa20 | ||
![]() |
bf2ee7bcdc | ||
![]() |
e3f743339a | ||
![]() |
e7456399e4 | ||
![]() |
ce228ee091 | ||
![]() |
42c938e3f0 | ||
![]() |
bb3edd36af | ||
![]() |
da398ef8d6 | ||
![]() |
d44bc16e4b | ||
![]() |
9c18c2c610 | ||
![]() |
08b196956d | ||
![]() |
fa6fd6bf86 | ||
![]() |
3048f979ca | ||
![]() |
b44e844880 | ||
![]() |
c6d06a9448 | ||
![]() |
65aa5873c3 | ||
![]() |
cd2dfdc870 | ||
![]() |
5dd93e88fe | ||
![]() |
b2d0caf86b | ||
![]() |
200ed7c1bd | ||
![]() |
793e5e1184 | ||
![]() |
98aaf8317b | ||
![]() |
749b0b0932 | ||
![]() |
a5b37bd7d2 | ||
![]() |
331f291e4c | ||
![]() |
6fec1c7e11 | ||
![]() |
f19611e1e4 | ||
![]() |
b6ea152df2 | ||
![]() |
2b80945ac7 | ||
![]() |
a6395acd6c | ||
![]() |
bb915780ac | ||
![]() |
a22c6891b7 | ||
![]() |
95b7480ab4 | ||
![]() |
2dfed32c46 | ||
![]() |
4e2cd47820 | ||
![]() |
392749f633 | ||
![]() |
a4c2f50b64 | ||
![]() |
bae54f7645 | ||
![]() |
25472de89e | ||
![]() |
243a5e0532 | ||
![]() |
0d33a981ec | ||
![]() |
3bb2d25f44 | ||
![]() |
f9643f3472 | ||
![]() |
c1d01b0c2e | ||
![]() |
03ec350c83 | ||
![]() |
10baa64c02 | ||
![]() |
fbf986ac2a | ||
![]() |
97fb8c0e3b | ||
![]() |
1e43ce75ff | ||
![]() |
804b997ed4 | ||
![]() |
194dbbe5a1 | ||
![]() |
b2cd5fda61 | ||
![]() |
743e8ae6e7 | ||
![]() |
999823c597 | ||
![]() |
110eef44f0 | ||
![]() |
54f31e8209 | ||
![]() |
40086daa62 | ||
![]() |
09f976802b | ||
![]() |
3fbe233a15 | ||
![]() |
9dc95021db | ||
![]() |
6dc1b0f6e2 | ||
![]() |
f700216cb5 | ||
![]() |
1eba298b0d | ||
![]() |
fd5418d92c | ||
![]() |
8334a88c63 | ||
![]() |
14dadeab09 | ||
![]() |
bce71a00bd | ||
![]() |
78c87cd13a | ||
![]() |
13877c3a67 | ||
![]() |
cfff0126ab | ||
![]() |
f9cfe301c9 | ||
![]() |
215c200ccb | ||
![]() |
132f3e024b | ||
![]() |
e921fc2691 | ||
![]() |
6c39ea99ee | ||
![]() |
312850148a | ||
![]() |
ebe351e550 | ||
![]() |
ab91c143a3 | ||
![]() |
1e24e21d56 | ||
![]() |
896870e9b0 | ||
![]() |
6cd668df12 | ||
![]() |
90cb4917b5 | ||
![]() |
508a27204c | ||
![]() |
007a6b26d9 | ||
![]() |
b225583881 | ||
![]() |
69d794cbcb | ||
![]() |
9eb8f0f90d | ||
![]() |
27fae182dc | ||
![]() |
d4dd2b37f3 | ||
![]() |
754ff34108 | ||
![]() |
86cc9b8633 | ||
![]() |
05602e2bf4 | ||
![]() |
1c87f0f9b1 | ||
![]() |
a72467f574 | ||
![]() |
87fbcf9376 | ||
![]() |
178a0307a2 |
16
.clang-format
Normal file
16
.clang-format
Normal file
@@ -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
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,3 +6,6 @@
|
||||
#Build & install directories
|
||||
build/
|
||||
install/
|
||||
|
||||
# Development friendly files
|
||||
tags
|
||||
|
27
CONTRIBUTORS.md
Normal file
27
CONTRIBUTORS.md
Normal file
@@ -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>
|
@@ -1,7 +1,8 @@
|
||||
The 2-Clause BSD License
|
||||
SPDX short identifier: BSD-2-Clause
|
||||
|
||||
Copyright (c) 2019 Western Digital Corporation or its affiliates.
|
||||
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:
|
||||
@@ -22,4 +23,3 @@ 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.
|
||||
|
||||
|
118
Makefile
118
Makefile
@@ -33,6 +33,17 @@ endif
|
||||
ifeq ($(install_dir),$(build_dir))
|
||||
$(error Install 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 := @
|
||||
@@ -43,16 +54,18 @@ else
|
||||
endif
|
||||
|
||||
# Setup path of directories
|
||||
export platform_subdir=platform/$(PLATFORM)
|
||||
export platform_dir=$(CURDIR)/$(platform_subdir)
|
||||
export platform_common_dir=$(CURDIR)/platform/common
|
||||
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 lib_dir=$(CURDIR)/lib
|
||||
export libsbi_dir=$(CURDIR)/lib/sbi
|
||||
export libsbiutils_dir=$(CURDIR)/lib/utils
|
||||
export firmware_dir=$(CURDIR)/firmware
|
||||
|
||||
# Find library version
|
||||
OPENSBI_VERSION_MAJOR=`grep MAJOR $(include_dir)/sbi/sbi_version.h | sed 's/.*MAJOR.*\([0-9][0-9]*\)/\1/'`
|
||||
OPENSBI_VERSION_MINOR=`grep MINOR $(include_dir)/sbi/sbi_version.h | sed 's/.*MINOR.*\([0-9][0-9]*\)/\1/'`
|
||||
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
|
||||
@@ -85,40 +98,40 @@ endif
|
||||
|
||||
# Setup list of objects.mk files
|
||||
ifdef PLATFORM
|
||||
platform-object-mks=$(shell if [ -d $(platform_dir) ]; then find $(platform_dir) -iname "objects.mk" | sort -r; fi)
|
||||
platform-common-object-mks=$(shell if [ -d $(platform_common_dir) ]; then find $(platform_common_dir) -iname "objects.mk" | sort -r; fi)
|
||||
platform-object-mks=$(shell if [ -d $(platform_src_dir)/ ]; then find $(platform_src_dir) -iname "objects.mk" | sort -r; fi)
|
||||
endif
|
||||
lib-object-mks=$(shell if [ -d $(lib_dir) ]; then find $(lib_dir) -iname "objects.mk" | sort -r; fi)
|
||||
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_dir)/config.mk
|
||||
include $(platform_src_dir)/config.mk
|
||||
endif
|
||||
|
||||
# Include all object.mk files
|
||||
ifdef PLATFORM
|
||||
include $(platform-object-mks)
|
||||
include $(platform-common-object-mks)
|
||||
endif
|
||||
include $(lib-object-mks)
|
||||
include $(libsbi-object-mks)
|
||||
include $(libsbiutils-object-mks)
|
||||
include $(firmware-object-mks)
|
||||
|
||||
# Setup list of objects
|
||||
lib-objs-path-y=$(foreach obj,$(lib-objs-y),$(build_dir)/lib/$(obj))
|
||||
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),$(build_dir)/$(platform_subdir)/$(obj))
|
||||
platform-dtb-path-y=$(foreach obj,$(platform-dtb-y),$(build_dir)/$(platform_subdir)/$(obj))
|
||||
platform-common-objs-path-y=$(foreach obj,$(platform-common-objs-y),$(build_dir)/platform/common/$(obj))
|
||||
firmware-bins-path-y=$(foreach bin,$(firmware-bins-y),$(build_dir)/$(platform_subdir)/firmware/$(bin))
|
||||
platform-objs-path-y=$(foreach obj,$(platform-objs-y),$(platform_build_dir)/$(obj))
|
||||
platform-dtb-path-y=$(foreach obj,$(platform-dtb-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+=$(platform-common-objs-path-y:.o=.dep)
|
||||
deps-y+=$(lib-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
|
||||
@@ -137,10 +150,12 @@ ifndef PLATFORM_RISCV_CODE_MODEL
|
||||
endif
|
||||
|
||||
# Setup compilation commands flags
|
||||
GENFLAGS = -I$(platform_dir)/include
|
||||
GENFLAGS += -I$(platform_common_dir)/include
|
||||
GENFLAGS = -I$(platform_src_dir)/include
|
||||
GENFLAGS += -I$(include_dir)
|
||||
GENFLAGS += $(platform-common-genflags-y)
|
||||
ifneq ($(OPENSBI_VERSION_GIT),)
|
||||
GENFLAGS += -DOPENSBI_VERSION_GIT="\"$(OPENSBI_VERSION_GIT)\""
|
||||
endif
|
||||
GENFLAGS += $(libsbiutils-genflags-y)
|
||||
GENFLAGS += $(platform-genflags-y)
|
||||
GENFLAGS += $(firmware-genflags-y)
|
||||
|
||||
@@ -152,6 +167,7 @@ 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)
|
||||
@@ -213,7 +229,7 @@ compile_cpp = $(CMD_PREFIX)mkdir -p `dirname $(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))"; \
|
||||
echo `dirname $(1)`/ \\ > $(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)`; \
|
||||
@@ -221,7 +237,7 @@ compile_cc = $(CMD_PREFIX)mkdir -p `dirname $(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))"; \
|
||||
echo `dirname $(1)`/ \\ > $(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)`; \
|
||||
@@ -241,8 +257,9 @@ compile_dts = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
|
||||
$(DTC) $(DTCFLAGS) -o $(1) $(2)
|
||||
|
||||
targets-y = $(build_dir)/lib/libsbi.a
|
||||
targets-y += $(build_dir)/lib/libsbiutils.a
|
||||
ifdef PLATFORM
|
||||
targets-y += $(build_dir)/$(platform_subdir)/lib/libplatsbi.a
|
||||
targets-y += $(platform_build_dir)/lib/libplatsbi.a
|
||||
targets-y += $(platform-dtb-path-y)
|
||||
endif
|
||||
targets-y += $(firmware-bins-path-y)
|
||||
@@ -257,16 +274,19 @@ all: $(targets-y)
|
||||
$(build_dir)/%.bin: $(build_dir)/%.elf
|
||||
$(call compile_objcopy,$@,$<)
|
||||
|
||||
$(build_dir)/%.elf: $(build_dir)/%.o $(build_dir)/%.elf.ld $(build_dir)/$(platform_subdir)/lib/libplatsbi.a
|
||||
$(call compile_elf,$@,$@.ld,$< $(build_dir)/$(platform_subdir)/lib/libplatsbi.a)
|
||||
$(build_dir)/%.elf: $(build_dir)/%.o $(build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a
|
||||
$(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a)
|
||||
|
||||
$(build_dir)/$(platform_subdir)/%.ld: $(src_dir)/%.ldS
|
||||
$(platform_build_dir)/%.ld: $(src_dir)/%.ldS
|
||||
$(call compile_cpp,$@,$<)
|
||||
|
||||
$(build_dir)/lib/libsbi.a: $(lib-objs-path-y)
|
||||
$(build_dir)/lib/libsbi.a: $(libsbi-objs-path-y)
|
||||
$(call compile_ar,$@,$^)
|
||||
|
||||
$(build_dir)/$(platform_subdir)/lib/libplatsbi.a: $(lib-objs-path-y) $(platform-common-objs-path-y) $(platform-objs-path-y)
|
||||
$(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
|
||||
@@ -281,16 +301,28 @@ $(build_dir)/%.dep: $(src_dir)/%.S
|
||||
$(build_dir)/%.o: $(src_dir)/%.S
|
||||
$(call compile_as,$@,$<)
|
||||
|
||||
$(build_dir)/$(platform_subdir)/%.dep: $(src_dir)/%.c
|
||||
$(platform_build_dir)/%.dep: $(platform_src_dir)/%.c
|
||||
$(call compile_cc_dep,$@,$<)
|
||||
|
||||
$(build_dir)/$(platform_subdir)/%.o: $(src_dir)/%.c
|
||||
$(platform_build_dir)/%.o: $(platform_src_dir)/%.c
|
||||
$(call compile_cc,$@,$<)
|
||||
|
||||
$(build_dir)/$(platform_subdir)/%.dep: $(src_dir)/%.S
|
||||
$(platform_build_dir)/%.dep: $(platform_src_dir)/%.S
|
||||
$(call compile_as_dep,$@,$<)
|
||||
|
||||
$(build_dir)/$(platform_subdir)/%.o: $(src_dir)/%.S
|
||||
$(platform_build_dir)/%.o: $(platform_src_dir)/%.S
|
||||
$(call compile_as,$@,$<)
|
||||
|
||||
$(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,$@,$<)
|
||||
|
||||
$(build_dir)/%.dtb: $(src_dir)/%.dts
|
||||
@@ -325,7 +357,7 @@ ifneq ($(platform-runcmd),)
|
||||
$(platform-runcmd) $(RUN_ARGS)
|
||||
else
|
||||
ifdef PLATFORM
|
||||
@echo Platform $(PLATFORM) doesn't specify a run command
|
||||
@echo "Platform $(PLATFORM) doesn't specify a run command"
|
||||
@false
|
||||
else
|
||||
@echo Run command only available when targeting a platform
|
||||
@@ -334,6 +366,7 @@ endif
|
||||
endif
|
||||
|
||||
install_targets-y = install_libsbi
|
||||
install_targets-y += install_libsbiutils
|
||||
ifdef PLATFORM
|
||||
install_targets-y += install_libplatsbi
|
||||
install_targets-y += install_firmwares
|
||||
@@ -348,14 +381,19 @@ install_libsbi: $(build_dir)/lib/libsbi.a
|
||||
$(call inst_header_dir,$(install_dir)/include,$(include_dir)/sbi)
|
||||
$(call inst_file,$(install_dir)/lib/libsbi.a,$(build_dir)/lib/libsbi.a)
|
||||
|
||||
.PHONY: install_libsbiutils
|
||||
install_libsbiutils: $(build_dir)/lib/libsbiutils.a
|
||||
$(call inst_header_dir,$(install_dir)/include,$(include_dir)/sbi_utils)
|
||||
$(call inst_file,$(install_dir)/lib/libsbiutils.a,$(build_dir)/lib/libsbiutils.a)
|
||||
|
||||
.PHONY: install_libplatsbi
|
||||
install_libplatsbi: $(build_dir)/$(platform_subdir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a
|
||||
$(call inst_file,$(install_dir)/$(platform_subdir)/lib/libplatsbi.a,$(build_dir)/$(platform_subdir)/lib/libplatsbi.a)
|
||||
install_libplatsbi: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a
|
||||
$(call inst_file,$(install_dir)/platform/$(platform_subdir)/lib/libplatsbi.a,$(platform_build_dir)/lib/libplatsbi.a)
|
||||
|
||||
.PHONY: install_firmwares
|
||||
install_firmwares: $(build_dir)/$(platform_subdir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(firmware-bins-path-y)
|
||||
$(call inst_file_list,$(install_dir),$(build_dir),$(platform_subdir)/firmware,$(firmware-elfs-path-y))
|
||||
$(call inst_file_list,$(install_dir),$(build_dir),$(platform_subdir)/firmware,$(firmware-bins-path-y))
|
||||
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_dir),$(build_dir),platform/$(platform_subdir)/firmware,$(firmware-elfs-path-y))
|
||||
$(call inst_file_list,$(install_dir),$(build_dir),platform/$(platform_subdir)/firmware,$(firmware-bins-path-y))
|
||||
|
||||
.PHONY: install_docs
|
||||
install_docs: $(build_dir)/docs/latex/refman.pdf
|
||||
|
135
README.md
135
README.md
@@ -1,56 +1,61 @@
|
||||
Copyright (c) 2019 Western Digital Corporation or its affiliates
|
||||
and other contributors.
|
||||
|
||||
RISC-V Open Source Supervisor Binary Interface (OpenSBI)
|
||||
========================================================
|
||||
|
||||
The **RISC-V Supervisor Binary Interface (SBI)** is the recommended interface
|
||||
between:
|
||||
|
||||
1. A platform specific firmware running in M-mode and bootloader, 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
|
||||
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 in [Github].
|
||||
RISC-V Foundation on [Github] (https://github.com/riscv/riscv-sbi-doc).
|
||||
|
||||
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). OpenSBI implementation can be
|
||||
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
|
||||
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 interrupts
|
||||
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 *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 calls processing by integrating *libsbi.a* with necessary platform dependent
|
||||
hardware manipulation functions. For all supported platforms, OpenSBI also
|
||||
provides several runtime firmware examples built using the platform
|
||||
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
|
||||
*riskv-pk* bootloader (aka BBL) and enable the use of well known bootloaders
|
||||
such as [U-Boot].
|
||||
*riscv-pk* bootloader (aka BBL) and enable the use of well-known bootloaders
|
||||
such as [U-Boot] (https://git.denx.de/u-boot.git).
|
||||
|
||||
Required Toolchain
|
||||
------------------
|
||||
|
||||
OpenSBI can be compiled natively or cross-compiled on a x86 host. For
|
||||
cross-compilation, you can build your tool chain or just download from
|
||||
the [bootlin] (https://toolchains.bootlin.com/).
|
||||
cross-compilation, you can build your own toolchain or just download
|
||||
a prebuilt one from the
|
||||
[Bootlin toolchain repository] (https://toolchains.bootlin.com/).
|
||||
|
||||
Please note that only 64bit version of toolchain is available in bootlin
|
||||
for now.
|
||||
Please note that only a 64-bit version of the toolchain is available in
|
||||
the Bootlin toolchain repository for now.
|
||||
|
||||
Building and Installing OpenSBI Platform Independent Library
|
||||
------------------------------------------------------------
|
||||
Building and Installing the OpenSBI Platform-Independent Library
|
||||
----------------------------------------------------------------
|
||||
|
||||
OpenSBI platform independent static library *libsbi.a* can be natively compiled
|
||||
or cross-compiled on a host with a different base architecture than RISC-V.
|
||||
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.
|
||||
@@ -61,8 +66,8 @@ To build *libsbi.a* simply execute:
|
||||
make
|
||||
```
|
||||
|
||||
All compiled binaries as well as the result *libsbi.a* static library file will
|
||||
be placed in the *build/lib* directory. To specify an alternate build root
|
||||
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>
|
||||
@@ -74,38 +79,38 @@ make install
|
||||
```
|
||||
|
||||
This will create the *install* directory with all necessary include files
|
||||
copied under the *install/include* directory and library file copied in the
|
||||
*install/lib* directory. To specify an alternate installation root directory
|
||||
path, run:
|
||||
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 Firmwares
|
||||
-------------------------------------------------------------------------
|
||||
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
|
||||
line, the platform-specific static library *libplatsbi.a* and firmware examples
|
||||
are built for the platform *<platform_subdir>* present in the directory
|
||||
*platform* in OpenSBI top directory. For example, to compile the platform
|
||||
library and firmware examples for QEMU RISC-V *virt* machine,
|
||||
*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 *qemu/virt*.
|
||||
|
||||
To build *libsbi.a*, *libplatsbi.a* and the firmwares for one of the supported
|
||||
platform, run:
|
||||
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.
|
||||
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
|
||||
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 format: an ELF file
|
||||
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:
|
||||
@@ -113,19 +118,38 @@ 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
|
||||
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.
|
||||
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
|
||||
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
|
||||
*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.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
@@ -148,18 +172,23 @@ 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.
|
||||
|
||||
1. 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.
|
||||
* 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.
|
||||
|
||||
Contributing to OpenSBI
|
||||
-----------------------
|
||||
|
||||
The OpenSBI project encourages and welcomes contributions. Contributions should
|
||||
follow the rules described in OpenSBI [Contribution Guideline] document.
|
||||
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
|
||||
-------------
|
||||
|
||||
@@ -174,7 +203,7 @@ Detailed documentation of various aspects of OpenSBI can be found under the
|
||||
examples build supported by OpenSBI.
|
||||
|
||||
OpenSBI source code is also well documented. For source level documentation,
|
||||
doxygen style is used. Please refer to [Doxygen manual] for details on this
|
||||
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
|
||||
@@ -218,9 +247,11 @@ make I=<install_directory> install_docs
|
||||
[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 Support Guide]: docs/platform_guide.md
|
||||
[Platform Documentation]: docs/platform/platform.md
|
||||
[Firmware Documentation]: docs/firmware/fw.md
|
||||
[Doxygen manual]: http://www.stack.nl/~dimitri/doxygen/manual.html
|
||||
|
||||
[Doxygen manual]: http://www.doxygen.nl/manual/index.html
|
||||
[Kendryte standalone SDK]: https://github.com/kendryte/kendryte-standalone-sdk
|
||||
[third party notices]: ThirdPartyNotices.md
|
||||
|
19
ThirdPartyNotices.md
Normal file
19
ThirdPartyNotices.md
Normal file
@@ -0,0 +1,19 @@
|
||||
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).
|
||||
|
@@ -1,11 +1,20 @@
|
||||
OpenSBI Contribution Guideline
|
||||
==============================
|
||||
|
||||
All contributions to OpenSBI should be sent as GitHub Pull Requests (PRs) to
|
||||
[OpenSBI main repository].
|
||||
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 [Linux coding style].
|
||||
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
|
||||
@@ -15,13 +24,14 @@ Every submitted patch must have this tag.
|
||||
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:" and "top:".
|
||||
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 riscv/opensbi 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 riscv/opensbi main
|
||||
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
|
||||
@@ -69,6 +79,6 @@ By making a contribution to this project, I certify that:
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
[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/
|
||||
|
||||
|
@@ -44,7 +44,7 @@ PROJECT_NUMBER = "v@@OPENSBI_MAJOR@@.@@OPENSBI_MINOR@@"
|
||||
# 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 supervisor binary interface"
|
||||
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
|
||||
|
24
docs/external/coreboot.md
vendored
Normal file
24
docs/external/coreboot.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
OpenSBI as coreboot payload
|
||||
==============================
|
||||
|
||||
[coreboot](https://www.coreboot.org/) 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](https://github.com/hardenedlinux/embedded-iot_profile/blob/master/docs/riscv/hifiveunleashed_coreboot_notes-en.md) about how to build [out-of-tree code](https://github.com/hardenedlinux/coreboot-HiFiveUnleashed) to load OpenSBI.
|
@@ -9,12 +9,22 @@ 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 two different types of firmwares.
|
||||
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 only handles the address of the next booting stage
|
||||
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.
|
||||
|
||||
@@ -51,11 +61,14 @@ 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
|
||||
|
||||
@@ -75,3 +88,18 @@ 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.
|
||||
|
35
docs/firmware/fw_dynamic.md
Normal file
35
docs/firmware/fw_dynamic.md
Normal file
@@ -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*.
|
@@ -6,19 +6,19 @@ 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 OpenSBI firmware is capable of loading both the OpenSBI firmware and
|
||||
the booting stage binary to follow OpenSBI firmware.
|
||||
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.
|
||||
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
|
||||
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
|
||||
@@ -27,26 +27,26 @@ build directory under the *build/platform/<platform_subdir>/firmware* directory.
|
||||
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.
|
||||
follows:
|
||||
|
||||
* **FW_JUMP_ADDR** - Address of the entry point of the booting stage to be
|
||||
executed following OpenSBI firmware. This address generally correspond
|
||||
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 OpenSBI firmware. If this option is not provided,
|
||||
then OpenSBI firmware will pass zero as the FDT address to the following
|
||||
booting stage.
|
||||
the booting stage following the OpenSBI firmware. If this option is not
|
||||
provided, then the OpenSBI firmware will pass zero as the FDT address to the
|
||||
following booting stage.
|
||||
|
||||
*FW_JUMP* Example
|
||||
-----------------
|
||||
|
||||
The *[qemu/virt]* and *[qemu/sifive_u]* platforms illustrates how to configure
|
||||
The *[qemu/virt]* and *[qemu/sifive_u]* platforms illustrate how to configure
|
||||
and use a *FW_JUMP* firmware. Detailed information regarding these platforms
|
||||
can be found in the platforms documentation files.
|
||||
can be found in the platform documentation files.
|
||||
|
||||
[qemu/virt]: ../platform/qemu_virt.md
|
||||
[qemu/sifive_u]: ../platform/qemu_sifive_u.md
|
||||
|
@@ -2,22 +2,22 @@ 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 OpenSBI firmware execution.
|
||||
Typically, this payload will be a bootloader or an OS kernel.
|
||||
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 OpenSBI firmware is not capable of loading both OpenSBI firmware and
|
||||
the booting stage to follow OpenSBI firmware.
|
||||
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 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.
|
||||
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.
|
||||
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
|
||||
@@ -25,7 +25,7 @@ The *FW_PAYLOAD* firmware can be enabled by any of the following methods.
|
||||
|
||||
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
|
||||
platform-specific build directory under the
|
||||
*build/platform/<platform_subdir>/firmware* directory.
|
||||
|
||||
Configuration Options
|
||||
@@ -34,7 +34,7 @@ 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.
|
||||
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
|
||||
@@ -48,7 +48,7 @@ file. The parameters currently defined are as follows.
|
||||
*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* ignored.
|
||||
*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
|
||||
@@ -78,13 +78,12 @@ file. The parameters currently defined are as follows.
|
||||
*FW_PAYLOAD* Example
|
||||
--------------------
|
||||
|
||||
The *[qemu/virt]* and *[qemu/sifive_u]* platforms illustrates how to configure
|
||||
The *[qemu/virt]* and *[qemu/sifive_u]* platforms illustrate how to configure
|
||||
and use a *FW_PAYLOAD* firmware. Detailed information regarding these platforms
|
||||
can be found in the platforms documentation files.
|
||||
can be found in the platform documentation files.
|
||||
|
||||
The *kendryte/k210* platform also enables build of a *FW_PAYLOAD* using an
|
||||
The *kendryte/k210* platform also enables a build of a *FW_PAYLOAD* using an
|
||||
internally defined device tree file (*FW_PAYLOAD_FDT*).
|
||||
|
||||
[qemu/virt]: ../platform/qemu_virt.md
|
||||
[qemu/sifive_u]: ../platform/qemu_sifive_u.md
|
||||
|
||||
|
@@ -1,11 +1,10 @@
|
||||
Linux as a direct payload to OpenSBI
|
||||
====================================
|
||||
|
||||
OpenSBI has the capability to load Linux kernel image directly in supervisor
|
||||
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 payload to OpenSBI.
|
||||
provided as a payload to OpenSBI.
|
||||
|
||||
Detailed examples and platform guides can be found in both [QEMU](
|
||||
../platform/qemu_virt.md) and [HiFive Unleashed](../platform/sifive_fu540.md)
|
||||
platform guide respectively.
|
||||
Detailed examples can be found in both the [QEMU](../platform/qemu_virt.md)
|
||||
and the [HiFive Unleashed](../platform/sifive_fu540.md) platform guides.
|
||||
|
||||
|
@@ -3,31 +3,33 @@ 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
|
||||
OpenSBI firmware, becoming the boot stage following OpenSBI firmware
|
||||
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.
|
||||
|
||||
The current stable upstream code of U-boot does not yet include all patches
|
||||
The current stable upstream code of U-Boot does not yet include all patches
|
||||
necessary to fully support OpenSBI. To use U-Boot as an OpenSBI payload, the
|
||||
following out-of-tree patch series must be applied to the upstream U-Boot source
|
||||
code.
|
||||
code:
|
||||
|
||||
HiFive Unleashed support for U-Boot
|
||||
|
||||
https://lists.denx.de/pipermail/u-boot/2019-February/358058.html
|
||||
|
||||
This patch series enables a single CPU to execute U-Boot. As a result, the next
|
||||
stage boot code such as Linux kernel can also only execute a single CPU. U-Boot
|
||||
SMP support for RISC-V can be enabled with the following additional patches.
|
||||
stage boot code such as a Linux kernel can also only execute on a single CPU.
|
||||
U-Boot SMP support for RISC-V can be enabled with the following additional
|
||||
patches:
|
||||
|
||||
https://lists.denx.de/pipermail/u-boot/2019-February/358393.html
|
||||
|
||||
Building and Generating U-Boot images
|
||||
=====================================
|
||||
Please refer to U-Boot build documentation for detailed instructions on how to build U-Boot images.
|
||||
Please refer to the U-Boot build documentation for detailed instructions on
|
||||
how to build U-Boot images.
|
||||
|
||||
Once U-Boot images are built, Linux kernel image need to be converted to a format
|
||||
that U-Boot understands.
|
||||
Once U-Boot images are built, the Linux kernel image needs to be converted
|
||||
into a format that U-Boot understands:
|
||||
|
||||
```
|
||||
<uboot-dir>/tools/mkimage -A riscv -O linux -T kernel -C none -a 0x80200000 -e 0x80200000 -n Linux -d \
|
||||
|
@@ -3,46 +3,50 @@ 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
|
||||
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. *libplatsbi.a* - An example platform specific static library integrating
|
||||
*libsbi.a* with platform specific hooks. This library is available only for
|
||||
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 needs to be provided.
|
||||
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 OpenSBI generic static library together
|
||||
with an M-mode firmware or bootloader providing hardware specific hooks. Since
|
||||
OpenSBI is a statically linked library, users must ensure that the license of
|
||||
these external components is compatible with OpenSBI license.
|
||||
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*) are compiled with the
|
||||
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. *struct sbi_scratch* instance)
|
||||
2. The RISC-V *SP* register (i.e. stack pointer) must be set per-HART
|
||||
pointing to distinct non-overlapping stacks
|
||||
(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 *MSTATUS* and *MIE* CSRs when calling
|
||||
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
|
||||
@@ -62,4 +66,3 @@ bootloader to service the following interrupts and traps:
|
||||
|
||||
**Note:** external firmwares or bootloaders can be more conservative by
|
||||
forwarding all traps and interrupts to *sbi_trap_handler()*.
|
||||
|
||||
|
28
docs/platform/andes-ae350.md
Normal file
28
docs/platform/andes-ae350.md
Normal file
@@ -0,0 +1,28 @@
|
||||
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_PAYLOAD_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_PAYLOAD_FDT_PATH=<ae350.dtb path>
|
||||
```
|
37
docs/platform/ariane-fpga.md
Normal file
37
docs/platform/ariane-fpga.md
Normal file
@@ -0,0 +1,37 @@
|
||||
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 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=ariane-fpga* 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=ariane-fpga 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.
|
@@ -1,11 +1,12 @@
|
||||
OpenSBI Supported Platforms
|
||||
===========================
|
||||
|
||||
OpenSBI currently supports the following virtual and hardware platforms.
|
||||
OpenSBI currently supports the following virtual and hardware platforms:
|
||||
|
||||
* **QEMU RISC-V Virt Machine**: Platform support for QEMU *virt* virtual RISC-V
|
||||
machine. This virtual machine is intended for RISC-V software development and
|
||||
test. More details on this platform can be found in the file *[qemu_virt.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]*.
|
||||
|
||||
* **QEMU SiFive Unleashed Machine**: Platform support for the *sifive_u* QEMU
|
||||
virtual RISC-V machine. This is an emulation machine of the HiFive Unleashed
|
||||
@@ -18,13 +19,21 @@ OpenSBI currently supports the following virtual and hardware platforms.
|
||||
*[sifive_fu540.md]*.
|
||||
|
||||
* **Kendryte K210 SoC**: Platform support for the Kendryte K210 SoC used on
|
||||
boards such as the Kendryte KD233 and Sipeed MAIX Dock boards.
|
||||
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.
|
||||
|
||||
* **Andes AE350 SoC**: Platform support for the Andes's SoC (AE350).
|
||||
|
||||
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.
|
||||
*config.mk* and *platform.c* template files provides enough comments to
|
||||
facilitate the implementation.
|
||||
|
||||
[qemu_virt.md]: qemu_virt.md
|
||||
[qemu_sifive_u.md]: qemu_sifive_u.md
|
||||
[sifive_fu540.md]: sifive_fu540.md
|
||||
[ariane-fpga.md]: ariane-fpga.md
|
||||
[andes_ae350.md]: andes-ae350.md
|
||||
|
@@ -7,20 +7,28 @@ platform.
|
||||
To build this platform specific library and firmwares, provide the
|
||||
*PLATFORM=qemu/sifive_u* parameter to the top level `make` command line.
|
||||
|
||||
Note with QEMU v4.2 release, the QEMU *sifive_u* machine has been updated to
|
||||
closely match the SiFive HiFive Unleashed hardware and can therefore run the
|
||||
same firmware as what gets loaded onto the board, and OpenSBI's *qemu/sifive_u*
|
||||
platform should only be used with QEMU v4.1 release or before.
|
||||
|
||||
The special *qemu/sifive_u* platform support will be dropped in the future
|
||||
OpenSBI release.
|
||||
|
||||
Platform Options
|
||||
----------------
|
||||
|
||||
The *QEMU SiFive Unleashed Machine* platform does not have any platform specific
|
||||
options.
|
||||
|
||||
Executing on QEMU RISC-V 64bit
|
||||
------------------------------
|
||||
Executing on QEMU RISC-V 64-bit
|
||||
-------------------------------
|
||||
|
||||
**No Payload Case**
|
||||
|
||||
Build:
|
||||
```
|
||||
make PLATFORM=qemu/virt
|
||||
make PLATFORM=qemu/sifive_u
|
||||
```
|
||||
|
||||
Run:
|
||||
@@ -32,11 +40,11 @@ qemu-system-riscv64 -M sifive_u -m 256M -display none -serial stdio \
|
||||
**U-Boot as a Payload**
|
||||
|
||||
Note: the command line examples here assume that U-Boot was compiled using
|
||||
the `qemu-riscv64_smode_defconfig` configuration.
|
||||
the `sifive_fu540_defconfig` configuration.
|
||||
|
||||
Build:
|
||||
```
|
||||
make PLATFORM=qemu/virt FW_PAYLOAD_PATH=<uboot_build_directory>/u-boot.bin
|
||||
make PLATFORM=qemu/sifive_u FW_PAYLOAD_PATH=<uboot_build_directory>/u-boot.bin
|
||||
```
|
||||
|
||||
Run:
|
||||
|
@@ -1,22 +1,22 @@
|
||||
QEMU RISC-V Virt Machine Platform
|
||||
=================================
|
||||
|
||||
The **QEMU RISC-V Virt Machine** is virtual platform created for RISC-V
|
||||
software development and testing. It is also referred as
|
||||
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 platform specific library and firmwares, provide the
|
||||
To build the platform-specific library and firmware images, provide the
|
||||
*PLATFORM=qemu/virt* parameter to the top level `make` command.
|
||||
|
||||
Platform Options
|
||||
----------------
|
||||
|
||||
The *QEMU RISC-V Virt Machine* platform does not have any platform specific
|
||||
The *QEMU RISC-V Virt Machine* platform does not have any platform-specific
|
||||
options.
|
||||
|
||||
Execution on QEMU RISC-V 64bit
|
||||
------------------------------
|
||||
Execution on QEMU RISC-V 64-bit
|
||||
-------------------------------
|
||||
|
||||
**No Payload Case**
|
||||
|
||||
@@ -27,7 +27,7 @@ make PLATFORM=qemu/virt
|
||||
|
||||
Run:
|
||||
```
|
||||
qemu-system-riscv64 -M virt -m 256M -display none -serial stdio \
|
||||
qemu-system-riscv64 -M virt -m 256M -nographic \
|
||||
-kernel build/platform/qemu/virt/firmware/fw_payload.elf
|
||||
```
|
||||
|
||||
@@ -43,19 +43,19 @@ make PLATFORM=qemu/virt FW_PAYLOAD_PATH=<uboot_build_directory>/u-boot.bin
|
||||
|
||||
Run:
|
||||
```
|
||||
qemu-system-riscv64 -M virt -m 256M -display none -serial stdio \
|
||||
qemu-system-riscv64 -M virt -m 256M -nographic \
|
||||
-kernel build/platform/qemu/virt/firmware/fw_payload.elf
|
||||
```
|
||||
or
|
||||
```
|
||||
qemu-system-riscv64 -M virt -m 256M -display none -serial stdio \
|
||||
qemu-system-riscv64 -M virt -m 256M -nographic \
|
||||
-kernel build/platform/qemu/virt/firmware/fw_jump.elf \
|
||||
-device loader,file=<uboot_build_directory>/u-boot.bin,addr=0x80400000
|
||||
-device loader,file=<uboot_build_directory>/u-boot.bin,addr=0x80200000
|
||||
```
|
||||
|
||||
**Linux Kernel Payload**
|
||||
|
||||
Note: We assume that Linux kernel is compiled using
|
||||
Note: We assume that the Linux kernel is compiled using
|
||||
*arch/riscv/configs/defconfig*.
|
||||
|
||||
Build:
|
||||
@@ -65,7 +65,7 @@ make PLATFORM=qemu/virt FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/
|
||||
|
||||
Run:
|
||||
```
|
||||
qemu-system-riscv64 -M virt -m 256M -display none -serial stdio \
|
||||
qemu-system-riscv64 -M virt -m 256M -nographic \
|
||||
-kernel build/platform/qemu/virt/firmware/fw_payload.elf \
|
||||
-drive file=<path_to_linux_rootfs>,format=raw,id=hd0 \
|
||||
-device virtio-blk-device,drive=hd0 \
|
||||
@@ -73,7 +73,76 @@ qemu-system-riscv64 -M virt -m 256M -display none -serial stdio \
|
||||
```
|
||||
or
|
||||
```
|
||||
qemu-system-riscv64 -M virt -m 256M -display none -serial stdio \
|
||||
qemu-system-riscv64 -M virt -m 256M -nographic \
|
||||
-kernel build/platform/qemu/virt/firmware/fw_jump.elf \
|
||||
-device loader,file=<linux_build_directory>/arch/riscv/boot/Image,addr=0x80200000 \
|
||||
-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=qemu/virt
|
||||
```
|
||||
|
||||
Run:
|
||||
```
|
||||
qemu-system-riscv32 -M virt -m 256M -nographic \
|
||||
-kernel build/platform/qemu/virt/firmware/fw_payload.elf
|
||||
```
|
||||
|
||||
**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=qemu/virt FW_PAYLOAD_PATH=<uboot_build_directory>/u-boot.bin
|
||||
```
|
||||
|
||||
Run:
|
||||
```
|
||||
qemu-system-riscv32 -M virt -m 256M -nographic \
|
||||
-kernel build/platform/qemu/virt/firmware/fw_payload.elf
|
||||
```
|
||||
or
|
||||
```
|
||||
qemu-system-riscv32 -M virt -m 256M -nographic \
|
||||
-kernel build/platform/qemu/virt/firmware/fw_jump.elf \
|
||||
-device loader,file=<uboot_build_directory>/u-boot.bin,addr=0x80400000
|
||||
```
|
||||
|
||||
**Linux Kernel Payload**
|
||||
|
||||
Note: We assume that the Linux kernel is compiled using
|
||||
*arch/riscv/configs/rv32_defconfig* (kernel 5.1 and newer)
|
||||
respectively using *arch/riscv/configs/defconfig* plus setting
|
||||
CONFIG_ARCH_RV32I=y (kernel 5.0 and older).
|
||||
|
||||
Build:
|
||||
```
|
||||
make PLATFORM=qemu/virt FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image
|
||||
```
|
||||
|
||||
Run:
|
||||
```
|
||||
qemu-system-riscv32 -M virt -m 256M -nographic \
|
||||
-kernel build/platform/qemu/virt/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 \
|
||||
-kernel build/platform/qemu/virt/firmware/fw_jump.elf \
|
||||
-device loader,file=<linux_build_directory>/arch/riscv/boot/Image,addr=0x80400000 \
|
||||
-drive file=<path_to_linux_rootfs>,format=raw,id=hd0 \
|
||||
|
@@ -4,6 +4,9 @@ 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.
|
||||
|
||||
@@ -11,9 +14,10 @@ Platform Options
|
||||
----------------
|
||||
|
||||
As hart0 in the FU540 doesn't have an MMU, only harts 1-4 boot by default.
|
||||
A hart mask i.e. *FU540_ENABLED_HART_MASK* compile time option is provided to
|
||||
select any other hart for booting. Please keep in mind that this is not
|
||||
platform wide option. It can only be specified for FU540 platform in following way.
|
||||
A hart mask i.e. *FU540_ENABLED_HART_MASK* compile time option is provided
|
||||
to select any other hart for booting. Please keep in mind that this is not
|
||||
a generic option and it can only be specified for FU540 platform in the
|
||||
following way:
|
||||
|
||||
```
|
||||
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=Image FU540_ENABLED_HART_MASK=0x02
|
||||
@@ -23,52 +27,63 @@ This will let the board boot only hart1 instead of default 1-4.
|
||||
Building SiFive Fu540 Platform
|
||||
-----------------------------
|
||||
|
||||
As of this writing, the required Linux kernel and U-Boot patches are not
|
||||
accepted in mainline. Please follow the below instructions to cherry-pick
|
||||
them into your repository.
|
||||
|
||||
[U-Boot patches](../firmware/payload_uboot.md)
|
||||
|
||||
[Linux kernel patches](../firmware/payload_linux.md)
|
||||
In order to boot SMP Linux in U-Boot, Linux v5.1 (or higher) and latest
|
||||
U-Boot v2019.04 (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_PAYLOAD_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_PAYLOAD_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 v2019.04 (or higher)
|
||||
having SMP support. From, Linux v5.2 (or higher) device tree is hosted in
|
||||
Linux kernel and compiled as a part of Linux kernel build process.
|
||||
|
||||
The command-line example here assumes that U-Boot was compiled using sifive_fu540_defconfig configuration.
|
||||
|
||||
With SMP support enabled in U-Boot:
|
||||
The detailed U-Boot booting guide is avaialble at [U-Boot](https://gitlab.denx.de/u-boot/u-boot/blob/master/doc/board/sifive/fu540.rst)
|
||||
|
||||
```
|
||||
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot.bin
|
||||
or
|
||||
(For U-Boot which follows Linux v5.2 (or higher) DT bindings)
|
||||
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot.bin FW_PAYLOAD_FDT_PATH=<hifive-unleashed-a00.dtb path from Linux kernel>
|
||||
```
|
||||
|
||||
Without SMP support enabled in U-Boot:
|
||||
Generate the uImage from Linux Image.
|
||||
```
|
||||
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot.bin FU540_ENABLED_HART_MASK=0x02
|
||||
mkimage -A riscv -O linux -T kernel -C none -a 0x80200000 -e 0x80200000 -n Linux -d \
|
||||
<linux_build_directory>/arch/riscv/boot/Image \
|
||||
<linux_build_directory>/arch/riscv/boot/uImage
|
||||
```
|
||||
|
||||
**U-Boot & Linux Kernel as a single payload**
|
||||
|
||||
A single monolithic image containing both U-Boot & Linux can also be used if network boot setup is
|
||||
not available.
|
||||
A single monolithic image containing both U-Boot & Linux can also be used if
|
||||
network boot setup is not available.
|
||||
|
||||
1. Generate the uImage from Linux Image.
|
||||
```
|
||||
mkimage -A riscv -O linux -T kernel -C none -a 0x80200000 -e 0x80200000 -n Linux -d \
|
||||
<linux_build_directory>arch/riscv/boot/Image \
|
||||
<linux_build_directory>/arch/riscv/boot/Image \
|
||||
<linux_build_directory>/arch/riscv/boot/uImage
|
||||
```
|
||||
|
||||
2. Create a temporary image with u-boot.bin as the first payload. The command-line
|
||||
example here assumes that U-Boot was compiled using sifive_fu540_defconfig
|
||||
configuration.
|
||||
2. Create a temporary image with u-boot.bin as the first payload. The
|
||||
command-line example here assumes that U-Boot was compiled using
|
||||
sifive_fu540_defconfig configuration.
|
||||
```
|
||||
dd if=~/workspace/u-boot-riscv/u-boot.bin of=/tmp/temp.bin bs=1M
|
||||
```
|
||||
@@ -79,27 +94,31 @@ dd if=<linux_build_directory>/arch/riscv/boot/uImage of=/tmp/temp.bin bs=1M seek
|
||||
4. Compile OpenSBI with temp.bin (generated in step 3) as payload.
|
||||
```
|
||||
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=/tmp/temp.bin
|
||||
or
|
||||
(For U-Boot which follows Linux v5.2 (or higher) DT bindings)
|
||||
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=/tmp/temp.bin FW_PAYLOAD_FDT_PATH=<hifive-unleashed-a00.dtb path from Linux kernel>
|
||||
```
|
||||
|
||||
Flashing the OpenSBI firmware binary to storage media:
|
||||
-----------------------------------------------------
|
||||
The first stage boot loader([FSBL](https://github.com/sifive/freedom-u540-c000-bootloader))
|
||||
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).
|
||||
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.
|
||||
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 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.
|
||||
@@ -108,7 +127,7 @@ 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)
|
||||
${DISK}
|
||||
```
|
||||
|
||||
Booting SiFive Fu540 Platform
|
||||
@@ -116,67 +135,66 @@ 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.
|
||||
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.
|
||||
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 mac address of the board.
|
||||
|
||||
```
|
||||
setenv ethaddr <mac address of the board>
|
||||
```
|
||||
2. Set the ip address of the board.
|
||||
(Note: This step is optional)
|
||||
|
||||
2. Set the ip address of the board.
|
||||
```
|
||||
setenv ipaddr <ipaddr of the board>
|
||||
```
|
||||
3. Set the tftpboot server IP.
|
||||
|
||||
3. Set the tftpboot server IP.
|
||||
```
|
||||
setenv serverip <ipaddr of the tftp server>
|
||||
```
|
||||
4. Set the network gateway address.
|
||||
|
||||
4. Set the network gateway address.
|
||||
```
|
||||
setenv gatewayip <ipaddress of the network gateway>
|
||||
```
|
||||
|
||||
5. Load the Linux kernel image from the tftp server.
|
||||
|
||||
```
|
||||
tftpboot ${kernel_addr_r} /sifive/fu540/uImage
|
||||
tftpboot ${kernel_addr_r} <uImage path in tftpboot directory>
|
||||
```
|
||||
|
||||
6. 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.
|
||||
|
||||
6. 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} /sifive/fu540/uRamdisk
|
||||
tftpboot ${ramdisk_addr_r} <ramdisk path in tftpboot directory>
|
||||
```
|
||||
7. Set the boot command-line arguments.
|
||||
|
||||
7. Load the pre-compiled device tree via tftpboot.
|
||||
```
|
||||
tftpboot ${fdt_addr_r} <linux source>/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dtb
|
||||
```
|
||||
8. 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)
|
||||
|
||||
N.B. 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.
|
||||
|
||||
9. Now boot into Linux.
|
||||
```
|
||||
bootm ${kernel_addr_r} ${ramdisk_addr_r} ${fdtcontroladdr}
|
||||
|
||||
```
|
||||
or (if ramdisk is not loaded from network)
|
||||
```
|
||||
bootm ${kernel_addr_r} - ${fdtcontroladdr}
|
||||
bootm ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
|
||||
or
|
||||
(If ramdisk is not loaded from network)
|
||||
bootm ${kernel_addr_r} - ${fdt_addr_r}
|
||||
```
|
||||
|
||||
**U-Boot & Linux Kernel as a single payload**
|
||||
@@ -184,19 +202,14 @@ bootm ${kernel_addr_r} - ${fdtcontroladdr}
|
||||
At U-Boot prompt execute the following boot command to boot Linux.
|
||||
|
||||
```
|
||||
bootm ${kernel_addr_r} - ${fdtcontroladdr}
|
||||
bootm ${kernel_addr_r} - ${fdt_addr_r}
|
||||
```
|
||||
Booting SiFive Fu540 Platform with Microsemi Expansion board
|
||||
------------------------------------------------------------
|
||||
|
||||
Until the Linux kernel has in-tree support for device trees and mainline u-boot
|
||||
is fully supported on the HiFive Unleashed you can follow these steps to boot
|
||||
Linux with the Microsemi expansion board. This method should not be copied on
|
||||
future boards and is considered a temporary solution until we can use a more
|
||||
standardised boot flow.
|
||||
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_PAYLOAD_FDT_PATH.
|
||||
|
||||
To boot the Linux kernel with a device tree that has support for the Microsemi
|
||||
Expansion board you can include the following line when compiling the firmware:
|
||||
```
|
||||
FW_PAYLOAD_FDT="HiFiveUnleashed-MicroSemi-Expansion.dtb"
|
||||
```
|
||||
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.
|
||||
|
@@ -1,15 +1,15 @@
|
||||
OpenSBI Platform Support Guideline
|
||||
==================================
|
||||
|
||||
OpenSBI platform support allows an implementation to define a set of platform
|
||||
specific hooks (hardware manipulation functions) in the form of a
|
||||
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
|
||||
platform independent *libsbi.a* to execute platform specific operations.
|
||||
the platform-independent *libsbi.a* to execute platform-specific operations.
|
||||
|
||||
Each of the reference platform support provided by OpenSBI define an instance
|
||||
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
|
||||
*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
|
||||
@@ -22,21 +22,21 @@ 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 a new platform support
|
||||
-----------------------------
|
||||
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 *platform/* directory
|
||||
2. Create a platform configuration file named *config.mk* under
|
||||
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, select common drivers, and select firmware options
|
||||
3. Create *platform/<xyz>/objects.mk* file for listing the platform
|
||||
specific object files to be compiled
|
||||
4. Create *platform/<xyz>/platform.c* file providing a *struct sbi_platform*
|
||||
instance
|
||||
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 template platform support code is available under the *platform/template*
|
||||
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.
|
||||
|
@@ -7,5 +7,5 @@
|
||||
# Anup Patel <anup.patel@wdc.com>
|
||||
#
|
||||
|
||||
$(build_dir)/$(platform_subdir)/firmware/fw_payload.o: $(FW_PAYLOAD_PATH_FINAL)
|
||||
$(build_dir)/$(platform_subdir)/firmware/fw_payload.o: $(FW_PAYLOAD_FDT_PATH)
|
||||
$(platform_build_dir)/firmware/fw_payload.o: $(FW_PAYLOAD_PATH_FINAL)
|
||||
$(platform_build_dir)/firmware/fw_payload.o: $(FW_PAYLOAD_FDT_PATH)
|
||||
|
@@ -13,6 +13,34 @@
|
||||
#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
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.globl _start
|
||||
@@ -23,7 +51,186 @@ _start:
|
||||
* that is, for mhartid != 0
|
||||
*/
|
||||
csrr a6, CSR_MHARTID
|
||||
blt zero, a6, _wait_for_boot_hart
|
||||
blt zero, 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, _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, _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
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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)
|
||||
/* 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 a4, FW_OPTIONS
|
||||
#else
|
||||
add a4, zero, zero
|
||||
#endif
|
||||
call fw_options
|
||||
or a4, a4, a0
|
||||
REG_S a4, 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
|
||||
|
||||
/* Zero-out BSS */
|
||||
la a4, _bss_start
|
||||
@@ -34,12 +241,10 @@ _bss_zero:
|
||||
blt a4, a5, _bss_zero
|
||||
|
||||
/* Override pervious arg1 */
|
||||
add s0, a0, zero
|
||||
add s1, a1, zero
|
||||
MOV_3R s0, a0, s1, a1, s2, a2
|
||||
call fw_prev_arg1
|
||||
add t1, a0, zero
|
||||
add a0, s0, zero
|
||||
add a1, s1, zero
|
||||
MOV_3R a0, s0, a1, s1, a2, s2
|
||||
beqz t1, _prev_arg1_override_done
|
||||
add a1, t1, zero
|
||||
_prev_arg1_override_done:
|
||||
@@ -54,16 +259,15 @@ _prev_arg1_override_done:
|
||||
*/
|
||||
beqz a1, _fdt_reloc_done
|
||||
/* Mask values in a3 and a4 */
|
||||
li a3, ~0xf
|
||||
li a3, ~(__SIZEOF_POINTER__ - 1)
|
||||
li a4, 0xff
|
||||
/* t1 = destination FDT start address */
|
||||
add s0, a0, zero
|
||||
add s1, a1, zero
|
||||
MOV_3R s0, a0, s1, a1, s2, a2
|
||||
call fw_next_arg1
|
||||
add t1, a0, zero
|
||||
add a0, s0, zero
|
||||
add a1, s1, 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
|
||||
@@ -107,34 +311,33 @@ _fdt_reloc_again:
|
||||
blt t1, t2, _fdt_reloc_again
|
||||
_fdt_reloc_done:
|
||||
|
||||
/* Update boot hart flag */
|
||||
la a4, _boot_hart_done
|
||||
li a5, 1
|
||||
REG_S a5, (a4)
|
||||
j _wait_for_boot_hart
|
||||
/* 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
|
||||
|
||||
.align 3
|
||||
_boot_hart_done:
|
||||
RISCV_PTR 0
|
||||
.align 3
|
||||
|
||||
/* Wait for boot hart */
|
||||
/* waitting for boot hart done (_boot_status == 2) */
|
||||
_wait_for_boot_hart:
|
||||
la a4, _boot_hart_done
|
||||
REG_L a5, (a4)
|
||||
beqz a5, _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
|
||||
|
||||
/* Preload per-HART details
|
||||
* s6 -> HART ID
|
||||
* s7 -> HART Count
|
||||
* s8 -> HART Stack Size
|
||||
*/
|
||||
csrr s6, CSR_MHARTID
|
||||
la a4, platform
|
||||
#if __riscv_xlen == 64
|
||||
lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4)
|
||||
@@ -148,7 +351,7 @@ _start_warm:
|
||||
csrr s6, CSR_MHARTID
|
||||
bge s6, s7, _start_hang
|
||||
|
||||
/* Setup scratch space */
|
||||
/* find the scratch space for this hart */
|
||||
la tp, _fw_end
|
||||
mul a5, s7, s8
|
||||
add tp, tp, a5
|
||||
@@ -156,32 +359,9 @@ _start_warm:
|
||||
sub tp, tp, a5
|
||||
li a5, SBI_SCRATCH_SIZE
|
||||
sub tp, tp, a5
|
||||
csrw CSR_MSCRATCH, tp
|
||||
|
||||
/* Initialize 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)
|
||||
/* Note: fw_next_arg1() uses a0, a1, and ra */
|
||||
call fw_next_arg1
|
||||
REG_S a0, SBI_SCRATCH_NEXT_ARG1_OFFSET(tp)
|
||||
/* Note: fw_next_addr() uses a0, a1, and ra */
|
||||
call fw_next_addr
|
||||
REG_S a0, SBI_SCRATCH_NEXT_ADDR_OFFSET(tp)
|
||||
li a4, PRV_S
|
||||
REG_S a4, SBI_SCRATCH_NEXT_MODE_OFFSET(tp)
|
||||
la a4, _start_warm
|
||||
REG_S a4, SBI_SCRATCH_WARMBOOT_ADDR_OFFSET(tp)
|
||||
la a4, platform
|
||||
REG_S a4, SBI_SCRATCH_PLATFORM_ADDR_OFFSET(tp)
|
||||
la a4, _hartid_to_scratch
|
||||
REG_S a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp)
|
||||
REG_S zero, SBI_SCRATCH_IPI_TYPE_OFFSET(tp)
|
||||
REG_S zero, SBI_SCRATCH_TMP0_OFFSET(tp)
|
||||
/* update the mscratch */
|
||||
csrw CSR_MSCRATCH, tp
|
||||
|
||||
/* Setup stack */
|
||||
add sp, tp, zero
|
||||
@@ -189,14 +369,27 @@ _start_warm:
|
||||
/* Setup trap handler */
|
||||
la a4, _trap_handler
|
||||
csrw CSR_MTVEC, a4
|
||||
/* Make sure that mtvec is updated */
|
||||
1: csrr a5, CSR_MTVEC
|
||||
bne a4, a5, 1b
|
||||
|
||||
/* Initialize SBI runtime */
|
||||
csrr a0, CSR_MSCRATCH
|
||||
Call sbi_init
|
||||
call sbi_init
|
||||
|
||||
/* We don't expect to reach here hence just hang */
|
||||
j _start_hang
|
||||
|
||||
.align 3
|
||||
_boot_status:
|
||||
RISCV_PTR 0
|
||||
_load_start:
|
||||
RISCV_PTR _fw_start
|
||||
_link_start:
|
||||
RISCV_PTR _fw_start
|
||||
_link_end:
|
||||
RISCV_PTR _fw_reloc_end
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.globl _hartid_to_scratch
|
||||
@@ -293,6 +486,16 @@ _trap_handler_all_mode:
|
||||
REG_S t0, SBI_TRAP_REGS_OFFSET(mepc)(sp)
|
||||
csrr t0, CSR_MSTATUS
|
||||
REG_S t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp)
|
||||
REG_S zero, SBI_TRAP_REGS_OFFSET(mstatusH)(sp)
|
||||
#if __riscv_xlen == 32
|
||||
csrr t0, CSR_MISA
|
||||
srli t0, t0, ('H' - 'A')
|
||||
andi t0, t0, 0x1
|
||||
beq t0, zero, _skip_mstatush_save
|
||||
csrr t0, CSR_MSTATUSH
|
||||
REG_S t0, SBI_TRAP_REGS_OFFSET(mstatusH)(sp)
|
||||
_skip_mstatush_save:
|
||||
#endif
|
||||
|
||||
/* Save all general regisers except SP and T0 */
|
||||
REG_S zero, SBI_TRAP_REGS_OFFSET(zero)(sp)
|
||||
@@ -367,6 +570,15 @@ _trap_handler_all_mode:
|
||||
csrw CSR_MEPC, t0
|
||||
REG_L t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp)
|
||||
csrw CSR_MSTATUS, t0
|
||||
#if __riscv_xlen == 32
|
||||
csrr t0, CSR_MISA
|
||||
srli t0, t0, ('H' - 'A')
|
||||
andi t0, t0, 0x1
|
||||
beq t0, zero, _skip_mstatush_restore
|
||||
REG_L t0, SBI_TRAP_REGS_OFFSET(mstatusH)(sp)
|
||||
csrw CSR_MSTATUSH, t0
|
||||
_skip_mstatush_restore:
|
||||
#endif
|
||||
|
||||
/* Restore T0 */
|
||||
REG_L t0, SBI_TRAP_REGS_OFFSET(t0)(sp)
|
||||
@@ -375,3 +587,42 @@ _trap_handler_all_mode:
|
||||
REG_L sp, SBI_TRAP_REGS_OFFSET(sp)(sp)
|
||||
|
||||
mret
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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
|
||||
|
@@ -50,6 +50,8 @@
|
||||
{
|
||||
PROVIDE(_data_start = .);
|
||||
|
||||
*(.sdata)
|
||||
*(.sdata.*)
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.readmostly.data)
|
||||
@@ -64,6 +66,8 @@
|
||||
.bss :
|
||||
{
|
||||
PROVIDE(_bss_start = .);
|
||||
*(.sbss)
|
||||
*(.sbss.*)
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
. = ALIGN(8);
|
||||
|
118
firmware/fw_dynamic.S
Normal file
118
firmware/fw_dynamic.S
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
_bad_dynamic_info:
|
||||
wfi
|
||||
j _bad_dynamic_info
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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:
|
||||
la a4, _dynamic_next_arg1
|
||||
REG_S a1, (a4)
|
||||
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
|
||||
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)
|
||||
ret
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.global fw_prev_arg1
|
||||
/*
|
||||
* We can only use a0, a1, and a2 registers here.
|
||||
* The previous arg1 should be returned in 'a0'.
|
||||
*/
|
||||
fw_prev_arg1:
|
||||
add a0, zero, zero
|
||||
ret
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.global fw_next_arg1
|
||||
/*
|
||||
* We can only use a0, a1, and a2 registers here.
|
||||
* The next arg1 should be returned in 'a0'.
|
||||
*/
|
||||
fw_next_arg1:
|
||||
la a0, _dynamic_next_arg1
|
||||
REG_L a0, (a0)
|
||||
ret
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
_dynamic_next_arg1:
|
||||
RISCV_PTR 0x0
|
||||
_dynamic_next_addr:
|
||||
RISCV_PTR 0x0
|
||||
_dynamic_next_mode:
|
||||
RISCV_PTR PRV_S
|
||||
_dynamic_options:
|
||||
RISCV_PTR 0x0
|
18
firmware/fw_dynamic.elf.ldS
Normal file
18
firmware/fw_dynamic.elf.ldS
Normal file
@@ -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 = .);
|
||||
}
|
@@ -9,19 +9,37 @@
|
||||
|
||||
#include "fw_base.S"
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.global fw_prev_arg1
|
||||
/*
|
||||
* We can only use a0, a1, and a2 registers here.
|
||||
* The previous arg1 should be returned in 'a0'.
|
||||
*/
|
||||
fw_prev_arg1:
|
||||
/* We return previous arg1 in 'a0' */
|
||||
add a0, zero, zero
|
||||
ret
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.global fw_next_arg1
|
||||
/*
|
||||
* We can only use a0, a1, and a2 registers here.
|
||||
* The next arg1 should be returned in 'a0'.
|
||||
*/
|
||||
fw_next_arg1:
|
||||
/* We return next arg1 in 'a0' */
|
||||
#ifdef FW_JUMP_FDT_ADDR
|
||||
li a0, FW_JUMP_FDT_ADDR
|
||||
#else
|
||||
@@ -32,12 +50,38 @@ fw_next_arg1:
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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:
|
||||
/* We return next address in 'a0' */
|
||||
la a0, _jump_addr
|
||||
REG_L a0, (a0)
|
||||
ret
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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
|
||||
|
@@ -13,4 +13,6 @@ ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
#include "fw_base.ldS"
|
||||
|
||||
PROVIDE(_fw_reloc_end = .);
|
||||
}
|
||||
|
@@ -9,11 +9,26 @@
|
||||
|
||||
#include "fw_base.S"
|
||||
|
||||
.align 4
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.global fw_prev_arg1
|
||||
/*
|
||||
* We can only use a0, a1, and a2 registers here.
|
||||
* The previous arg1 should be returned in 'a0'.
|
||||
*/
|
||||
fw_prev_arg1:
|
||||
/* We return previous arg1 in 'a0' */
|
||||
#ifdef FW_PAYLOAD_FDT_PATH
|
||||
la a0, fdt_bin
|
||||
#else
|
||||
@@ -21,11 +36,14 @@ fw_prev_arg1:
|
||||
#endif
|
||||
ret
|
||||
|
||||
.align 4
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.global fw_next_arg1
|
||||
/*
|
||||
* We can only use a0, a1, and a2 registers here.
|
||||
* The next arg1 should be returned in 'a0'.
|
||||
*/
|
||||
fw_next_arg1:
|
||||
/* We return next arg1 in 'a0' */
|
||||
#ifdef FW_PAYLOAD_FDT_ADDR
|
||||
li a0, FW_PAYLOAD_FDT_ADDR
|
||||
#else
|
||||
@@ -33,14 +51,40 @@ fw_next_arg1:
|
||||
#endif
|
||||
ret
|
||||
|
||||
.align 4
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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:
|
||||
/* We return next address in 'a0' */
|
||||
la a0, payload_bin
|
||||
ret
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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
|
||||
|
||||
.align 3
|
||||
.section .entry, "ax", %progbits
|
||||
.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
|
||||
|
||||
#ifdef FW_PAYLOAD_FDT_PATH
|
||||
.align 4
|
||||
.section .text, "ax", %progbits
|
||||
|
@@ -27,4 +27,6 @@ SECTIONS
|
||||
. = ALIGN(8);
|
||||
PROVIDE(_payload_end = .);
|
||||
}
|
||||
|
||||
PROVIDE(_fw_reloc_end = .);
|
||||
}
|
||||
|
@@ -17,6 +17,8 @@ ifdef FW_TEXT_START
|
||||
firmware-genflags-y += -DFW_TEXT_START=$(FW_TEXT_START)
|
||||
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)
|
||||
@@ -29,7 +31,7 @@ firmware-bins-$(FW_PAYLOAD) += fw_payload.bin
|
||||
ifdef FW_PAYLOAD_PATH
|
||||
FW_PAYLOAD_PATH_FINAL=$(FW_PAYLOAD_PATH)
|
||||
else
|
||||
FW_PAYLOAD_PATH_FINAL=$(build_dir)/$(platform_subdir)/firmware/payloads/test.bin
|
||||
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
|
||||
@@ -41,7 +43,7 @@ endif
|
||||
|
||||
ifndef FW_PAYLOAD_FDT_PATH
|
||||
ifdef FW_PAYLOAD_FDT
|
||||
FW_PAYLOAD_FDT_PATH=$(build_dir)/$(platform_subdir)/$(FW_PAYLOAD_FDT)
|
||||
FW_PAYLOAD_FDT_PATH=$(platform_build_dir)/$(FW_PAYLOAD_FDT)
|
||||
endif
|
||||
endif
|
||||
ifdef FW_PAYLOAD_FDT_PATH
|
||||
@@ -50,3 +52,7 @@ 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
|
||||
|
@@ -9,10 +9,35 @@
|
||||
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
|
||||
#define wfi() \
|
||||
do { \
|
||||
__asm__ __volatile__ ("wfi" ::: "memory"); \
|
||||
} while (0)
|
||||
#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)
|
||||
{
|
||||
|
61
include/sbi/fw_dynamic.h
Normal file
61
include/sbi/fw_dynamic.h
Normal file
@@ -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 __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 */
|
||||
#define FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET (2 * __SIZEOF_POINTER__)
|
||||
/** Offset of next_mode member in fw_dynamic_info */
|
||||
#define FW_DYNAMIC_INFO_NEXT_MODE_OFFSET (3 * __SIZEOF_POINTER__)
|
||||
/** Offset of options member in fw_dynamic_info */
|
||||
#define FW_DYNAMIC_INFO_OPTIONS_OFFSET (4 * __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 0x1
|
||||
|
||||
/** 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;
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@@ -12,6 +12,8 @@
|
||||
|
||||
#include <sbi/riscv_encoding.h>
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
#define __ASM_STR(x) x
|
||||
#else
|
||||
@@ -26,6 +28,10 @@
|
||||
#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)
|
||||
@@ -73,76 +79,85 @@
|
||||
#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_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_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_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_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_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_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"); \
|
||||
})
|
||||
#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)
|
||||
#define wfi() \
|
||||
do { \
|
||||
__asm__ __volatile__("wfi" ::: "memory"); \
|
||||
} while (0)
|
||||
|
||||
static inline int misa_extension(char ext)
|
||||
{
|
||||
@@ -168,11 +183,11 @@ static inline void misa_string(char *out, unsigned int out_sz)
|
||||
out++;
|
||||
}
|
||||
|
||||
int pmp_set(unsigned int n, unsigned long prot,
|
||||
unsigned long addr, unsigned long log2len);
|
||||
int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
|
||||
unsigned long log2len);
|
||||
|
||||
int pmp_get(unsigned int n, unsigned long *prot_out,
|
||||
unsigned long *addr_out, unsigned long *log2len_out);
|
||||
int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
|
||||
unsigned long *log2len_out);
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
|
@@ -14,11 +14,12 @@ typedef struct {
|
||||
volatile long counter;
|
||||
} atomic_t;
|
||||
|
||||
#define ATOMIC_INIT(_lptr, val) \
|
||||
(_lptr)->counter = (val)
|
||||
#define ATOMIC_INIT(_lptr, val) (_lptr)->counter = (val)
|
||||
|
||||
#define ATOMIC_INITIALIZER(val) \
|
||||
{ .counter = (val), }
|
||||
#define ATOMIC_INITIALIZER(val) \
|
||||
{ \
|
||||
.counter = (val), \
|
||||
}
|
||||
|
||||
long atomic_read(atomic_t *atom);
|
||||
|
||||
@@ -34,6 +35,9 @@ long arch_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.
|
||||
|
@@ -10,6 +10,8 @@
|
||||
#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"
|
||||
|
||||
@@ -37,17 +39,19 @@
|
||||
/* CPU relax for busy loop */
|
||||
#define cpu_relax() asm volatile ("" : : : "memory")
|
||||
|
||||
#define __smp_store_release(p, v) \
|
||||
do { \
|
||||
RISCV_FENCE(rw,w); \
|
||||
*(p) = (v); \
|
||||
} while (0)
|
||||
/* clang-format on */
|
||||
|
||||
#define __smp_load_acquire(p) \
|
||||
({ \
|
||||
typeof(*p) ___p1 = *(p); \
|
||||
RISCV_FENCE(r,rw); \
|
||||
___p1; \
|
||||
})
|
||||
#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
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
/* TODO: Make constants usable in assembly with _AC() macro */
|
||||
|
||||
/* clang-format off */
|
||||
#define MSTATUS_UIE 0x00000001
|
||||
#define MSTATUS_SIE 0x00000002
|
||||
#define MSTATUS_HIE 0x00000004
|
||||
@@ -37,15 +38,30 @@
|
||||
#define MSTATUS_TW 0x00200000
|
||||
#define MSTATUS_TSR 0x00400000
|
||||
#define MSTATUS32_SD 0x80000000
|
||||
#if __riscv_xlen == 64
|
||||
#define MSTATUS_UXL 0x0000000300000000
|
||||
#define MSTATUS_SXL 0x0000000C00000000
|
||||
#define MSTATUS_MTL 0x0000004000000000
|
||||
#define MSTATUS_MTL_SHIFT 38
|
||||
#define MSTATUS_MPV 0x0000008000000000
|
||||
#define MSTATUS_MPV_HIFT 39
|
||||
#else
|
||||
#define MSTATUSH_UXL 0x00000003
|
||||
#define MSTATUSH_SXL 0x0000000C
|
||||
#define MSTATUSH_MTL 0x00000040
|
||||
#define MSTATUSH_MTL_SHIFT 6
|
||||
#define MSTATUSH_MPV 0x00000080
|
||||
#define MSTATUSH_MPV_HIFT 7
|
||||
#endif
|
||||
#define MSTATUS64_SD 0x8000000000000000
|
||||
|
||||
#define SSTATUS_UIE 0x00000001
|
||||
#define SSTATUS_SIE 0x00000002
|
||||
#define SSTATUS_UPIE 0x00000010
|
||||
#define SSTATUS_SPIE 0x00000020
|
||||
#define SSTATUS_SPP 0x00000100
|
||||
#define SSTATUS_SPIE_SHIFT 5
|
||||
#define SSTATUS_SPIE (1UL << MSTATUS_SPIE_SHIFT)
|
||||
#define SSTATUS_SPP_SHIFT 8
|
||||
#define SSTATUS_SPP (1UL << MSTATUS_SPP_SHIFT)
|
||||
#define SSTATUS_FS 0x00006000
|
||||
#define SSTATUS_XS 0x00018000
|
||||
#define SSTATUS_SUM 0x00040000
|
||||
@@ -54,6 +70,14 @@
|
||||
#define SSTATUS_UXL 0x0000000300000000
|
||||
#define SSTATUS64_SD 0x8000000000000000
|
||||
|
||||
#define HSTATUS_VTSR 0x00400000
|
||||
#define HSTATUS_VTVM 0x00100000
|
||||
#define HSTATUS_SP2V 0x00000200
|
||||
#define HSTATUS_SP2P 0x00000100
|
||||
#define HSTATUS_SPV 0x00000080
|
||||
#define HSTATUS_STL 0x00000040
|
||||
#define HSTATUS_SPRV 0x00000001
|
||||
|
||||
#define DCSR_XDEBUGVER (3U<<30)
|
||||
#define DCSR_NDRESET (1<<29)
|
||||
#define DCSR_FULLRESET (1<<28)
|
||||
@@ -248,6 +272,25 @@
|
||||
#define CSR_STVAL 0x143
|
||||
#define CSR_SIP 0x144
|
||||
#define CSR_SATP 0x180
|
||||
|
||||
#define CSR_HSTATUS 0x600
|
||||
#define CSR_HEDELEG 0x602
|
||||
#define CSR_HIDELEG 0x603
|
||||
#define CSR_HTIMEDELTA 0x605
|
||||
#define CSR_HTIMEDELTAH 0x615
|
||||
#define CSR_HCOUNTERNEN 0x606
|
||||
#define CSR_HGATP 0x680
|
||||
|
||||
#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
|
||||
|
||||
#define CSR_MSTATUS 0x300
|
||||
#define CSR_MISA 0x301
|
||||
#define CSR_MEDELEG 0x302
|
||||
@@ -255,6 +298,7 @@
|
||||
#define CSR_MIE 0x304
|
||||
#define CSR_MTVEC 0x305
|
||||
#define CSR_MCOUNTEREN 0x306
|
||||
#define CSR_MSTATUSH 0x310
|
||||
#define CSR_MSCRATCH 0x340
|
||||
#define CSR_MEPC 0x341
|
||||
#define CSR_MCAUSE 0x342
|
||||
@@ -287,6 +331,7 @@
|
||||
#define CSR_DCSR 0x7b0
|
||||
#define CSR_DPC 0x7b1
|
||||
#define CSR_DSCRATCH 0x7b2
|
||||
|
||||
#define CSR_MCYCLE 0xb00
|
||||
#define CSR_MINSTRET 0xb02
|
||||
#define CSR_MHPMCOUNTER3 0xb03
|
||||
@@ -424,8 +469,8 @@
|
||||
#define CAUSE_MISALIGNED_STORE 0x6
|
||||
#define CAUSE_STORE_ACCESS 0x7
|
||||
#define CAUSE_USER_ECALL 0x8
|
||||
#define CAUSE_SUPERVISOR_ECALL 0x9
|
||||
#define CAUSE_HYPERVISOR_ECALL 0xa
|
||||
#define CAUSE_HYPERVISOR_ECALL 0x9
|
||||
#define CAUSE_SUPERVISOR_ECALL 0xa
|
||||
#define CAUSE_MACHINE_ECALL 0xb
|
||||
#define CAUSE_FETCH_PAGE_FAULT 0xc
|
||||
#define CAUSE_LOAD_PAGE_FAULT 0xd
|
||||
@@ -501,6 +546,9 @@
|
||||
#define INSN_MATCH_C_FSWSP 0xe002
|
||||
#define INSN_MASK_C_FSWSP 0xe003
|
||||
|
||||
#define INSN_MASK_WFI 0xffffff00
|
||||
#define INSN_MATCH_WFI 0x10500000
|
||||
|
||||
#define INSN_LEN(insn) ((((insn) & 0x3) < 0x3) ? 2 : 4)
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
@@ -561,4 +609,6 @@
|
||||
(s32)(((insn) >> 7) & 0x1f))
|
||||
#define MASK_FUNCT3 0x7000
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#endif
|
||||
|
@@ -21,28 +21,49 @@
|
||||
|
||||
#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 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_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)
|
||||
@@ -50,11 +71,7 @@
|
||||
#define GET_FFLAGS() csr_read(CSR_FFLAGS)
|
||||
#define SET_FFLAGS(value) csr_write(CSR_FFLAGS, (value))
|
||||
|
||||
#define SET_FS_DIRTY() ((void) 0)
|
||||
|
||||
#else
|
||||
#error "Floating point emulation not supported.\n"
|
||||
#endif
|
||||
#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))
|
||||
@@ -62,8 +79,10 @@
|
||||
#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 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))
|
||||
@@ -71,3 +90,5 @@
|
||||
#define GET_F64_RS2S(insn, regs) (GET_F64_REG(RVC_RS2S(insn), 0, regs))
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@@ -15,23 +15,23 @@
|
||||
|
||||
static inline void __raw_writeb(u8 val, volatile void *addr)
|
||||
{
|
||||
asm volatile("sb %0, 0(%1)" : : "r" (val), "r" (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));
|
||||
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));
|
||||
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));
|
||||
asm volatile("sd %0, 0(%1)" : : "r"(val), "r"(addr));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -39,7 +39,7 @@ static inline u8 __raw_readb(const volatile void *addr)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
asm volatile("lb %0, 0(%1)" : "=r" (val) : "r" (addr));
|
||||
asm volatile("lb %0, 0(%1)" : "=r"(val) : "r"(addr));
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ static inline u16 __raw_readw(const volatile void *addr)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
asm volatile("lh %0, 0(%1)" : "=r" (val) : "r" (addr));
|
||||
asm volatile("lh %0, 0(%1)" : "=r"(val) : "r"(addr));
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ static inline u32 __raw_readl(const volatile void *addr)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
asm volatile("lw %0, 0(%1)" : "=r" (val) : "r" (addr));
|
||||
asm volatile("lw %0, 0(%1)" : "=r"(val) : "r"(addr));
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -64,12 +64,15 @@ static inline u64 __raw_readq(const volatile void *addr)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
asm volatile("ld %0, 0(%1)" : "=r" (val) : "r" (addr));
|
||||
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)
|
||||
@@ -106,4 +109,6 @@ static inline u64 __raw_readq(const volatile void *addr)
|
||||
#define writeq(v,c) ({ __io_bw(); __raw_writeq((v),(c)); __io_aw(); })
|
||||
#endif
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#endif
|
||||
|
@@ -14,13 +14,14 @@ typedef struct {
|
||||
volatile long lock;
|
||||
} spinlock_t;
|
||||
|
||||
#define __RISCV_SPIN_UNLOCKED 0
|
||||
#define __RISCV_SPIN_UNLOCKED 0
|
||||
|
||||
#define SPIN_LOCK_INIT(_lptr) \
|
||||
(_lptr)->lock = __RISCV_SPIN_UNLOCKED
|
||||
#define SPIN_LOCK_INIT(_lptr) (_lptr)->lock = __RISCV_SPIN_UNLOCKED
|
||||
|
||||
#define SPIN_LOCK_INITIALIZER \
|
||||
{ .lock = __RISCV_SPIN_UNLOCKED, }
|
||||
#define SPIN_LOCK_INITIALIZER \
|
||||
{ \
|
||||
.lock = __RISCV_SPIN_UNLOCKED, \
|
||||
}
|
||||
|
||||
int spin_lock_check(spinlock_t *lock);
|
||||
|
||||
|
49
include/sbi/riscv_unpriv.h
Normal file
49
include/sbi/riscv_unpriv.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2019 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __RISCV_UNPRIV_H__
|
||||
#define __RISCV_UNPRIV_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
struct sbi_scratch;
|
||||
|
||||
struct unpriv_trap {
|
||||
unsigned long ilen;
|
||||
unsigned long cause;
|
||||
unsigned long tval;
|
||||
};
|
||||
|
||||
#define DECLARE_UNPRIVILEGED_LOAD_FUNCTION(type) \
|
||||
type load_##type(const type *addr, \
|
||||
struct sbi_scratch *scratch, \
|
||||
struct unpriv_trap *trap);
|
||||
|
||||
#define DECLARE_UNPRIVILEGED_STORE_FUNCTION(type) \
|
||||
void store_##type(type *addr, type val, \
|
||||
struct sbi_scratch *scratch, \
|
||||
struct unpriv_trap *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 get_insn(ulong mepc, bool virt, struct sbi_scratch *scratch,
|
||||
struct unpriv_trap *trap);
|
||||
|
||||
#endif
|
@@ -93,6 +93,6 @@ static inline int __ffs(unsigned long word)
|
||||
*
|
||||
* Undefined if no zero exists, so code should check against ~0UL first.
|
||||
*/
|
||||
#define ffz(x) __ffs(~(x))
|
||||
#define ffz(x) __ffs(~(x))
|
||||
|
||||
#endif
|
||||
|
@@ -13,19 +13,20 @@
|
||||
#define likely(x) __builtin_expect((x), 1)
|
||||
#define unlikely(x) __builtin_expect((x), 0)
|
||||
|
||||
#define ROUNDUP(a, b) ((((a)-1)/(b)+1)*(b))
|
||||
#define ROUNDDOWN(a, b) ((a)/(b)*(b))
|
||||
#define ROUNDUP(a, b) ((((a)-1) / (b) + 1) * (b))
|
||||
#define ROUNDDOWN(a, b) ((a) / (b) * (b))
|
||||
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define CLAMP(a, lo, hi) MIN(MAX(a, lo), hi)
|
||||
|
||||
#define EXTRACT_FIELD(val, which) (((val) & (which)) / ((which) & ~((which)-1)))
|
||||
#define INSERT_FIELD(val, which, fieldval) (((val) & ~(which)) | ((fieldval) * ((which) & ~((which)-1))))
|
||||
#define INSERT_FIELD(val, which, fieldval) \
|
||||
(((val) & ~(which)) | ((fieldval) * ((which) & ~((which)-1))))
|
||||
|
||||
#define STR(x) XSTR(x)
|
||||
#define XSTR(x) #x
|
||||
|
||||
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
|
||||
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
|
||||
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
|
||||
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
|
||||
#endif
|
||||
|
@@ -12,11 +12,11 @@
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
#define __printf(a, b) __attribute__((format(printf, a, b)))
|
||||
#define __printf(a, b) __attribute__((format(printf, a, b)))
|
||||
|
||||
bool sbi_isprintable(char ch);
|
||||
|
||||
char sbi_getc(void);
|
||||
int sbi_getc(void);
|
||||
|
||||
void sbi_putc(char ch);
|
||||
|
||||
@@ -26,12 +26,15 @@ 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(3, 4) sbi_snprintf(char *out, u32 out_sz, const char *format, ...);
|
||||
|
||||
int __printf(1, 2) sbi_printf(const char *format, ...);
|
||||
|
||||
struct sbi_scratch;
|
||||
|
||||
int __printf(2, 3) sbi_dprintf(struct sbi_scratch *scratch,
|
||||
const char *format, ...);
|
||||
|
||||
int sbi_console_init(struct sbi_scratch *scratch);
|
||||
|
||||
#endif
|
||||
|
@@ -19,6 +19,8 @@
|
||||
* leave it unchanged in asm.
|
||||
*/
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
#define _AC(X,Y) X
|
||||
#define _AT(T,X) X
|
||||
@@ -40,4 +42,6 @@
|
||||
#define __STR(s) #s
|
||||
#define STRINGIFY(s) __STR(s)
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#endif
|
||||
|
@@ -19,8 +19,7 @@ u16 sbi_ecall_version_major(void);
|
||||
|
||||
u16 sbi_ecall_version_minor(void);
|
||||
|
||||
int sbi_ecall_handler(u32 hartid, ulong mcause,
|
||||
struct sbi_trap_regs *regs,
|
||||
int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch);
|
||||
|
||||
#endif
|
||||
|
@@ -10,39 +10,36 @@
|
||||
#ifndef __SBI_ECALL_INTERFACE_H__
|
||||
#define __SBI_ECALL_INTERFACE_H__
|
||||
|
||||
#define SBI_ECALL_SET_TIMER 0
|
||||
#define SBI_ECALL_CONSOLE_PUTCHAR 1
|
||||
#define SBI_ECALL_CONSOLE_GETCHAR 2
|
||||
#define SBI_ECALL_CLEAR_IPI 3
|
||||
#define SBI_ECALL_SEND_IPI 4
|
||||
#define SBI_ECALL_REMOTE_FENCE_I 5
|
||||
#define SBI_ECALL_REMOTE_SFENCE_VMA 6
|
||||
#define SBI_ECALL_REMOTE_SFENCE_VMA_ASID 7
|
||||
#define SBI_ECALL_SHUTDOWN 8
|
||||
/* clang-format off */
|
||||
|
||||
#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; \
|
||||
})
|
||||
enum sbi_ext_id {
|
||||
SBI_EXT_0_1_SET_TIMER = 0x0,
|
||||
SBI_EXT_0_1_CONSOLE_PUTCHAR = 0x1,
|
||||
SBI_EXT_0_1_CONSOLE_GETCHAR = 0x2,
|
||||
SBI_EXT_0_1_CLEAR_IPI = 0x3,
|
||||
SBI_EXT_0_1_SEND_IPI = 0x4,
|
||||
SBI_EXT_0_1_REMOTE_FENCE_I = 0x5,
|
||||
SBI_EXT_0_1_REMOTE_SFENCE_VMA = 0x6,
|
||||
SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID = 0x7,
|
||||
SBI_EXT_0_1_SHUTDOWN = 0x8,
|
||||
SBI_EXT_BASE = 0x10,
|
||||
};
|
||||
|
||||
#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)
|
||||
enum sbi_ext_base_fid {
|
||||
SBI_EXT_BASE_GET_SPEC_VERSION = 0,
|
||||
SBI_EXT_BASE_GET_IMP_ID,
|
||||
SBI_EXT_BASE_GET_IMP_VERSION,
|
||||
SBI_EXT_BASE_PROBE_EXT,
|
||||
SBI_EXT_BASE_GET_MVENDORID,
|
||||
SBI_EXT_BASE_GET_MARCHID,
|
||||
SBI_EXT_BASE_GET_MIMPID,
|
||||
};
|
||||
|
||||
#define sbi_ecall_console_putc(c) \
|
||||
SBI_ECALL_1(SBI_ECALL_CONSOLE_PUTCHAR, (c));
|
||||
|
||||
static inline void sbi_ecall_console_puts(const char *str)
|
||||
{
|
||||
while (str && *str)
|
||||
sbi_ecall_console_putc(*str++);
|
||||
}
|
||||
#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
|
||||
/* clang-format on */
|
||||
|
||||
#endif
|
||||
|
@@ -12,16 +12,13 @@
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
struct sbi_trap_regs;
|
||||
struct sbi_scratch;
|
||||
|
||||
int sbi_emulate_csr_read(int csr_num,
|
||||
u32 hartid, ulong mstatus,
|
||||
struct sbi_scratch *scratch,
|
||||
ulong *csr_val);
|
||||
int sbi_emulate_csr_read(int csr_num, u32 hartid, struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch, ulong *csr_val);
|
||||
|
||||
int sbi_emulate_csr_write(int csr_num,
|
||||
u32 hartid, ulong mstatus,
|
||||
struct sbi_scratch *scratch,
|
||||
ulong csr_val);
|
||||
int sbi_emulate_csr_write(int csr_num, u32 hartid, struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch, ulong csr_val);
|
||||
|
||||
#endif
|
||||
|
@@ -10,16 +10,25 @@
|
||||
#ifndef __SBI_ERROR_H__
|
||||
#define __SBI_ERROR_H__
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#define SBI_OK 0
|
||||
#define SBI_EUNKNOWN -1
|
||||
#define SBI_EFAIL -2
|
||||
#define SBI_EFAIL -1
|
||||
#define SBI_ENOTSUPP -2
|
||||
#define SBI_EINVAL -3
|
||||
#define SBI_ENOENT -4
|
||||
#define SBI_ENOTSUPP -5
|
||||
#define SBI_DENIED -4
|
||||
#define SBI_INVALID_ADDR -5
|
||||
#define SBI_ENODEV -6
|
||||
#define SBI_ENOSYS -7
|
||||
#define SBI_ETIMEDOUT -8
|
||||
#define SBI_EIO -9
|
||||
#define SBI_EILL -10
|
||||
#define SBI_ENOSPC -11
|
||||
#define SBI_ENOMEM -12
|
||||
#define SBI_ETRAP -13
|
||||
#define SBI_EUNKNOWN -14
|
||||
#define SBI_ENOENT -15
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#endif
|
||||
|
42
include/sbi/sbi_fifo.h
Normal file
42
include/sbi/sbi_fifo.h
Normal file
@@ -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
|
@@ -14,16 +14,20 @@
|
||||
|
||||
struct sbi_scratch;
|
||||
|
||||
int sbi_hart_init(struct sbi_scratch *scratch, u32 hartid);
|
||||
int sbi_hart_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot);
|
||||
|
||||
void *sbi_hart_get_trap_info(struct sbi_scratch *scratch);
|
||||
|
||||
void sbi_hart_set_trap_info(struct sbi_scratch *scratch, void *data);
|
||||
|
||||
void sbi_hart_pmp_dump(struct sbi_scratch *scratch);
|
||||
|
||||
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);
|
||||
void __attribute__((noreturn))
|
||||
sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,
|
||||
unsigned long next_addr, unsigned long next_mode,
|
||||
bool next_virt);
|
||||
|
||||
void sbi_hart_mark_available(u32 hartid);
|
||||
|
||||
|
@@ -10,17 +10,27 @@
|
||||
#ifndef __SBI_IPI_H__
|
||||
#define __SBI_IPI_H__
|
||||
|
||||
#include <sbi/riscv_unpriv.h>
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#define SBI_IPI_EVENT_SOFT 0x1
|
||||
#define SBI_IPI_EVENT_FENCE_I 0x2
|
||||
#define SBI_IPI_EVENT_SFENCE_VMA 0x4
|
||||
#define SBI_IPI_EVENT_HALT 0x8
|
||||
#define SBI_IPI_EVENT_SFENCE_VMA_ASID 0x8
|
||||
#define SBI_IPI_EVENT_HALT 0x10
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
struct sbi_scratch;
|
||||
|
||||
int sbi_ipi_send_many(struct sbi_scratch *scratch,
|
||||
ulong *pmask, u32 event);
|
||||
struct sbi_ipi_data {
|
||||
unsigned long ipi_type;
|
||||
};
|
||||
|
||||
int sbi_ipi_send_many(struct sbi_scratch *scratch, struct unpriv_trap *uptrap,
|
||||
ulong *pmask, u32 event, void *data);
|
||||
|
||||
void sbi_ipi_clear_smode(struct sbi_scratch *scratch);
|
||||
|
||||
|
@@ -10,56 +10,66 @@
|
||||
#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 (0x0)
|
||||
#define SBI_PLATFORM_NAME_OFFSET (0x08)
|
||||
/** Offset of features in struct sbi_platform */
|
||||
#define SBI_PLATFORM_FEATURES_OFFSET (0x40)
|
||||
#define SBI_PLATFORM_FEATURES_OFFSET (0x48)
|
||||
/** Offset of hart_count in struct sbi_platform */
|
||||
#define SBI_PLATFORM_HART_COUNT_OFFSET (0x48)
|
||||
#define SBI_PLATFORM_HART_COUNT_OFFSET (0x50)
|
||||
/** Offset of hart_stack_size in struct sbi_platform */
|
||||
#define SBI_PLATFORM_HART_STACK_SIZE_OFFSET (0x4c)
|
||||
#define SBI_PLATFORM_HART_STACK_SIZE_OFFSET (0x54)
|
||||
/** Offset of disabled_hart_mask in struct sbi_platform */
|
||||
#define SBI_PLATFORM_DISABLED_HART_OFFSET (0x58)
|
||||
/** Offset of tlb_range_flush_limit in struct sbi_platform */
|
||||
#define SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_OFFSET (0x60)
|
||||
/** Offset of platform_ops_addr in struct sbi_platform */
|
||||
#define SBI_PLATFORM_OPS_OFFSET (0x68)
|
||||
/** Offset of firmware_context in struct sbi_platform */
|
||||
#define SBI_PLATFORM_FIRMWARE_CONTEXT_OFFSET (0x68 + __SIZEOF_POINTER__)
|
||||
|
||||
#define SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_DEFAULT (1UL << 12)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <sbi/sbi_version.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_ecall.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
|
||||
/** Possible feature flags of a platform */
|
||||
enum sbi_platform_features {
|
||||
/** Platform has timer value */
|
||||
SBI_PLATFORM_HAS_TIMER_VALUE = (1 << 0),
|
||||
SBI_PLATFORM_HAS_TIMER_VALUE = (1 << 0),
|
||||
/** Platform has HART hotplug support */
|
||||
SBI_PLATFORM_HAS_HART_HOTPLUG = (1 << 1),
|
||||
SBI_PLATFORM_HAS_HART_HOTPLUG = (1 << 1),
|
||||
/** Platform has PMP support */
|
||||
SBI_PLATFORM_HAS_PMP = (1 << 2),
|
||||
SBI_PLATFORM_HAS_PMP = (1 << 2),
|
||||
/** Platform has S-mode counter enable */
|
||||
SBI_PLATFORM_HAS_SCOUNTEREN = (1 << 3),
|
||||
SBI_PLATFORM_HAS_SCOUNTEREN = (1 << 3),
|
||||
/** Platform has M-mode counter enable */
|
||||
SBI_PLATFORM_HAS_MCOUNTEREN = (1 << 4),
|
||||
SBI_PLATFORM_HAS_MCOUNTEREN = (1 << 4),
|
||||
/** Platform has fault delegation support */
|
||||
SBI_PLATFORM_HAS_MFAULTS_DELEGATION = (1 << 5),
|
||||
SBI_PLATFORM_HAS_MFAULTS_DELEGATION = (1 << 5),
|
||||
};
|
||||
|
||||
/** Default feature set for a platform */
|
||||
#define SBI_PLATFORM_DEFAULT_FEATURES \
|
||||
(SBI_PLATFORM_HAS_TIMER_VALUE | \
|
||||
SBI_PLATFORM_HAS_PMP | \
|
||||
SBI_PLATFORM_HAS_SCOUNTEREN | \
|
||||
SBI_PLATFORM_HAS_MCOUNTEREN | \
|
||||
#define SBI_PLATFORM_DEFAULT_FEATURES \
|
||||
(SBI_PLATFORM_HAS_TIMER_VALUE | SBI_PLATFORM_HAS_PMP | \
|
||||
SBI_PLATFORM_HAS_SCOUNTEREN | SBI_PLATFORM_HAS_MCOUNTEREN | \
|
||||
SBI_PLATFORM_HAS_MFAULTS_DELEGATION)
|
||||
|
||||
/** Representation of a platform */
|
||||
struct sbi_platform {
|
||||
/** 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;
|
||||
/** Mask representing the set of disabled HARTs */
|
||||
u64 disabled_hart_mask;
|
||||
|
||||
/** Platform functions */
|
||||
struct sbi_platform_operations {
|
||||
/** Platform early initialization */
|
||||
int (*early_init)(bool cold_boot);
|
||||
/** Platform final initialization */
|
||||
@@ -71,13 +81,13 @@ struct sbi_platform {
|
||||
* Get PMP regions details (namely: protection, base address,
|
||||
* and size) for given HART
|
||||
*/
|
||||
int (*pmp_region_info)(u32 hartid, u32 index,
|
||||
ulong *prot, ulong *addr, ulong *log2size);
|
||||
int (*pmp_region_info)(u32 hartid, u32 index, ulong *prot, ulong *addr,
|
||||
ulong *log2size);
|
||||
|
||||
/** Write a character to the platform console output */
|
||||
void (*console_putc)(char ch);
|
||||
/** Read a character from the platform console input */
|
||||
char (*console_getc)(void);
|
||||
int (*console_getc)(void);
|
||||
/** Initialize the platform console */
|
||||
int (*console_init)(void);
|
||||
|
||||
@@ -86,8 +96,6 @@ struct sbi_platform {
|
||||
|
||||
/** Send IPI to a target HART */
|
||||
void (*ipi_send)(u32 target_hart);
|
||||
/** Wait for target HART to acknowledge IPI */
|
||||
void (*ipi_sync)(u32 target_hart);
|
||||
/** Clear IPI for a target HART */
|
||||
void (*ipi_clear)(u32 target_hart);
|
||||
/** Initialize IPI for current HART */
|
||||
@@ -106,31 +114,74 @@ struct sbi_platform {
|
||||
int (*system_reboot)(u32 type);
|
||||
/** Shutdown or poweroff the platform */
|
||||
int (*system_shutdown)(u32 type);
|
||||
|
||||
/** 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,
|
||||
unsigned long *args, unsigned long *out_value,
|
||||
unsigned long *out_trap_cause,
|
||||
unsigned long *out_trap_val);
|
||||
} __packed;
|
||||
|
||||
/** 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;
|
||||
/** Mask representing the set of disabled HARTs */
|
||||
u64 disabled_hart_mask;
|
||||
/* Maximum value of tlb flush range request*/
|
||||
u64 tlb_range_flush_limit;
|
||||
/** Pointer to sbi platform operations */
|
||||
unsigned long platform_ops_addr;
|
||||
/** Pointer to system firmware specific context */
|
||||
unsigned long firmware_context;
|
||||
} __packed;
|
||||
|
||||
/** Get pointer to sbi_platform for sbi_scratch pointer */
|
||||
#define sbi_platform_ptr(__s) \
|
||||
((struct sbi_platform *)((__s)->platform_addr))
|
||||
#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() \
|
||||
((struct sbi_platform *)(sbi_scratch_thishart_ptr()->platform_addr))
|
||||
#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) \
|
||||
#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) \
|
||||
#define sbi_platform_has_hart_hotplug(__p) \
|
||||
((__p)->features & SBI_PLATFORM_HAS_HART_HOTPLUG)
|
||||
/** Check whether the platform has PMP support */
|
||||
#define sbi_platform_has_pmp(__p) \
|
||||
((__p)->features & SBI_PLATFORM_HAS_PMP)
|
||||
#define sbi_platform_has_pmp(__p) ((__p)->features & SBI_PLATFORM_HAS_PMP)
|
||||
/** Check whether the platform supports scounteren CSR */
|
||||
#define sbi_platform_has_scounteren(__p) \
|
||||
#define sbi_platform_has_scounteren(__p) \
|
||||
((__p)->features & SBI_PLATFORM_HAS_SCOUNTEREN)
|
||||
/** Check whether the platform supports mcounteren CSR */
|
||||
#define sbi_platform_has_mcounteren(__p) \
|
||||
#define sbi_platform_has_mcounteren(__p) \
|
||||
((__p)->features & SBI_PLATFORM_HAS_MCOUNTEREN)
|
||||
/** Check whether the platform supports fault delegation */
|
||||
#define sbi_platform_has_mfaults_delegation(__p) \
|
||||
#define sbi_platform_has_mfaults_delegation(__p) \
|
||||
((__p)->features & SBI_PLATFORM_HAS_MFAULTS_DELEGATION)
|
||||
|
||||
/**
|
||||
@@ -140,7 +191,7 @@ struct sbi_platform {
|
||||
*
|
||||
* @return pointer to platform name on success and NULL on failure
|
||||
*/
|
||||
static inline const char *sbi_platform_name(struct sbi_platform *plat)
|
||||
static inline const char *sbi_platform_name(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat)
|
||||
return plat->name;
|
||||
@@ -155,7 +206,7 @@ static inline const char *sbi_platform_name(struct sbi_platform *plat)
|
||||
*
|
||||
* @return TRUE if HART is disabled and FALSE otherwise
|
||||
*/
|
||||
static inline bool sbi_platform_hart_disabled(struct sbi_platform *plat,
|
||||
static inline bool sbi_platform_hart_disabled(const struct sbi_platform *plat,
|
||||
u32 hartid)
|
||||
{
|
||||
if (plat && (plat->disabled_hart_mask & (1 << hartid)))
|
||||
@@ -163,6 +214,22 @@ static inline bool sbi_platform_hart_disabled(struct sbi_platform *plat,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 && plat->tlb_range_flush_limit)
|
||||
return plat->tlb_range_flush_limit;
|
||||
return SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total number of HARTs supported by the platform
|
||||
*
|
||||
@@ -170,7 +237,7 @@ static inline bool sbi_platform_hart_disabled(struct sbi_platform *plat,
|
||||
*
|
||||
* @return total number of HARTs
|
||||
*/
|
||||
static inline u32 sbi_platform_hart_count(struct sbi_platform *plat)
|
||||
static inline u32 sbi_platform_hart_count(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat)
|
||||
return plat->hart_count;
|
||||
@@ -184,7 +251,7 @@ static inline u32 sbi_platform_hart_count(struct sbi_platform *plat)
|
||||
*
|
||||
* @return stack size in bytes
|
||||
*/
|
||||
static inline u32 sbi_platform_hart_stack_size(struct sbi_platform *plat)
|
||||
static inline u32 sbi_platform_hart_stack_size(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat)
|
||||
return plat->hart_stack_size;
|
||||
@@ -199,11 +266,11 @@ static inline u32 sbi_platform_hart_stack_size(struct sbi_platform *plat)
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_early_init(struct sbi_platform *plat,
|
||||
static inline int sbi_platform_early_init(const struct sbi_platform *plat,
|
||||
bool cold_boot)
|
||||
{
|
||||
if (plat && plat->early_init)
|
||||
return plat->early_init(cold_boot);
|
||||
if (plat && sbi_platform_ops(plat)->early_init)
|
||||
return sbi_platform_ops(plat)->early_init(cold_boot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -215,11 +282,11 @@ static inline int sbi_platform_early_init(struct sbi_platform *plat,
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_final_init(struct sbi_platform *plat,
|
||||
static inline int sbi_platform_final_init(const struct sbi_platform *plat,
|
||||
bool cold_boot)
|
||||
{
|
||||
if (plat && plat->final_init)
|
||||
return plat->final_init(cold_boot);
|
||||
if (plat && sbi_platform_ops(plat)->final_init)
|
||||
return sbi_platform_ops(plat)->final_init(cold_boot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -231,11 +298,11 @@ static inline int sbi_platform_final_init(struct sbi_platform *plat,
|
||||
*
|
||||
* @return number of PMP regions
|
||||
*/
|
||||
static inline u32 sbi_platform_pmp_region_count(struct sbi_platform *plat,
|
||||
static inline u32 sbi_platform_pmp_region_count(const struct sbi_platform *plat,
|
||||
u32 hartid)
|
||||
{
|
||||
if (plat && plat->pmp_region_count)
|
||||
return plat->pmp_region_count(hartid);
|
||||
if (plat && sbi_platform_ops(plat)->pmp_region_count)
|
||||
return sbi_platform_ops(plat)->pmp_region_count(hartid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -252,14 +319,14 @@ static inline u32 sbi_platform_pmp_region_count(struct sbi_platform *plat,
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_pmp_region_info(struct sbi_platform *plat,
|
||||
u32 hartid, u32 index,
|
||||
ulong *prot, ulong *addr,
|
||||
ulong *log2size)
|
||||
static inline int sbi_platform_pmp_region_info(const struct sbi_platform *plat,
|
||||
u32 hartid, u32 index,
|
||||
ulong *prot, ulong *addr,
|
||||
ulong *log2size)
|
||||
{
|
||||
if (plat && plat->pmp_region_info)
|
||||
return plat->pmp_region_info(hartid, index,
|
||||
prot, addr, log2size);
|
||||
if (plat && sbi_platform_ops(plat)->pmp_region_info)
|
||||
return sbi_platform_ops(plat)->pmp_region_info(hartid, index, prot, addr,
|
||||
log2size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -269,11 +336,11 @@ static inline int sbi_platform_pmp_region_info(struct sbi_platform *plat,
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param ch character to write
|
||||
*/
|
||||
static inline void sbi_platform_console_putc(struct sbi_platform *plat,
|
||||
char ch)
|
||||
static inline void sbi_platform_console_putc(const struct sbi_platform *plat,
|
||||
char ch)
|
||||
{
|
||||
if (plat && plat->console_putc)
|
||||
plat->console_putc(ch);
|
||||
if (plat && sbi_platform_ops(plat)->console_putc)
|
||||
sbi_platform_ops(plat)->console_putc(ch);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,11 +350,11 @@ static inline void sbi_platform_console_putc(struct sbi_platform *plat,
|
||||
*
|
||||
* @return character read from console input
|
||||
*/
|
||||
static inline char sbi_platform_console_getc(struct sbi_platform *plat)
|
||||
static inline int sbi_platform_console_getc(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && plat->console_getc)
|
||||
return plat->console_getc();
|
||||
return 0;
|
||||
if (plat && sbi_platform_ops(plat)->console_getc)
|
||||
return sbi_platform_ops(plat)->console_getc();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,10 +364,10 @@ static inline char sbi_platform_console_getc(struct sbi_platform *plat)
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_console_init(struct sbi_platform *plat)
|
||||
static inline int sbi_platform_console_init(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && plat->console_init)
|
||||
return plat->console_init();
|
||||
if (plat && sbi_platform_ops(plat)->console_init)
|
||||
return sbi_platform_ops(plat)->console_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -312,11 +379,11 @@ static inline int sbi_platform_console_init(struct sbi_platform *plat)
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_irqchip_init(struct sbi_platform *plat,
|
||||
static inline int sbi_platform_irqchip_init(const struct sbi_platform *plat,
|
||||
bool cold_boot)
|
||||
{
|
||||
if (plat && plat->irqchip_init)
|
||||
return plat->irqchip_init(cold_boot);
|
||||
if (plat && sbi_platform_ops(plat)->irqchip_init)
|
||||
return sbi_platform_ops(plat)->irqchip_init(cold_boot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -326,24 +393,11 @@ static inline int sbi_platform_irqchip_init(struct sbi_platform *plat,
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param target_hart HART ID of IPI target
|
||||
*/
|
||||
static inline void sbi_platform_ipi_send(struct sbi_platform *plat,
|
||||
static inline void sbi_platform_ipi_send(const struct sbi_platform *plat,
|
||||
u32 target_hart)
|
||||
{
|
||||
if (plat && plat->ipi_send)
|
||||
plat->ipi_send(target_hart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for target HART to acknowledge IPI
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param target_hart HART ID of IPI target
|
||||
*/
|
||||
static inline void sbi_platform_ipi_sync(struct sbi_platform *plat,
|
||||
u32 target_hart)
|
||||
{
|
||||
if (plat && plat->ipi_sync)
|
||||
plat->ipi_sync(target_hart);
|
||||
if (plat && sbi_platform_ops(plat)->ipi_send)
|
||||
sbi_platform_ops(plat)->ipi_send(target_hart);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,11 +406,11 @@ static inline void sbi_platform_ipi_sync(struct sbi_platform *plat,
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param target_hart HART ID of IPI target
|
||||
*/
|
||||
static inline void sbi_platform_ipi_clear(struct sbi_platform *plat,
|
||||
static inline void sbi_platform_ipi_clear(const struct sbi_platform *plat,
|
||||
u32 target_hart)
|
||||
{
|
||||
if (plat && plat->ipi_clear)
|
||||
plat->ipi_clear(target_hart);
|
||||
if (plat && sbi_platform_ops(plat)->ipi_clear)
|
||||
sbi_platform_ops(plat)->ipi_clear(target_hart);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,11 +421,11 @@ static inline void sbi_platform_ipi_clear(struct sbi_platform *plat,
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_ipi_init(struct sbi_platform *plat,
|
||||
static inline int sbi_platform_ipi_init(const struct sbi_platform *plat,
|
||||
bool cold_boot)
|
||||
{
|
||||
if (plat && plat->ipi_init)
|
||||
return plat->ipi_init(cold_boot);
|
||||
if (plat && sbi_platform_ops(plat)->ipi_init)
|
||||
return sbi_platform_ops(plat)->ipi_init(cold_boot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -380,12 +434,12 @@ static inline int sbi_platform_ipi_init(struct sbi_platform *plat,
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
*
|
||||
* @return 64bit timer value
|
||||
* @return 64-bit timer value
|
||||
*/
|
||||
static inline u64 sbi_platform_timer_value(struct sbi_platform *plat)
|
||||
static inline u64 sbi_platform_timer_value(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && plat->timer_value)
|
||||
return plat->timer_value();
|
||||
if (plat && sbi_platform_ops(plat)->timer_value)
|
||||
return sbi_platform_ops(plat)->timer_value();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -395,11 +449,11 @@ static inline u64 sbi_platform_timer_value(struct sbi_platform *plat)
|
||||
* @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(struct sbi_platform *plat,
|
||||
u64 next_event)
|
||||
static inline void
|
||||
sbi_platform_timer_event_start(const struct sbi_platform *plat, u64 next_event)
|
||||
{
|
||||
if (plat && plat->timer_event_start)
|
||||
plat->timer_event_start(next_event);
|
||||
if (plat && sbi_platform_ops(plat)->timer_event_start)
|
||||
sbi_platform_ops(plat)->timer_event_start(next_event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -407,10 +461,11 @@ static inline void sbi_platform_timer_event_start(struct sbi_platform *plat,
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
*/
|
||||
static inline void sbi_platform_timer_event_stop(struct sbi_platform *plat)
|
||||
static inline void
|
||||
sbi_platform_timer_event_stop(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && plat->timer_event_stop)
|
||||
plat->timer_event_stop();
|
||||
if (plat && sbi_platform_ops(plat)->timer_event_stop)
|
||||
sbi_platform_ops(plat)->timer_event_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -421,11 +476,11 @@ static inline void sbi_platform_timer_event_stop(struct sbi_platform *plat)
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_timer_init(struct sbi_platform *plat,
|
||||
static inline int sbi_platform_timer_init(const struct sbi_platform *plat,
|
||||
bool cold_boot)
|
||||
{
|
||||
if (plat && plat->timer_init)
|
||||
return plat->timer_init(cold_boot);
|
||||
if (plat && sbi_platform_ops(plat)->timer_init)
|
||||
return sbi_platform_ops(plat)->timer_init(cold_boot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -437,11 +492,11 @@ static inline int sbi_platform_timer_init(struct sbi_platform *plat,
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_system_reboot(struct sbi_platform *plat,
|
||||
static inline int sbi_platform_system_reboot(const struct sbi_platform *plat,
|
||||
u32 type)
|
||||
{
|
||||
if (plat && plat->system_reboot)
|
||||
return plat->system_reboot(type);
|
||||
if (plat && sbi_platform_ops(plat)->system_reboot)
|
||||
return sbi_platform_ops(plat)->system_reboot(type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -453,14 +508,62 @@ static inline int sbi_platform_system_reboot(struct sbi_platform *plat,
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_system_shutdown(struct sbi_platform *plat,
|
||||
static inline int sbi_platform_system_shutdown(const struct sbi_platform *plat,
|
||||
u32 type)
|
||||
{
|
||||
if (plat && plat->system_shutdown)
|
||||
return plat->system_shutdown(type);
|
||||
if (plat && sbi_platform_ops(plat)->system_shutdown)
|
||||
return sbi_platform_ops(plat)->system_shutdown(type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 args pointer to arguments passed by the caller
|
||||
* @param out_value output value that can be filled the callee
|
||||
* @param out_tcause trap cause that can be filled the callee
|
||||
* @param out_tvalue possible trap value that can be filled 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,
|
||||
unsigned long *args,
|
||||
unsigned long *out_value,
|
||||
unsigned long *out_tcause,
|
||||
unsigned long *out_tval)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->vendor_ext_provider) {
|
||||
return sbi_platform_ops(plat)->vendor_ext_provider(extid,
|
||||
funcid, args,
|
||||
out_value,
|
||||
out_tcause,
|
||||
out_tval);
|
||||
}
|
||||
|
||||
return SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@@ -12,6 +12,8 @@
|
||||
|
||||
#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 */
|
||||
@@ -28,16 +30,21 @@
|
||||
#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 ipi_type member in sbi_scratch */
|
||||
#define SBI_SCRATCH_IPI_TYPE_OFFSET (8 * __SIZEOF_POINTER__)
|
||||
/** Offset of tmp0 member in sbi_scratch */
|
||||
#define SBI_SCRATCH_TMP0_OFFSET (9 * __SIZEOF_POINTER__)
|
||||
/** Maximum size of sbi_scratch */
|
||||
#define SBI_SCRATCH_SIZE 256
|
||||
#define SBI_SCRATCH_TMP0_OFFSET (8 * __SIZEOF_POINTER__)
|
||||
/** Offset of options member in sbi_scratch */
|
||||
#define SBI_SCRATCH_OPTIONS_OFFSET (9 * __SIZEOF_POINTER__)
|
||||
/** Offset of extra space in sbi_scratch */
|
||||
#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (10 * __SIZEOF_POINTER__)
|
||||
/** Maximum size of sbi_scratch and sbi_ipi_data */
|
||||
#define SBI_SCRATCH_SIZE (64 * __SIZEOF_POINTER__)
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
|
||||
/** Representation of per-HART scratch space */
|
||||
struct sbi_scratch {
|
||||
@@ -57,19 +64,44 @@ struct sbi_scratch {
|
||||
unsigned long platform_addr;
|
||||
/** Address of HART ID to sbi_scratch conversion function */
|
||||
unsigned long hartid_to_scratch;
|
||||
/** IPI type (or flags) */
|
||||
unsigned long ipi_type;
|
||||
/** Temporary storage */
|
||||
unsigned long tmp0;
|
||||
/** Options for OpenSBI library */
|
||||
unsigned long options;
|
||||
} __packed;
|
||||
|
||||
/** 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))
|
||||
#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))
|
||||
#define sbi_scratch_thishart_arg1_ptr() \
|
||||
((void *)(sbi_scratch_thishart_ptr()->next_arg1))
|
||||
|
||||
/** 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))
|
||||
|
||||
#endif
|
||||
|
||||
|
39
include/sbi/sbi_string.h
Normal file
39
include/sbi/sbi_string.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
int sbi_strcmp(const char *a, const char *b);
|
||||
|
||||
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
|
@@ -18,10 +18,10 @@ int sbi_system_early_init(struct sbi_scratch *scratch, bool cold_boot);
|
||||
|
||||
int sbi_system_final_init(struct sbi_scratch *scratch, bool cold_boot);
|
||||
|
||||
void __attribute__((noreturn)) sbi_system_reboot(struct sbi_scratch *scratch,
|
||||
u32 type);
|
||||
void __attribute__((noreturn))
|
||||
sbi_system_reboot(struct sbi_scratch *scratch, u32 type);
|
||||
|
||||
void __attribute__((noreturn)) sbi_system_shutdown(struct sbi_scratch *scratch,
|
||||
u32 type);
|
||||
void __attribute__((noreturn))
|
||||
sbi_system_shutdown(struct sbi_scratch *scratch, u32 type);
|
||||
|
||||
#endif
|
||||
|
@@ -16,6 +16,14 @@ struct sbi_scratch;
|
||||
|
||||
u64 sbi_timer_value(struct sbi_scratch *scratch);
|
||||
|
||||
u64 sbi_timer_virt_value(struct sbi_scratch *scratch);
|
||||
|
||||
u64 sbi_timer_get_delta(struct sbi_scratch *scratch);
|
||||
|
||||
void sbi_timer_set_delta(struct sbi_scratch *scratch, ulong delta);
|
||||
|
||||
void sbi_timer_set_delta_upper(struct sbi_scratch *scratch, ulong delta_upper);
|
||||
|
||||
void sbi_timer_event_stop(struct sbi_scratch *scratch);
|
||||
|
||||
void sbi_timer_event_start(struct sbi_scratch *scratch, u64 next_event);
|
||||
|
51
include/sbi/sbi_tlb.h
Normal file
51
include/sbi/sbi_tlb.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#define SBI_TLB_FLUSH_ALL ((unsigned long)-1)
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#define SBI_TLB_FIFO_NUM_ENTRIES 8
|
||||
|
||||
enum sbi_tlb_info_types {
|
||||
SBI_TLB_FLUSH_VMA,
|
||||
SBI_TLB_FLUSH_VMA_ASID,
|
||||
SBI_TLB_FLUSH_VMA_VMID,
|
||||
SBI_ITLB_FLUSH
|
||||
};
|
||||
|
||||
struct sbi_scratch;
|
||||
|
||||
struct sbi_tlb_info {
|
||||
unsigned long start;
|
||||
unsigned long size;
|
||||
unsigned long asid;
|
||||
unsigned long type;
|
||||
unsigned long shart_mask;
|
||||
};
|
||||
|
||||
#define SBI_TLB_INFO_SIZE sizeof(struct sbi_tlb_info)
|
||||
|
||||
int sbi_tlb_fifo_update(struct sbi_scratch *scratch, u32 hartid, void *data);
|
||||
|
||||
void sbi_tlb_fifo_process(struct sbi_scratch *scratch);
|
||||
|
||||
int sbi_tlb_fifo_init(struct sbi_scratch *scratch, bool cold_boot);
|
||||
|
||||
void sbi_tlb_fifo_sync(struct sbi_scratch *scratch);
|
||||
|
||||
#endif
|
@@ -10,6 +10,8 @@
|
||||
#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 */
|
||||
@@ -78,14 +80,17 @@
|
||||
#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 34
|
||||
#define SBI_TRAP_REGS_last 35
|
||||
|
||||
/* 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__)
|
||||
#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)
|
||||
#define SBI_TRAP_REGS_SIZE SBI_TRAP_REGS_OFFSET(last)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
@@ -161,16 +166,16 @@ struct sbi_trap_regs {
|
||||
unsigned long mepc;
|
||||
/** mstatus register state */
|
||||
unsigned long mstatus;
|
||||
/** mstatusH register state (only for 32-bit) */
|
||||
unsigned long mstatusH;
|
||||
} __packed;
|
||||
|
||||
struct sbi_scratch;
|
||||
|
||||
int sbi_trap_redirect(struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch,
|
||||
int sbi_trap_redirect(struct sbi_trap_regs *regs, struct sbi_scratch *scratch,
|
||||
ulong epc, ulong cause, ulong tval);
|
||||
|
||||
void sbi_trap_handler(struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch);
|
||||
void sbi_trap_handler(struct sbi_trap_regs *regs, struct sbi_scratch *scratch);
|
||||
|
||||
#endif
|
||||
|
||||
|
@@ -10,6 +10,8 @@
|
||||
#ifndef __SBI_TYPES_H__
|
||||
#define __SBI_TYPES_H__
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
typedef char s8;
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned char uint8_t;
|
||||
@@ -58,4 +60,6 @@ typedef unsigned long physical_size_t;
|
||||
#define __packed __attribute__((packed))
|
||||
#define __noreturn __attribute__((noreturn))
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#endif
|
||||
|
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* 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/riscv_encoding.h>
|
||||
#include <sbi/sbi_bits.h>
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
#define DECLARE_UNPRIVILEGED_LOAD_FUNCTION(type, insn) \
|
||||
static inline type load_##type(const type *addr, ulong mepc) \
|
||||
{ \
|
||||
register ulong __mepc asm ("a2") = mepc; \
|
||||
register ulong __mstatus asm ("a3"); \
|
||||
type val; \
|
||||
asm ("csrrs %0, "STR(CSR_MSTATUS)", %3\n" \
|
||||
#insn " %1, %2\n" \
|
||||
"csrw "STR(CSR_MSTATUS)", %0" \
|
||||
: "+&r" (__mstatus), "=&r" (val) \
|
||||
: "m" (*addr), "r" (MSTATUS_MPRV), "r" (__mepc)); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
#define DECLARE_UNPRIVILEGED_STORE_FUNCTION(type, insn) \
|
||||
static inline void store_##type(type *addr, type val, ulong mepc) \
|
||||
{ \
|
||||
register ulong __mepc asm ("a2") = mepc; \
|
||||
register ulong __mstatus asm ("a3"); \
|
||||
asm volatile ("csrrs %0, "STR(CSR_MSTATUS)", %3\n" \
|
||||
#insn " %1, %2\n" \
|
||||
"csrw "STR(CSR_MSTATUS)", %0" \
|
||||
: "+&r" (__mstatus) \
|
||||
: "r" (val), "m" (*addr), "r" (MSTATUS_MPRV), "r" (__mepc)); \
|
||||
}
|
||||
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u8, lbu)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u16, lhu)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(s8, lb)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(s16, lh)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(s32, lw)
|
||||
DECLARE_UNPRIVILEGED_STORE_FUNCTION(u8, sb)
|
||||
DECLARE_UNPRIVILEGED_STORE_FUNCTION(u16, sh)
|
||||
DECLARE_UNPRIVILEGED_STORE_FUNCTION(u32, sw)
|
||||
#if __riscv_xlen == 64
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u32, lwu)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u64, ld)
|
||||
DECLARE_UNPRIVILEGED_STORE_FUNCTION(u64, sd)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(ulong, ld)
|
||||
#else
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u32, lw)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(ulong, lw)
|
||||
|
||||
static inline u64 load_u64(const u64 *addr, ulong mepc)
|
||||
{
|
||||
return load_u32((u32 *)addr, mepc)
|
||||
+ ((u64)load_u32((u32 *)addr + 1, mepc) << 32);
|
||||
}
|
||||
|
||||
static inline void store_u64(u64 *addr, u64 val, ulong mepc)
|
||||
{
|
||||
store_u32((u32 *)addr, val, mepc);
|
||||
store_u32((u32 *)addr + 1, val >> 32, mepc);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline ulong get_insn(ulong mepc, ulong *mstatus)
|
||||
{
|
||||
register ulong __mepc asm ("a2") = mepc;
|
||||
register ulong __mstatus asm ("a3");
|
||||
ulong val;
|
||||
#ifndef __riscv_compressed
|
||||
asm ("csrrs %[mstatus], "STR(CSR_MSTATUS)", %[mprv]\n"
|
||||
#if __riscv_xlen == 64
|
||||
STR(LWU) " %[insn], (%[addr])\n"
|
||||
#else
|
||||
STR(LW) " %[insn], (%[addr])\n"
|
||||
#endif
|
||||
"csrw "STR(CSR_MSTATUS)", %[mstatus]"
|
||||
: [mstatus] "+&r" (__mstatus), [insn] "=&r" (val)
|
||||
: [mprv] "r" (MSTATUS_MPRV | MSTATUS_MXR), [addr] "r" (__mepc));
|
||||
#else
|
||||
ulong rvc_mask = 3, tmp;
|
||||
asm ("csrrs %[mstatus], "STR(CSR_MSTATUS)", %[mprv]\n"
|
||||
"and %[tmp], %[addr], 2\n"
|
||||
"bnez %[tmp], 1f\n"
|
||||
#if __riscv_xlen == 64
|
||||
STR(LWU) " %[insn], (%[addr])\n"
|
||||
#else
|
||||
STR(LW) " %[insn], (%[addr])\n"
|
||||
#endif
|
||||
"and %[tmp], %[insn], %[rvc_mask]\n"
|
||||
"beq %[tmp], %[rvc_mask], 2f\n"
|
||||
"sll %[insn], %[insn], %[xlen_minus_16]\n"
|
||||
"srl %[insn], %[insn], %[xlen_minus_16]\n"
|
||||
"j 2f\n"
|
||||
"1:\n"
|
||||
"lhu %[insn], (%[addr])\n"
|
||||
"and %[tmp], %[insn], %[rvc_mask]\n"
|
||||
"bne %[tmp], %[rvc_mask], 2f\n"
|
||||
"lhu %[tmp], 2(%[addr])\n"
|
||||
"sll %[tmp], %[tmp], 16\n"
|
||||
"add %[insn], %[insn], %[tmp]\n"
|
||||
"2: csrw "STR(CSR_MSTATUS)", %[mstatus]"
|
||||
: [mstatus] "+&r" (__mstatus), [insn] "=&r" (val), [tmp] "=&r" (tmp)
|
||||
: [mprv] "r" (MSTATUS_MPRV | MSTATUS_MXR), [addr] "r" (__mepc),
|
||||
[rvc_mask] "r" (rvc_mask), [xlen_minus_16] "i" (__riscv_xlen - 16));
|
||||
#endif
|
||||
*mstatus = __mstatus;
|
||||
return val;
|
||||
}
|
||||
|
||||
#endif
|
@@ -10,7 +10,15 @@
|
||||
#ifndef __SBI_VERSION_H__
|
||||
#define __SBI_VERSION_H__
|
||||
|
||||
#define OPENSBI_VERSION_MAJOR 0
|
||||
#define OPENSBI_VERSION_MINOR 3
|
||||
#define OPENSBI_VERSION_MAJOR 0
|
||||
#define OPENSBI_VERSION_MINOR 5
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@@ -14,10 +14,12 @@
|
||||
|
||||
void plic_fdt_fixup(void *fdt, const char *compat);
|
||||
|
||||
int plic_warm_irqchip_init(u32 target_hart,
|
||||
int m_cntx_id, int s_cntx_id);
|
||||
int plic_warm_irqchip_init(u32 target_hart, int m_cntx_id, int s_cntx_id);
|
||||
|
||||
int plic_cold_irqchip_init(unsigned long base,
|
||||
u32 num_sources, u32 hart_count);
|
||||
int plic_cold_irqchip_init(unsigned long base, u32 num_sources, u32 hart_count);
|
||||
|
||||
void plic_set_thresh(u32 cntxid, u32 val);
|
||||
|
||||
void plic_set_ie(u32 cntxid, u32 word_index, u32 val);
|
||||
|
||||
#endif
|
@@ -14,9 +14,8 @@
|
||||
|
||||
void sifive_uart_putc(char ch);
|
||||
|
||||
char sifive_uart_getc(void);
|
||||
int sifive_uart_getc(void);
|
||||
|
||||
int sifive_uart_init(unsigned long base,
|
||||
u32 in_freq, u32 baudrate);
|
||||
int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate);
|
||||
|
||||
#endif
|
@@ -14,10 +14,9 @@
|
||||
|
||||
void uart8250_putc(char ch);
|
||||
|
||||
char uart8250_getc(void);
|
||||
int uart8250_getc(void);
|
||||
|
||||
int uart8250_init(unsigned long base,
|
||||
u32 in_freq, u32 baudrate,
|
||||
u32 reg_shift, u32 reg_width);
|
||||
int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
|
||||
u32 reg_width);
|
||||
|
||||
#endif
|
@@ -1,25 +0,0 @@
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
# Copyright (c) 2019 Western Digital Corporation or its affiliates.
|
||||
#
|
||||
# Authors:
|
||||
# Anup Patel <anup.patel@wdc.com>
|
||||
#
|
||||
|
||||
lib-objs-y += riscv_asm.o
|
||||
lib-objs-y += riscv_atomic.o
|
||||
lib-objs-y += riscv_hardfp.o
|
||||
lib-objs-y += riscv_locks.o
|
||||
|
||||
lib-objs-y += sbi_console.o
|
||||
lib-objs-y += sbi_ecall.o
|
||||
lib-objs-y += sbi_emulate_csr.o
|
||||
lib-objs-y += sbi_hart.o
|
||||
lib-objs-y += sbi_illegal_insn.o
|
||||
lib-objs-y += sbi_init.o
|
||||
lib-objs-y += sbi_ipi.o
|
||||
lib-objs-y += sbi_misaligned_ldst.o
|
||||
lib-objs-y += sbi_system.o
|
||||
lib-objs-y += sbi_timer.o
|
||||
lib-objs-y += sbi_trap.o
|
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
* 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_types.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_atomic.h>
|
||||
#include <sbi/riscv_barrier.h>
|
||||
#include <sbi/sbi_bits.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 __xchg(ptr, new, size) \
|
||||
({ \
|
||||
__typeof__(ptr) __ptr = (ptr); \
|
||||
__typeof__(*(ptr)) __new = (new); \
|
||||
__typeof__(*(ptr)) __ret; \
|
||||
register unsigned int __rc; \
|
||||
switch (size) { \
|
||||
case 4: \
|
||||
__asm__ __volatile__ ( \
|
||||
"0: lr.w %0, %2\n" \
|
||||
" sc.w.rl %1, %z3, %2\n" \
|
||||
" bnez %1, 0b\n" \
|
||||
" fence rw, rw\n" \
|
||||
: "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
|
||||
: "rJ" (__new) \
|
||||
: "memory"); \
|
||||
break; \
|
||||
case 8: \
|
||||
__asm__ __volatile__ ( \
|
||||
"0: lr.d %0, %2\n" \
|
||||
" sc.d.rl %1, %z3, %2\n" \
|
||||
" bnez %1, 0b\n" \
|
||||
" fence rw, rw\n" \
|
||||
: "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
|
||||
: "rJ" (__new) \
|
||||
: "memory"); \
|
||||
break; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#define xchg(ptr, n) \
|
||||
({ \
|
||||
__typeof__(*(ptr)) _n_ = (n); \
|
||||
(__typeof__(*(ptr))) __xchg((ptr), _n_, sizeof(*(ptr))); \
|
||||
})
|
||||
|
||||
#define __cmpxchg(ptr, old, new, size) \
|
||||
({ \
|
||||
__typeof__(ptr) __ptr = (ptr); \
|
||||
__typeof__(*(ptr)) __old = (old); \
|
||||
__typeof__(*(ptr)) __new = (new); \
|
||||
__typeof__(*(ptr)) __ret; \
|
||||
register unsigned int __rc; \
|
||||
switch (size) { \
|
||||
case 4: \
|
||||
__asm__ __volatile__ ( \
|
||||
"0: lr.w %0, %2\n" \
|
||||
" bne %0, %z3, 1f\n" \
|
||||
" sc.w.rl %1, %z4, %2\n" \
|
||||
" bnez %1, 0b\n" \
|
||||
" fence rw, rw\n" \
|
||||
"1:\n" \
|
||||
: "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
|
||||
: "rJ" (__old), "rJ" (__new) \
|
||||
: "memory"); \
|
||||
break; \
|
||||
case 8: \
|
||||
__asm__ __volatile__ ( \
|
||||
"0: lr.d %0, %2\n" \
|
||||
" bne %0, %z3, 1f\n" \
|
||||
" sc.d.rl %1, %z4, %2\n" \
|
||||
" bnez %1, 0b\n" \
|
||||
" fence rw, rw\n" \
|
||||
"1:\n" \
|
||||
: "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
|
||||
: "rJ" (__old), "rJ" (__new) \
|
||||
: "memory"); \
|
||||
break; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#define cmpxchg(ptr, o, n) \
|
||||
({ \
|
||||
__typeof__(*(ptr)) _o_ = (o); \
|
||||
__typeof__(*(ptr)) _n_ = (n); \
|
||||
(__typeof__(*(ptr))) __cmpxchg((ptr), \
|
||||
_o_, _n_, sizeof(*(ptr))); \
|
||||
})
|
||||
|
||||
long arch_atomic_cmpxchg(atomic_t *atom, long oldval, long newval)
|
||||
{
|
||||
#ifdef __riscv_atomic
|
||||
return __sync_val_compare_and_swap(&atom->counter, oldval, newval);
|
||||
#else
|
||||
return cmpxchg(&atom->counter, oldval, newval);
|
||||
#endif
|
||||
}
|
||||
|
||||
long arch_atomic_xchg(atomic_t *atom, long newval)
|
||||
{
|
||||
/* Atomically set new value and return old value. */
|
||||
#ifdef __riscv_atomic
|
||||
/*
|
||||
* The name of GCC built-in macro __sync_lock_test_and_set()
|
||||
* is misleading. A more appropriate name for GCC built-in
|
||||
* macro would be __sync_val_exchange().
|
||||
*/
|
||||
return __sync_lock_test_and_set(&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
|
||||
/*
|
||||
* The name of GCC built-in macro __sync_lock_test_and_set()
|
||||
* is misleading. A more appropriate name for GCC built-in
|
||||
* macro would be __sync_val_exchange().
|
||||
*/
|
||||
return __sync_lock_test_and_set(ptr, newval);
|
||||
#else
|
||||
return xchg(ptr, newval);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (BITS_PER_LONG == 64)
|
||||
#define __AMO(op) "amo" #op ".d"
|
||||
#elif (BITS_PER_LONG == 32)
|
||||
#define __AMO(op) "amo" #op ".w"
|
||||
#else
|
||||
#error "Unexpected BITS_PER_LONG"
|
||||
#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);
|
||||
}
|
30
lib/sbi/objects.mk
Normal file
30
lib/sbi/objects.mk
Normal file
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# 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 += riscv_unpriv.o
|
||||
|
||||
libsbi-objs-y += sbi_console.o
|
||||
libsbi-objs-y += sbi_ecall.o
|
||||
libsbi-objs-y += sbi_emulate_csr.o
|
||||
libsbi-objs-y += sbi_fifo.o
|
||||
libsbi-objs-y += sbi_hart.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_scratch.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_string.o
|
@@ -163,28 +163,26 @@ static unsigned long ctz(unsigned long x)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pmp_set(unsigned int n, unsigned long prot,
|
||||
unsigned long addr, unsigned long log2len)
|
||||
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)
|
||||
if (n >= PMP_COUNT || log2len > __riscv_xlen || log2len < PMP_SHIFT)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* calculate PMP register and offset */
|
||||
/* calculate PMP register and offset */
|
||||
#if __riscv_xlen == 32
|
||||
pmpcfg_csr = CSR_PMPCFG0 + (n >> 2);
|
||||
pmpcfg_csr = CSR_PMPCFG0 + (n >> 2);
|
||||
pmpcfg_shift = (n & 3) << 3;
|
||||
#elif __riscv_xlen == 64
|
||||
pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
|
||||
pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
|
||||
pmpcfg_shift = (n & 7) << 3;
|
||||
#else
|
||||
pmpcfg_csr = -1;
|
||||
pmpcfg_csr = -1;
|
||||
pmpcfg_shift = -1;
|
||||
#endif
|
||||
pmpaddr_csr = CSR_PMPADDR0 + n;
|
||||
@@ -194,7 +192,7 @@ int pmp_set(unsigned int n, unsigned long prot,
|
||||
/* encode PMP config */
|
||||
prot |= (log2len == PMP_SHIFT) ? PMP_A_NA4 : PMP_A_NAPOT;
|
||||
cfgmask = ~(0xff << pmpcfg_shift);
|
||||
pmpcfg = (csr_read_num(pmpcfg_csr) & cfgmask);
|
||||
pmpcfg = (csr_read_num(pmpcfg_csr) & cfgmask);
|
||||
pmpcfg |= ((prot << pmpcfg_shift) & ~cfgmask);
|
||||
|
||||
/* encode PMP address */
|
||||
@@ -205,7 +203,7 @@ int pmp_set(unsigned int n, unsigned long prot,
|
||||
pmpaddr = -1UL;
|
||||
} else {
|
||||
addrmask = (1UL << (log2len - PMP_SHIFT)) - 1;
|
||||
pmpaddr = ((addr >> PMP_SHIFT) & ~addrmask);
|
||||
pmpaddr = ((addr >> PMP_SHIFT) & ~addrmask);
|
||||
pmpaddr |= (addrmask >> 1);
|
||||
}
|
||||
}
|
||||
@@ -217,28 +215,27 @@ int pmp_set(unsigned int n, unsigned long prot,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pmp_get(unsigned int n, unsigned long *prot_out,
|
||||
unsigned long *addr_out, unsigned long *log2len_out)
|
||||
int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
|
||||
unsigned long *log2len_out)
|
||||
{
|
||||
int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr;
|
||||
unsigned long cfgmask, pmpcfg, prot;
|
||||
unsigned long t1, addr, log2len;
|
||||
|
||||
/* check parameters */
|
||||
if (n >= PMP_COUNT || !prot_out ||
|
||||
!addr_out || !log2len_out)
|
||||
if (n >= PMP_COUNT || !prot_out || !addr_out || !log2len_out)
|
||||
return SBI_EINVAL;
|
||||
*prot_out = *addr_out = *log2len_out = 0;
|
||||
|
||||
/* calculate PMP register and offset */
|
||||
#if __riscv_xlen == 32
|
||||
pmpcfg_csr = CSR_PMPCFG0 + (n >> 2);
|
||||
pmpcfg_csr = CSR_PMPCFG0 + (n >> 2);
|
||||
pmpcfg_shift = (n & 3) << 3;
|
||||
#elif __riscv_xlen == 64
|
||||
pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
|
||||
pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
|
||||
pmpcfg_shift = (n & 7) << 3;
|
||||
#else
|
||||
pmpcfg_csr = -1;
|
||||
pmpcfg_csr = -1;
|
||||
pmpcfg_shift = -1;
|
||||
#endif
|
||||
pmpaddr_csr = CSR_PMPADDR0 + n;
|
||||
@@ -247,28 +244,28 @@ int pmp_get(unsigned int n, unsigned long *prot_out,
|
||||
|
||||
/* decode PMP config */
|
||||
cfgmask = (0xff << pmpcfg_shift);
|
||||
pmpcfg = csr_read_num(pmpcfg_csr) & cfgmask;
|
||||
prot = pmpcfg >> 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;
|
||||
addr = 0;
|
||||
log2len = __riscv_xlen;
|
||||
} else {
|
||||
t1 = ctz(~addr);
|
||||
addr = (addr & ~((1UL << t1) - 1)) << PMP_SHIFT;
|
||||
t1 = ctz(~addr);
|
||||
addr = (addr & ~((1UL << t1) - 1)) << PMP_SHIFT;
|
||||
log2len = (t1 + PMP_SHIFT + 1);
|
||||
}
|
||||
} else {
|
||||
addr = csr_read_num(pmpaddr_csr) << PMP_SHIFT;
|
||||
addr = csr_read_num(pmpaddr_csr) << PMP_SHIFT;
|
||||
log2len = PMP_SHIFT;
|
||||
}
|
||||
|
||||
/* return details */
|
||||
*prot_out = prot;
|
||||
*addr_out = addr;
|
||||
*prot_out = prot;
|
||||
*addr_out = addr;
|
||||
*log2len_out = log2len;
|
||||
|
||||
return 0;
|
256
lib/sbi/riscv_atomic.c
Normal file
256
lib/sbi/riscv_atomic.c
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* 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_types.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_atomic.h>
|
||||
#include <sbi/riscv_barrier.h>
|
||||
#include <sbi/sbi_bits.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))) __xchg((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 arch_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 arch_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 (BITS_PER_LONG == 64)
|
||||
#define __AMO(op) "amo" #op ".d"
|
||||
#elif (BITS_PER_LONG == 32)
|
||||
#define __AMO(op) "amo" #op ".w"
|
||||
#else
|
||||
#error "Unexpected BITS_PER_LONG"
|
||||
#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);
|
||||
}
|
@@ -19,11 +19,10 @@ 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)
|
||||
__asm__ __volatile__(
|
||||
" amoswap.w %0, %2, %1\n" RISCV_ACQUIRE_BARRIER
|
||||
: "=r"(busy), "+A"(lock->lock)
|
||||
: "r"(tmp)
|
||||
: "memory");
|
||||
|
||||
return !busy;
|
158
lib/sbi/riscv_unpriv.c
Normal file
158
lib/sbi/riscv_unpriv.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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/riscv_unpriv.h>
|
||||
#include <sbi/sbi_bits.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
|
||||
#define DEFINE_UNPRIVILEGED_LOAD_FUNCTION(type, insn, insnlen) \
|
||||
type load_##type(const type *addr, \
|
||||
struct sbi_scratch *scratch, \
|
||||
struct unpriv_trap *trap) \
|
||||
{ \
|
||||
register ulong __mstatus asm("a2"); \
|
||||
type val = 0; \
|
||||
trap->ilen = insnlen; \
|
||||
trap->cause = 0; \
|
||||
trap->tval = 0; \
|
||||
sbi_hart_set_trap_info(scratch, trap); \
|
||||
asm volatile( \
|
||||
"csrrs %0, " STR(CSR_MSTATUS) ", %3\n" \
|
||||
#insn " %1, %2\n" \
|
||||
"csrw " STR(CSR_MSTATUS) ", %0" \
|
||||
: "+&r"(__mstatus), "=&r"(val) \
|
||||
: "m"(*addr), "r"(MSTATUS_MPRV)); \
|
||||
sbi_hart_set_trap_info(scratch, NULL); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
#define DEFINE_UNPRIVILEGED_STORE_FUNCTION(type, insn, insnlen) \
|
||||
void store_##type(type *addr, type val, \
|
||||
struct sbi_scratch *scratch, \
|
||||
struct unpriv_trap *trap) \
|
||||
{ \
|
||||
register ulong __mstatus asm("a3"); \
|
||||
trap->ilen = insnlen; \
|
||||
trap->cause = 0; \
|
||||
trap->tval = 0; \
|
||||
sbi_hart_set_trap_info(scratch, trap); \
|
||||
asm volatile( \
|
||||
"csrrs %0, " STR(CSR_MSTATUS) ", %3\n" \
|
||||
#insn " %1, %2\n" \
|
||||
"csrw " STR(CSR_MSTATUS) ", %0" \
|
||||
: "+&r"(__mstatus) \
|
||||
: "r"(val), "m"(*addr), "r"(MSTATUS_MPRV)); \
|
||||
sbi_hart_set_trap_info(scratch, NULL); \
|
||||
}
|
||||
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u8, lbu, 4)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u16, lhu, 4)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s8, lb, 4)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s16, lh, 4)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s32, lw, 2)
|
||||
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u8, sb, 4)
|
||||
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u16, sh, 4)
|
||||
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u32, sw, 2)
|
||||
#if __riscv_xlen == 64
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lwu, 4)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u64, ld, 2)
|
||||
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u64, sd, 2)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, ld, 2)
|
||||
#else
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lw, 2)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, lw, 2)
|
||||
|
||||
u64 load_u64(const u64 *addr,
|
||||
struct sbi_scratch *scratch, struct unpriv_trap *trap)
|
||||
{
|
||||
u64 ret = load_u32((u32 *)addr, scratch, trap);
|
||||
|
||||
if (trap->cause)
|
||||
return 0;
|
||||
ret |= ((u64)load_u32((u32 *)addr + 1, scratch, trap) << 32);
|
||||
if (trap->cause)
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void store_u64(u64 *addr, u64 val,
|
||||
struct sbi_scratch *scratch, struct unpriv_trap *trap)
|
||||
{
|
||||
store_u32((u32 *)addr, val, scratch, trap);
|
||||
if (trap->cause)
|
||||
return;
|
||||
|
||||
store_u32((u32 *)addr + 1, val >> 32, scratch, trap);
|
||||
if (trap->cause)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ulong get_insn(ulong mepc, bool virt, struct sbi_scratch *scratch,
|
||||
struct unpriv_trap *trap)
|
||||
{
|
||||
ulong __mstatus = 0, __vsstatus = 0, val = 0;
|
||||
#ifdef __riscv_compressed
|
||||
ulong rvc_mask = 3, tmp;
|
||||
#endif
|
||||
|
||||
trap->ilen = 4;
|
||||
trap->cause = 0;
|
||||
trap->tval = 0;
|
||||
sbi_hart_set_trap_info(scratch, trap);
|
||||
|
||||
if (virt)
|
||||
__vsstatus = csr_read_set(CSR_VSSTATUS, SSTATUS_MXR);
|
||||
|
||||
#ifndef __riscv_compressed
|
||||
asm("csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n"
|
||||
#if __riscv_xlen == 64
|
||||
STR(LWU) " %[insn], (%[addr])\n"
|
||||
#else
|
||||
STR(LW) " %[insn], (%[addr])\n"
|
||||
#endif
|
||||
"csrw " STR(CSR_MSTATUS) ", %[mstatus]"
|
||||
: [mstatus] "+&r"(__mstatus), [insn] "=&r"(val)
|
||||
: [mprv] "r"(MSTATUS_MPRV | MSTATUS_MXR), [addr] "r"(mepc));
|
||||
#else
|
||||
asm("csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n"
|
||||
"lhu %[insn], (%[addr])\n"
|
||||
"and %[tmp], %[insn], %[rvc_mask]\n"
|
||||
"bne %[tmp], %[rvc_mask], 2f\n"
|
||||
"lhu %[tmp], 2(%[addr])\n"
|
||||
"sll %[tmp], %[tmp], 16\n"
|
||||
"add %[insn], %[insn], %[tmp]\n"
|
||||
"2: csrw " STR(CSR_MSTATUS) ", %[mstatus]"
|
||||
: [mstatus] "+&r"(__mstatus), [insn] "=&r"(val), [tmp] "=&r"(tmp)
|
||||
: [mprv] "r"(MSTATUS_MPRV | MSTATUS_MXR), [addr] "r"(mepc),
|
||||
[rvc_mask] "r"(rvc_mask));
|
||||
#endif
|
||||
|
||||
if (virt)
|
||||
csr_write(CSR_VSSTATUS, __vsstatus);
|
||||
|
||||
sbi_hart_set_trap_info(scratch, NULL);
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return val;
|
||||
}
|
@@ -11,22 +11,19 @@
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/riscv_locks.h>
|
||||
|
||||
static struct sbi_platform *console_plat = NULL;
|
||||
static spinlock_t console_out_lock = SPIN_LOCK_INITIALIZER;
|
||||
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')) {
|
||||
if (((31 < c) && (c < 127)) || (c == '\f') || (c == '\r') ||
|
||||
(c == '\n') || (c == '\t')) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char sbi_getc(void)
|
||||
int sbi_getc(void)
|
||||
{
|
||||
return sbi_platform_console_getc(console_plat);
|
||||
}
|
||||
@@ -50,25 +47,26 @@ void sbi_puts(const char *str)
|
||||
|
||||
void sbi_gets(char *s, int maxwidth, char endchar)
|
||||
{
|
||||
char ch, *retval = s;
|
||||
int ch;
|
||||
char *retval = s;
|
||||
|
||||
while ((ch = sbi_getc()) != endchar && maxwidth > 1) {
|
||||
*retval = ch;
|
||||
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 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;
|
||||
#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)
|
||||
{
|
||||
@@ -88,9 +86,10 @@ static void printc(char **out, u32 *out_len, char ch)
|
||||
}
|
||||
}
|
||||
|
||||
static int prints(char **out, u32 *out_len, const char *string, int width, int flags)
|
||||
static int prints(char **out, u32 *out_len, const char *string, int width,
|
||||
int flags)
|
||||
{
|
||||
int pc = 0;
|
||||
int pc = 0;
|
||||
char padchar = ' ';
|
||||
|
||||
if (width > 0) {
|
||||
@@ -134,10 +133,10 @@ static int printi(char **out, u32 *out_len, long long i, int b, int sg,
|
||||
|
||||
if (sg && b == 10 && i < 0) {
|
||||
neg = 1;
|
||||
u = -i;
|
||||
u = -i;
|
||||
}
|
||||
|
||||
s = print_buf + PRINT_BUF_LEN - 1;
|
||||
s = print_buf + PRINT_BUF_LEN - 1;
|
||||
*s = '\0';
|
||||
|
||||
if (!u) {
|
||||
@@ -210,59 +209,59 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
|
||||
if (*format == 's') {
|
||||
char *s = va_arg(args, char *);
|
||||
acnt += sizeof(char *);
|
||||
pc += prints(out, out_len,
|
||||
s ? s : "(null)", width, flags);
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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)) {
|
||||
while (acnt &
|
||||
(sizeof(unsigned long long) - 1)) {
|
||||
va_arg(args, int);
|
||||
acnt += sizeof(int);
|
||||
}
|
||||
if (sizeof(unsigned long long) ==
|
||||
sizeof(unsigned long)) {
|
||||
sizeof(unsigned long)) {
|
||||
tmp = va_arg(args, unsigned long long);
|
||||
acnt += sizeof(unsigned long long);
|
||||
} else {
|
||||
@@ -270,48 +269,51 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
|
||||
va_arg(args, unsigned long);
|
||||
((unsigned long *)&tmp)[1] =
|
||||
va_arg(args, unsigned long);
|
||||
acnt += 2*sizeof(unsigned long);
|
||||
acnt += 2 * sizeof(unsigned long);
|
||||
}
|
||||
if (*(format + 2) == 'u') {
|
||||
format += 2;
|
||||
pc += printi(out, out_len, tmp,
|
||||
10, 0, width, flags, 'a');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
va_arg(args, long), 10, 1,
|
||||
width, flags, '0');
|
||||
acnt += sizeof(long);
|
||||
}
|
||||
}
|
||||
@@ -324,7 +326,7 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
out:
|
||||
out:
|
||||
printc(out, out_len, *format);
|
||||
++pc;
|
||||
}
|
||||
@@ -373,6 +375,19 @@ int sbi_printf(const char *format, ...)
|
||||
return retval;
|
||||
}
|
||||
|
||||
int sbi_dprintf(struct sbi_scratch *scratch, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int retval = 0;
|
||||
|
||||
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);
|
242
lib/sbi/sbi_ecall.c
Normal file
242
lib/sbi/sbi_ecall.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* 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_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_hart.h>
|
||||
#include <sbi/sbi_version.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
|
||||
#define SBI_ECALL_VERSION_MAJOR 0
|
||||
#define SBI_ECALL_VERSION_MINOR 2
|
||||
#define SBI_OPENSBI_IMPID 1
|
||||
|
||||
u16 sbi_ecall_version_major(void)
|
||||
{
|
||||
return SBI_ECALL_VERSION_MAJOR;
|
||||
}
|
||||
|
||||
u16 sbi_ecall_version_minor(void)
|
||||
{
|
||||
return SBI_ECALL_VERSION_MINOR;
|
||||
}
|
||||
|
||||
int sbi_check_extension(struct sbi_scratch *scratch, unsigned long extid,
|
||||
unsigned long *out_val)
|
||||
{
|
||||
/**
|
||||
* Each extension apart from base & 0.1, will be implemented as
|
||||
* platform specific feature. Thus, extension probing can be achieved
|
||||
* by checking the feature bits of the platform. We can create a map
|
||||
* between extension ID & feature and use a generic function to check
|
||||
* or just use a switch case for every new extension support added
|
||||
* TODO: Implement it.
|
||||
*/
|
||||
|
||||
if ((extid >= SBI_EXT_0_1_SET_TIMER &&
|
||||
extid <= SBI_EXT_0_1_SHUTDOWN) || (extid == SBI_EXT_BASE)) {
|
||||
*out_val = 1;
|
||||
} else if (extid >= SBI_EXT_VENDOR_START &&
|
||||
extid <= SBI_EXT_VENDOR_END) {
|
||||
*out_val = sbi_platform_vendor_ext_check(
|
||||
sbi_platform_ptr(scratch),
|
||||
extid);
|
||||
} else
|
||||
*out_val = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sbi_ecall_vendor_ext_handler(struct sbi_scratch *scratch,
|
||||
unsigned long extid, unsigned long funcid,
|
||||
unsigned long *args, unsigned long *out_val,
|
||||
unsigned long *out_tcause,
|
||||
unsigned long *out_tval)
|
||||
{
|
||||
return sbi_platform_vendor_ext_provider(sbi_platform_ptr(scratch),
|
||||
extid, funcid, args, out_val,
|
||||
out_tcause, out_tval);
|
||||
}
|
||||
|
||||
int sbi_ecall_base_handler(struct sbi_scratch *scratch, unsigned long extid,
|
||||
unsigned long funcid, unsigned long *args,
|
||||
unsigned long *out_val, unsigned long *out_tcause,
|
||||
unsigned long *out_tval)
|
||||
{
|
||||
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_OPENSBI_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_check_extension(scratch, args[0], out_val);
|
||||
default:
|
||||
ret = SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sbi_ecall_0_1_handler(struct sbi_scratch *scratch, unsigned long extid,
|
||||
unsigned long *args, unsigned long *tval,
|
||||
unsigned long *tcause)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sbi_tlb_info tlb_info;
|
||||
u32 source_hart = sbi_current_hartid();
|
||||
struct unpriv_trap uptrap = {0};
|
||||
|
||||
switch (extid) {
|
||||
case SBI_EXT_0_1_SET_TIMER:
|
||||
#if __riscv_xlen == 32
|
||||
sbi_timer_event_start(scratch,
|
||||
(((u64)args[1] << 32) | (u64)args[0]));
|
||||
#else
|
||||
sbi_timer_event_start(scratch, (u64)args[0]);
|
||||
#endif
|
||||
break;
|
||||
case SBI_EXT_0_1_CONSOLE_PUTCHAR:
|
||||
sbi_putc(args[0]);
|
||||
break;
|
||||
case SBI_EXT_0_1_CONSOLE_GETCHAR:
|
||||
ret = sbi_getc();
|
||||
break;
|
||||
case SBI_EXT_0_1_CLEAR_IPI:
|
||||
sbi_ipi_clear_smode(scratch);
|
||||
break;
|
||||
case SBI_EXT_0_1_SEND_IPI:
|
||||
ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)args[0],
|
||||
SBI_IPI_EVENT_SOFT, NULL);
|
||||
break;
|
||||
case SBI_EXT_0_1_REMOTE_FENCE_I:
|
||||
tlb_info.start = 0;
|
||||
tlb_info.size = 0;
|
||||
tlb_info.type = SBI_ITLB_FLUSH;
|
||||
tlb_info.shart_mask = 1UL << source_hart;
|
||||
ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)args[0],
|
||||
SBI_IPI_EVENT_FENCE_I, &tlb_info);
|
||||
break;
|
||||
case SBI_EXT_0_1_REMOTE_SFENCE_VMA:
|
||||
tlb_info.start = (unsigned long)args[1];
|
||||
tlb_info.size = (unsigned long)args[2];
|
||||
tlb_info.type = SBI_TLB_FLUSH_VMA;
|
||||
tlb_info.shart_mask = 1UL << source_hart;
|
||||
|
||||
ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)args[0],
|
||||
SBI_IPI_EVENT_SFENCE_VMA, &tlb_info);
|
||||
break;
|
||||
case SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID:
|
||||
tlb_info.start = (unsigned long)args[1];
|
||||
tlb_info.size = (unsigned long)args[2];
|
||||
tlb_info.asid = (unsigned long)args[3];
|
||||
tlb_info.type = SBI_TLB_FLUSH_VMA_ASID;
|
||||
tlb_info.shart_mask = 1UL << source_hart;
|
||||
|
||||
ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)args[0],
|
||||
SBI_IPI_EVENT_SFENCE_VMA_ASID,
|
||||
&tlb_info);
|
||||
break;
|
||||
case SBI_EXT_0_1_SHUTDOWN:
|
||||
sbi_system_shutdown(scratch, 0);
|
||||
break;
|
||||
default:
|
||||
ret = SBI_ENOTSUPP;
|
||||
};
|
||||
|
||||
if (ret == SBI_ETRAP) {
|
||||
*tcause = uptrap.cause;
|
||||
*tval = uptrap.tval;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long extension_id = regs->a7;
|
||||
unsigned long func_id = regs->a6;
|
||||
unsigned long out_val;
|
||||
unsigned long out_tval;
|
||||
unsigned long out_tcause;
|
||||
bool is_0_1_spec = 0;
|
||||
unsigned long args[6];
|
||||
|
||||
args[0] = regs->a0;
|
||||
args[1] = regs->a1;
|
||||
args[2] = regs->a2;
|
||||
args[3] = regs->a3;
|
||||
args[4] = regs->a4;
|
||||
args[5] = regs->a5;
|
||||
|
||||
if (extension_id >= SBI_EXT_0_1_SET_TIMER &&
|
||||
extension_id <= SBI_EXT_0_1_SHUTDOWN) {
|
||||
ret = sbi_ecall_0_1_handler(scratch, extension_id, args,
|
||||
&out_tval, &out_tcause);
|
||||
is_0_1_spec = 1;
|
||||
} else if (extension_id == SBI_EXT_BASE)
|
||||
ret = sbi_ecall_base_handler(scratch, extension_id, func_id,
|
||||
args, &out_val,
|
||||
&out_tval, &out_tcause);
|
||||
else if (extension_id >= SBI_EXT_VENDOR_START &&
|
||||
extension_id <= SBI_EXT_VENDOR_END) {
|
||||
ret = sbi_ecall_vendor_ext_handler(scratch, extension_id,
|
||||
func_id, args, &out_val,
|
||||
&out_tval, &out_tcause);
|
||||
} else {
|
||||
ret = SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
if (ret == SBI_ETRAP) {
|
||||
sbi_trap_redirect(regs, scratch, regs->mepc,
|
||||
out_tcause, out_tval);
|
||||
} else {
|
||||
/* 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;
|
||||
}
|
@@ -14,18 +14,30 @@
|
||||
#include <sbi/sbi_emulate_csr.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
|
||||
int sbi_emulate_csr_read(int csr_num,
|
||||
u32 hartid, ulong mstatus,
|
||||
struct sbi_scratch *scratch,
|
||||
ulong *csr_val)
|
||||
int sbi_emulate_csr_read(int csr_num, u32 hartid, struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch, ulong *csr_val)
|
||||
{
|
||||
int ret = 0;
|
||||
ulong cen = -1UL;
|
||||
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
|
||||
|
||||
if (EXTRACT_FIELD(mstatus, MSTATUS_MPP) == PRV_U)
|
||||
if (prev_mode == PRV_U)
|
||||
cen = csr_read(CSR_SCOUNTEREN);
|
||||
|
||||
switch (csr_num) {
|
||||
case CSR_HTIMEDELTA:
|
||||
if (prev_mode == PRV_S && !virt)
|
||||
*csr_val = sbi_timer_get_delta(scratch);
|
||||
else
|
||||
ret = SBI_ENOTSUPP;
|
||||
break;
|
||||
case CSR_CYCLE:
|
||||
if (!((cen >> (CSR_CYCLE - CSR_CYCLE)) & 1))
|
||||
return -1;
|
||||
@@ -34,7 +46,8 @@ int sbi_emulate_csr_read(int csr_num,
|
||||
case CSR_TIME:
|
||||
if (!((cen >> (CSR_TIME - CSR_CYCLE)) & 1))
|
||||
return -1;
|
||||
*csr_val = sbi_timer_value(scratch);
|
||||
*csr_val = (virt) ? sbi_timer_virt_value(scratch):
|
||||
sbi_timer_value(scratch);
|
||||
break;
|
||||
case CSR_INSTRET:
|
||||
if (!((cen >> (CSR_INSTRET - CSR_CYCLE)) & 1))
|
||||
@@ -52,6 +65,12 @@ int sbi_emulate_csr_read(int csr_num,
|
||||
*csr_val = csr_read(CSR_MHPMCOUNTER4);
|
||||
break;
|
||||
#if __riscv_xlen == 32
|
||||
case CSR_HTIMEDELTAH:
|
||||
if (prev_mode == PRV_S && !virt)
|
||||
*csr_val = sbi_timer_get_delta(scratch) >> 32;
|
||||
else
|
||||
ret = SBI_ENOTSUPP;
|
||||
break;
|
||||
case CSR_CYCLEH:
|
||||
if (!((cen >> (CSR_CYCLE - CSR_CYCLE)) & 1))
|
||||
return -1;
|
||||
@@ -60,7 +79,8 @@ int sbi_emulate_csr_read(int csr_num,
|
||||
case CSR_TIMEH:
|
||||
if (!((cen >> (CSR_TIME - CSR_CYCLE)) & 1))
|
||||
return -1;
|
||||
*csr_val = sbi_timer_value(scratch) >> 32;
|
||||
*csr_val = (virt) ? sbi_timer_virt_value(scratch) >> 32:
|
||||
sbi_timer_value(scratch) >> 32;
|
||||
break;
|
||||
case CSR_INSTRETH:
|
||||
if (!((cen >> (CSR_INSTRET - CSR_CYCLE)) & 1))
|
||||
@@ -85,20 +105,35 @@ int sbi_emulate_csr_read(int csr_num,
|
||||
*csr_val = csr_read(CSR_MHPMEVENT4);
|
||||
break;
|
||||
default:
|
||||
sbi_printf("%s: hartid%d: invalid csr_num=0x%x\n",
|
||||
__func__, hartid, csr_num);
|
||||
return SBI_ENOTSUPP;
|
||||
ret = SBI_ENOTSUPP;
|
||||
break;
|
||||
};
|
||||
|
||||
return 0;
|
||||
if (ret)
|
||||
sbi_dprintf(scratch, "%s: hartid%d: invalid csr_num=0x%x\n",
|
||||
__func__, hartid, csr_num);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sbi_emulate_csr_write(int csr_num,
|
||||
u32 hartid, ulong mstatus,
|
||||
struct sbi_scratch *scratch,
|
||||
ulong csr_val)
|
||||
int sbi_emulate_csr_write(int csr_num, u32 hartid, struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch, 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(scratch, csr_val);
|
||||
else
|
||||
ret = SBI_ENOTSUPP;
|
||||
break;
|
||||
case CSR_CYCLE:
|
||||
csr_write(CSR_MCYCLE, csr_val);
|
||||
break;
|
||||
@@ -112,6 +147,12 @@ int sbi_emulate_csr_write(int csr_num,
|
||||
csr_write(CSR_MHPMCOUNTER4, csr_val);
|
||||
break;
|
||||
#if __riscv_xlen == 32
|
||||
case CSR_HTIMEDELTAH:
|
||||
if (prev_mode == PRV_S && !virt)
|
||||
sbi_timer_set_delta_upper(scratch, csr_val);
|
||||
else
|
||||
ret = SBI_ENOTSUPP;
|
||||
break;
|
||||
case CSR_CYCLEH:
|
||||
csr_write(CSR_MCYCLEH, csr_val);
|
||||
break;
|
||||
@@ -132,10 +173,13 @@ int sbi_emulate_csr_write(int csr_num,
|
||||
csr_write(CSR_MHPMEVENT4, csr_val);
|
||||
break;
|
||||
default:
|
||||
sbi_printf("%s: hartid%d: invalid csr_num=0x%x\n",
|
||||
__func__, hartid, csr_num);
|
||||
return SBI_ENOTSUPP;
|
||||
ret = SBI_ENOTSUPP;
|
||||
break;
|
||||
};
|
||||
|
||||
return 0;
|
||||
if (ret)
|
||||
sbi_dprintf(scratch, "%s: hartid%d: invalid csr_num=0x%x\n",
|
||||
__func__, hartid, csr_num);
|
||||
|
||||
return ret;
|
||||
}
|
192
lib/sbi/sbi_fifo.c
Normal file
192
lib/sbi/sbi_fifo.c
Normal file
@@ -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;
|
||||
}
|
@@ -28,15 +28,14 @@ unsigned int sbi_current_hartid()
|
||||
|
||||
static void mstatus_init(struct sbi_scratch *scratch, u32 hartid)
|
||||
{
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
/* Enable FPU */
|
||||
if (misa_extension('D') || misa_extension('F'))
|
||||
csr_write(CSR_MSTATUS, MSTATUS_FS);
|
||||
|
||||
/* Enable user/supervisor use of perf counters */
|
||||
if (misa_extension('S') &&
|
||||
sbi_platform_has_scounteren(plat))
|
||||
if (misa_extension('S') && sbi_platform_has_scounteren(plat))
|
||||
csr_write(CSR_SCOUNTEREN, -1);
|
||||
if (sbi_platform_has_mcounteren(plat))
|
||||
csr_write(CSR_MCOUNTEREN, -1);
|
||||
@@ -53,8 +52,6 @@ static int fp_init(u32 hartid)
|
||||
{
|
||||
#ifdef __riscv_flen
|
||||
int i;
|
||||
#else
|
||||
unsigned long fd_mask;
|
||||
#endif
|
||||
|
||||
if (!misa_extension('D') && !misa_extension('F'))
|
||||
@@ -67,11 +64,6 @@ static int fp_init(u32 hartid)
|
||||
for (i = 0; i < 32; i++)
|
||||
init_fp_reg(i);
|
||||
csr_write(CSR_FCSR, 0);
|
||||
#else
|
||||
fd_mask = (1 << ('F' - 'A')) | (1 << ('D' - 'A'));
|
||||
csr_clear(CSR_MISA, fd_mask);
|
||||
if (csr_read(CSR_MISA) & fd_mask)
|
||||
return SBI_ENOTSUPP;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@@ -79,7 +71,7 @@ static int fp_init(u32 hartid)
|
||||
|
||||
static int delegate_traps(struct sbi_scratch *scratch, u32 hartid)
|
||||
{
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
unsigned long interrupts, exceptions;
|
||||
|
||||
if (!misa_extension('S'))
|
||||
@@ -88,14 +80,21 @@ static int delegate_traps(struct sbi_scratch *scratch, u32 hartid)
|
||||
|
||||
/* 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) |
|
||||
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) and we let
|
||||
* HS-mode handle supervisor calls (i.e. ecalls from VS-mode)
|
||||
*/
|
||||
if (misa_extension('H'))
|
||||
exceptions |= (1U << CAUSE_SUPERVISOR_ECALL);
|
||||
|
||||
csr_write(CSR_MIDELEG, interrupts);
|
||||
csr_write(CSR_MEDELEG, exceptions);
|
||||
|
||||
@@ -122,7 +121,7 @@ unsigned long log2roundup(unsigned long x)
|
||||
|
||||
void sbi_hart_pmp_dump(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
unsigned long prot, addr, size, l2l;
|
||||
unsigned int i;
|
||||
|
||||
@@ -160,13 +159,13 @@ static int pmp_init(struct sbi_scratch *scratch, u32 hartid)
|
||||
u32 i, count;
|
||||
unsigned long fw_start, fw_size_log2;
|
||||
ulong prot, addr, log2size;
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (!sbi_platform_has_pmp(plat))
|
||||
return 0;
|
||||
|
||||
fw_size_log2 = log2roundup(scratch->fw_size);
|
||||
fw_start = scratch->fw_start & ~((1UL << fw_size_log2) - 1UL);
|
||||
fw_start = scratch->fw_start & ~((1UL << fw_size_log2) - 1UL);
|
||||
|
||||
pmp_set(0, 0, fw_start, fw_size_log2);
|
||||
|
||||
@@ -175,8 +174,8 @@ static int pmp_init(struct sbi_scratch *scratch, u32 hartid)
|
||||
count = (PMP_COUNT - 1);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (sbi_platform_pmp_region_info(plat, hartid, i,
|
||||
&prot, &addr, &log2size))
|
||||
if (sbi_platform_pmp_region_info(plat, hartid, i, &prot, &addr,
|
||||
&log2size))
|
||||
continue;
|
||||
pmp_set(i + 1, prot, addr, log2size);
|
||||
}
|
||||
@@ -184,10 +183,19 @@ static int pmp_init(struct sbi_scratch *scratch, u32 hartid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sbi_hart_init(struct sbi_scratch *scratch, u32 hartid)
|
||||
static unsigned long trap_info_offset;
|
||||
|
||||
int sbi_hart_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (cold_boot) {
|
||||
trap_info_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__,
|
||||
"HART_TRAP_INFO");
|
||||
if (!trap_info_offset)
|
||||
return SBI_ENOMEM;
|
||||
}
|
||||
|
||||
mstatus_init(scratch, hartid);
|
||||
|
||||
rc = fp_init(hartid);
|
||||
@@ -201,6 +209,29 @@ int sbi_hart_init(struct sbi_scratch *scratch, u32 hartid)
|
||||
return pmp_init(scratch, hartid);
|
||||
}
|
||||
|
||||
void *sbi_hart_get_trap_info(struct sbi_scratch *scratch)
|
||||
{
|
||||
unsigned long *trap_info;
|
||||
|
||||
if (!trap_info_offset)
|
||||
return NULL;
|
||||
|
||||
trap_info = sbi_scratch_offset_ptr(scratch, trap_info_offset);
|
||||
|
||||
return (void *)(*trap_info);
|
||||
}
|
||||
|
||||
void sbi_hart_set_trap_info(struct sbi_scratch *scratch, void *data)
|
||||
{
|
||||
unsigned long *trap_info;
|
||||
|
||||
if (!trap_info_offset)
|
||||
return;
|
||||
|
||||
trap_info = sbi_scratch_offset_ptr(scratch, trap_info_offset);
|
||||
*trap_info = (unsigned long)data;
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) sbi_hart_hang(void)
|
||||
{
|
||||
while (1)
|
||||
@@ -208,12 +239,16 @@ void __attribute__((noreturn)) sbi_hart_hang(void)
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) sbi_hart_switch_mode(unsigned long arg0,
|
||||
unsigned long arg1,
|
||||
unsigned long next_addr,
|
||||
unsigned long next_mode)
|
||||
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:
|
||||
@@ -233,7 +268,25 @@ void __attribute__((noreturn)) sbi_hart_switch_mode(unsigned long arg0,
|
||||
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);
|
||||
valH = INSERT_FIELD(valH, MSTATUSH_MTL, 0);
|
||||
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')) {
|
||||
val = INSERT_FIELD(val, MSTATUS_MTL, 0);
|
||||
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);
|
||||
|
||||
@@ -248,13 +301,13 @@ void __attribute__((noreturn)) sbi_hart_switch_mode(unsigned long arg0,
|
||||
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));
|
||||
register unsigned long a0 asm("a0") = arg0;
|
||||
register unsigned long a1 asm("a1") = arg1;
|
||||
__asm__ __volatile__("mret" : : "r"(a0), "r"(a1));
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static spinlock_t avail_hart_mask_lock = SPIN_LOCK_INITIALIZER;
|
||||
static spinlock_t avail_hart_mask_lock = SPIN_LOCK_INITIALIZER;
|
||||
static volatile unsigned long avail_hart_mask = 0;
|
||||
|
||||
void sbi_hart_mark_available(u32 hartid)
|
||||
@@ -290,14 +343,14 @@ struct sbi_scratch *sbi_hart_id_to_scratch(struct sbi_scratch *scratch,
|
||||
return ((h2s)scratch->hartid_to_scratch)(hartid);
|
||||
}
|
||||
|
||||
#define COLDBOOT_WAIT_BITMAP_SIZE __riscv_xlen
|
||||
static spinlock_t coldboot_wait_bitmap_lock = SPIN_LOCK_INITIALIZER;
|
||||
#define COLDBOOT_WAIT_BITMAP_SIZE __riscv_xlen
|
||||
static spinlock_t coldboot_lock = SPIN_LOCK_INITIALIZER;
|
||||
static unsigned long coldboot_done = 0;
|
||||
static unsigned long coldboot_wait_bitmap = 0;
|
||||
|
||||
void sbi_hart_wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
{
|
||||
unsigned long mipval;
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if ((sbi_platform_hart_count(plat) <= hartid) ||
|
||||
(COLDBOOT_WAIT_BITMAP_SIZE <= hartid))
|
||||
@@ -306,32 +359,46 @@ void sbi_hart_wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
/* Set MSIE bit to receive IPI */
|
||||
csr_set(CSR_MIE, MIP_MSIP);
|
||||
|
||||
do {
|
||||
spin_lock(&coldboot_wait_bitmap_lock);
|
||||
coldboot_wait_bitmap |= (1UL << hartid);
|
||||
spin_unlock(&coldboot_wait_bitmap_lock);
|
||||
/* Acquire coldboot lock */
|
||||
spin_lock(&coldboot_lock);
|
||||
|
||||
/* Mark current HART as waiting */
|
||||
coldboot_wait_bitmap |= (1UL << hartid);
|
||||
|
||||
/* Wait for coldboot to finish using WFI */
|
||||
while (!coldboot_done) {
|
||||
spin_unlock(&coldboot_lock);
|
||||
wfi();
|
||||
mipval = csr_read(CSR_MIP);
|
||||
spin_lock(&coldboot_lock);
|
||||
};
|
||||
|
||||
spin_lock(&coldboot_wait_bitmap_lock);
|
||||
coldboot_wait_bitmap &= ~(1UL << hartid);
|
||||
spin_unlock(&coldboot_wait_bitmap_lock);
|
||||
} while (!(mipval && MIP_MSIP));
|
||||
/* Unmark current HART as waiting */
|
||||
coldboot_wait_bitmap &= ~(1UL << hartid);
|
||||
|
||||
csr_clear(CSR_MIP, MIP_MSIP);
|
||||
/* Release coldboot lock */
|
||||
spin_unlock(&coldboot_lock);
|
||||
|
||||
/* Clear current HART IPI */
|
||||
sbi_platform_ipi_clear(plat, hartid);
|
||||
}
|
||||
|
||||
void sbi_hart_wake_coldboot_harts(struct sbi_scratch *scratch, u32 hartid)
|
||||
{
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
int max_hart = sbi_platform_hart_count(plat);
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
int max_hart = sbi_platform_hart_count(plat);
|
||||
|
||||
for(int i = 0; i < max_hart ; i++) {
|
||||
/* send an IPI to every other hart */
|
||||
spin_lock(&coldboot_wait_bitmap_lock);
|
||||
/* Acquire coldboot lock */
|
||||
spin_lock(&coldboot_lock);
|
||||
|
||||
/* Mark coldboot done */
|
||||
coldboot_done = 1;
|
||||
|
||||
/* Send an IPI to all HARTs waiting for coldboot */
|
||||
for (int i = 0; i < max_hart; i++) {
|
||||
if ((i != hartid) && (coldboot_wait_bitmap & (1UL << i)))
|
||||
sbi_platform_ipi_send(plat, i);
|
||||
spin_unlock(&coldboot_wait_bitmap_lock);
|
||||
}
|
||||
|
||||
/* Release coldboot lock */
|
||||
spin_unlock(&coldboot_lock);
|
||||
}
|
@@ -9,38 +9,47 @@
|
||||
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_encoding.h>
|
||||
#include <sbi/riscv_unpriv.h>
|
||||
#include <sbi/sbi_bits.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,
|
||||
u32 hartid, ulong mcause,
|
||||
typedef int (*illegal_insn_func)(ulong insn, u32 hartid, ulong mcause,
|
||||
struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch);
|
||||
|
||||
static int truly_illegal_insn(ulong insn,
|
||||
u32 hartid, ulong mcause,
|
||||
static int truly_illegal_insn(ulong insn, u32 hartid, ulong mcause,
|
||||
struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch)
|
||||
{
|
||||
return sbi_trap_redirect(regs, scratch, regs->mepc, mcause, insn);
|
||||
}
|
||||
|
||||
static int system_opcode_insn(ulong insn,
|
||||
u32 hartid, ulong mcause,
|
||||
static int system_opcode_insn(ulong insn, u32 hartid, ulong mcause,
|
||||
struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch)
|
||||
{
|
||||
int do_write, rs1_num = (insn >> 15) & 0x1f;
|
||||
ulong rs1_val = GET_RS1(insn, regs);
|
||||
int csr_num = (u32)insn >> 20;
|
||||
int csr_num = (u32)insn >> 20;
|
||||
ulong csr_val, new_csr_val;
|
||||
|
||||
if (sbi_emulate_csr_read(csr_num, hartid, regs->mstatus,
|
||||
scratch, &csr_val))
|
||||
/*
|
||||
* WFI always traps as illegal instruction when executed from
|
||||
* VS/VU mode so we just forward it to HS-mode.
|
||||
*/
|
||||
#if __riscv_xlen == 32
|
||||
if ((regs->mstatusH & MSTATUSH_MPV) &&
|
||||
#else
|
||||
if ((regs->mstatus & MSTATUS_MPV) &&
|
||||
#endif
|
||||
(insn & INSN_MASK_WFI) == INSN_MATCH_WFI)
|
||||
return sbi_trap_redirect(regs, scratch,
|
||||
regs->mepc, mcause, insn);
|
||||
|
||||
if (sbi_emulate_csr_read(csr_num, hartid, regs, scratch, &csr_val))
|
||||
return truly_illegal_insn(insn, hartid, mcause,
|
||||
regs, scratch);
|
||||
|
||||
@@ -48,16 +57,17 @@ static int system_opcode_insn(ulong insn,
|
||||
switch (GET_RM(insn)) {
|
||||
case 1:
|
||||
new_csr_val = rs1_val;
|
||||
do_write = 1;
|
||||
do_write = 1;
|
||||
break;
|
||||
case 2:
|
||||
new_csr_val = csr_val | rs1_val;
|
||||
break;
|
||||
case 3: new_csr_val = csr_val & ~rs1_val;
|
||||
case 3:
|
||||
new_csr_val = csr_val & ~rs1_val;
|
||||
break;
|
||||
case 5:
|
||||
new_csr_val = rs1_num;
|
||||
do_write = 1;
|
||||
do_write = 1;
|
||||
break;
|
||||
case 6:
|
||||
new_csr_val = csr_val | rs1_num;
|
||||
@@ -66,15 +76,12 @@ static int system_opcode_insn(ulong insn,
|
||||
new_csr_val = csr_val & ~rs1_num;
|
||||
break;
|
||||
default:
|
||||
return truly_illegal_insn(insn, hartid, mcause,
|
||||
regs, scratch);
|
||||
return truly_illegal_insn(insn, hartid, mcause, regs, scratch);
|
||||
};
|
||||
|
||||
if (do_write &&
|
||||
sbi_emulate_csr_write(csr_num, hartid, regs->mstatus,
|
||||
scratch, new_csr_val))
|
||||
return truly_illegal_insn(insn, hartid, mcause,
|
||||
regs, scratch);
|
||||
if (do_write && sbi_emulate_csr_write(csr_num, hartid, regs,
|
||||
scratch, new_csr_val))
|
||||
return truly_illegal_insn(insn, hartid, mcause, regs, scratch);
|
||||
|
||||
SET_RD(insn, regs, csr_val);
|
||||
|
||||
@@ -122,17 +129,24 @@ int sbi_illegal_insn_handler(u32 hartid, ulong mcause,
|
||||
struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch)
|
||||
{
|
||||
ulong mstatus;
|
||||
ulong insn = csr_read(mbadaddr);
|
||||
ulong insn = csr_read(CSR_MTVAL);
|
||||
#if __riscv_xlen == 32
|
||||
bool virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
|
||||
#else
|
||||
bool virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
|
||||
#endif
|
||||
struct unpriv_trap uptrap;
|
||||
|
||||
if (unlikely((insn & 3) != 3)) {
|
||||
if (insn == 0) {
|
||||
mstatus = csr_read(CSR_MSTATUS);
|
||||
insn = get_insn(regs->mepc, &mstatus);
|
||||
insn = get_insn(regs->mepc, virt, scratch, &uptrap);
|
||||
if (uptrap.cause)
|
||||
return sbi_trap_redirect(regs, scratch,
|
||||
regs->mepc, uptrap.cause, uptrap.tval);
|
||||
}
|
||||
if ((insn & 3) != 3)
|
||||
return truly_illegal_insn(insn, hartid, mcause,
|
||||
regs, scratch);
|
||||
return truly_illegal_insn(insn, hartid, mcause, regs,
|
||||
scratch);
|
||||
}
|
||||
|
||||
return illegal_insn_table[(insn & 0x7c) >> 2](insn, hartid, mcause,
|
@@ -18,27 +18,60 @@
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi/sbi_version.h>
|
||||
|
||||
#define BANNER \
|
||||
" ____ _____ ____ _____\n" \
|
||||
" / __ \\ / ____| _ \\_ _|\n" \
|
||||
" | | | |_ __ ___ _ __ | (___ | |_) || |\n" \
|
||||
#define BANNER \
|
||||
" ____ _____ ____ _____\n" \
|
||||
" / __ \\ / ____| _ \\_ _|\n" \
|
||||
" | | | |_ __ ___ _ __ | (___ | |_) || |\n" \
|
||||
" | | | | '_ \\ / _ \\ '_ \\ \\___ \\| _ < | |\n" \
|
||||
" | |__| | |_) | __/ | | |____) | |_) || |_\n" \
|
||||
" \\____/| .__/ \\___|_| |_|_____/|____/_____|\n" \
|
||||
" | |\n" \
|
||||
" | |__| | |_) | __/ | | |____) | |_) || |_\n" \
|
||||
" \\____/| .__/ \\___|_| |_|_____/|____/_____|\n" \
|
||||
" | |\n" \
|
||||
" |_|\n\n"
|
||||
|
||||
static void sbi_boot_prints(struct sbi_scratch *scratch, u32 hartid)
|
||||
{
|
||||
char str[64];
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
misa_string(str, sizeof(str));
|
||||
#ifdef OPENSBI_VERSION_GIT
|
||||
sbi_printf("\nOpenSBI %s (%s %s)\n", OPENSBI_VERSION_GIT,
|
||||
__DATE__, __TIME__);
|
||||
#else
|
||||
sbi_printf("\nOpenSBI v%d.%d (%s %s)\n", OPENSBI_VERSION_MAJOR,
|
||||
OPENSBI_VERSION_MINOR, __DATE__, __TIME__);
|
||||
#endif
|
||||
|
||||
sbi_printf(BANNER);
|
||||
|
||||
/* Platform details */
|
||||
sbi_printf("Platform Name : %s\n", sbi_platform_name(plat));
|
||||
sbi_printf("Platform HART Features : RV%d%s\n", misa_xlen(), str);
|
||||
sbi_printf("Platform Max HARTs : %d\n",
|
||||
sbi_platform_hart_count(plat));
|
||||
sbi_printf("Current Hart : %u\n", hartid);
|
||||
/* Firmware details */
|
||||
sbi_printf("Firmware Base : 0x%lx\n", scratch->fw_start);
|
||||
sbi_printf("Firmware Size : %d KB\n",
|
||||
(u32)(scratch->fw_size / 1024));
|
||||
/* Generic details */
|
||||
sbi_printf("Runtime SBI Version : %d.%d\n",
|
||||
sbi_ecall_version_major(), sbi_ecall_version_minor());
|
||||
sbi_printf("\n");
|
||||
|
||||
sbi_hart_pmp_dump(scratch);
|
||||
}
|
||||
|
||||
static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
{
|
||||
int rc;
|
||||
char str[64];
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
rc = sbi_system_early_init(scratch, TRUE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_hart_init(scratch, hartid);
|
||||
rc = sbi_hart_init(scratch, hartid, TRUE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
@@ -62,41 +95,20 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
misa_string(str, sizeof(str));
|
||||
sbi_printf("\nOpenSBI v%d.%d (%s %s)\n",
|
||||
OPENSBI_VERSION_MAJOR, OPENSBI_VERSION_MINOR,
|
||||
__DATE__, __TIME__);
|
||||
|
||||
sbi_printf(BANNER);
|
||||
|
||||
/* Platform details */
|
||||
sbi_printf("Platform Name : %s\n", sbi_platform_name(plat));
|
||||
sbi_printf("Platform HART Features : RV%d%s\n", misa_xlen(), str);
|
||||
sbi_printf("Platform Max HARTs : %d\n",
|
||||
sbi_platform_hart_count(plat));
|
||||
sbi_printf("Current Hart : %u\n", hartid);
|
||||
/* Firmware details */
|
||||
sbi_printf("Firmware Base : 0x%lx\n", scratch->fw_start);
|
||||
sbi_printf("Firmware Size : %d KB\n",
|
||||
(u32)(scratch->fw_size / 1024));
|
||||
/* Generic details */
|
||||
sbi_printf("Runtime SBI Version : %d.%d\n",
|
||||
sbi_ecall_version_major(), sbi_ecall_version_minor());
|
||||
sbi_printf("\n");
|
||||
|
||||
sbi_hart_pmp_dump(scratch);
|
||||
if (!(scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS))
|
||||
sbi_boot_prints(scratch, hartid);
|
||||
|
||||
if (!sbi_platform_has_hart_hotplug(plat))
|
||||
sbi_hart_wake_coldboot_harts(scratch, hartid);
|
||||
sbi_hart_mark_available(hartid);
|
||||
sbi_hart_switch_mode(hartid, scratch->next_arg1,
|
||||
scratch->next_addr, scratch->next_mode);
|
||||
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;
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (!sbi_platform_has_hart_hotplug(plat))
|
||||
sbi_hart_wait_for_coldboot(scratch, hartid);
|
||||
@@ -108,7 +120,7 @@ static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_hart_init(scratch, hartid);
|
||||
rc = sbi_hart_init(scratch, hartid, FALSE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
@@ -135,7 +147,8 @@ static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
sbi_hart_hang();
|
||||
else
|
||||
sbi_hart_switch_mode(hartid, scratch->next_arg1,
|
||||
scratch->next_addr, scratch->next_mode);
|
||||
scratch->next_addr,
|
||||
scratch->next_mode, FALSE);
|
||||
}
|
||||
|
||||
static atomic_t coldboot_lottery = ATOMIC_INITIALIZER(0);
|
||||
@@ -154,9 +167,9 @@ static atomic_t coldboot_lottery = ATOMIC_INITIALIZER(0);
|
||||
*/
|
||||
void __noreturn sbi_init(struct sbi_scratch *scratch)
|
||||
{
|
||||
bool coldboot = FALSE;
|
||||
u32 hartid = sbi_current_hartid();
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
bool coldboot = FALSE;
|
||||
u32 hartid = sbi_current_hartid();
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (sbi_platform_hart_disabled(plat, hartid))
|
||||
sbi_hart_hang();
|
158
lib/sbi/sbi_ipi.c
Normal file
158
lib/sbi/sbi_ipi.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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/riscv_unpriv.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_tlb.h>
|
||||
|
||||
static unsigned long ipi_data_off;
|
||||
|
||||
static int sbi_ipi_send(struct sbi_scratch *scratch, u32 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;
|
||||
|
||||
if (sbi_platform_hart_disabled(plat, hartid))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Set IPI type on remote hart's scratch area and
|
||||
* trigger the interrupt
|
||||
*/
|
||||
remote_scratch = sbi_hart_id_to_scratch(scratch, hartid);
|
||||
ipi_data = sbi_scratch_offset_ptr(remote_scratch, ipi_data_off);
|
||||
if (event == SBI_IPI_EVENT_SFENCE_VMA ||
|
||||
event == SBI_IPI_EVENT_SFENCE_VMA_ASID ||
|
||||
event == SBI_IPI_EVENT_FENCE_I ) {
|
||||
ret = sbi_tlb_fifo_update(remote_scratch, hartid, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
atomic_raw_set_bit(event, &ipi_data->ipi_type);
|
||||
smp_wmb();
|
||||
sbi_platform_ipi_send(plat, hartid);
|
||||
|
||||
if (event == SBI_IPI_EVENT_SFENCE_VMA ||
|
||||
event == SBI_IPI_EVENT_SFENCE_VMA_ASID ||
|
||||
event == SBI_IPI_EVENT_FENCE_I ) {
|
||||
sbi_tlb_fifo_sync(scratch);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sbi_ipi_send_many(struct sbi_scratch *scratch, struct unpriv_trap *uptrap,
|
||||
ulong *pmask, u32 event, void *data)
|
||||
{
|
||||
ulong i, m;
|
||||
ulong mask = sbi_hart_available_mask();
|
||||
u32 hartid = sbi_current_hartid();
|
||||
|
||||
if (pmask) {
|
||||
mask &= load_ulong(pmask, scratch, uptrap);
|
||||
if (uptrap->cause)
|
||||
return SBI_ETRAP;
|
||||
}
|
||||
|
||||
/* Send IPIs to every other hart on the set */
|
||||
for (i = 0, m = mask; m; i++, m >>= 1)
|
||||
if ((m & 1UL) && (i != hartid))
|
||||
sbi_ipi_send(scratch, i, event, data);
|
||||
|
||||
/*
|
||||
* If the current hart is on the set, send an IPI
|
||||
* to it as well
|
||||
*/
|
||||
if (mask & (1UL << hartid))
|
||||
sbi_ipi_send(scratch, hartid, event, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sbi_ipi_clear_smode(struct sbi_scratch *scratch)
|
||||
{
|
||||
csr_clear(CSR_MIP, MIP_SSIP);
|
||||
}
|
||||
|
||||
void sbi_ipi_process(struct sbi_scratch *scratch)
|
||||
{
|
||||
unsigned long ipi_type;
|
||||
unsigned int ipi_event;
|
||||
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 = sbi_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;
|
||||
|
||||
switch (ipi_event) {
|
||||
case SBI_IPI_EVENT_SOFT:
|
||||
csr_set(CSR_MIP, MIP_SSIP);
|
||||
break;
|
||||
case SBI_IPI_EVENT_FENCE_I:
|
||||
case SBI_IPI_EVENT_SFENCE_VMA:
|
||||
case SBI_IPI_EVENT_SFENCE_VMA_ASID:
|
||||
sbi_tlb_fifo_process(scratch);
|
||||
break;
|
||||
case SBI_IPI_EVENT_HALT:
|
||||
sbi_hart_hang();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
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;
|
||||
} else {
|
||||
if (!ipi_data_off)
|
||||
return SBI_ENOMEM;
|
||||
}
|
||||
|
||||
ipi_data = sbi_scratch_offset_ptr(scratch, ipi_data_off);
|
||||
ipi_data->ipi_type = 0x00;
|
||||
|
||||
ret = sbi_tlb_fifo_init(scratch, cold_boot);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable software interrupts */
|
||||
csr_set(CSR_MIE, MIP_MSIP);
|
||||
|
||||
return sbi_platform_ipi_init(sbi_platform_ptr(scratch), cold_boot);
|
||||
}
|
@@ -9,11 +9,11 @@
|
||||
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_encoding.h>
|
||||
#include <sbi/riscv_unpriv.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];
|
||||
@@ -26,81 +26,102 @@ int sbi_misaligned_load_handler(u32 hartid, ulong mcause,
|
||||
struct sbi_scratch *scratch)
|
||||
{
|
||||
union reg_data val;
|
||||
ulong mstatus = csr_read(CSR_MSTATUS);
|
||||
ulong insn = get_insn(regs->mepc, &mstatus);
|
||||
struct unpriv_trap uptrap;
|
||||
ulong addr = csr_read(CSR_MTVAL);
|
||||
int i, fp = 0, shift = 0, len = 0;
|
||||
#if __riscv_xlen == 32
|
||||
bool virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
|
||||
#else
|
||||
bool virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
|
||||
#endif
|
||||
ulong insn = get_insn(regs->mepc, virt, scratch, &uptrap);
|
||||
|
||||
if (uptrap.cause)
|
||||
return sbi_trap_redirect(regs, scratch, regs->mepc,
|
||||
uptrap.cause, uptrap.tval);
|
||||
|
||||
if ((insn & INSN_MASK_LW) == INSN_MATCH_LW) {
|
||||
len = 4;
|
||||
len = 4;
|
||||
shift = 8 * (sizeof(ulong) - len);
|
||||
#if __riscv_xlen == 64
|
||||
} else if ((insn & INSN_MASK_LD) == INSN_MATCH_LD) {
|
||||
len = 8;
|
||||
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;
|
||||
fp = 1;
|
||||
len = 8;
|
||||
} else if ((insn & INSN_MASK_FLW) == INSN_MATCH_FLW) {
|
||||
fp = 1;
|
||||
fp = 1;
|
||||
len = 4;
|
||||
#endif
|
||||
} else if ((insn & INSN_MASK_LH) == INSN_MATCH_LH) {
|
||||
len = 2;
|
||||
len = 2;
|
||||
shift = 8 * (sizeof(ulong) - len);
|
||||
} else if ((insn & INSN_MASK_LHU) == INSN_MATCH_LHU) {
|
||||
len = 2;
|
||||
#ifdef __riscv_compressed
|
||||
# if __riscv_xlen >= 64
|
||||
#if __riscv_xlen >= 64
|
||||
} else if ((insn & INSN_MASK_C_LD) == INSN_MATCH_C_LD) {
|
||||
len = 8;
|
||||
len = 8;
|
||||
shift = 8 * (sizeof(ulong) - len);
|
||||
insn = RVC_RS2S(insn) << SH_RD;
|
||||
insn = RVC_RS2S(insn) << SH_RD;
|
||||
} else if ((insn & INSN_MASK_C_LDSP) == INSN_MATCH_C_LDSP &&
|
||||
((insn >> SH_RD) & 0x1f)) {
|
||||
len = 8;
|
||||
len = 8;
|
||||
shift = 8 * (sizeof(ulong) - len);
|
||||
# endif
|
||||
} else if ((insn & INSN_MASK_C_LW) ==INSN_MATCH_C_LW) {
|
||||
len = 4;
|
||||
#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;
|
||||
insn = RVC_RS2S(insn) << SH_RD;
|
||||
} else if ((insn & INSN_MASK_C_LWSP) == INSN_MATCH_C_LWSP &&
|
||||
((insn >> SH_RD) & 0x1f)) {
|
||||
len = 4;
|
||||
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;
|
||||
fp = 1;
|
||||
len = 8;
|
||||
insn = RVC_RS2S(insn) << SH_RD;
|
||||
} else if ((insn & INSN_MASK_C_FLDSP) == INSN_MATCH_C_FLDSP) {
|
||||
fp = 1;
|
||||
fp = 1;
|
||||
len = 8;
|
||||
# if __riscv_xlen == 32
|
||||
#if __riscv_xlen == 32
|
||||
} else if ((insn & INSN_MASK_C_FLW) == INSN_MATCH_C_FLW) {
|
||||
fp = 1;
|
||||
len = 4;
|
||||
fp = 1;
|
||||
len = 4;
|
||||
insn = RVC_RS2S(insn) << SH_RD;
|
||||
} else if ((insn & INSN_MASK_C_FLWSP) == INSN_MATCH_C_FLWSP) {
|
||||
fp = 1;
|
||||
fp = 1;
|
||||
len = 4;
|
||||
# endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
} else
|
||||
return SBI_EILL;
|
||||
return sbi_trap_redirect(regs, scratch, regs->mepc,
|
||||
mcause, addr);
|
||||
|
||||
val.data_u64 = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
val.data_bytes[i] = load_u8((void *)(addr + i), regs->mepc);
|
||||
for (i = 0; i < len; i++) {
|
||||
val.data_bytes[i] = load_u8((void *)(addr + i),
|
||||
scratch, &uptrap);
|
||||
if (uptrap.cause)
|
||||
return sbi_trap_redirect(regs, scratch, regs->mepc,
|
||||
uptrap.cause, uptrap.tval);
|
||||
}
|
||||
|
||||
if (!fp)
|
||||
SET_RD(insn, regs, 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(insn);
|
||||
|
||||
@@ -112,10 +133,19 @@ int sbi_misaligned_store_handler(u32 hartid, ulong mcause,
|
||||
struct sbi_scratch *scratch)
|
||||
{
|
||||
union reg_data val;
|
||||
ulong mstatus = csr_read(CSR_MSTATUS);
|
||||
ulong insn = get_insn(regs->mepc, &mstatus);
|
||||
struct unpriv_trap uptrap;
|
||||
ulong addr = csr_read(CSR_MTVAL);
|
||||
int i, len = 0;
|
||||
#if __riscv_xlen == 32
|
||||
bool virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
|
||||
#else
|
||||
bool virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
|
||||
#endif
|
||||
ulong insn = get_insn(regs->mepc, virt, scratch, &uptrap);
|
||||
|
||||
if (uptrap.cause)
|
||||
return sbi_trap_redirect(regs, scratch, regs->mepc,
|
||||
uptrap.cause, uptrap.tval);
|
||||
|
||||
val.data_ulong = GET_RS2(insn, regs);
|
||||
|
||||
@@ -125,51 +155,61 @@ int sbi_misaligned_store_handler(u32 hartid, ulong mcause,
|
||||
} 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;
|
||||
len = 8;
|
||||
val.data_u64 = GET_F64_RS2(insn, regs);
|
||||
} else if ((insn & INSN_MASK_FSW) == INSN_MATCH_FSW) {
|
||||
len = 4;
|
||||
len = 4;
|
||||
val.data_ulong = GET_F32_RS2(insn, regs);
|
||||
#endif
|
||||
} else if ((insn & INSN_MASK_SH) == INSN_MATCH_SH) {
|
||||
len = 2;
|
||||
#ifdef __riscv_compressed
|
||||
# if __riscv_xlen >= 64
|
||||
#if __riscv_xlen >= 64
|
||||
} else if ((insn & INSN_MASK_C_SD) == INSN_MATCH_C_SD) {
|
||||
len = 8;
|
||||
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;
|
||||
len = 8;
|
||||
val.data_ulong = GET_RS2C(insn, regs);
|
||||
# endif
|
||||
#endif
|
||||
} else if ((insn & INSN_MASK_C_SW) == INSN_MATCH_C_SW) {
|
||||
len = 4;
|
||||
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;
|
||||
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;
|
||||
len = 8;
|
||||
val.data_u64 = GET_F64_RS2S(insn, regs);
|
||||
} else if ((insn & INSN_MASK_C_FSDSP) == INSN_MATCH_C_FSDSP) {
|
||||
len = 8;
|
||||
len = 8;
|
||||
val.data_u64 = GET_F64_RS2C(insn, regs);
|
||||
# if __riscv_xlen == 32
|
||||
#if __riscv_xlen == 32
|
||||
} else if ((insn & INSN_MASK_C_FSW) == INSN_MATCH_C_FSW) {
|
||||
len = 4;
|
||||
len = 4;
|
||||
val.data_ulong = GET_F32_RS2S(insn, regs);
|
||||
} else if ((insn & INSN_MASK_C_FSWSP) == INSN_MATCH_C_FSWSP) {
|
||||
len = 4;
|
||||
len = 4;
|
||||
val.data_ulong = GET_F32_RS2C(insn, regs);
|
||||
# endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
} else
|
||||
return SBI_EILL;
|
||||
return sbi_trap_redirect(regs, scratch, regs->mepc,
|
||||
mcause, addr);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
store_u8((void *)(addr + i), val.data_bytes[i], regs->mepc);
|
||||
for (i = 0; i < len; i++) {
|
||||
store_u8((void *)(addr + i), val.data_bytes[i],
|
||||
scratch, &uptrap);
|
||||
if (uptrap.cause)
|
||||
return sbi_trap_redirect(regs, scratch, regs->mepc,
|
||||
uptrap.cause, uptrap.tval);
|
||||
}
|
||||
|
||||
regs->mepc += INSN_LEN(insn);
|
||||
|
59
lib/sbi/sbi_scratch.c
Normal file
59
lib/sbi/sbi_scratch.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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_scratch.h>
|
||||
|
||||
static spinlock_t extra_lock = SPIN_LOCK_INITIALIZER;
|
||||
static unsigned long extra_offset = SBI_SCRATCH_EXTRA_SPACE_OFFSET;
|
||||
|
||||
unsigned long sbi_scratch_alloc_offset(unsigned long size, const char *owner)
|
||||
{
|
||||
unsigned long ret = 0;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
while (size & (__SIZEOF_POINTER__ - 1))
|
||||
size++;
|
||||
|
||||
spin_lock(&extra_lock);
|
||||
|
||||
if (SBI_SCRATCH_SIZE < (extra_offset + size))
|
||||
goto done;
|
||||
|
||||
ret = extra_offset;
|
||||
extra_offset += size;
|
||||
|
||||
done:
|
||||
spin_unlock(&extra_lock);
|
||||
|
||||
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.
|
||||
*/
|
||||
}
|
@@ -12,17 +12,18 @@
|
||||
* bugs as well. Use any optimized routines from newlib or glibc if required.
|
||||
*/
|
||||
|
||||
#include <plat/string.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
|
||||
int strcmp(const char *a, const char *b)
|
||||
int sbi_strcmp(const char *a, const char *b)
|
||||
{
|
||||
/* search first diff or end of string */
|
||||
for (; *a == *b && *a != '\0'; a++, b++);
|
||||
for (; *a == *b && *a != '\0'; a++, b++)
|
||||
;
|
||||
|
||||
return *a - *b;
|
||||
}
|
||||
|
||||
size_t strlen(const char *str)
|
||||
size_t sbi_strlen(const char *str)
|
||||
{
|
||||
unsigned long ret = 0;
|
||||
|
||||
@@ -34,7 +35,7 @@ size_t strlen(const char *str)
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t strnlen(const char *str, size_t count)
|
||||
size_t sbi_strnlen(const char *str, size_t count)
|
||||
{
|
||||
unsigned long ret = 0;
|
||||
|
||||
@@ -47,31 +48,31 @@ size_t strnlen(const char *str, size_t count)
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *strcpy(char *dest, const char *src)
|
||||
char *sbi_strcpy(char *dest, const char *src)
|
||||
{
|
||||
char *ret = dest;
|
||||
|
||||
while(*src != '\0') {
|
||||
while (*src != '\0') {
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *strncpy(char *dest, const char *src, size_t count)
|
||||
char *sbi_strncpy(char *dest, const char *src, size_t count)
|
||||
{
|
||||
char *ret = dest;
|
||||
|
||||
while(count-- && *src != '\0') {
|
||||
while (count-- && *src != '\0') {
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *strchr(const char *s, int c)
|
||||
char *sbi_strchr(const char *s, int c)
|
||||
{
|
||||
while(*s != '\0' && *s != (char)c)
|
||||
while (*s != '\0' && *s != (char)c)
|
||||
s++;
|
||||
|
||||
if (*s == '\0')
|
||||
@@ -80,9 +81,9 @@ char *strchr(const char *s, int c)
|
||||
return (char *)s;
|
||||
}
|
||||
|
||||
char *strrchr(const char *s, int c)
|
||||
char *sbi_strrchr(const char *s, int c)
|
||||
{
|
||||
const char *last = s + strlen(s);
|
||||
const char *last = s + sbi_strlen(s);
|
||||
|
||||
while (last > s && *last != (char)c)
|
||||
last--;
|
||||
@@ -92,11 +93,11 @@ char *strrchr(const char *s, int c)
|
||||
else
|
||||
return (char *)last;
|
||||
}
|
||||
void *memset(void *s, int c, size_t count)
|
||||
void *sbi_memset(void *s, int c, size_t count)
|
||||
{
|
||||
char *temp = s;
|
||||
|
||||
while(count > 0 ){
|
||||
while (count > 0) {
|
||||
count--;
|
||||
*temp++ = c;
|
||||
}
|
||||
@@ -104,9 +105,9 @@ void *memset(void *s, int c, size_t count)
|
||||
return s;
|
||||
}
|
||||
|
||||
void *memcpy(void *dest, const void *src, size_t count)
|
||||
void *sbi_memcpy(void *dest, const void *src, size_t count)
|
||||
{
|
||||
char *temp1 = dest;
|
||||
char *temp1 = dest;
|
||||
const char *temp2 = src;
|
||||
|
||||
while (count > 0) {
|
||||
@@ -117,16 +118,16 @@ void *memcpy(void *dest, const void *src, size_t count)
|
||||
return dest;
|
||||
}
|
||||
|
||||
void *memmove(void *dest, const void *src, size_t count)
|
||||
void *sbi_memmove(void *dest, const void *src, size_t count)
|
||||
{
|
||||
char *temp1 = (char *)dest;
|
||||
char *temp1 = (char *)dest;
|
||||
const char *temp2 = (char *)src;
|
||||
|
||||
if (src == dest)
|
||||
return dest;
|
||||
|
||||
if (dest < src) {
|
||||
while(count > 0) {
|
||||
while (count > 0) {
|
||||
*temp1++ = *temp2++;
|
||||
count--;
|
||||
}
|
||||
@@ -134,7 +135,7 @@ void *memmove(void *dest, const void *src, size_t count)
|
||||
temp1 = dest + count - 1;
|
||||
temp2 = src + count - 1;
|
||||
|
||||
while(count > 0) {
|
||||
while (count > 0) {
|
||||
*temp1-- = *temp2--;
|
||||
count--;
|
||||
}
|
||||
@@ -143,7 +144,7 @@ void *memmove(void *dest, const void *src, size_t count)
|
||||
return dest;
|
||||
}
|
||||
|
||||
int memcmp(const void *s1, const void *s2, size_t count)
|
||||
int sbi_memcmp(const void *s1, const void *s2, size_t count)
|
||||
{
|
||||
const char *temp1 = s1;
|
||||
const char *temp2 = s2;
|
||||
@@ -159,13 +160,13 @@ int memcmp(const void *s1, const void *s2, size_t count)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *memchr(const void *s, int c, size_t count)
|
||||
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);
|
||||
return (void *)(temp - 1);
|
||||
}
|
||||
count--;
|
||||
}
|
@@ -15,33 +15,31 @@
|
||||
|
||||
int sbi_system_early_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
return sbi_platform_early_init(sbi_platform_ptr(scratch),
|
||||
cold_boot);
|
||||
return sbi_platform_early_init(sbi_platform_ptr(scratch), cold_boot);
|
||||
}
|
||||
|
||||
int sbi_system_final_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
return sbi_platform_final_init(sbi_platform_ptr(scratch),
|
||||
cold_boot);
|
||||
return sbi_platform_final_init(sbi_platform_ptr(scratch), cold_boot);
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) sbi_system_reboot(struct sbi_scratch *scratch,
|
||||
u32 type)
|
||||
void __attribute__((noreturn))
|
||||
sbi_system_reboot(struct sbi_scratch *scratch, u32 type)
|
||||
|
||||
{
|
||||
sbi_platform_system_reboot(sbi_platform_ptr(scratch), type);
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) sbi_system_shutdown(struct sbi_scratch *scratch,
|
||||
u32 type)
|
||||
void __attribute__((noreturn))
|
||||
sbi_system_shutdown(struct sbi_scratch *scratch, u32 type)
|
||||
{
|
||||
/* First try the platform-specific method */
|
||||
sbi_platform_system_shutdown(sbi_platform_ptr(scratch), type);
|
||||
|
||||
/* If that fails (or is not implemented) send an IPI on every
|
||||
* hart to hang and then hang the current hart */
|
||||
sbi_ipi_send_many(scratch, NULL, SBI_IPI_EVENT_HALT);
|
||||
sbi_ipi_send_many(scratch, NULL, NULL, SBI_IPI_EVENT_HALT, NULL);
|
||||
|
||||
sbi_hart_hang();
|
||||
}
|
115
lib/sbi/sbi_timer.c
Normal file
115
lib/sbi/sbi_timer.c
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <sbi/sbi_timer.h>
|
||||
|
||||
static unsigned long time_delta_off;
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
u64 get_ticks(void)
|
||||
{
|
||||
u32 lo, hi, tmp;
|
||||
__asm__ __volatile__("1:\n"
|
||||
"rdtimeh %0\n"
|
||||
"rdtime %1\n"
|
||||
"rdtimeh %2\n"
|
||||
"bne %0, %2, 1b"
|
||||
: "=&r"(hi), "=&r"(lo), "=&r"(tmp));
|
||||
return ((u64)hi << 32) | lo;
|
||||
}
|
||||
#else
|
||||
u64 get_ticks(void)
|
||||
{
|
||||
unsigned long n;
|
||||
|
||||
__asm__ __volatile__("rdtime %0" : "=r"(n));
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
||||
u64 sbi_timer_value(struct sbi_scratch *scratch)
|
||||
{
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (sbi_platform_has_timer_value(plat))
|
||||
return sbi_platform_timer_value(plat);
|
||||
else
|
||||
return get_ticks();
|
||||
}
|
||||
|
||||
u64 sbi_timer_virt_value(struct sbi_scratch *scratch)
|
||||
{
|
||||
u64 *time_delta = sbi_scratch_offset_ptr(scratch, time_delta_off);
|
||||
|
||||
return sbi_timer_value(scratch) + *time_delta;
|
||||
}
|
||||
|
||||
u64 sbi_timer_get_delta(struct sbi_scratch *scratch)
|
||||
{
|
||||
u64 *time_delta = sbi_scratch_offset_ptr(scratch, time_delta_off);
|
||||
|
||||
return *time_delta;
|
||||
}
|
||||
|
||||
void sbi_timer_set_delta(struct sbi_scratch *scratch, ulong delta)
|
||||
{
|
||||
u64 *time_delta = sbi_scratch_offset_ptr(scratch, time_delta_off);
|
||||
|
||||
*time_delta = (u64)delta;
|
||||
}
|
||||
|
||||
void sbi_timer_set_delta_upper(struct sbi_scratch *scratch, ulong delta_upper)
|
||||
{
|
||||
u64 *time_delta = sbi_scratch_offset_ptr(scratch, time_delta_off);
|
||||
|
||||
*time_delta &= 0xffffffffULL;
|
||||
*time_delta |= ((u64)delta_upper << 32);
|
||||
}
|
||||
|
||||
void sbi_timer_event_stop(struct sbi_scratch *scratch)
|
||||
{
|
||||
sbi_platform_timer_event_stop(sbi_platform_ptr(scratch));
|
||||
}
|
||||
|
||||
void sbi_timer_event_start(struct sbi_scratch *scratch, u64 next_event)
|
||||
{
|
||||
sbi_platform_timer_event_start(sbi_platform_ptr(scratch), next_event);
|
||||
csr_clear(CSR_MIP, MIP_STIP);
|
||||
csr_set(CSR_MIE, MIP_MTIP);
|
||||
}
|
||||
|
||||
void sbi_timer_process(struct sbi_scratch *scratch)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
return sbi_platform_timer_init(sbi_platform_ptr(scratch), cold_boot);
|
||||
}
|
319
lib/sbi/sbi_tlb.c
Normal file
319
lib/sbi/sbi_tlb.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* 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_scratch.h>
|
||||
#include <sbi/sbi_tlb.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");
|
||||
}
|
||||
|
||||
static void sbi_tlb_fifo_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");
|
||||
}
|
||||
}
|
||||
|
||||
static void sbi_tlb_fifo_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");
|
||||
}
|
||||
}
|
||||
|
||||
static void sbi_tlb_local_flush(struct sbi_tlb_info *tinfo)
|
||||
{
|
||||
if (tinfo->type == SBI_TLB_FLUSH_VMA) {
|
||||
sbi_tlb_fifo_sfence_vma(tinfo);
|
||||
} else if (tinfo->type == SBI_TLB_FLUSH_VMA_ASID) {
|
||||
sbi_tlb_fifo_sfence_vma_asid(tinfo);
|
||||
} else if (tinfo->type == SBI_ITLB_FLUSH)
|
||||
__asm__ __volatile("fence.i");
|
||||
else
|
||||
sbi_printf("Invalid tlb flush request type [%lu]\n",
|
||||
tinfo->type);
|
||||
return;
|
||||
}
|
||||
|
||||
static void sbi_tlb_entry_process(struct sbi_scratch *scratch,
|
||||
struct sbi_tlb_info *tinfo)
|
||||
{
|
||||
u32 i;
|
||||
u64 m;
|
||||
struct sbi_scratch *rscratch = NULL;
|
||||
unsigned long *rtlb_sync = NULL;
|
||||
|
||||
sbi_tlb_local_flush(tinfo);
|
||||
for (i = 0, m = tinfo->shart_mask; m; i++, m >>= 1) {
|
||||
if (!(m & 1UL))
|
||||
continue;
|
||||
|
||||
rscratch = sbi_hart_id_to_scratch(scratch, i);
|
||||
rtlb_sync = sbi_scratch_offset_ptr(rscratch, tlb_sync_off);
|
||||
while (atomic_raw_xchg_ulong(rtlb_sync, 1)) ;
|
||||
}
|
||||
}
|
||||
|
||||
static void sbi_tlb_fifo_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(scratch, &tinfo);
|
||||
deq_count++;
|
||||
if (deq_count > count)
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void sbi_tlb_fifo_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(scratch, &tinfo);
|
||||
}
|
||||
|
||||
void sbi_tlb_fifo_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_fifo_process_count(scratch, 1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int __sbi_tlb_fifo_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;
|
||||
curr->shart_mask = curr->shart_mask | next->shart_mask;
|
||||
ret = SBI_FIFO_UPDATED;
|
||||
} else if (next->start >= curr->start && next_end <= curr_end) {
|
||||
curr->shart_mask = curr->shart_mask | next->shart_mask;
|
||||
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_fifo_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->type == SBI_TLB_FLUSH_VMA_ASID &&
|
||||
curr->type == SBI_TLB_FLUSH_VMA_ASID) {
|
||||
if (next->asid == curr->asid)
|
||||
ret = __sbi_tlb_fifo_range_check(curr, next);
|
||||
} else if (next->type == SBI_TLB_FLUSH_VMA &&
|
||||
curr->type == SBI_TLB_FLUSH_VMA) {
|
||||
ret = __sbi_tlb_fifo_range_check(curr, next);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sbi_tlb_fifo_update(struct sbi_scratch *rscratch, u32 hartid, void *data)
|
||||
{
|
||||
int ret;
|
||||
struct sbi_fifo *tlb_fifo_r;
|
||||
struct sbi_scratch *lscratch;
|
||||
struct sbi_tlb_info *tinfo = data;
|
||||
u32 curr_hartid = sbi_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 (hartid == curr_hartid) {
|
||||
sbi_tlb_local_flush(tinfo);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lscratch = sbi_hart_id_to_scratch(rscratch, curr_hartid);
|
||||
tlb_fifo_r = sbi_scratch_offset_ptr(rscratch, tlb_fifo_off);
|
||||
|
||||
ret = sbi_fifo_inplace_update(tlb_fifo_r, data, sbi_tlb_fifo_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_fifo_process_count(lscratch, 1);
|
||||
sbi_dprintf(rscratch, "hart%d: hart%d tlb fifo full\n",
|
||||
curr_hartid, hartid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sbi_tlb_fifo_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
286
lib/sbi/sbi_trap.c
Normal file
286
lib/sbi/sbi_trap.c
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* 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_unpriv.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_timer.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
|
||||
static void __noreturn sbi_trap_error(const char *msg, int rc, u32 hartid,
|
||||
ulong mcause, ulong mtval,
|
||||
struct sbi_trap_regs *regs)
|
||||
{
|
||||
sbi_printf("%s: hart%d: %s (error %d)\n", __func__, hartid, msg, rc);
|
||||
sbi_printf("%s: hart%d: mcause=0x%" PRILX " mtval=0x%" PRILX "\n",
|
||||
__func__, hartid, mcause, mtval);
|
||||
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 scratch pointer to sbi_scratch of current HART
|
||||
* @param epc error PC for lower privledge mode
|
||||
* @param cause exception cause for lower privledge mode
|
||||
* @param tval trap value for lower privledge mode
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
int sbi_trap_redirect(struct sbi_trap_regs *regs, struct sbi_scratch *scratch,
|
||||
ulong epc, ulong cause, ulong tval)
|
||||
{
|
||||
ulong hstatus, vsstatus, prev_mode;
|
||||
#if __riscv_xlen == 32
|
||||
bool prev_virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
|
||||
bool prev_stage2 = (regs->mstatusH & MSTATUSH_MTL) ? TRUE : FALSE;
|
||||
#else
|
||||
bool prev_virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
|
||||
bool prev_stage2 = (regs->mstatus & MSTATUS_MTL) ? 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 && !prev_stage2) {
|
||||
switch (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 and MTL bits */
|
||||
#if __riscv_xlen == 32
|
||||
regs->mstatusH &= ~MSTATUSH_MPV;
|
||||
regs->mstatusH |= (next_virt) ? MSTATUSH_MPV : 0UL;
|
||||
regs->mstatusH &= ~MSTATUSH_MTL;
|
||||
#else
|
||||
regs->mstatus &= ~MSTATUS_MPV;
|
||||
regs->mstatus |= (next_virt) ? MSTATUS_MPV : 0UL;
|
||||
regs->mstatus &= ~MSTATUS_MTL;
|
||||
#endif
|
||||
|
||||
/* Update HSTATUS for VS/VU-mode to HS-mode transition */
|
||||
if (misa_extension('H') && prev_virt && !next_virt) {
|
||||
/* Update HSTATUS SP2P, SP2V, SPV, and STL bits */
|
||||
hstatus = csr_read(CSR_HSTATUS);
|
||||
hstatus &= ~HSTATUS_SP2P;
|
||||
hstatus |= (regs->mstatus & MSTATUS_SPP) ? HSTATUS_SP2P : 0;
|
||||
hstatus &= ~HSTATUS_SP2V;
|
||||
hstatus |= (hstatus & HSTATUS_SPV) ? HSTATUS_SP2V : 0;
|
||||
hstatus &= ~HSTATUS_SPV;
|
||||
hstatus |= (prev_virt) ? HSTATUS_SPV : 0;
|
||||
hstatus &= ~HSTATUS_STL;
|
||||
hstatus |= (prev_stage2) ? HSTATUS_STL : 0;
|
||||
csr_write(CSR_HSTATUS, hstatus);
|
||||
}
|
||||
|
||||
/* Update exception related CSRs */
|
||||
if (next_virt) {
|
||||
/* Update VS-mode exception info */
|
||||
csr_write(CSR_VSTVAL, tval);
|
||||
csr_write(CSR_VSEPC, epc);
|
||||
csr_write(CSR_VSCAUSE, 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, tval);
|
||||
csr_write(CSR_SEPC, epc);
|
||||
csr_write(CSR_SCAUSE, 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. Stack pointer (SP) is setup for current HART
|
||||
* 5. Interrupts are disabled in MSTATUS CSR
|
||||
*
|
||||
* @param regs pointer to register state
|
||||
* @param scratch pointer to sbi_scratch of current HART
|
||||
*/
|
||||
void sbi_trap_handler(struct sbi_trap_regs *regs, struct sbi_scratch *scratch)
|
||||
{
|
||||
int rc = SBI_ENOTSUPP;
|
||||
const char *msg = "trap handler failed";
|
||||
u32 hartid = sbi_current_hartid();
|
||||
ulong mcause = csr_read(CSR_MCAUSE);
|
||||
ulong mtval = csr_read(CSR_MTVAL);
|
||||
struct unpriv_trap *uptrap;
|
||||
|
||||
if (mcause & (1UL << (__riscv_xlen - 1))) {
|
||||
mcause &= ~(1UL << (__riscv_xlen - 1));
|
||||
switch (mcause) {
|
||||
case IRQ_M_TIMER:
|
||||
sbi_timer_process(scratch);
|
||||
break;
|
||||
case IRQ_M_SOFT:
|
||||
sbi_ipi_process(scratch);
|
||||
break;
|
||||
default:
|
||||
msg = "unhandled external interrupt";
|
||||
goto trap_error;
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mcause) {
|
||||
case CAUSE_ILLEGAL_INSTRUCTION:
|
||||
rc = sbi_illegal_insn_handler(hartid, mcause, regs, scratch);
|
||||
msg = "illegal instruction handler failed";
|
||||
break;
|
||||
case CAUSE_MISALIGNED_LOAD:
|
||||
rc = sbi_misaligned_load_handler(hartid, mcause, regs, scratch);
|
||||
msg = "misaligned load handler failed";
|
||||
break;
|
||||
case CAUSE_MISALIGNED_STORE:
|
||||
rc = sbi_misaligned_store_handler(hartid, mcause, regs,
|
||||
scratch);
|
||||
msg = "misaligned store handler failed";
|
||||
break;
|
||||
case CAUSE_SUPERVISOR_ECALL:
|
||||
case CAUSE_HYPERVISOR_ECALL:
|
||||
rc = sbi_ecall_handler(hartid, mcause, regs, scratch);
|
||||
msg = "ecall handler failed";
|
||||
break;
|
||||
case CAUSE_LOAD_ACCESS:
|
||||
case CAUSE_STORE_ACCESS:
|
||||
case CAUSE_LOAD_PAGE_FAULT:
|
||||
case CAUSE_STORE_PAGE_FAULT:
|
||||
uptrap = sbi_hart_get_trap_info(scratch);
|
||||
if ((regs->mstatus & MSTATUS_MPRV) && uptrap) {
|
||||
rc = 0;
|
||||
regs->mepc += uptrap->ilen;
|
||||
uptrap->cause = mcause;
|
||||
uptrap->tval = mtval;
|
||||
} else {
|
||||
rc = sbi_trap_redirect(regs, scratch, regs->mepc,
|
||||
mcause, mtval);
|
||||
}
|
||||
msg = "page/access fault handler failed";
|
||||
break;
|
||||
default:
|
||||
/* If the trap came from S or U mode, redirect it there */
|
||||
rc = sbi_trap_redirect(regs, scratch, regs->mepc,
|
||||
mcause, mtval);
|
||||
break;
|
||||
};
|
||||
|
||||
trap_error:
|
||||
if (rc) {
|
||||
sbi_trap_error(msg, rc, hartid, mcause, csr_read(CSR_MTVAL),
|
||||
regs);
|
||||
}
|
||||
}
|
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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_ipi.h>
|
||||
#include <sbi/sbi_system.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
|
||||
#define SBI_ECALL_VERSION_MAJOR 0
|
||||
#define SBI_ECALL_VERSION_MINOR 1
|
||||
|
||||
u16 sbi_ecall_version_major(void)
|
||||
{
|
||||
return SBI_ECALL_VERSION_MAJOR;
|
||||
}
|
||||
|
||||
u16 sbi_ecall_version_minor(void)
|
||||
{
|
||||
return SBI_ECALL_VERSION_MINOR;
|
||||
}
|
||||
|
||||
int sbi_ecall_handler(u32 hartid, ulong mcause,
|
||||
struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch)
|
||||
{
|
||||
int ret = SBI_ENOTSUPP;
|
||||
|
||||
switch (regs->a7) {
|
||||
case SBI_ECALL_SET_TIMER:
|
||||
#if __riscv_xlen == 32
|
||||
sbi_timer_event_start(scratch,
|
||||
(((u64)regs->a1 << 32) | (u64)regs->a0));
|
||||
#else
|
||||
sbi_timer_event_start(scratch, (u64)regs->a0);
|
||||
#endif
|
||||
ret = 0;
|
||||
break;
|
||||
case SBI_ECALL_CONSOLE_PUTCHAR:
|
||||
sbi_putc(regs->a0);
|
||||
ret = 0;
|
||||
break;
|
||||
case SBI_ECALL_CONSOLE_GETCHAR:
|
||||
regs->a0 = sbi_getc();
|
||||
ret = 0;
|
||||
break;
|
||||
case SBI_ECALL_CLEAR_IPI:
|
||||
sbi_ipi_clear_smode(scratch);
|
||||
ret = 0;
|
||||
break;
|
||||
case SBI_ECALL_SEND_IPI:
|
||||
ret = sbi_ipi_send_many(scratch, (ulong *)regs->a0,
|
||||
SBI_IPI_EVENT_SOFT);
|
||||
break;
|
||||
case SBI_ECALL_REMOTE_FENCE_I:
|
||||
ret = sbi_ipi_send_many(scratch, (ulong *)regs->a0,
|
||||
SBI_IPI_EVENT_FENCE_I);
|
||||
break;
|
||||
case SBI_ECALL_REMOTE_SFENCE_VMA:
|
||||
case SBI_ECALL_REMOTE_SFENCE_VMA_ASID:
|
||||
ret = sbi_ipi_send_many(scratch, (ulong *)regs->a0,
|
||||
SBI_IPI_EVENT_SFENCE_VMA);
|
||||
break;
|
||||
case SBI_ECALL_SHUTDOWN:
|
||||
sbi_system_shutdown(scratch, 0);
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
if (!ret) {
|
||||
regs->mepc += 4;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
111
lib/sbi_ipi.c
111
lib/sbi_ipi.c
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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_barrier.h>
|
||||
#include <sbi/riscv_atomic.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_bitops.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi/sbi_unpriv.h>
|
||||
|
||||
static int sbi_ipi_send(struct sbi_scratch *scratch, u32 hartid, u32 event)
|
||||
{
|
||||
struct sbi_scratch *remote_scratch = NULL;
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (sbi_platform_hart_disabled(plat, hartid))
|
||||
return -1;
|
||||
|
||||
/* Set IPI type on remote hart's scratch area and
|
||||
* trigger the interrupt
|
||||
*/
|
||||
remote_scratch = sbi_hart_id_to_scratch(scratch, hartid);
|
||||
atomic_raw_set_bit(event, &remote_scratch->ipi_type);
|
||||
mb();
|
||||
sbi_platform_ipi_send(plat, hartid);
|
||||
if (event != SBI_IPI_EVENT_SOFT)
|
||||
sbi_platform_ipi_sync(plat, hartid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sbi_ipi_send_many(struct sbi_scratch *scratch,
|
||||
ulong *pmask, u32 event)
|
||||
{
|
||||
ulong i, m;
|
||||
ulong mask = sbi_hart_available_mask();
|
||||
u32 hartid = sbi_current_hartid();
|
||||
|
||||
if (pmask)
|
||||
mask &= load_ulong(pmask, csr_read(CSR_MEPC));
|
||||
|
||||
/* send IPIs to every other hart on the set */
|
||||
for (i = 0, m = mask; m; i++, m >>= 1)
|
||||
if ((m & 1UL) && (i != hartid))
|
||||
sbi_ipi_send(scratch, i, event);
|
||||
|
||||
/* If the current hart is on the set, send an IPI
|
||||
* to it as well
|
||||
*/
|
||||
if (mask & (1UL << hartid))
|
||||
sbi_ipi_send(scratch, hartid, event);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sbi_ipi_clear_smode(struct sbi_scratch *scratch)
|
||||
{
|
||||
csr_clear(CSR_MIP, MIP_SSIP);
|
||||
}
|
||||
|
||||
void sbi_ipi_process(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
volatile unsigned long ipi_type;
|
||||
unsigned int ipi_event;
|
||||
u32 hartid = sbi_current_hartid();
|
||||
|
||||
sbi_platform_ipi_clear(plat, hartid);
|
||||
|
||||
do {
|
||||
ipi_type = scratch->ipi_type;
|
||||
rmb();
|
||||
ipi_event = __ffs(ipi_type);
|
||||
switch (ipi_event) {
|
||||
case SBI_IPI_EVENT_SOFT:
|
||||
csr_set(CSR_MIP, MIP_SSIP);
|
||||
break;
|
||||
case SBI_IPI_EVENT_FENCE_I:
|
||||
__asm__ __volatile("fence.i");
|
||||
break;
|
||||
case SBI_IPI_EVENT_SFENCE_VMA:
|
||||
__asm__ __volatile("sfence.vma");
|
||||
break;
|
||||
case SBI_IPI_EVENT_HALT:
|
||||
sbi_hart_hang();
|
||||
break;
|
||||
};
|
||||
ipi_type = atomic_raw_clear_bit(ipi_event, &scratch->ipi_type);
|
||||
} while(ipi_type > 0);
|
||||
}
|
||||
|
||||
int sbi_ipi_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
/* Enable software interrupts */
|
||||
csr_set(CSR_MIE, MIP_MSIP);
|
||||
|
||||
return sbi_platform_ipi_init(sbi_platform_ptr(scratch),
|
||||
cold_boot);
|
||||
}
|
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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_timer.h>
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
u64 get_ticks(void)
|
||||
{
|
||||
u32 lo, hi, tmp;
|
||||
__asm__ __volatile__ (
|
||||
"1:\n"
|
||||
"rdtimeh %0\n"
|
||||
"rdtime %1\n"
|
||||
"rdtimeh %2\n"
|
||||
"bne %0, %2, 1b"
|
||||
: "=&r" (hi), "=&r" (lo), "=&r" (tmp));
|
||||
return ((u64)hi << 32) | lo;
|
||||
}
|
||||
#else
|
||||
u64 get_ticks(void)
|
||||
{
|
||||
unsigned long n;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"rdtime %0"
|
||||
: "=r" (n));
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
||||
u64 sbi_timer_value(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (sbi_platform_has_timer_value(plat))
|
||||
return sbi_platform_timer_value(plat);
|
||||
else
|
||||
return get_ticks();
|
||||
}
|
||||
|
||||
void sbi_timer_event_stop(struct sbi_scratch *scratch)
|
||||
{
|
||||
sbi_platform_timer_event_stop(sbi_platform_ptr(scratch));
|
||||
}
|
||||
|
||||
void sbi_timer_event_start(struct sbi_scratch *scratch, u64 next_event)
|
||||
{
|
||||
sbi_platform_timer_event_start(sbi_platform_ptr(scratch),
|
||||
next_event);
|
||||
csr_clear(CSR_MIP, MIP_STIP);
|
||||
csr_set(CSR_MIE, MIP_MTIP);
|
||||
}
|
||||
|
||||
void sbi_timer_process(struct sbi_scratch *scratch)
|
||||
{
|
||||
csr_clear(CSR_MIE, MIP_MTIP);
|
||||
csr_set(CSR_MIP, MIP_STIP);
|
||||
}
|
||||
|
||||
int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
return sbi_platform_timer_init(sbi_platform_ptr(scratch),
|
||||
cold_boot);
|
||||
}
|
188
lib/sbi_trap.c
188
lib/sbi_trap.c
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* 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_timer.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
|
||||
static void __noreturn sbi_trap_error(const char *msg,
|
||||
int rc, u32 hartid,
|
||||
ulong mcause, ulong mtval,
|
||||
struct sbi_trap_regs *regs)
|
||||
{
|
||||
sbi_printf("%s: hart%d: %s (error %d)\n",
|
||||
__func__, hartid, msg, rc);
|
||||
sbi_printf("%s: hart%d: mcause=0x%"PRILX" mtval=0x%"PRILX"\n",
|
||||
__func__, hartid, mcause, mtval);
|
||||
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 scratch pointer to sbi_scratch of current HART
|
||||
* @param epc error PC for lower privledge mode
|
||||
* @param cause exception cause for lower privledge mode
|
||||
* @param tval trap value for lower privledge mode
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
int sbi_trap_redirect(struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch,
|
||||
ulong epc, ulong cause, ulong tval)
|
||||
{
|
||||
ulong new_mstatus, prev_mode;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Update S-mode exception info */
|
||||
csr_write(CSR_STVAL, tval);
|
||||
csr_write(CSR_SEPC, epc);
|
||||
csr_write(CSR_SCAUSE, cause);
|
||||
|
||||
/* Set MEPC to S-mode exception vector base */
|
||||
regs->mepc = csr_read(CSR_STVEC);
|
||||
|
||||
/* Initial value of new MSTATUS */
|
||||
new_mstatus = regs->mstatus;
|
||||
|
||||
/* Clear MPP, SPP, SPIE, and SIE */
|
||||
new_mstatus &= ~(MSTATUS_MPP |
|
||||
MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE);
|
||||
|
||||
/* Set SPP */
|
||||
if (prev_mode == PRV_S)
|
||||
new_mstatus |= (1UL << MSTATUS_SPP_SHIFT);
|
||||
|
||||
/* Set SPIE */
|
||||
if (regs->mstatus & MSTATUS_SIE)
|
||||
new_mstatus |= (1UL << MSTATUS_SPIE_SHIFT);
|
||||
|
||||
/* Set MPP */
|
||||
new_mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
|
||||
|
||||
/* Set new value in MSTATUS */
|
||||
regs->mstatus = new_mstatus;
|
||||
|
||||
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. Stack pointer (SP) is setup for current HART
|
||||
* 5. Interrupts are disabled in MSTATUS CSR
|
||||
*
|
||||
* @param regs pointer to register state
|
||||
* @param scratch pointer to sbi_scratch of current HART
|
||||
*/
|
||||
void sbi_trap_handler(struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch)
|
||||
{
|
||||
int rc = SBI_ENOTSUPP;
|
||||
const char *msg = "trap handler failed";
|
||||
u32 hartid = sbi_current_hartid();
|
||||
ulong mcause = csr_read(CSR_MCAUSE);
|
||||
|
||||
if (mcause & (1UL << (__riscv_xlen - 1))) {
|
||||
mcause &= ~(1UL << (__riscv_xlen - 1));
|
||||
switch (mcause) {
|
||||
case IRQ_M_TIMER:
|
||||
sbi_timer_process(scratch);
|
||||
break;
|
||||
case IRQ_M_SOFT:
|
||||
sbi_ipi_process(scratch);
|
||||
break;
|
||||
default:
|
||||
msg = "unhandled external interrupt";
|
||||
goto trap_error;
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mcause) {
|
||||
case CAUSE_ILLEGAL_INSTRUCTION:
|
||||
rc = sbi_illegal_insn_handler(hartid, mcause, regs, scratch);
|
||||
msg = "illegal instruction handler failed";
|
||||
break;
|
||||
case CAUSE_MISALIGNED_LOAD:
|
||||
rc = sbi_misaligned_load_handler(hartid, mcause, regs, scratch);
|
||||
msg = "misaligned load handler failed";
|
||||
break;
|
||||
case CAUSE_MISALIGNED_STORE:
|
||||
rc = sbi_misaligned_store_handler(hartid, mcause, regs, scratch);
|
||||
msg = "misaligned store handler failed";
|
||||
break;
|
||||
case CAUSE_SUPERVISOR_ECALL:
|
||||
case CAUSE_HYPERVISOR_ECALL:
|
||||
rc = sbi_ecall_handler(hartid, mcause, regs, scratch);
|
||||
msg = "ecall handler failed";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
trap_error:
|
||||
if (rc) {
|
||||
sbi_trap_error(msg, rc, hartid, mcause, csr_read(CSR_MTVAL), regs);
|
||||
}
|
||||
}
|
@@ -7,4 +7,4 @@
|
||||
# Anup Patel <anup.patel@wdc.com>
|
||||
#
|
||||
|
||||
platform-common-objs-$(PLATFORM_SYS_CLINT) += sys/clint.o
|
||||
libsbiutils-objs-y += irqchip/plic.o
|
118
lib/utils/irqchip/plic.c
Normal file
118
lib/utils/irqchip/plic.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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_string.h>
|
||||
#include <sbi_utils/irqchip/plic.h>
|
||||
#include <libfdt.h>
|
||||
#include <fdt.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 u32 plic_hart_count;
|
||||
static u32 plic_num_sources;
|
||||
static volatile void *plic_base;
|
||||
|
||||
static void plic_set_priority(u32 source, u32 val)
|
||||
{
|
||||
volatile void *plic_priority =
|
||||
plic_base + PLIC_PRIORITY_BASE + 4 * source;
|
||||
writel(val, plic_priority);
|
||||
}
|
||||
|
||||
void plic_set_thresh(u32 cntxid, u32 val)
|
||||
{
|
||||
volatile void *plic_thresh =
|
||||
plic_base + PLIC_CONTEXT_BASE + PLIC_CONTEXT_STRIDE * cntxid;
|
||||
writel(val, plic_thresh);
|
||||
}
|
||||
|
||||
void plic_set_ie(u32 cntxid, u32 word_index, u32 val)
|
||||
{
|
||||
volatile void *plic_ie =
|
||||
plic_base + PLIC_ENABLE_BASE + PLIC_ENABLE_STRIDE * cntxid;
|
||||
writel(val, plic_ie + word_index * 4);
|
||||
}
|
||||
|
||||
void plic_fdt_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);
|
||||
}
|
||||
}
|
||||
|
||||
int plic_warm_irqchip_init(u32 target_hart, int m_cntx_id, int s_cntx_id)
|
||||
{
|
||||
size_t i, ie_words = plic_num_sources / 32 + 1;
|
||||
|
||||
if (plic_hart_count <= target_hart)
|
||||
return -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(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(s_cntx_id, i, 0);
|
||||
}
|
||||
|
||||
/* By default, enable M-mode threshold */
|
||||
if (m_cntx_id > -1)
|
||||
plic_set_thresh(m_cntx_id, 1);
|
||||
|
||||
/* By default, disable S-mode threshold */
|
||||
if (s_cntx_id > -1)
|
||||
plic_set_thresh(s_cntx_id, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plic_cold_irqchip_init(unsigned long base, u32 num_sources, u32 hart_count)
|
||||
{
|
||||
int i;
|
||||
|
||||
plic_hart_count = hart_count;
|
||||
plic_num_sources = num_sources;
|
||||
plic_base = (void *)base;
|
||||
|
||||
/* Configure default priorities of all IRQs */
|
||||
for (i = 1; i <= plic_num_sources; i++)
|
||||
plic_set_priority(i, 1);
|
||||
|
||||
return 0;
|
||||
}
|
1
lib/utils/libfdt/.clang-format
Normal file
1
lib/utils/libfdt/.clang-format
Normal file
@@ -0,0 +1 @@
|
||||
DisableFormat: true
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user