Initial riscv64 port (#274)

* initial riscv64 port

* enable Paging Mode feature for all architectures

* riscv: add missing protocol docs

* riscv: fix tests

* docs: clarify `LIMINE_PAGING_MODE_DEFAULT` macro

* build: fix whitespace in common/GNUmakefile

* riscv: default to Sv48 paging when supported

* vmm: make `VMM_MAX_LEVEL` 1-indexed

* limine: do not call `reported_addr()` before finaling paging mode

smp/riscv: do not overwrite the argument passed to APs

* limine/riscv: update default paging mode in limine.h

* test/riscv: pad OVMF.fd when downloading it
This commit is contained in:
xvanc 2023-06-03 18:36:06 -05:00 committed by GitHub
parent e91196d452
commit 9274ee656e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1302 additions and 83 deletions

View File

@ -10,7 +10,7 @@ jobs:
steps:
- name: Install dependencies
run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S base-devel git autoconf automake nasm curl mtools llvm clang lld aarch64-linux-gnu-gcc
run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S base-devel git autoconf automake nasm curl mtools llvm clang lld aarch64-linux-gnu-gcc riscv64-linux-gnu-gcc
- name: Checkout code
uses: actions/checkout@v3
@ -26,3 +26,6 @@ jobs:
- name: Build the bootloader (GNU, aarch64)
run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=aarch64-linux-gnu --enable-werror --enable-uefi-aarch64 && make all && make maintainer-clean
- name: Build the bootloader (GNU, riscv64)
run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=riscv64-linux-gnu --enable-werror --enable-uefi-riscv64 && make all && make maintainer-clean

1
.gitignore vendored
View File

@ -40,5 +40,6 @@
/common-uefi-ia32
/common-uefi-x86-64
/common-uefi-aarch64
/common-uefi-riscv64
/decompressor-build
/stage1.stamp

View File

@ -40,6 +40,7 @@ override BUILD_BIOS := @BUILD_BIOS@
override BUILD_UEFI_X86_64 := @BUILD_UEFI_X86_64@
override BUILD_UEFI_IA32 := @BUILD_UEFI_IA32@
override BUILD_UEFI_AARCH64 := @BUILD_UEFI_AARCH64@
override BUILD_UEFI_RISCV64 := @BUILD_UEFI_RISCV64@
override BUILD_CD_EFI := @BUILD_CD_EFI@
override BUILD_PXE := @BUILD_PXE@
override BUILD_CD := @BUILD_CD@
@ -104,7 +105,7 @@ all: $(call MKESCAPE,$(BINDIR))/Makefile
$(MAKE) all1
.PHONY: all1
all1: $(BUILD_UEFI_X86_64) $(BUILD_UEFI_IA32) $(BUILD_UEFI_AARCH64) $(BUILD_BIOS)
all1: $(BUILD_UEFI_X86_64) $(BUILD_UEFI_IA32) $(BUILD_UEFI_AARCH64) $(BUILD_UEFI_RISCV64) $(BUILD_BIOS)
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine'
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine-cd-efi.bin'
@ -131,7 +132,7 @@ limine:
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine'
.PHONY: clean
clean: limine-bios-clean limine-uefi-ia32-clean limine-uefi-x86-64-clean limine-uefi-aarch64-clean
clean: limine-bios-clean limine-uefi-ia32-clean limine-uefi-x86-64-clean limine-uefi-aarch64-clean limine-uefi-riscv64-clean
rm -rf '$(call SHESCAPE,$(BINDIR))' '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp'
.PHONY: install
@ -161,6 +162,9 @@ endif
ifeq ($(BUILD_UEFI_AARCH64),limine-uefi-aarch64)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTAA64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
ifeq ($(BUILD_UEFI_RISCV64),limine-uefi-riscv64)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTRISCV64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
ifeq ($(BUILD_UEFI_X86_64),limine-uefi-x86-64)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTX64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
@ -204,7 +208,7 @@ endif
limine-bios: common-bios decompressor
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp'
$(call MKESCAPE,$(BINDIR))/limine-cd-efi.bin: $(if $(BUILD_UEFI_IA32),$(call MKESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI) $(if $(BUILD_UEFI_X86_64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI) $(if $(BUILD_UEFI_AARCH64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI)
$(call MKESCAPE,$(BINDIR))/limine-cd-efi.bin: $(if $(BUILD_UEFI_IA32),$(call MKESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI) $(if $(BUILD_UEFI_X86_64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI) $(if $(BUILD_UEFI_AARCH64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI) $(if $(BUILD_UEFI_RISCV64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI)
ifneq ($(BUILD_CD_EFI),no)
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
rm -f '$(call SHESCAPE,$(BINDIR))/limine-cd-efi.bin'
@ -214,6 +218,8 @@ ifneq ($(BUILD_CD_EFI),no)
mmd -D s -i '$(call SHESCAPE,$(BINDIR))/limine-cd-efi.bin' ::/EFI/BOOT && \
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' ] && \
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-cd-efi.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' ::/EFI/BOOT ) || true ) && \
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' ] && \
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-cd-efi.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' ::/EFI/BOOT ) || true ) && \
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' ] && \
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-cd-efi.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' ::/EFI/BOOT ) || true ) && \
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI' ] && \
@ -252,6 +258,15 @@ limine-uefi-aarch64:
$(MAKE) common-uefi-aarch64
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTAA64.EFI'
$(call MKESCAPE,$(BINDIR))/BOOTRISCV64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' '$(call SHESCAPE,$(BINDIR))/'
.PHONY: limine-uefi-riscv64
limine-uefi-riscv64:
$(MAKE) common-uefi-riscv64
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTRISCV64.EFI'
.PHONY: limine-bios-clean
limine-bios-clean: common-bios-clean decompressor-clean
@ -264,6 +279,9 @@ limine-uefi-ia32-clean: common-uefi-ia32-clean
.PHONY: limine-uefi-aarch64-clean
limine-uefi-aarch64-clean: common-uefi-aarch64-clean
.PHONY: limine-uefi-riscv64-clean
limine-uefi-riscv64-clean: common-uefi-riscv64-clean
.PHONY: dist
dist:
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)"
@ -275,7 +293,7 @@ dist:
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/freestanding-headers/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/.gitignore"
libgcc_needed="i686 x86_64-no-red-zone aarch64"; \
libgcc_needed="i686 x86_64-no-red-zone aarch64 riscv64"; \
for f in $$libgcc_needed; do \
mv '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/libgcc-$$f.a" '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/libgcc-$$f.a.save"; \
done; \
@ -328,6 +346,17 @@ common-uefi-aarch64:
common-uefi-aarch64-clean:
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64'
.PHONY: common-uefi-riscv64
common-uefi-riscv64:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/uefi-riscv64-toolchain.mk' \
TARGET=uefi-riscv64 \
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64'
.PHONY: common-uefi-riscv64-clean
common-uefi-riscv64-clean:
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64'
.PHONY: common-uefi-ia32
common-uefi-ia32:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \

View File

@ -197,6 +197,34 @@ Size Request (see below).
All other general purpose registers (including `X29` and `X30`) are set to 0. Vector registers are in an undefined state.
### riscv64
At entry the machine is executing in Supervisor mode.
`pc` will be the entry point as defined as part of the executable file format,
unless the an Entry Point feature is requested (see below), in which case,
the value of `pc` is going to be taken from there.
`x1`(`ra`) is undefined, the kernel must not return from the entry point.
`x2`(`sp`) is set to point to a stack, in bootloader-reclaimable memory, which is
at least 64KiB (65536 bytes) in size, or the size specified in the Stack
Size Request (see below).
`x3`(`gp`) is undefined, kernel must load its own global pointer if needed.
All other general purpose registers, with the exception of `x5`(`t0`), are set to 0.
If booted by EFI/UEFI, boot services are exited.
`stvec` is in an undefined state. `sstatus.SIE` and `sie` are set to 0.
`sstatus.FS` and `sstatus.XS` are both set to `Off`.
Paging is enable with the paging mode specified by the Paging Mode feature (see below).
The (A)PLIC, if present, is in an undefined state.
## Feature List
Request IDs are composed of 4 64-bit unsigned integers, but the first 2 are
@ -632,8 +660,93 @@ struct limine_video_mode {
};
```
### Paging Mode Feature
The Paging Mode feature allows the kernel to control which paging mode is enabled
before control is passed to it.
ID:
```c
#define LIMINE_PAGING_MODE_REQUEST { LIMINE_COMMON_MAGIC, 0x95c1a0edab0944cb, 0xa4e5cb3842f7488a }
```
Request:
```c
struct limine_paging_mode_request {
uint64_t id[4];
uint64_t revision;
struct limine_paging_mode_response *response;
uint64_t mode;
uint64_t flags;
};
```
Both the `mode` and `flags` fields are architecture-specific.
The `LIMINE_PAGING_MODE_DEFAULT` macro is provided by all architectures to select
the default paging mode (see below).
Response:
```c
struct limine_paging_mode_response {
uint64_t revision;
uint64_t mode;
uint64_t flags;
};
```
The response indicates which paging mode was actually enabled by the bootloader.
Kernels must be prepared to handle the case where the requested paging mode is
not supported by the hardware.
#### x86_64
Values for `mode`:
```c
#define LIMINE_PAGING_MODE_X86_64_4LVL 0
#define LIMINE_PAGING_MODE_X86_64_5LVL 1
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL
```
No `flags` are currently defined.
The default mode (when this request is not provided) is `LIMINE_PAGING_MODE_X86_64_4LVL`.
#### aarch64
Values for `mode`:
```c
#define LIMINE_PAGING_MODE_AARCH64_4LVL 0
#define LIMINE_PAGING_MODE_AARCH64_5LVL 1
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL
```
No `flags` are currently defined.
The default mode (when this request is not provided) is `LIMINE_PAGING_MODE_AARCH64_4LVL`.
#### riscv64
Values for `mode`:
```c
#define LIMINE_PAGING_MODE_RISCV_SV39 0
#define LIMINE_PAGING_MODE_RISCV_SV48 1
#define LIMINE_PAGING_MODE_RISCV_SV57 2
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48
```
No `flags` are currently defined.
The default mode (when this request is not provided) is `LIMINE_PAGING_MODE_RISCV_SV48`.
### 5-Level Paging Feature
Note: *This feature has been deprecated in favor of the [Paging Mode feature](#paging-mode-feature)
and will be removed entirely in a future release.*
ID:
```c
#define LIMINE_5_LEVEL_PAGING_REQUEST { LIMINE_COMMON_MAGIC, 0x94469551da9b3192, 0xebe5e86db7382888 }
@ -775,6 +888,53 @@ processor. This field is unused for the structure describing the bootstrap
processor.
* `extra_argument` - A free for use field.
#### riscv64
Response:
```c
struct limine_smp_response {
uint64_t revision;
uint32_t flags;
uint64_t bsp_hartid;
uint64_t cpu_count;
struct limine_smp_info **cpus;
};
```
* `flags` - Always zero
* `bsp_hartid` - Hart ID of the bootstrap processor as reported by the UEFI RISC-V Boot Protocol or the SBI.
* `cpu_count` - How many CPUs are present. It includes the bootstrap processor.
* `cpus` - Pointer to an array of `cpu_count` pointers to
`struct limine_smp_info` structures.
Notes: The presence of this request will prompt the bootloader to bootstrap
the secondary processors. This will not be done if this request is not present.
```c
struct limine_smp_info;
typedef void (*limine_goto_address)(struct limine_smp_info *);
struct limine_smp_info {
uint32_t processor_id;
uint64_t hartid;
uint64_t reserved;
limine_goto_address goto_address;
uint64_t extra_argument;
};
```
* `processor_id` - ACPI Processor UID as specified by the MADT (always 0 on non-ACPI systems).
* `hartid` - Hart ID of the processor as specified by the MADT or Device Tree.
* `goto_address` - An atomic write to this field causes the parked CPU to
jump to the written address, on a 64KiB (or Stack Size Request size) stack. A pointer to the
`struct limine_smp_info` structure of the CPU is passed in `x10`(`a0`). Other than
that, the CPU state will be the same as described for the bootstrap
processor. This field is unused for the structure describing the bootstrap
processor.
* `extra_argument` - A free for use field.
### Memory Map Feature
ID:

View File

@ -15,6 +15,7 @@ as the reference implementation for the [Limine boot protocol](/PROTOCOL.md).
* IA-32 (32-bit x86)
* x86_64
* aarch64 (arm64)
* riscv64
### Supported boot protocols
* Linux
@ -40,7 +41,7 @@ opening issues or pull requests related to this.
For 32-bit x86 systems, support is only ensured starting with those with
Pentium Pro (i686) class CPUs.
All x86_64 and aarch64 (UEFI) systems are supported.
All x86_64, aarch64, and riscv64 (UEFI) systems are supported.
## Packaging status

View File

@ -30,6 +30,8 @@ else ifeq ($(TARGET),uefi-ia32)
override OBJCOPY2ELF_FLAGS := -B i386 -O elf32-i386
else ifeq ($(TARGET),uefi-aarch64)
override OBJCOPY2ELF_FLAGS := -B aarch64 -O elf64-littleaarch64
else ifeq ($(TARGET),uefi-riscv64)
override OBJCOPY2ELF_FLAGS := -B riscv64 -O elf64-littleriscv
else
$(error Invalid target)
endif
@ -127,6 +129,26 @@ ifeq ($(TARGET),uefi-aarch64)
-DUEFI
endif
ifeq ($(TARGET),uefi-riscv64)
ifeq ($(CC_FOR_TARGET_IS_CLANG),yes)
override RISCV_CFLAGS += -march=rv64imac -mabi=lp64
else
override RISCV_CFLAGS += -march=rv64imac_zicsr_zifencei -mabi=lp64
endif
override CFLAGS_FOR_TARGET += \
-fPIE \
-fshort-wchar \
$(RISCV_CFLAGS)
override CPPFLAGS_FOR_TARGET := \
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc' \
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc/riscv64' \
$(CPPFLAGS_FOR_TARGET) \
-DUEFI \
-D__riscv64
endif
override LDFLAGS_FOR_TARGET += \
-nostdlib \
-z max-page-size=0x1000
@ -169,6 +191,15 @@ ifeq ($(TARGET),uefi-aarch64)
-z text
endif
ifeq ($(TARGET),uefi-riscv64)
override LDFLAGS_FOR_TARGET += \
-m elf64lriscv \
-static \
-pie \
--no-dynamic-linker \
-z text
endif
override C_FILES := $(shell find . -type f -name '*.c')
ifeq ($(TARGET),bios)
override ASMX86_FILES := $(shell find . -type f -name '*.asm_x86')
@ -198,6 +229,12 @@ ifeq ($(TARGET),uefi-aarch64)
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM64_FILES:.asm_aarch64=.o) $(ASM64U_FILES:.asm_uefi_aarch64=.o))
endif
ifeq ($(TARGET),uefi-riscv64)
override ASM64_FILES := $(shell find . -type f -name '*.asm_riscv64')
override ASM64U_FILES := $(shell find . -type f -name '*.asm_uefi_riscv64')
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM64_FILES:.asm_riscv64=.o) $(ASM64U_FILES:.asm_uefi_riscv64=.o))
endif
override HEADER_DEPS := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.d))
@ -211,6 +248,8 @@ else ifeq ($(TARGET),uefi-ia32)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTIA32.EFI
else ifeq ($(TARGET),uefi-aarch64)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTAA64.EFI
else ifeq ($(TARGET),uefi-riscv64)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTRISCV64.EFI
endif
ifeq ($(TARGET),bios)
@ -376,6 +415,45 @@ $(call MKESCAPE,$(BUILDDIR))/limine.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-riscv64)
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
cd '$(call SHESCAPE,$(BUILDDIR))' && \
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text'
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
$(call MKESCAPE,$(BUILDDIR))/BOOTRISCV64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-riscv64.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_riscv64.o: $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MAKE) -C '$(call SHESCAPE,$(BUILDDIR))/limine-efi/gnuefi' \
CC="$(CC_FOR_TARGET)" \
CFLAGS="$(BASE_CFLAGS) $(RISCV_CFLAGS)" \
CPPFLAGS='-nostdinc -I$(call SHESCAPE,$(SRCDIR))/../freestanding-headers' \
ARCH=riscv64
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_riscv64.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_riscv64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-riscv64.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_riscv64.o $(OBJ) ../libgcc-binaries/libgcc-riscv64.a
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_riscv64.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_riscv64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-riscv64.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_riscv64.o $(OBJ) ../libgcc-binaries/libgcc-riscv64.a $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-ia32)
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
@ -430,6 +508,12 @@ $(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-riscv64)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-ia32)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
@ -490,6 +574,16 @@ $(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_uefi_aarch64
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-riscv64)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_riscv64
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_uefi_riscv64
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-ia32)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_ia32
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"

