1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-26 01:05:07 +00:00
serenity/Kernel/Arch/riscv64/CSR.h
Nico Weber 4409b33145 AK: Make IndexSequence use size_t
This makes it possible to use MakeIndexSequqnce in functions like:

    template<typename T, size_t N>
    constexpr auto foo(T (&a)[N])

This means AK/StdLibExtraDetails.h must now include AK/Types.h
for size_t, which means AK/Types.h can no longer include
AK/StdLibExtras.h (which arguably it shouldn't do anyways),
which requires rejiggering some things.

(IMHO Types.h shouldn't use AK::Details metaprogramming at all.
FlatPtr doesn't necessarily have to use Conditional<> and ssize_t could
maybe be in its own header or something. But since it's tangential to
this PR, going with the tried and true "lift things that cause the
cycle up to the top" approach.)
2024-02-11 18:53:00 +01:00

387 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2023, Sönke Holz <sholz8530@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/BitCast.h>
#include <AK/Format.h>
#include <AK/StdLibExtras.h>
#include <AK/Types.h>
#include <AK/Platform.h>
VALIDATE_IS_RISCV64()
// Documentation for the CSRs:
// RISC-V ISA Manual, Volume II (https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf)
namespace Kernel::RISCV64::CSR {
// 2.2 CSR Listing
enum class Address : u16 {
// Supervisor Trap Setup
SSTATUS = 0x100,
SIE = 0x104,
STVEC = 0x105,
// Supervisor Protection and Translation
SATP = 0x180,
// Unprivileged Counters/Timers
TIME = 0xc01,
};
ALWAYS_INLINE FlatPtr read(Address address)
{
FlatPtr ret;
asm volatile("csrr %0, %1"
: "=r"(ret)
: "i"(address));
return ret;
}
ALWAYS_INLINE void write(Address address, FlatPtr value)
{
asm volatile("csrw %0, %1" ::"i"(address), "Kr"(value));
}
ALWAYS_INLINE FlatPtr read_and_set_bits(Address address, FlatPtr bit_mask)
{
FlatPtr ret;
asm volatile("csrrs %0, %1, %2"
: "=r"(ret)
: "i"(address), "Kr"(bit_mask));
return ret;
}
ALWAYS_INLINE void set_bits(Address address, FlatPtr bit_mask)
{
asm volatile("csrs %0, %1" ::"i"(address), "Kr"(bit_mask));
}
ALWAYS_INLINE void clear_bits(Address address, FlatPtr bit_mask)
{
asm volatile("csrc %0, %1" ::"i"(address), "Kr"(bit_mask));
}
// 4.1.11 Supervisor Address Translation and Protection (satp) Register
struct [[gnu::packed]] alignas(u64) SATP {
enum class Mode : u64 {
Bare = 0,
Sv39 = 8,
Sv48 = 9,
Sv67 = 10,
};
// Physical page number of root page table
u64 PPN : 44;
// Address space identifier
u64 ASID : 16;
// Current address-translation scheme
Mode MODE : 4;
static ALWAYS_INLINE void write(SATP satp)
{
CSR::write(CSR::Address::SATP, bit_cast<FlatPtr>(satp));
}
static ALWAYS_INLINE SATP read()
{
return bit_cast<SATP>(CSR::read(CSR::Address::SATP));
}
};
static_assert(AssertSize<SATP, 8>());
// 4.1.1 Supervisor Status Register (sstatus)
struct [[gnu::packed]] alignas(u64) SSTATUS {
// Useful for CSR::{set,clear}_bits
enum class Offset {
SIE = 1,
SPIE = 5,
UBE = 6,
SPP = 8,
VS = 9,
FS = 13,
XS = 15,
SUM = 18,
MXR = 19,
UXL = 32,
SD = 63,
};
enum class PrivilegeMode : u64 {
User = 0,
Supervisor = 1,
};
enum class FloatingPointStatus : u64 {
Off = 0,
Initial = 1,
Clean = 2,
Dirty = 3,
};
enum class VectorStatus : u64 {
Off = 0,
Initial = 1,
Clean = 2,
Dirty = 3,
};
enum class UserModeExtensionsStatus : u64 {
AllOff = 0,
NoneDirtyOrClean_SomeOn = 1,
NoneDirty_SomeOn = 2,
SomeDirty = 3,
};
enum class XLEN : u64 {
Bits32 = 1,
Bits64 = 2,
Bits128 = 3,
};
u64 _reserved0 : 1;
// Enables or disables all interrupts in supervisor mode
u64 SIE : 1;
u64 _reserved2 : 3;
// Indicates whether supervisor interrupts were enabled prior to trapping into supervisor mode
// When a trap is taken into supervisor mode, SPIE is set to SIE, and SIE is set to 0. When
// an SRET instruction is executed, SIE is set to SPIE, then SPIE is set to 1.
u64 SPIE : 1;
// Controls the endianness of explicit memory accesses made from
// U-mode, which may differ from the endianness of memory accesses in S-mode
u64 UBE : 1;
u64 _reserved7 : 1;
// Indicates the privilege level at which a hart was executing before entering supervisor mode
PrivilegeMode SPP : 1;
// Encodes the status of the vector extension state, including the vector registers v0v31 and
// the CSRs vcsr, vxrm, vxsat, vstart, vl, vtype, and vlenb.
VectorStatus VS : 2;
u64 _reserved11 : 2;
// Encodes the status of the floating-point unit state,
// including the floating-point registers f0f31 and the CSRs fcsr, frm, and fflags.
FloatingPointStatus FS : 2;
// The XS field encodes the status of additional user-mode extensions and associated state.
UserModeExtensionsStatus XS : 2;
u64 _reserved17 : 1;
// The SUM (permit Supervisor User Memory access) bit modifies the privilege with which S-mode
// loads and stores access virtual memory. When SUM=0, S-mode memory accesses to pages that are
// accessible by U-mode (U=1 in Figure 5.18) will fault. When SUM=1, these accesses are permitted.
// SUM has no effect when page-based virtual memory is not in effect, nor when executing in U-mode.
// Note that S-mode can never execute instructions from user pages, regardless of the state of SUM.
u64 SUM : 1;
// The MXR (Make eXecutable Readable) bit modifies the privilege with which loads access virtual
// memory. When MXR=0, only loads from pages marked readable (R=1 in Figure 5.18) will succeed.
// When MXR=1, loads from pages marked either readable or executable (R=1 or X=1) will succeed.
// MXR has no effect when page-based virtual memory is not in effect.
u64 MXR : 1;
u64 _reserved20 : 12;
// Controls the value of XLEN for U-mode
XLEN UXL : 2;
u64 _reserved34 : 29;
// The SD bit is a read-only bit that summarizes whether either the FS, VS, or XS fields signal the
// presence of some dirty state that will require saving extended user context to memory.
u64 SD : 1;
static ALWAYS_INLINE void write(SSTATUS sstatus)
{
CSR::write(CSR::Address::SSTATUS, bit_cast<FlatPtr>(sstatus));
}
static ALWAYS_INLINE SSTATUS read()
{
return bit_cast<SSTATUS>(CSR::read(CSR::Address::SSTATUS));
}
};
static_assert(AssertSize<SSTATUS, 8>());
// 4.1.8 Supervisor Cause Register (scause)
constexpr u64 SCAUSE_INTERRUPT_MASK = 1LU << 63;
enum class SCAUSE : u64 {
// Interrupts
SupervisorSoftwareInterrupt = SCAUSE_INTERRUPT_MASK | 1,
SupervisorTimerInterrupt = SCAUSE_INTERRUPT_MASK | 5,
SupervisorExternalInterrupt = SCAUSE_INTERRUPT_MASK | 9,
// Exceptions
InstructionAddressMisaligned = 0,
InstructionAccessFault = 1,
IllegalInstrction = 2,
Breakpoint = 3,
LoadAddressMisaligned = 4,
LoadAccessFault = 5,
StoreOrAMOAddressMisaligned = 6,
StoreOrAMOAccessFault = 7,
EnvironmentCallFromUMode = 8,
EnvironmentCallFromSMode = 9,
InstructionPageFault = 12,
LoadPageFault = 13,
StoreOrAMOPageFault = 15,
};
}
template<>
struct AK::Formatter<Kernel::RISCV64::CSR::SSTATUS> : AK::Formatter<FormatString> {
ErrorOr<void> format(FormatBuilder& builder, Kernel::RISCV64::CSR::SSTATUS value)
{
if (value.SD)
TRY(builder.put_literal("SD "sv));
switch (value.UXL) {
case Kernel::RISCV64::CSR::SSTATUS::XLEN::Bits32:
TRY(builder.put_literal("UXL=32 "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::XLEN::Bits64:
TRY(builder.put_literal("UXL=64 "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::XLEN::Bits128:
TRY(builder.put_literal("UXL=128 "sv));
break;
}
if (value.MXR)
TRY(builder.put_literal("MXR "sv));
if (value.SUM)
TRY(builder.put_literal("SUM "sv));
switch (value.XS) {
case Kernel::RISCV64::CSR::SSTATUS::UserModeExtensionsStatus::AllOff:
TRY(builder.put_literal("XS=AllOff "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::UserModeExtensionsStatus::NoneDirtyOrClean_SomeOn:
TRY(builder.put_literal("XS=NoneDirtyOrClean_SomeOn "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::UserModeExtensionsStatus::NoneDirty_SomeOn:
TRY(builder.put_literal("XS=NoneDirty_SomeOn "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::UserModeExtensionsStatus::SomeDirty:
TRY(builder.put_literal("XS=SomeDirty "sv));
break;
}
switch (value.FS) {
case Kernel::RISCV64::CSR::SSTATUS::FloatingPointStatus::Off:
TRY(builder.put_literal("FS=Off "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::FloatingPointStatus::Initial:
TRY(builder.put_literal("FS=Initial "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::FloatingPointStatus::Clean:
TRY(builder.put_literal("FS=Clean "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::FloatingPointStatus::Dirty:
TRY(builder.put_literal("FS=Dirty "sv));
break;
}
switch (value.VS) {
case Kernel::RISCV64::CSR::SSTATUS::VectorStatus::Off:
TRY(builder.put_literal("VS=Off "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::VectorStatus::Initial:
TRY(builder.put_literal("VS=Initial "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::VectorStatus::Clean:
TRY(builder.put_literal("VS=Clean "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::VectorStatus::Dirty:
TRY(builder.put_literal("VS=Dirty "sv));
break;
}
switch (value.SPP) {
case Kernel::RISCV64::CSR::SSTATUS::PrivilegeMode::User:
TRY(builder.put_literal("SPP=User "sv));
break;
case Kernel::RISCV64::CSR::SSTATUS::PrivilegeMode::Supervisor:
TRY(builder.put_literal("SPP=Supervisor "sv));
break;
}
if (value.UBE)
TRY(builder.put_literal("UBE "sv));
if (value.SPIE)
TRY(builder.put_literal("SPIE "sv));
if (value.SIE)
TRY(builder.put_literal("SIE "sv));
TRY(builder.put_literal("("sv));
TRY(builder.put_u64(bit_cast<u64>(value), 16, true, false, true, false, FormatBuilder::Align::Right, 16));
TRY(builder.put_literal(")"sv));
return {};
}
};
template<>
struct AK::Formatter<Kernel::RISCV64::CSR::SCAUSE> : AK::Formatter<FormatString> {
ErrorOr<void> format(FormatBuilder& builder, Kernel::RISCV64::CSR::SCAUSE value)
{
switch (value) {
case Kernel::RISCV64::CSR::SCAUSE::SupervisorSoftwareInterrupt:
return builder.put_string("Supervisor software interrupt"sv);
case Kernel::RISCV64::CSR::SCAUSE::SupervisorTimerInterrupt:
return builder.put_string("Supervisor timer interrupt"sv);
case Kernel::RISCV64::CSR::SCAUSE::SupervisorExternalInterrupt:
return builder.put_string("Supervisor external interrupt"sv);
case Kernel::RISCV64::CSR::SCAUSE::InstructionAddressMisaligned:
return builder.put_string("Instruction address misaligned"sv);
case Kernel::RISCV64::CSR::SCAUSE::InstructionAccessFault:
return builder.put_string("Instruction access fault"sv);
case Kernel::RISCV64::CSR::SCAUSE::IllegalInstrction:
return builder.put_string("Illegal instruction"sv);
case Kernel::RISCV64::CSR::SCAUSE::Breakpoint:
return builder.put_string("Breakpoint"sv);
case Kernel::RISCV64::CSR::SCAUSE::LoadAddressMisaligned:
return builder.put_string("Load address misaligned"sv);
case Kernel::RISCV64::CSR::SCAUSE::LoadAccessFault:
return builder.put_string("Load access fault"sv);
case Kernel::RISCV64::CSR::SCAUSE::StoreOrAMOAddressMisaligned:
return builder.put_string("Store/AMO address misaligned"sv);
case Kernel::RISCV64::CSR::SCAUSE::StoreOrAMOAccessFault:
return builder.put_string("Store/AMO access fault"sv);
case Kernel::RISCV64::CSR::SCAUSE::EnvironmentCallFromUMode:
return builder.put_string("Environment call from U-mode"sv);
case Kernel::RISCV64::CSR::SCAUSE::EnvironmentCallFromSMode:
return builder.put_string("Environment call from S-mode"sv);
case Kernel::RISCV64::CSR::SCAUSE::InstructionPageFault:
return builder.put_string("Instruction page fault"sv);
case Kernel::RISCV64::CSR::SCAUSE::LoadPageFault:
return builder.put_string("Load page fault"sv);
case Kernel::RISCV64::CSR::SCAUSE::StoreOrAMOPageFault:
return builder.put_string("Store/AMO page fault"sv);
default:
VERIFY_NOT_REACHED();
}
}
};