Meta+Kernel: Automatically generate a RISC-V extension header

This header will be used to generate the RISC-V CPUFeature definition
and set the feature bits based on the "riscv,isa-extensions" devicetree
property.
This commit is contained in:
Sönke Holz committed 2025-06-03 14:09:21 +02:00
1 parent 568a02293c
commit 9a5589c695
3 files changed
+253

No files matched your search

+15
View File
@@ -537,6 +537,19 @@ elseif("${SERENITY_ARCH}" STREQUAL "aarch64")
# by the compiler. The CPU cannot access global variables without the MMU as the kernel is linked for a virtual address in high memory.
set_source_files_properties(${SOURCES_RUNNING_WITHOUT_MMU} PROPERTIES COMPILE_FLAGS "-fno-stack-protector -fno-sanitize=all")
elseif("${SERENITY_ARCH}" STREQUAL "riscv64")
find_package(Python3 COMPONENTS Interpreter REQUIRED)
set(extensions_header "${CMAKE_CURRENT_BINARY_DIR}/Arch/riscv64/Extensions.h")
add_custom_command(
OUTPUT "${extensions_header}"
COMMAND ${Python3_EXECUTABLE} "${SerenityOS_SOURCE_DIR}/Meta/riscv_extensions_generator.py" "${SerenityOS_SOURCE_DIR}/Meta/riscv-extensions.txt" -o "${extensions_header}"
DEPENDS "${SerenityOS_SOURCE_DIR}/Meta/riscv_extensions_generator.py" "${SerenityOS_SOURCE_DIR}/Meta/riscv-extensions.txt"
VERBATIM
)
add_custom_target(generate_riscv_extensions_header DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/Arch/riscv64/Extensions.h")
set(SOURCES_RUNNING_WITHOUT_MMU
Arch/riscv64/MMU.cpp
Arch/riscv64/pre_init.cpp
@@ -550,6 +563,8 @@ elseif("${SERENITY_ARCH}" STREQUAL "riscv64")
${KERNEL_SOURCES}
${SOURCES_RUNNING_WITHOUT_MMU}
"${extensions_header}"
Arch/Processor.cpp
kprintf.cpp
+108
View File
@@ -0,0 +1,108 @@
# This file is used to generate the RISC-V Extensions.h header.
# It is automatically sorted by Meta/riscv_extensions_generator.py to keep the canonical extension order defined by the RISC-V ISA manual.
# To add an extension, simply insert it anywhere in this file and run the script to sort it correctly.
# Single-letter extensions
I
M
A
F
D
Q
C
V
H
# Unprivileged extensions
Zicbom
Zicbop
Zicboz
Ziccrse
Zicntr
Zicond
Zicsr
Zifencei
Zihintntl
Zihintpause
Zihpm
Zimop
Zaamo
Zabha
Zacas
Zalrsc
Zawrs
Zfa
Zfbfmin
Zfh
Zfhmin
Zca
Zcb
Zcd
Zcf
Zcmop
Zba
Zbb
Zbc
Zbkb
Zbkc
Zbkx
Zbs
Zk
Zkn
Zknd
Zkne
Zknh
Zkr
Zks
Zksed
Zksh
Zkt
Ztso
Zvbb
Zvbc
Zve32f
Zve32x
Zve64d
Zve64f
Zve64x
Zvfbfmin
Zvfbfwma
Zvfh
Zvfhmin
Zvkb
Zvkg
Zvkn
Zvknc
Zvkned
Zvkng
Zvknha
Zvknhb
Zvks
Zvksc
Zvksed
Zvksg
Zvksh
Zvkt
# Supervisor extensions
Ssaia
Sscofpmf
Ssnpm
Sstc
Svade
Svadu
Svinval
Svnapot
Svpbmt
Svvptc
# Hypervisor extensions
# Machine extensions
Smaia
Smmpm
Smnpm
Smstateen
# Non-standard extensions
+130
View File
@@ -0,0 +1,130 @@
#!/usr/bin/env python3
import argparse
import os
from pathlib import Path
parser = argparse.ArgumentParser()
parser.add_argument("extension_file")
parser.add_argument("-o", "--output")
args = parser.parse_args()
SINGLE_LETTER_EXTENSION_ORDER = "iemafdgqlcbkjtpvh"
single_letter_extensions = set()
unprivileged_extensions = set() # Z*
supervisor_extensions = set() # Ss*, Sv*
hypervisor_extensions = set() # Sh*
machine_extensions = set() # Sm*
non_standard_extensions = set() # X*
with open(args.extension_file) as extension_file:
for line in extension_file.readlines():
extension = line.partition("#")[0].strip()
if not extension:
continue
extension = extension.lower()
if extension.startswith("z"):
unprivileged_extensions.add(extension)
elif extension.startswith("ss") or extension.startswith("sv"):
supervisor_extensions.add(extension)
elif extension.startswith("sh"):
hypervisor_extensions.add(extension)
elif extension.startswith("sm"):
machine_extensions.add(extension)
elif extension.startswith("x"):
non_standard_extensions.add(extension)
elif len(extension) == 1:
single_letter_extensions.add(extension)
else:
raise Exception(f"Unknown extension category: {extension}")
def unprivileged_extension_sort_key(extension):
# Z* extensions are sorted first by their related single-letter extension and then alphabetically.
related_single_letter_extension = extension[1]
return SINGLE_LETTER_EXTENSION_ORDER.index(related_single_letter_extension), extension[2:]
sorted_single_letter_extensions = sorted(single_letter_extensions, key=SINGLE_LETTER_EXTENSION_ORDER.index)
sorted_unprivileged_extensions = sorted(unprivileged_extensions, key=unprivileged_extension_sort_key)
sorted_supervisor_extensions = sorted(supervisor_extensions)
sorted_hypervisor_extensions = sorted(hypervisor_extensions)
sorted_machine_extensions = sorted(machine_extensions)
sorted_non_standard_extensions = sorted(non_standard_extensions)
sorted_extension_file = """\
# This file is used to generate the RISC-V Extensions.h header.
# It is automatically sorted by Meta/riscv_extensions_generator.py to keep the canonical extension order defined by the RISC-V ISA manual.
# To add an extension, simply insert it anywhere in this file and run the script to sort it correctly.
""" # noqa: E501
extension_header = """\
// This file was automatically generated by Meta/riscv_extensions_generator.py.
#pragma once
namespace Kernel {
// The extensions are sorted in canonical order, see https://github.com/riscv/riscv-isa-manual/blob/main/src/naming.adoc.
// 1st argument: extension name (used as our CPUFeature flag name)
// 2nd argument: lowercase extension name (used in the devicetree, see https://www.kernel.org/doc/Documentation/devicetree/bindings/riscv/extensions.yaml)
// 3rd argument: CPUFeature bitmask index
#define ENUMERATE_RISCV_EXTENSIONS(E) \\
""" # noqa: E501
i = 0
for extension_category, extension_list in (
("Single-letter extensions", sorted_single_letter_extensions),
("Unprivileged extensions", sorted_unprivileged_extensions),
("Supervisor extensions", sorted_supervisor_extensions),
("Hypervisor extensions", sorted_hypervisor_extensions),
("Machine extensions", sorted_machine_extensions),
("Non-standard extensions", sorted_non_standard_extensions),
):
sorted_extension_file += f"# {extension_category}\n"
for extension in extension_list:
capitalized_extension = extension[0].upper() + extension[1:]
extension_header += f" E({capitalized_extension}, {extension}, {i}) \\\n"
sorted_extension_file += capitalized_extension + "\n"
i += 1
sorted_extension_file += "\n"
# Remove the last trailing " \".
extension_header = extension_header.removesuffix(" \\\n")
extension_header += "\n\n}"
# FIXME: Share this function with TIFFGenerator.py.
def update_file(target: Path, new_content: str):
should_update = True
if target.exists():
with target.open('r') as file:
content = file.read()
if content == new_content:
should_update = False
if should_update:
with target.open('w') as file:
file.write(new_content)
extension_file_path = Path(args.extension_file)
extension_header_path = Path(args.output)
update_file(extension_file_path, sorted_extension_file)
os.makedirs(extension_header_path.parent, exist_ok=True)
update_file(extension_header_path, extension_header)