View File

@ -0,0 +1,11 @@
.global efi_main
.extern uefi_entry
efi_main:
.option push
.option norelax
lla gp, __global_pointer$
.option pop
mv fp, zero
mv ra, zero
j uefi_entry

View File

@ -28,11 +28,14 @@
#define ARCH_X86_64 0x3e
#define ARCH_X86_32 0x03
#define ARCH_AARCH64 0xb7
#define ARCH_RISCV 0xf3
#define BITS_LE 0x01
#define ELFCLASS64 0x02
#define ET_DYN 0x0003
#define SHT_RELA 0x00000004
#define R_X86_64_RELATIVE 0x00000008
#define R_AARCH64_RELATIVE 0x00000403
#define R_RISCV_RELATIVE 0x00000003
/* Indices into identification array */
#define EI_CLASS 4
@ -103,6 +106,8 @@ int elf_bits(uint8_t *elf) {
case ARCH_X86_64:
case ARCH_AARCH64:
return 64;
case ARCH_RISCV:
return (hdr->ident[EI_CLASS] == ELFCLASS64) ? 64 : 32;
case ARCH_X86_32:
return 32;
default:
@ -226,6 +231,8 @@ static bool elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *b
case R_X86_64_RELATIVE:
#elif defined (__aarch64__)
case R_AARCH64_RELATIVE:
#elif defined (__riscv64)
case R_RISCV_RELATIVE:
#else
#error Unknown architecture
#endif
@ -281,6 +288,11 @@ bool elf64_load_section(uint8_t *elf, void *buffer, const char *name, size_t lim
printv("elf: Not an aarch64 ELF file.\n");
return false;
}
#elif defined (__riscv64)
if (hdr->machine != ARCH_RISCV && hdr->ident[EI_CLASS] == ELFCLASS64) {
printv("elf: Not a riscv64 ELF file.\n");
return false;
}
#else
#error Unknown architecture
#endif
@ -416,6 +428,10 @@ bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t
if (hdr->machine != ARCH_AARCH64) {
panic(true, "elf: Not an aarch64 ELF file.\n");
}
#elif defined (__riscv64)
if (hdr->machine != ARCH_RISCV && hdr->ident[EI_CLASS] == ELFCLASS64) {
panic(true, "elf: Not a riscv64 ELF file.\n");
}
#else
#error Unknown architecture
#endif

View File

@ -109,6 +109,46 @@ uint32_t hex2bin(uint8_t *str, uint32_t size) {
#if defined (UEFI)
#if defined (__riscv)
RISCV_EFI_BOOT_PROTOCOL *get_riscv_boot_protocol(void) {
EFI_GUID boot_proto_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
RISCV_EFI_BOOT_PROTOCOL *proto;
// LocateProtocol() is available from EFI version 1.1
if (gBS->Hdr.Revision >= ((1 << 16) | 10)) {
if (gBS->LocateProtocol(&boot_proto_guid, NULL, (void **)&proto) == EFI_SUCCESS) {
return proto;
}
}
UINTN bufsz = 0;
if (gBS->LocateHandle(ByProtocol, &boot_proto_guid, NULL, &bufsz, NULL) != EFI_BUFFER_TOO_SMALL)
return NULL;
EFI_HANDLE *handles_buf = ext_mem_alloc(bufsz);
if (handles_buf == NULL)
return NULL;
if (bufsz < sizeof(EFI_HANDLE))
goto error;
if (gBS->LocateHandle(ByProtocol, &boot_proto_guid, NULL, &bufsz, handles_buf) != EFI_SUCCESS)
goto error;
if (gBS->HandleProtocol(handles_buf[0], &boot_proto_guid, (void **)&proto) != EFI_SUCCESS)
goto error;
pmm_free(handles_buf, bufsz);
return proto;
error:
pmm_free(handles_buf, bufsz);
return NULL;
}
#endif
no_unwind bool efi_boot_services_exited = false;
bool efi_exit_boot_services(void) {
@ -162,6 +202,8 @@ retry:
asm volatile ("cli" ::: "memory");
#elif defined (__aarch64__)
asm volatile ("msr daifset, #15" ::: "memory");
#elif defined (__riscv64)
asm volatile ("csrci sstatus, 0x2" ::: "memory");
#else
#error Unknown architecture
#endif

View File

@ -10,6 +10,9 @@
#include <lib/libc.h>
#if defined (UEFI)
# include <efi.h>
# if defined (__riscv64)
# include <protocol/riscv/efiboot.h>
# endif
#endif
#if defined (UEFI)
@ -57,7 +60,7 @@ uint64_t strtoui(const char *s, const char **end, int base);
#if defined (__i386__)
void memcpy32to64(uint64_t, uint64_t, uint64_t);
#elif defined (__x86_64__) || defined (__aarch64__)
#elif defined (__x86_64__) || defined (__aarch64__) || defined(__riscv64)
# define memcpy32to64(X, Y, Z) memcpy((void *)(uintptr_t)(X), (void *)(uintptr_t)(Y), Z)
#else
#error Unknown architecture
@ -98,6 +101,11 @@ noreturn void enter_in_current_el(uint64_t entry, uint64_t sp, uint64_t sctlr,
noreturn void enter_in_el1(uint64_t entry, uint64_t sp, uint64_t sctlr,
uint64_t mair, uint64_t tcr, uint64_t ttbr0,
uint64_t ttbr1, uint64_t target_x0);
#elif defined (__riscv64)
noreturn void riscv_spinup(uint64_t entry, uint64_t sp, uint64_t satp);
#if defined (UEFI)
RISCV_EFI_BOOT_PROTOCOL *get_riscv_boot_protocol(void);
#endif
#else
#error Unknown architecture
#endif

View File

@ -74,7 +74,7 @@ noreturn void panic(bool allow_menu, const char *fmt, ...) {
for (;;) {
#if defined (__x86_64__) || defined (__i386__)
asm ("hlt");
#elif defined (__aarch64__)
#elif defined (__aarch64__) || defined (__riscv64)
asm ("wfi");
#else
#error Unknown architecture

View File

@ -0,0 +1,45 @@
.section .text
.global riscv_spinup
riscv_spinup:
csrci sstatus, 0x2
csrw sie, zero
csrw stvec, zero
mv t0, a0
mv sp, a1
csrw satp, a2
mv a0, zero
mv a1, zero
mv a2, zero
mv a3, zero
mv a4, zero
mv a5, zero
mv a6, zero
mv a7, zero
mv s0, zero
mv s1, zero
mv s2, zero
mv s3, zero
mv s4, zero
mv s5, zero
mv s6, zero
mv s7, zero
mv s8, zero
mv s9, zero
mv s10, zero
mv s11, zero
mv t1, zero
mv t2, zero
mv t3, zero
mv t4, zero
mv t5, zero
mv t6, zero
mv tp, zero
mv gp, zero
mv ra, zero
jr t0

View File

@ -346,7 +346,7 @@ void _term_write(struct flanterm_context *term, uint64_t buf, uint64_t count) {
}
bool native = false;
#if defined (__x86_64__) || defined (__aarch64__)
#if defined (__x86_64__) || defined (__aarch64__) || defined (__riscv64)
native = true;
#elif !defined (__i386__)
#error Unknown architecture

View File

@ -54,6 +54,8 @@ void print_stacktrace(size_t *base_ptr) {
"movq %%rbp, %0"
#elif defined (__aarch64__)
"mov %0, x29"
#elif defined (__riscv64)
"mv %0, fp; addi %0, %0, -16"
#endif
: "=r"(base_ptr)
:: "memory"
@ -73,7 +75,11 @@ void print_stacktrace(size_t *base_ptr) {
print(" [%p]\n", ret_addr);
if (!old_bp)
break;
#if defined (__riscv)
base_ptr = (void *)(old_bp - 16);
#else
base_ptr = (void*)old_bp;
#endif
}
print("End of trace. ");
}

View File

@ -0,0 +1,94 @@
OUTPUT_FORMAT(elf64-littleriscv)
OUTPUT_ARCH(riscv)
ENTRY(_start)
PHDRS
{
text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ;
data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ;
dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ;
}
SECTIONS
{
. = 0;
__image_base = .;
__image_size = __image_end - __image_base;
.text : {
*(.pe_header)
. = ALIGN(0x1000);
*(.text .text.*)
. = ALIGN(0x1000);
} :text
__text_start = __image_base + 0x1000;
__text_size = SIZEOF(.text) - 0x1000;
__text_end = __text_start + __text_size;
.data.sbat : {
*(.data.sbat)
. = ALIGN(0x1000);
} :data
PROVIDE(__sbat_sizev = 1);
__sbat_start = __text_end;
__sbat_size = SIZEOF(.data.sbat);
__sbat_end = __sbat_start + __sbat_size;
.data.reloc : {
*(.data.reloc)
. = ALIGN(0x1000);
} :data
__reloc_start = __sbat_end;
__reloc_size = SIZEOF(.data.reloc);
__reloc_end = __reloc_start + __reloc_size;
.data : {
*(.rodata .rodata.*)
#ifdef LINKER_NOMAP
full_map = .;
#else
*(.full_map)
#endif
*(.no_unwind)
data_begin = .;
*(.data .data.*)
*(.sdata .sdata.*)
__global_pointer$ = .;
*(.sbss .sbss.*)
*(.bss .bss.*)
*(COMMON)
data_end = .;
} :data
.rela : {
*(.rela .rela.*)
} :data
.got : {
*(.got .got.*)
} :data
.dynamic : {
*(.dynamic)
. = ALIGN(0x1000);
} :data :dynamic
__data_start = __reloc_end;
__data_size = SIZEOF(.data) + SIZEOF(.rela) + SIZEOF(.got) + SIZEOF(.dynamic);
__data_end = __data_start + __data_size;
__image_end = __data_end;
/DISCARD/ : {
*(.eh_frame)
*(.note .note.*)
}
}

View File

@ -958,21 +958,21 @@ noreturn void boot(char *config) {
linux_load(config, cmdline);
#else
quiet = false;
print("TODO: Linux is not available on aarch64.\n\n");
print("TODO: Linux is not available on aarch64 or riscv64.\n\n");
#endif
} else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) {
#if defined (__x86_64__) || defined (__i386__)
multiboot1_load(config, cmdline);
#else
quiet = false;
print("Multiboot 1 is not available on aarch64.\n\n");
print("Multiboot 1 is not available on aarch64 or riscv64.\n\n");
#endif
} else if (!strcmp(proto, "multiboot2")) {
#if defined (__x86_64__) || defined (__i386__)
multiboot2_load(config, cmdline);
#else
quiet = false;
print("Multiboot 2 is not available on aarch64.\n\n");
print("Multiboot 2 is not available on aarch64 or riscv64.\n\n");
#endif
} else if (!strcmp(proto, "chainload_next")) {
chainload_next(config, cmdline);

View File

@ -0,0 +1,21 @@
.section .data
.p2align 3
stack_at_first_entry:
.8byte 0
.section .text
.global menu
.extern _menu
menu:
lla t0, stack_at_first_entry
ld t1, (t0)
beqz t1, 1f
mv sp, t1
j 2f
1: sd sp, (t0)
2: mv fp, zero
mv ra, zero
j _menu

View File

@ -10,6 +10,7 @@
typedef uint64_t pt_entry_t;
static uint64_t page_sizes[5];
static pt_entry_t *get_next_level(pagemap_t pagemap, pt_entry_t *current_level,
uint64_t virt, enum page_size desired_sz,
size_t level_idx, size_t entry);
@ -28,9 +29,12 @@ static pt_entry_t *get_next_level(pagemap_t pagemap, pt_entry_t *current_level,
#define PT_IS_LARGE(x) (((x) & (PT_FLAG_VALID | PT_FLAG_LARGE)) == (PT_FLAG_VALID | PT_FLAG_LARGE))
#define PT_TO_VMM_FLAGS(x) ((x) & (PT_FLAG_WRITE | PT_FLAG_NX))
pagemap_t new_pagemap(int lv) {
#define pte_new(addr, flags) ((pt_entry_t)(addr) | (flags))
#define pte_addr(pte) ((pte) & PT_PADDR_MASK)
pagemap_t new_pagemap(int paging_mode) {
pagemap_t pagemap;
pagemap.levels = lv;
pagemap.levels = paging_mode == PAGING_MODE_X86_64_5LVL ? 5 : 4;
pagemap.top_level = ext_mem_alloc(PT_SIZE);
return pagemap;
}
@ -146,6 +150,9 @@ void vmm_assert_4k_pages(void) {
#define PT_IS_LARGE(x) (((x) & (PT_FLAG_VALID | PT_FLAG_TABLE)) == PT_FLAG_VALID)
#define PT_TO_VMM_FLAGS(x) (pt_to_vmm_flags_internal(x))
#define pte_new(addr, flags) ((pt_entry_t)(addr) | (flags))
#define pte_addr(pte) ((pte) & PT_PADDR_MASK)
static uint64_t pt_to_vmm_flags_internal(pt_entry_t entry) {
uint64_t flags = 0;
@ -159,9 +166,9 @@ static uint64_t pt_to_vmm_flags_internal(pt_entry_t entry) {
return flags;
}
pagemap_t new_pagemap(int lv) {
pagemap_t new_pagemap(int paging_mode) {
pagemap_t pagemap;
pagemap.levels = lv;
pagemap.levels = paging_mode == PAGING_MODE_AARCH64_5LVL ? 5 : 4;
pagemap.top_level[0] = ext_mem_alloc(PT_SIZE);
pagemap.top_level[1] = ext_mem_alloc(PT_SIZE);
return pagemap;
@ -221,57 +228,165 @@ level4:
pml1[pml1_entry] = (pt_entry_t)(phys_addr | real_flags | PT_FLAG_4K_PAGE);
}
#elif defined (__riscv64)
#define PT_FLAG_VALID ((uint64_t)1 << 0)
#define PT_FLAG_READ ((uint64_t)1 << 1)
#define PT_FLAG_WRITE ((uint64_t)1 << 2)
#define PT_FLAG_EXEC ((uint64_t)1 << 3)
#define PT_FLAG_USER ((uint64_t)1 << 4)
#define PT_FLAG_ACCESSED ((uint64_t)1 << 6)
#define PT_FLAG_DIRTY ((uint64_t)1 << 7)
#define PT_PADDR_MASK ((uint64_t)0x003ffffffffffc00)
#define PT_FLAG_RWX (PT_FLAG_READ | PT_FLAG_WRITE | PT_FLAG_EXEC)
#define PT_TABLE_FLAGS PT_FLAG_VALID
#define PT_IS_TABLE(x) (((x) & (PT_FLAG_VALID | PT_FLAG_RWX)) == PT_FLAG_VALID)
#define PT_IS_LARGE(x) (((x) & (PT_FLAG_VALID | PT_FLAG_RWX)) > PT_FLAG_VALID)
#define PT_TO_VMM_FLAGS(x) (pt_to_vmm_flags_internal(x))
#define pte_new(addr, flags) (((pt_entry_t)(addr) >> 2) | (flags))
#define pte_addr(pte) (((pte) & PT_PADDR_MASK) << 2)
static uint64_t pt_to_vmm_flags_internal(pt_entry_t entry) {
uint64_t flags = 0;
if (entry & PT_FLAG_WRITE)
flags |= VMM_FLAG_WRITE;
if (!(entry & PT_FLAG_EXEC))
flags |= VMM_FLAG_NOEXEC;
return flags;
}
uint64_t paging_mode_higher_half(int paging_mode) {
switch (paging_mode) {
case PAGING_MODE_RISCV_SV39:
return 0xffffffc000000000;
case PAGING_MODE_RISCV_SV48:
return 0xffff800000000000;
case PAGING_MODE_RISCV_SV57:
return 0xff00000000000000;
default:
panic(false, "paging_mode_higher_half: invalid mode");
}
}
int vmm_max_paging_mode(void)
{
static int max_level;
if (max_level > 0)
goto done;
pt_entry_t *table = ext_mem_alloc(PT_SIZE);
// Test each paging mode starting with Sv57.
// Since writes to `satp` with an invalid MODE have no effect, and pages can be mapped at
// any level, we can identity map the entire lower half (very likely guaranteeing everything
// this code needs will be mapped) and check if enabling the paging mode succeeds.
int lvl = 4;
for (; lvl >= 2; lvl--) {
pt_entry_t entry = PT_FLAG_ACCESSED | PT_FLAG_DIRTY | PT_FLAG_RWX | PT_FLAG_VALID;
for (int i = 0; i < 256; i++) {
table[i] = entry;
entry += page_sizes[lvl];
}
uint64_t satp = ((uint64_t)(6 + lvl) << 60) | ((uint64_t)table >> 12);
csr_write("satp", satp);
if (csr_read("satp") == satp) {
max_level = lvl;
break;
}
}
csr_write("satp", 0);
pmm_free(table, PT_SIZE);
if (max_level == 0)
panic(false, "vmm: paging is not supported");
done:
return 6 + max_level;
}
pagemap_t new_pagemap(int paging_mode) {
pagemap_t pagemap;
pagemap.paging_mode = paging_mode;
pagemap.max_page_size = paging_mode - 6;
pagemap.top_level = ext_mem_alloc(PT_SIZE);
return pagemap;
}
void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size page_size) {
// Truncate the requested page size to the maximum supported.
if (page_size > pagemap.max_page_size)
page_size = pagemap.max_page_size;
// Convert VMM_FLAG_* into PT_FLAG_*.
// Set the ACCESSED and DIRTY flags to avoid faults.
pt_entry_t ptflags = PT_FLAG_VALID | PT_FLAG_READ | PT_FLAG_ACCESSED | PT_FLAG_DIRTY;
if (flags & VMM_FLAG_WRITE)
ptflags |= PT_FLAG_WRITE;
if (!(flags & VMM_FLAG_NOEXEC))
ptflags |= PT_FLAG_EXEC;
// Start at the highest level.
// The values of `enum page_size` map to the level index at which that size is mapped.
int level = pagemap.max_page_size;
pt_entry_t *table = pagemap.top_level;
for (;;) {
int index = (virt_addr >> (12 + 9 * level)) & 0x1ff;
// Stop when we reach the level for the requested page size.
if (level == (int)page_size) {
table[index] = pte_new(phys_addr, ptflags);
break;
}
table = get_next_level(pagemap, table, virt_addr, page_size, level, index);
level--;
}
}
#else
#error Unknown architecture
#endif
// Maps level indexes to the page size for that level.
_Static_assert(VMM_MAX_LEVEL <= 5, "6-level paging not supported");
static uint64_t page_sizes[5] = {
0x1000,
0x200000,
0x40000000,
0x800000000000,
0x100000000000000,
};
static pt_entry_t *get_next_level(pagemap_t pagemap, pt_entry_t *current_level,
uint64_t virt, enum page_size desired_sz,
size_t level_idx, size_t entry) {
pt_entry_t *ret;
if (PT_IS_TABLE(current_level[entry])) {
ret = (pt_entry_t *)(size_t)(current_level[entry] & PT_PADDR_MASK);
ret = (pt_entry_t *)(size_t)pte_addr(current_level[entry]);
} else {
if (PT_IS_LARGE(current_level[entry])) {
// We are replacing an existing large page with a smaller page.
// Split the previous mapping into mappings of the newly requested size
// before performing the requested map operation.
uint64_t old_page_size, new_page_size;
switch (level_idx) {
case 2:
old_page_size = 0x40000000;
break;
case 1:
old_page_size = 0x200000;
break;
if ((level_idx >= VMM_MAX_LEVEL) || (level_idx == 0))
panic(false, "Unexpected level in get_next_level");
if (desired_sz >= VMM_MAX_LEVEL)
panic(false, "Unexpected page size in get_next_level");
default:
panic(false, "Unexpected level in get_next_level");
}
switch (desired_sz) {
case Size1GiB:
new_page_size = 0x40000000;
break;
case Size2MiB:
new_page_size = 0x200000;
break;
case Size4KiB:
new_page_size = 0x1000;
break;
default:
panic(false, "Unexpected page size in get_next_level");
}
uint64_t old_page_size = page_sizes[level_idx];
uint64_t new_page_size = page_sizes[desired_sz];
// Save all the information from the old entry at this level
uint64_t old_flags = PT_TO_VMM_FLAGS(current_level[entry]);
uint64_t old_phys = current_level[entry] & PT_PADDR_MASK;
uint64_t old_phys = pte_addr(current_level[entry]);
uint64_t old_virt = virt & ~(old_page_size - 1);
if (old_phys & (old_page_size - 1))
@ -279,7 +394,7 @@ static pt_entry_t *get_next_level(pagemap_t pagemap, pt_entry_t *current_level,
// Allocate a table for the next level
ret = ext_mem_alloc(PT_SIZE);
current_level[entry] = (pt_entry_t)(size_t)ret | PT_TABLE_FLAGS;
current_level[entry] = pte_new((size_t)ret, PT_TABLE_FLAGS);
// Recreate the old mapping with smaller pages
for (uint64_t i = 0; i < old_page_size; i += new_page_size) {
@ -288,11 +403,9 @@ static pt_entry_t *get_next_level(pagemap_t pagemap, pt_entry_t *current_level,
} else {
// Allocate a table for the next level
ret = ext_mem_alloc(PT_SIZE);
current_level[entry] = (pt_entry_t)(size_t)ret | PT_TABLE_FLAGS;
current_level[entry] = pte_new((size_t)ret, PT_TABLE_FLAGS);
}
}
return ret;
}

View File

@ -10,6 +10,19 @@
#define VMM_FLAG_NOEXEC ((uint64_t)1 << 63)
#define VMM_FLAG_FB ((uint64_t)0)
#define VMM_MAX_LEVEL 3
#define PAGING_MODE_X86_64_4LVL 0
#define PAGING_MODE_X86_64_5LVL 1
static inline uint64_t paging_mode_higher_half(int paging_mode) {
if (paging_mode == PAGING_MODE_X86_64_5LVL) {
return 0xff00000000000000;
} else {
return 0xffff800000000000;
}
}
typedef struct {
int levels;
void *top_level;
@ -32,6 +45,21 @@ void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_
#define VMM_FLAG_NOEXEC ((uint64_t)1 << 1)
#define VMM_FLAG_FB ((uint64_t)1 << 2)
#define VMM_MAX_LEVEL 3
#define PAGING_MODE_AARCH64_4LVL 0
#define PAGING_MODE_AARCH64_5LVL 1
#define paging_mode_va_bits(mode) ((mode) ? 57 : 48)
static inline uint64_t paging_mode_higher_half(int paging_mode) {
if (paging_mode == PAGING_MODE_AARCH64_5LVL) {
return 0xff00000000000000;
} else {
return 0xffff800000000000;
}
}
typedef struct {
int levels;
void *top_level[2];
@ -47,8 +75,43 @@ void vmm_assert_4k_pages(void);
pagemap_t new_pagemap(int lv);
void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size page_size);
#elif defined (__riscv64)
// We use fake flags here because these don't properly map onto the
// RISC-V flags.
#define VMM_FLAG_WRITE ((uint64_t)1 << 0)
#define VMM_FLAG_NOEXEC ((uint64_t)1 << 1)
#define VMM_FLAG_FB ((uint64_t)1 << 2)
#define VMM_MAX_LEVEL 5
#define PAGING_MODE_RISCV_SV39 8
#define PAGING_MODE_RISCV_SV48 9
#define PAGING_MODE_RISCV_SV57 10
enum page_size {
Size4KiB,
Size2MiB,
Size1GiB,
Size512GiB,
Size256TiB
};
typedef struct {
enum page_size max_page_size;
int paging_mode;
void *top_level;
} pagemap_t;
uint64_t paging_mode_higher_half(int paging_mode);
int vmm_max_paging_mode(void);
pagemap_t new_pagemap(int paging_mode);
void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, enum page_size page_size);
#else
#error Unknown architecture
#endif
int vmm_max_paging_mode(void);
#endif

View File

@ -35,10 +35,10 @@
#define MAX_REQUESTS 128
#define MAX_MEMMAP 256
static pagemap_t build_pagemap(bool level5pg, bool nx, struct elf_range *ranges, size_t ranges_count,
static pagemap_t build_pagemap(int paging_mode, bool nx, struct elf_range *ranges, size_t ranges_count,
uint64_t physical_base, uint64_t virtual_base,
uint64_t direct_map_offset) {
pagemap_t pagemap = new_pagemap(level5pg ? 5 : 4);
pagemap_t pagemap = new_pagemap(paging_mode);
if (ranges_count == 0) {
// Map 0 to 2GiB at 0xffffffff80000000
@ -183,7 +183,7 @@ extern symbol limine_spinup_32;
| ((uint64_t)1 << 8) /* TTBR0 Inner WB RW-Allocate */ \
| ((uint64_t)(tsz) << 0)) /* Address bits in TTBR0 */
#else
#elif !defined (__riscv64)
#error Unknown architecture
#endif
@ -191,6 +191,13 @@ static uint64_t physical_base, virtual_base, slide, direct_map_offset;
static size_t requests_count;
static void **requests;
static void set_paging_mode(int paging_mode, bool kaslr) {
direct_map_offset = paging_mode_higher_half(paging_mode);
if (kaslr) {
direct_map_offset += (rand64() & ~((uint64_t)0x40000000 - 1)) & 0xfffffffffff;
}
}
static uint64_t reported_addr(void *addr) {
return (uint64_t)(uintptr_t)addr + direct_map_offset;
}
@ -408,41 +415,106 @@ noreturn void limine_load(char *config, char *cmdline) {
printv("limine: ELF entry point: %X\n", entry_point);
printv("limine: Requests count: %u\n", requests_count);
// 5 level paging feature & HHDM slide
bool want_5lv;
FEAT_START
// Check if 5-level paging is available
bool level5pg = false;
// TODO(qookie): aarch64 also has optional 5 level paging when using 4K pages
// Paging Mode
int paging_mode, max_paging_mode;
#if defined (__x86_64__) || defined (__i386__)
paging_mode = max_paging_mode = PAGING_MODE_X86_64_4LVL;
if (cpuid(0x00000007, 0, &eax, &ebx, &ecx, &edx) && (ecx & (1 << 16))) {
printv("limine: CPU has 5-level paging support\n");
level5pg = true;
max_paging_mode = PAGING_MODE_X86_64_5LVL;
}
#elif defined (__aarch64__)
paging_mode = max_paging_mode = PAGING_MODE_AARCH64_4LVL;
// TODO(qookie): aarch64 also has optional 5 level paging when using 4K pages
#elif defined (__riscv64)
max_paging_mode = vmm_max_paging_mode();
paging_mode = max_paging_mode >= PAGING_MODE_RISCV_SV48 ? PAGING_MODE_RISCV_SV48 : PAGING_MODE_RISCV_SV39;
#else
#error Unknown architecture
#endif
struct limine_5_level_paging_request *lv5pg_request = get_request(LIMINE_5_LEVEL_PAGING_REQUEST);
want_5lv = lv5pg_request != NULL && level5pg;
#if defined (__riscv64)
#define paging_mode_limine_to_vmm(x) (PAGING_MODE_RISCV_SV39 + (x))
#define paging_mode_vmm_to_limine(x) ((x) - PAGING_MODE_RISCV_SV39)
#else
#define paging_mode_limine_to_vmm(x) (x)
#define paging_mode_vmm_to_limine(x) (x)
#endif
direct_map_offset = want_5lv ? 0xff00000000000000 : 0xffff800000000000;
bool have_paging_mode_request = false;
bool paging_mode_set = false;
FEAT_START
struct limine_paging_mode_request *pm_request = get_request(LIMINE_PAGING_MODE_REQUEST);
if (pm_request == NULL)
break;
have_paging_mode_request = true;
if (kaslr) {
direct_map_offset += (rand64() & ~((uint64_t)0x40000000 - 1)) & 0xfffffffffff;
if (pm_request->mode > LIMINE_PAGING_MODE_MAX) {
print("warning: ignoring invalid mode in paging mode request\n");
break;
}
if (want_5lv) {
void *lv5pg_response = ext_mem_alloc(sizeof(struct limine_5_level_paging_response));
lv5pg_request->response = reported_addr(lv5pg_response);
}
paging_mode = paging_mode_limine_to_vmm(pm_request->mode);
if (paging_mode > max_paging_mode)
paging_mode = max_paging_mode;
set_paging_mode(paging_mode, kaslr);
paging_mode_set = true;
struct limine_paging_mode_response *pm_response =
ext_mem_alloc(sizeof(struct limine_paging_mode_response));
pm_response->mode = paging_mode_vmm_to_limine(paging_mode);
pm_request->response = reported_addr(pm_response);
FEAT_END
// 5 level paging feature & HHDM slide
FEAT_START
struct limine_5_level_paging_request *lv5pg_request = get_request(LIMINE_5_LEVEL_PAGING_REQUEST);
if (lv5pg_request == NULL)
break;
if (have_paging_mode_request) {
print("paging: ignoring 5-level paging request in favor of paging mode request\n");
break;
}
#if defined (__x86_64__) || defined (__i386__)
if (max_paging_mode < PAGING_MODE_X86_64_5LVL)
break;
paging_mode = PAGING_MODE_X86_64_5LVL;
#elif defined (__aarch64__)
if (max_paging_mode < PAGING_MODE_AARCH64_5LVL)
break;
paging_mode = PAGING_MODE_AARCH64_5LVL;
#elif defined (__riscv64)
print("warning: the 5-level paging request is not supported on RISC-V\n");
#else
#error Unknown architecture
#endif
set_paging_mode(paging_mode, kaslr);
paging_mode_set = true;
void *lv5pg_response = ext_mem_alloc(sizeof(struct limine_5_level_paging_response));
lv5pg_request->response = reported_addr(lv5pg_response);
FEAT_END
if (!paging_mode_set) {
set_paging_mode(paging_mode, kaslr);
}
#if defined (__aarch64__)
uint64_t aa64mmfr0;
asm volatile ("mrs %0, id_aa64mmfr0_el1" : "=r" (aa64mmfr0));
uint64_t pa = aa64mmfr0 & 0xF;
uint64_t tsz = 64 - (want_5lv ? 57 : 48);
uint64_t tsz = 64 - paging_mode_va_bits(paging_mode);
#endif
struct limine_file *kf = ext_mem_alloc(sizeof(struct limine_file));
@ -795,7 +867,7 @@ term_fail:
#if defined (__i386__)
actual_callback = (void *)limine_term_callback;
limine_term_callback_ptr = terminal_request->callback;
#elif defined (__x86_64__) || defined (__aarch64__)
#elif defined (__x86_64__) || defined (__aarch64__) || defined (__riscv64)
actual_callback = (void *)terminal_request->callback;
#else
#error Unknown architecture
@ -811,7 +883,7 @@ term_fail:
limine_term_write_ptr = (uintptr_t)term_write_shim;
terminal_response->write = (uintptr_t)(void *)limine_term_write_entry;
#elif defined (__x86_64__) || defined (__aarch64__)
#elif defined (__x86_64__) || defined (__aarch64__) || defined (__riscv64)
terminal_response->write = (uintptr_t)term_write_shim;
#else
#error Unknown architecture
@ -1003,9 +1075,22 @@ FEAT_END
#endif
pagemap_t pagemap = {0};
pagemap = build_pagemap(want_5lv, nx_available, ranges, ranges_count,
pagemap = build_pagemap(paging_mode, nx_available, ranges, ranges_count,
physical_base, virtual_base, direct_map_offset);
#if defined (__riscv64)
// Fetch the BSP's Hart ID before exiting boot services.
size_t bsp_hartid;
bool have_bsp_hartid = false;
RISCV_EFI_BOOT_PROTOCOL *riscv_boot_proto = get_riscv_boot_protocol();
if (riscv_boot_proto != NULL) {
if (riscv_boot_proto->GetBootHartId(riscv_boot_proto, &bsp_hartid) == EFI_SUCCESS) {
have_bsp_hartid = true;
}
}
#endif
#if defined (UEFI)
efi_exit_boot_services();
#endif
@ -1022,7 +1107,7 @@ FEAT_START
#if defined (__x86_64__) || defined (__i386__)
uint32_t bsp_lapic_id;
smp_info = init_smp(&cpu_count, &bsp_lapic_id,
true, want_5lv,
true, paging_mode,
pagemap, smp_request->flags & LIMINE_SMP_X2APIC, nx_available,
direct_map_offset, true);
#elif defined (__aarch64__)
@ -1030,6 +1115,13 @@ FEAT_START
smp_info = init_smp(&cpu_count, &bsp_mpidr,
pagemap, LIMINE_MAIR(fb_attr), LIMINE_TCR(tsz, pa), LIMINE_SCTLR);
#elif defined (__riscv64)
if (!have_bsp_hartid) {
printv("smp: failed to get bsp's hart id\n");
break;
}
smp_info = init_smp(&cpu_count, bsp_hartid, pagemap);
#else
#error Unknown architecture
#endif
@ -1051,6 +1143,8 @@ FEAT_START
smp_response->bsp_lapic_id = bsp_lapic_id;
#elif defined (__aarch64__)
smp_response->bsp_mpidr = bsp_mpidr;
#elif defined (__riscv64)
smp_response->bsp_hartid = bsp_hartid;
#else
#error Unknown architecture
#endif
@ -1155,7 +1249,7 @@ FEAT_END
uint64_t reported_stack = reported_addr(stack);
common_spinup(limine_spinup_32, 8,
want_5lv, (uint32_t)(uintptr_t)pagemap.top_level,
paging_mode, (uint32_t)(uintptr_t)pagemap.top_level,
(uint32_t)entry_point, (uint32_t)(entry_point >> 32),
(uint32_t)reported_stack, (uint32_t)(reported_stack >> 32),
(uint32_t)(uintptr_t)local_gdt, nx_available);
@ -1165,6 +1259,11 @@ FEAT_END
enter_in_el1(entry_point, (uint64_t)stack, LIMINE_SCTLR, LIMINE_MAIR(fb_attr), LIMINE_TCR(tsz, pa),
(uint64_t)pagemap.top_level[0],
(uint64_t)pagemap.top_level[1], 0);
#elif defined (__riscv64)
uint64_t reported_stack = reported_addr(stack);
uint64_t satp = make_satp(pagemap.paging_mode, pagemap.top_level);
riscv_spinup(entry_point, reported_stack, satp);
#else
#error Unknown architecture
#endif

View File

@ -287,6 +287,39 @@ inline int current_el(void) {
return v;
}
#elif defined (__riscv64)
inline uint64_t rdtsc(void) {
uint64_t v;
asm ("rdtime %0" : "=r"(v));
return v;
}
#define csr_read(csr) ({\
size_t v;\
asm volatile ("csrr %0, " csr : "=r"(v));\
v;\
})
#define csr_write(csr, v) ({\
size_t old;\
asm volatile ("csrrw %0, " csr ", %1" : "=r"(old) : "r"(v));\
old;\
})
#define make_satp(mode, ppn) (((size_t)(mode) << 60) | ((size_t)(ppn) >> 12))
#define locked_read(var) ({ \
typeof(*var) locked_read__ret; \
asm volatile ( \
"ld %0, (%1); fence r, rw" \
: "=r"(locked_read__ret) \
: "r"(var) \
: "memory" \
); \
locked_read__ret; \
})
#else
#error Unknown architecture
#endif

View File

@ -0,0 +1,15 @@
.global sbicall
sbicall:
mv t0, a0
mv t1, a1
mv a0, a2
mv a1, a3
mv a2, a4
mv a3, a5
mv a4, a6
mv a5, a7
mv a7, t0
mv a6, t1
ecall
ret

32
common/sys/sbi.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef __SYS__SBI_H__
#define __SYS__SBI_H__
#include <stddef.h>
#include <stdint.h>
struct sbiret {
long error;
long value;
};
#define SBI_SUCCESS ((long)0)
#define SBI_ERR_FAILED ((long)-1)
#define SBI_ERR_NOT_SUPPORTED ((long)-2)
#define SBI_ERR_INVALID_PARAM ((long)-3)
#define SBI_ERR_DENIED ((long)-4)
#define SBI_ERR_INVALID_ADDRESS ((long)-5)
#define SBI_ERR_ALREADY_AVAILABLE ((long)-6)
#define SBI_ERR_ALREADY_STARTED ((long)-7)
#define SBI_ERR_ALREADY_STOPPED ((long)-8)
extern struct sbiret sbicall(int eid, int fid, ...);
#define SBI_EID_HSM 0x48534d
static inline struct sbiret sbi_hart_start(unsigned long hartid,
unsigned long start_addr,
unsigned long opaque) {
return sbicall(SBI_EID_HSM, 0, hartid, start_addr, opaque);
}
#endif

View File

@ -13,6 +13,9 @@
#include <mm/pmm.h>
#define LIMINE_NO_POINTERS
#include <limine.h>
#if defined (__riscv64)
#include <sys/sbi.h>
#endif
struct madt {
struct sdt header;
@ -64,6 +67,16 @@ struct madt_gicc {
uint16_t spe_overflow_gsiv;
} __attribute__((packed));
// Reference: https://github.com/riscv-non-isa/riscv-acpi/issues/15
struct madt_riscv_intc {
struct madt_header header;
uint8_t version;
uint8_t reserved;
uint32_t flags;
uint64_t hartid;
uint32_t acpi_processor_uid;
} __attribute__((packed));
#if defined (__x86_64__) || defined (__i386__)
struct trampoline_passed_info {
@ -77,7 +90,7 @@ struct trampoline_passed_info {
static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr,
struct limine_smp_info *info_struct,
bool longmode, bool lv5, uint32_t pagemap,
bool longmode, int paging_mode, uint32_t pagemap,
bool x2apic, bool nx, uint64_t hhdm, bool wp) {
// Prepare the trampoline
static void *trampoline = NULL;
@ -97,7 +110,7 @@ static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr,
passed_info->smp_tpl_booted_flag = 0;
passed_info->smp_tpl_pagemap = pagemap;
passed_info->smp_tpl_target_mode = ((uint32_t)x2apic << 2)
| ((uint32_t)lv5 << 1)
| ((uint32_t)paging_mode << 1)
| ((uint32_t)nx << 3)
| ((uint32_t)wp << 4)
| ((uint32_t)longmode << 0);
@ -137,7 +150,7 @@ static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr,
struct limine_smp_info *init_smp(size_t *cpu_count,
uint32_t *_bsp_lapic_id,
bool longmode,
bool lv5,
int paging_mode,
pagemap_t pagemap,
bool x2apic,
bool nx,
@ -244,7 +257,7 @@ struct limine_smp_info *init_smp(size_t *cpu_count,
// Try to start the AP
if (!smp_start_ap(lapic->lapic_id, &gdtr, info_struct,
longmode, lv5, (uintptr_t)pagemap.top_level,
longmode, paging_mode, (uintptr_t)pagemap.top_level,
x2apic, nx, hhdm, wp)) {
print("smp: FAILED to bring-up AP\n");
continue;
@ -281,7 +294,7 @@ struct limine_smp_info *init_smp(size_t *cpu_count,
// Try to start the AP
if (!smp_start_ap(x2lapic->x2apic_id, &gdtr, info_struct,
longmode, lv5, (uintptr_t)pagemap.top_level,
longmode, paging_mode, (uintptr_t)pagemap.top_level,
true, nx, hhdm, wp)) {
print("smp: FAILED to bring-up AP\n");
continue;
@ -556,6 +569,110 @@ struct limine_smp_info *init_smp(size_t *cpu_count,
return NULL;
}
#elif defined (__riscv64)
struct trampoline_passed_info {
uint64_t smp_tpl_booted_flag;
uint64_t smp_tpl_satp;
uint64_t smp_tpl_info_struct;
};
static bool smp_start_ap(size_t hartid, size_t satp, struct limine_smp_info *info_struct) {
static struct trampoline_passed_info passed_info;
passed_info.smp_tpl_booted_flag = 0;
passed_info.smp_tpl_satp = satp;
passed_info.smp_tpl_info_struct = (uint64_t)info_struct;
asm volatile ("" ::: "memory");
struct sbiret ret = sbi_hart_start(hartid, (size_t)smp_trampoline_start, (size_t)&passed_info);
if (ret.error != SBI_SUCCESS)
return false;
for (int i = 0; i < 1000000; i++) {
if (locked_read(&passed_info.smp_tpl_booted_flag) == 1)
return true;
}
return false;
}
struct limine_smp_info *init_smp(size_t *cpu_count,
size_t bsp_hartid,
pagemap_t pagemap) {
// No RSDP means no ACPI.
// Parsing the Device Tree is the only other method for detecting APs.
if (acpi_get_rsdp() == NULL) {
printv("smp: ACPI is required to detect APs.\n");
return NULL;
}
struct madt *madt = acpi_get_table("APIC", 0);
if (madt == NULL)
return NULL;
size_t max_cpus = 0;
for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
(uintptr_t)madt_ptr < (uintptr_t)madt + madt->header.length;
madt_ptr += *(madt_ptr + 1)) {
switch (*madt_ptr) {
case 0x18: {
struct madt_riscv_intc *intc = (void *)madt_ptr;
// Check if we can actually try to start the AP
if ((intc->flags & 1) ^ ((intc->flags >> 1) & 1))
max_cpus++;
continue;
}
}
}
struct limine_smp_info *ret = ext_mem_alloc(max_cpus * sizeof(struct limine_smp_info));
*cpu_count = 0;
// Try to start all APs
for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
(uintptr_t)madt_ptr < (uintptr_t)madt + madt->header.length;
madt_ptr += *(madt_ptr + 1)) {
switch (*madt_ptr) {
case 0x18: {
struct madt_riscv_intc *intc = (void *)madt_ptr;
// Check if we can actually try to start the AP
if (!((intc->flags & 1) ^ ((intc->flags >> 1) & 1)))
continue;
struct limine_smp_info *info_struct = &ret[*cpu_count];
info_struct->processor_id = intc->acpi_processor_uid;
info_struct->hartid = intc->hartid;
// Do not try to restart the BSP
if (intc->hartid == bsp_hartid) {
(*cpu_count)++;
continue;
}
printv("smp: Found candidate AP for bring-up. Hart ID: %u\n", intc->hartid);
// Try to start the AP.
size_t satp = make_satp(pagemap.paging_mode, pagemap.top_level);
if (!smp_start_ap(intc->hartid, satp, info_struct)) {
print("smp: FAILED to bring-up AP\n");
continue;
}
(*cpu_count)++;
continue;
}
}
}
return ret;
}
#else
#error Unknown architecture
#endif

View File

@ -13,7 +13,7 @@
struct limine_smp_info *init_smp(size_t *cpu_count,
uint32_t *_bsp_lapic_id,
bool longmode,
bool lv5,
int paging_mode,
pagemap_t pagemap,
bool x2apic,
bool nx,
@ -28,6 +28,13 @@ struct limine_smp_info *init_smp(size_t *cpu_count,
uint64_t mair,
uint64_t tcr,
uint64_t sctlr);
#elif defined (__riscv64)
struct limine_smp_info *init_smp(size_t *cpu_count,
uint64_t bsp_hartid,
pagemap_t pagemap);
#else
#error Unknown architecture
#endif

View File

@ -0,0 +1,63 @@
.global smp_trampoline_start
smp_trampoline_start:
// The AP begins executing here with the following state:
// satp = 0
// sstatus.SIE = 0
// a0 = hartid
// a1 = struct trampoline_passed_info *
//
// All other registers are undefined.
ld a0, 16(a1)
ld t0, 8(a1)
csrw satp, t0
// Tell the BSP we've started.
li t0, 1
fence rw, w
sd t0, (a1)
// Zero all the things.
// Preserve a0
mv a1, zero
mv a2, zero
mv a3, zero
mv a4, zero
mv a5, zero
mv a6, zero
mv a7, zero
mv s0, zero
mv s1, zero
mv s2, zero
mv s3, zero
mv s4, zero
mv s5, zero
mv s6, zero
mv s7, zero
mv s8, zero
mv s9, zero
mv s10, zero
mv s11, zero
mv t1, zero
mv t2, zero
mv t3, zero
mv t4, zero
mv t5, zero
mv t6, zero
mv tp, zero
mv ra, zero
csrw sie, zero
csrw stvec, zero
// Wait for kernel to tell us where to go.
0: .insn i 0x0F, 0, x0, x0, 0x010 // pause
ld t0, 24(a0)
fence r, rw
beqz t0, 0b
// Load sp from reserved field of info struct
ld sp, 16(a0)
jr t0

View File

@ -212,6 +212,34 @@ fi
AC_SUBST([BUILD_UEFI_AARCH64])
BUILD_UEFI_RISCV64="$BUILD_ALL"
AC_ARG_ENABLE([uefi-riscv64],
[AS_HELP_STRING([--enable-uefi-riscv64], [enable building the riscv64 UEFI port])],
[BUILD_UEFI_RISCV64="$enableval"])
if test "x$BUILD_UEFI_RISCV64" = "xno"; then
BUILD_UEFI_RISCV64=""
else
mkdir -p "$BUILDDIR/toolchain-files"
CC="$CC" \
ARCHITECTURE=riscv64 \
FREESTANDING_TOOLCHAIN_SUFFIX="_FOR_TARGET" \
FREESTANDING_TOOLCHAIN="$TOOLCHAIN_FOR_TARGET" \
WANT_FREESTANDING_CC=yes \
FREESTANDING_CC="$CC_FOR_TARGET" \
WANT_FREESTANDING_LD=yes \
FREESTANDING_LD="$LD_FOR_TARGET" \
WANT_FREESTANDING_OBJCOPY=yes \
FREESTANDING_OBJCOPY="$OBJCOPY_FOR_TARGET" \
WANT_FREESTANDING_OBJDUMP=yes \
FREESTANDING_OBJDUMP="$OBJDUMP_FOR_TARGET" \
"$SRCDIR/freestanding-toolchain" >"$BUILDDIR/toolchain-files/uefi-riscv64-toolchain.mk" || exit 1
BUILD_UEFI_RISCV64="limine-uefi-riscv64"
fi
AC_SUBST([BUILD_UEFI_RISCV64])
BUILD_CD_EFI="$BUILD_ALL"
AC_ARG_ENABLE([uefi-cd],

View File

@ -233,20 +233,62 @@ struct LIMINE_DEPRECATED limine_terminal_request {
LIMINE_DEPRECATED_IGNORE_END
/* Paging mode */
#define LIMINE_PAGING_MODE_REQUEST { LIMINE_COMMON_MAGIC, 0x95c1a0edab0944cb, 0xa4e5cb3842f7488a }
#if defined (__x86_64__) || defined (__i386__)
#define LIMINE_PAGING_MODE_X86_64_4LVL 0
#define LIMINE_PAGING_MODE_X86_64_5LVL 1
#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_X86_64_5LVL
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL
#elif defined (__aarch64__)
#define LIMINE_PAGING_MODE_AARCH64_4LVL 0
#define LIMINE_PAGING_MODE_AARCH64_5LVL 1
#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_AARCH64_5LVL
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL
#elif defined (__riscv) && (__riscv_xlen == 64)
#define LIMINE_PAGING_MODE_RISCV_SV39 0
#define LIMINE_PAGING_MODE_RISCV_SV48 1
#define LIMINE_PAGING_MODE_RISCV_SV57 2
#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_RISCV_SV57
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48
#else
#error Unknown architecture
#endif
struct limine_paging_mode_response {
uint64_t revision;
uint64_t mode;
uint64_t flags;
};
struct limine_paging_mode_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_paging_mode_response *) response;
uint64_t mode;
uint64_t flags;
};
/* 5-level paging */
#define LIMINE_5_LEVEL_PAGING_REQUEST { LIMINE_COMMON_MAGIC, 0x94469551da9b3192, 0xebe5e86db7382888 }
struct limine_5_level_paging_response {
LIMINE_DEPRECATED_IGNORE_START
struct LIMINE_DEPRECATED limine_5_level_paging_response {
uint64_t revision;
};
struct limine_5_level_paging_request {
struct LIMINE_DEPRECATED limine_5_level_paging_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_5_level_paging_response *) response;
};
LIMINE_DEPRECATED_IGNORE_END
/* SMP */
#define LIMINE_SMP_REQUEST { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 }
@ -294,6 +336,24 @@ struct limine_smp_response {
LIMINE_PTR(struct limine_smp_info **) cpus;
};
#elif defined (__riscv) && (__riscv_xlen == 64)
struct limine_smp_info {
uint32_t processor_id;
uint64_t hartid;
uint64_t reserved;
LIMINE_PTR(limine_goto_address) goto_address;
uint64_t extra_argument;
};
struct limine_smp_response {
uint64_t revision;
uint32_t flags;
uint64_t bsp_hartid;
uint64_t cpu_count;
LIMINE_PTR(struct limine_smp_info **) cpus;
};
#else
#error Unknown architecture
#endif

28
test.mk
View File

@ -11,6 +11,10 @@ ovmf-aa64:
mkdir -p ovmf-aa64
cd ovmf-aa64 && curl -o OVMF-AA64.zip https://efi.akeo.ie/OVMF/OVMF-AA64.zip && 7z x OVMF-AA64.zip
ovmf-rv64:
mkdir -p ovmf-rv64
cd ovmf-rv64 && curl -o OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASERISCV64_VIRT.fd && dd if=/dev/zero of=OVMF.fd bs=1 count=0 seek=33554432
ovmf-ia32:
$(MKDIR_P) ovmf-ia32
cd ovmf-ia32 && curl -o OVMF-IA32.zip https://efi.akeo.ie/OVMF/OVMF-IA32.zip && 7z x OVMF-IA32.zip
@ -236,6 +240,30 @@ uefi-aa64-test:
rm -rf test_image loopback_dev
qemu-system-aarch64 -m 512M -M virt -cpu cortex-a72 -bios ovmf-aa64/OVMF.fd -net none -smp 4 -device ramfb -device qemu-xhci -device usb-kbd -hda test.hdd -serial stdio
.PHONY: uefi-rv64-test
uefi-rv64-test:
$(MAKE) ovmf-rv64
$(MAKE) test-clean
$(MAKE) test.hdd
$(MAKE) limine-uefi-riscv64
$(MAKE) -C test TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/uefi-riscv64-toolchain.mk'
rm -rf test_image/
mkdir test_image
sudo losetup -Pf --show test.hdd > loopback_dev
sudo partprobe `cat loopback_dev`
sudo mkfs.fat -F 32 `cat loopback_dev`p1
sudo mount `cat loopback_dev`p1 test_image
sudo mkdir test_image/boot
sudo cp -rv $(BINDIR)/* test_image/boot/
sudo cp -rv test/* test_image/boot/
sudo $(MKDIR_P) test_image/EFI/BOOT
sudo cp $(BINDIR)/BOOTRISCV64.EFI test_image/EFI/BOOT/
sync
sudo umount test_image/
sudo losetup -d `cat loopback_dev`
rm -rf test_image loopback_dev
qemu-system-riscv64 -m 512M -M virt -cpu rv64 -drive if=pflash,unit=1,format=raw,file=ovmf-rv64/OVMF.fd -net none -smp 4 -device ramfb -device qemu-xhci -device usb-kbd -device virtio-blk-device,drive=hd0 -drive id=hd0,format=raw,file=test.hdd -serial stdio
.PHONY: uefi-ia32-test
uefi-ia32-test:
$(MAKE) ovmf-ia32

View File

@ -151,6 +151,16 @@ struct limine_dtb_request _dtb_request = {
__attribute__((section(".limine_reqs")))
void *dtb_req = &_dtb_request;
struct limine_paging_mode_request _pm_request = {
.id = LIMINE_PAGING_MODE_REQUEST,
.revision = 0, .response = NULL,
.mode = LIMINE_PAGING_MODE_DEFAULT,
.flags = 0,
};
__attribute__((section(".limine_reqs")))
void *pm_req = &_pm_request;
static char *get_memmap_type(uint64_t type) {
switch (type) {
case LIMINE_MEMMAP_USABLE:
@ -216,6 +226,8 @@ void ap_entry(struct limine_smp_info *info) {
#elif defined (__aarch64__)
e9_printf("My GIC CPU Interface no.: %x", info->gic_iface_no);
e9_printf("My MPIDR: %x", info->mpidr);
#elif defined (__riscv)
e9_printf("My Hart ID: %x", info->hartid);
#endif
__atomic_fetch_add(&ctr, 1, __ATOMIC_SEQ_CST);
@ -412,6 +424,8 @@ FEAT_START
e9_printf("BSP LAPIC ID: %x", smp_response->bsp_lapic_id);
#elif defined (__aarch64__)
e9_printf("BSP MPIDR: %x", smp_response->bsp_mpidr);
#elif defined (__riscv)
e9_printf("BSP Hart ID: %x", smp_response->bsp_hartid);
#endif
e9_printf("CPU count: %d", smp_response->cpu_count);
for (size_t i = 0; i < smp_response->cpu_count; i++) {
@ -422,6 +436,8 @@ FEAT_START
#elif defined (__aarch64__)
e9_printf("GIC CPU Interface no.: %x", cpu->gic_iface_no);
e9_printf("MPIDR: %x", cpu->mpidr);
#elif defined (__riscv)
e9_printf("Hart ID: %x", cpu->hartid);
#endif
@ -429,6 +445,8 @@ FEAT_START
if (cpu->lapic_id != smp_response->bsp_lapic_id) {
#elif defined (__aarch64__)
if (cpu->mpidr != smp_response->bsp_mpidr) {
#elif defined (__riscv)
if (cpu->hartid != smp_response->bsp_hartid) {
#endif
uint32_t old_ctr = __atomic_load_n(&ctr, __ATOMIC_SEQ_CST);
@ -469,5 +487,17 @@ FEAT_START
e9_printf("Device tree blob pointer: %x", dtb_response->dtb_ptr);
FEAT_END
FEAT_START
e9_printf("");
if (_pm_request.response == NULL) {
e9_printf("Paging mode not passed");
break;
}
struct limine_paging_mode_response *pm_response = _pm_request.response;
e9_printf("Paging mode feature, revision %d", pm_response->revision);
e9_printf(" mode: %d", pm_response->mode);
e9_printf(" flags: %x", pm_response->flags);
FEAT_END
for (;;);
}