From 9bd3c542b4fc9994a9ebd8a0a38cf322eb4fc25c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Holz?= Date: Sat, 2 Sep 2023 22:52:30 +0200 Subject: [PATCH] Kernel/riscv64: Add basic SBI support --- Kernel/Arch/riscv64/SBI.cpp | 156 +++++++++++++++++++++++++++++++ Kernel/Arch/riscv64/SBI.h | 180 ++++++++++++++++++++++++++++++++++++ Kernel/CMakeLists.txt | 4 +- 3 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 Kernel/Arch/riscv64/SBI.cpp create mode 100644 Kernel/Arch/riscv64/SBI.h diff --git a/Kernel/Arch/riscv64/SBI.cpp b/Kernel/Arch/riscv64/SBI.cpp new file mode 100644 index 0000000000..e636d83dac --- /dev/null +++ b/Kernel/Arch/riscv64/SBI.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2023, Sönke Holz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include + +namespace Kernel::SBI { + +static bool s_sbi_is_legacy = false; + +static SBIErrorOr sbi_ecall0(EID extension_id, u32 function_id) +{ + register unsigned long a0 asm("a0"); + register unsigned long a1 asm("a1"); + register unsigned long a6 asm("a6") = function_id; + register unsigned long a7 asm("a7") = to_underlying(extension_id); + asm volatile("ecall" + : "=r"(a0), "=r"(a1) + : "r"(a6), "r"(a7) + : "memory"); + if (a0 == to_underlying(SBIError::Success)) + return static_cast(a1); + + return static_cast(a0); +} + +static SBIErrorOr sbi_ecall1(EID extension_id, u32 function_id, unsigned long arg0) +{ + register unsigned long a0 asm("a0") = arg0; + register unsigned long a1 asm("a1"); + register unsigned long a6 asm("a6") = function_id; + register unsigned long a7 asm("a7") = to_underlying(extension_id); + asm volatile("ecall" + : "+r"(a0), "=r"(a1) + : "r"(a0), "r"(a6), "r"(a7) + : "memory"); + if (a0 == to_underlying(SBIError::Success)) + return static_cast(a1); + + return static_cast(a0); +} + +namespace Base { + +SBIErrorOr get_spec_version() +{ + auto version = TRY(SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetSpecVersion))); + return bit_cast(static_cast(version)); +} + +SBIErrorOr get_impl_id() +{ + return SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetImplID)); +} + +SBIErrorOr get_impl_version() +{ + return SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetImplVersion)); +} + +SBIErrorOr probe_extension(EID extension_id) +{ + return SBI::sbi_ecall1(EID::Base, to_underlying(FID::ProbeExtension), to_underlying(extension_id)); +} + +SBIErrorOr get_mvendorid() +{ + return SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetMVENDORID)); +} + +SBIErrorOr get_marchid() +{ + return SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetMARCHID)); +} + +SBIErrorOr get_mimpid() +{ + return SBI::sbi_ecall0(EID::Base, to_underlying(FID::GetMIMPID)); +} + +} + +namespace Legacy { + +static long sbi_legacy_ecall1(LegacyEID extension_id, unsigned long arg0) +{ + register unsigned long a0 asm("a0") = arg0; + register unsigned long a7 asm("a7") = to_underlying(extension_id); + asm volatile("ecall" + : "+r"(a0) + : "r"(a0), "r"(a7) + : "memory"); + return static_cast(a0); +} + +LegacySBIErrorOr set_timer(u64 stime_value) +{ + auto err = sbi_legacy_ecall1(LegacyEID::SetTimer, stime_value); + if (err == 0) + return {}; + + return err; +} + +LegacySBIErrorOr console_putchar(int ch) +{ + auto err = sbi_legacy_ecall1(LegacyEID::ConsolePutchar, ch); + if (err == 0) + return {}; + + return err; +} + +} + +namespace Timer { + +SBIErrorOr set_timer(u64 stime_value) +{ + TRY(SBI::sbi_ecall1(EID::Timer, to_underlying(FID::SetTimer), stime_value)); + return {}; +} + +} + +namespace DBCN { + +SBIErrorOr debug_console_write_byte(u8 byte) +{ + TRY(SBI::sbi_ecall1(EID::DebugConsole, to_underlying(FID::DebugConsoleWriteByte), byte)); + return {}; +} + +} + +void initialize() +{ + auto spec_version = Base::get_spec_version(); + if (spec_version.is_error()) { + s_sbi_is_legacy = true; + dbgln("SBI: Specification version: 0.1"); + } else { + dbgln("SBI: Specification version: {}", spec_version.value()); + dbgln("SBI: Implementation ID: {}", MUST(Base::get_impl_id())); + dbgln("SBI: Implementation version: {:#x}", MUST(Base::get_impl_version())); + dbgln("SBI: mvendorid: {:#x}", MUST(Base::get_mvendorid())); + dbgln("SBI: marchid: {:#x}", MUST(Base::get_marchid())); + dbgln("SBI: mimpid: {:#x}", MUST(Base::get_mimpid())); + } +} + +} diff --git a/Kernel/Arch/riscv64/SBI.h b/Kernel/Arch/riscv64/SBI.h new file mode 100644 index 0000000000..218e7d434e --- /dev/null +++ b/Kernel/Arch/riscv64/SBI.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2023, Sönke Holz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +// Documentation about the SBI: +// RISC-V Supervisor Binary Interface Specification (https://github.com/riscv-non-isa/riscv-sbi-doc) + +namespace Kernel::SBI { + +// Chapter 3. Binary Encoding +enum class SBIError : long { + // SBI_SUCCESS: Completed successfully + Success = 0, + // SBI_ERR_FAILED: Failed + Failed = -1, + // SBI_ERR_NOT_SUPPORTED: Not supported + NotSupported = -2, + // SBI_ERR_INVALID_PARAM: Invalid parameter(s) + InvalidParam = -3, + // SBI_ERR_DENIED: Denied or not allowed + Denied = -4, + // SBI_ERR_INVALID_ADDRESS: Invalid address(s) + InvalidAddress = -5, + // SBI_ERR_ALREADY_AVAILABLE: Already available + AlreadyAvailable = -6, + // SBI_ERR_ALREADY_STARTED: Already started + AlreadyStarted = -7, + // SBI_ERR_ALREADY_STOPPED: Already stopped + AlreadyStopped = -8, + // SBI_ERR_NO_SHMEM: Shared memory not available + NoSHMEM = -9, +}; + +template +using SBIErrorOr = ErrorOr; + +enum class EID : i32 { + // Base Extension + Base = 0x10, + // Debug Console Extension ("DBCN") + DebugConsole = 0x4442434E, + // Timer Extension ("TIME") + Timer = 0x54494D45, +}; + +// Chapter 4. Base Extension (EID #0x10) +// Required extension since SBI v0.2 +namespace Base { + +enum class FID : i32 { + GetSpecVersion = 0, + GetImplID = 1, + GetImplVersion = 2, + ProbeExtension = 3, + GetMVENDORID = 4, + GetMARCHID = 5, + GetMIMPID = 6, +}; + +struct SpecificationVersion { + u32 minor : 24; + u32 major : 7; + u32 reserved : 1; +}; +static_assert(AssertSize()); + +// Get SBI specification version (FID #0) +// Returns the current SBI specification version. This function must always succeed. +// The minor number of the SBI specification is encoded in the low 24 bits, +// with the major number encoded in the next 7 bits. Bit 31 must be 0 and is reserved for future expansion. +SBIErrorOr get_spec_version(); + +// Get SBI implementation ID (FID #1) +// Returns the current SBI implementation ID, which is different for every SBI implementation. It is +// intended that this implementation ID allows software to probe for SBI implementation quirks. +SBIErrorOr get_impl_id(); + +// Get SBI implementation version (FID #2) +// Returns the current SBI implementation version. The encoding of this version number is specific to +// the SBI implementation. +SBIErrorOr get_impl_version(); + +// Probe SBI extension (FID #3) +// Returns 0 if the given SBI extension ID (EID) is not available, or 1 if it is available unless defined as +// any other non-zero value by the implementation. +SBIErrorOr probe_extension(EID extension_id); + +// Get machine vendor ID (FID #4) +// Return a value that is legal for the mvendorid CSR and 0 is always a legal value for this CSR. +SBIErrorOr get_mvendorid(); + +// Get machine architecture ID (FID #5) +// Return a value that is legal for the marchid CSR and 0 is always a legal value for this CSR. +SBIErrorOr get_marchid(); + +// Get machine implementation ID (FID #6) +// Return a value that is legal for the mimpid CSR and 0 is always a legal value for this CSR. +SBIErrorOr get_mimpid(); + +} + +// Chapter 5. Legacy Extensions (EIDs #0x00 - #0x0F) +namespace Legacy { + +enum class LegacyEID : i32 { + SetTimer = 0, + ConsolePutchar = 1, + ConsoleGetchar = 2, + ClearIPI = 3, + SendIPI = 4, + RemoteFENCEI = 5, + RemoteSFENCEVMA = 6, + RemoteSFENCEVMAWithASID = 7, + SystemShutdown = 8, +}; + +template +using LegacySBIErrorOr = ErrorOr; + +// Set Timer (EID #0x00) +// Programs the clock for next event after stime_value time. This function also clears the pending +// timer interrupt bit. +LegacySBIErrorOr set_timer(u64 stime_value); + +// Console Putchar (EID #0x01) +// Write data present in ch to debug console. +LegacySBIErrorOr console_putchar(int ch); + +} + +// Chapter 6. Timer Extension (EID #0x54494D45 "TIME") +// Since SBI v0.2 +namespace Timer { + +enum class FID : i32 { + SetTimer = 0, +}; + +// Set Timer (FID #0) +// Programs the clock for next event after stime_value time. stime_value is in absolute time. This +// function must clear the pending timer interrupt bit as well. +SBIErrorOr set_timer(u64 stime_value); + +} + +// Chapter 12. Debug Console Extension (EID #0x4442434E "DBCN") +// Since SBI v2.0 +namespace DBCN { + +enum class FID : i32 { + DebugConsoleWrite = 0, + DebugConsoleRead = 1, + DebugConsoleWriteByte = 2, +}; + +// Console Write Byte (FID #2) +// Write a single byte to the debug console. +SBIErrorOr debug_console_write_byte(u8 byte); + +} + +void initialize(); + +} + +template<> +struct AK::Formatter : Formatter { + ErrorOr format(FormatBuilder& builder, Kernel::SBI::Base::SpecificationVersion const& version) + { + VERIFY(version.reserved == 0); + return Formatter::format(builder, "{}.{}"sv, version.major, version.minor); + } +}; diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 4482e206ec..17d0ca76ca 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -499,9 +499,11 @@ elseif("${SERENITY_ARCH}" STREQUAL "aarch64") elseif("${SERENITY_ARCH}" STREQUAL "riscv64") set(KERNEL_SOURCES ${KERNEL_SOURCES} - Arch/riscv64/boot.S Arch/Processor.cpp kprintf.cpp + + Arch/riscv64/boot.S + Arch/riscv64/SBI.cpp ) add_compile_options(-fno-stack-protector -fno-sanitize=all)