From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6331351C14; Wed, 8 Apr 2026 04:46:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775623596; cv=none; b=YnZUSKNHpXxsORGlFepMqoljAuNTz6+fjz1zi/M0xdv/v/IfUQZXuDPnMoq5uNB+0kLolw74UFx+XGM12FtoGiomZp/0Zh5Hd84t94szTMU4LtN+5Xn9HxWiahiMxsTxhNVUVMertEvSRJmJh3cSgFJIxay98CEP6cjl9W0VFSQ= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775623596; c=relaxed/simple; bh=K9XWCzivDs6i48E5zsP/IuCT0gwfLQaMkAnGcOmDduA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cYR7CGxD5IRCsmXeMB9wiFc0T00LlqDR/EJ5FcxKBJSTmbgq2cxfjn58c83AqCCa2ji+nVxaCY+eNn5YidUHLi6/HB5J9JW788mQ2AJ2MXO8NncaJy1yAru+N83SxUkxKD26b/b2wsjhVWBD/wVSmY3yuU7kFJ51wvYguKn48JM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=tJqF0Wzi; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="tJqF0Wzi" Received: by smtp.kernel.org (Postfix) with ESMTPS id 40C30C2BC9E; Wed, 8 Apr 2026 04:46:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775623596; bh=K9XWCzivDs6i48E5zsP/IuCT0gwfLQaMkAnGcOmDduA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=tJqF0WziF1JhBmNSxAjfzidvBrKNiiMUpqrgEeAULpeLLxytD+KwNDPR9gplEnYDV mfFmxZY7zQ5M9A14mpRbh5T7hxFp+vUxhhX934Imf8pvFhdxYjKfp3B2vblr+04iFe UnOoJv8bjLdNkY6aCjBCqwzAF59NEoCIg/RfI6LEeoELbRNLUgI36UYQyT07JrkzZv LwiGlsaEfoXkJ67sWQfT52s8RBX4nftY0sePQO51fUhuepHmKlLfqxnX4cJsWXOZWT ZTRQMpix/xxH3PPKPkowGykPhxt29ylozfFcsRlJVLVg0ebdPBjGtHELi87HNS+u2w riWZXdo3P46pw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3892AFD5F72; Wed, 8 Apr 2026 04:46:36 +0000 (UTC) From: Charlie Jenkins via B4 Relay Date: Tue, 07 Apr 2026 21:45:49 -0700 Subject: [PATCH 01/16] riscv: Introduce instruction table generation Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260407-riscv_insn_table-v1-1-54b4736a1e77@gmail.com> References: <20260407-riscv_insn_table-v1-0-54b4736a1e77@gmail.com> In-Reply-To: <20260407-riscv_insn_table-v1-0-54b4736a1e77@gmail.com> To: Paul Walmsley , Palmer Dabbelt , Alexandre Ghiti , Anup Patel , Atish Patra , Conor Dooley , Paolo Bonzini , Andrew Morton , Shuah Khan Cc: linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, kvm@vger.kernel.org, kvm-riscv@lists.infradead.org, linux-kselftest@vger.kernel.org, Charlie Jenkins X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1775623594; l=123019; i=thecharlesjenkins@gmail.com; s=2026030; h=from:subject:message-id; bh=18hJ/oiACwr0zkyzDCquqngIbo8dkhmgkrX1hbG37TQ=; b=vnVNYnO3WuRB9g9wNVqZXinZ5tG/XmHL5HxYWhWhfKLpMk0bg6JMPAR9y+Jp/LSXrYIxXHFZ3 TI+aqKmbcd6BGvooypKAwZq5SaV8jY4F6jAjE3nlBKqVKZO8UFqSdaH X-Developer-Key: i=thecharlesjenkins@gmail.com; a=ed25519; pk=vpF2USrG+aB6CTbSt34rzJKsAVe/l+GAXo1IomCMETk= X-Endpoint-Received: by B4 Relay for thecharlesjenkins@gmail.com/2026030 with auth_id=663 X-Original-From: Charlie Jenkins Reply-To: thecharlesjenkins@gmail.com From: Charlie Jenkins Eliminate the need to hand-write riscv instructions by using a shell script to autogenerate a header from an instruction table. This is modeled after the syscall table infrastructure. The table is generated externally by riscv-unified-db [1], but is in a simple format to make it possible to use other tools or modify manually. [1] https://github.com/riscv-software-src/riscv-unified-db Signed-off-by: Charlie Jenkins --- This change immediately modifies all of the riscv_insn_* macros. I don't have a clean way of sharing my test cases but what I did was compare the old and new versions of the functions using the following test case: for (unsigned int i = 0; i < ((1ULL << 32) - 1); i++) {\ bool old = riscv_insn_is_##name(i);\ bool new = new_riscv_insn_is_##name(i);\ if (old != new) {\ printf(#name " %u\n", i);\ }\ } for (unsigned int i = 0; i < ((1ULL << 32) - 1); i++) {\ bool old = riscv_insn_is_##name(i);\ bool new = new_riscv_insn_is_##name(i);\ \ if (old != new) {\ printf(#name " %u\n", i);\ return;\ }\ } void check() { check_64(auipc) check_64(jalr) check_64(jal) check_64(beq) check_64(bne) check_64(blt) check_64(bge) check_64(bltu) check_64(bgeu) check_64(ebreak) check_64(sret) check_64(fence) check_32(c_jr) check_32(c_jalr) check_32(c_j) check_32(c_beqz) check_32(c_bnez) check_32(c_ebreak) } This does a simple brute force to check all possible numbers. The only difference with the new version is that the fence instruction refers to the literal "fence" instruction whereas the previous version could have matched on to fence, fence.i, or fence.tso. This does not make a material difference because riscv_insn_is_fence() isn't currently in use. --- arch/riscv/Makefile | 3 + arch/riscv/include/asm/Kbuild | 1 + arch/riscv/include/asm/insn.h | 72 +-- arch/riscv/tools/Makefile | 22 + arch/riscv/tools/insn.tbl | 1391 +++++++++++++++++++++++++++++++++++++++++ arch/riscv/tools/insn_tbl.sh | 263 ++++++++ 6 files changed, 1710 insertions(+), 42 deletions(-) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 4c6de57f65ef..d665b015988b 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -171,6 +171,9 @@ BOOT_TARGETS := Image Image.gz Image.bz2 Image.lz4 Image.lzma Image.lzo Image.zs all: $(notdir $(KBUILD_IMAGE)) +archprepare: + $(Q)$(MAKE) $(build)=arch/riscv/tools insn + loader.bin: loader Image.gz Image.bz2 Image.lz4 Image.lzma Image.lzo Image.zst Image.xz loader xipImage vmlinuz.efi: Image diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild index bd5fc9403295..82feba7b0eb5 100644 --- a/arch/riscv/include/asm/Kbuild +++ b/arch/riscv/include/asm/Kbuild @@ -2,6 +2,7 @@ syscall-y += syscall_table_32.h syscall-y += syscall_table_64.h +generated-y += insn_gen.h generic-y += early_ioremap.h generic-y += flat.h generic-y += fprobe.h diff --git a/arch/riscv/include/asm/insn.h b/arch/riscv/include/asm/insn.h index c3005573e8c9..d562b2b40ba1 100644 --- a/arch/riscv/include/asm/insn.h +++ b/arch/riscv/include/asm/insn.h @@ -8,6 +8,36 @@ #include +/* + * Generate a function to check if a sequence of bits matches an instruction + */ +#define __RISCV_INSN_FUNCS(name) \ +static __always_inline bool riscv_insn_is_##name(u32 _insn) \ +{ \ + BUILD_BUG_ON(~(riscv_insn_##name##_MASK) & (riscv_insn_##name##_MATCH)); \ + return (_insn & (riscv_insn_##name##_MASK)) == (riscv_insn_##name##_MATCH); \ +} + +/* + * Generate a function to check if a sequence of bits matches an instruction + * with constraints. Some instructions require inputs to be specific values. + */ +#define __RISCV_INSN_FUNCS_CONSTRAINED(name, constraints) \ +static __always_inline bool riscv_insn_is_##name(u32 _insn) \ +{ \ + BUILD_BUG_ON(~(riscv_insn_##name##_MASK) & (riscv_insn_##name##_MATCH)); \ + return ((_insn & (riscv_insn_##name##_MASK)) == (riscv_insn_##name##_MATCH)) && \ + (constraints); \ +} + +#define __RISCV_INSN_FUNCS_UNSUPPORTED(name) \ +static __always_inline bool riscv_insn_is_##name(u32 _insn) \ +{ \ + return 0; \ +} + +#include + #define RV_INSN_FUNCT3_MASK GENMASK(14, 12) #define RV_INSN_FUNCT3_OPOFF 12 #define RV_INSN_OPCODE_MASK GENMASK(6, 0) @@ -233,36 +263,6 @@ #define __INSN_OPCODE_MASK _UL(0x7F) #define __INSN_BRANCH_OPCODE _UL(RVG_OPCODE_BRANCH) -#define __RISCV_INSN_FUNCS(name, mask, val) \ -static __always_inline bool riscv_insn_is_##name(u32 code) \ -{ \ - BUILD_BUG_ON(~(mask) & (val)); \ - return (code & (mask)) == (val); \ -} \ - -#if __riscv_xlen == 32 -/* C.JAL is an RV32C-only instruction */ -__RISCV_INSN_FUNCS(c_jal, RVC_MASK_C_JAL, RVC_MATCH_C_JAL) -#else -#define riscv_insn_is_c_jal(opcode) 0 -#endif -__RISCV_INSN_FUNCS(auipc, RVG_MASK_AUIPC, RVG_MATCH_AUIPC) -__RISCV_INSN_FUNCS(jalr, RVG_MASK_JALR, RVG_MATCH_JALR) -__RISCV_INSN_FUNCS(jal, RVG_MASK_JAL, RVG_MATCH_JAL) -__RISCV_INSN_FUNCS(c_j, RVC_MASK_C_J, RVC_MATCH_C_J) -__RISCV_INSN_FUNCS(beq, RVG_MASK_BEQ, RVG_MATCH_BEQ) -__RISCV_INSN_FUNCS(bne, RVG_MASK_BNE, RVG_MATCH_BNE) -__RISCV_INSN_FUNCS(blt, RVG_MASK_BLT, RVG_MATCH_BLT) -__RISCV_INSN_FUNCS(bge, RVG_MASK_BGE, RVG_MATCH_BGE) -__RISCV_INSN_FUNCS(bltu, RVG_MASK_BLTU, RVG_MATCH_BLTU) -__RISCV_INSN_FUNCS(bgeu, RVG_MASK_BGEU, RVG_MATCH_BGEU) -__RISCV_INSN_FUNCS(c_beqz, RVC_MASK_C_BEQZ, RVC_MATCH_C_BEQZ) -__RISCV_INSN_FUNCS(c_bnez, RVC_MASK_C_BNEZ, RVC_MATCH_C_BNEZ) -__RISCV_INSN_FUNCS(c_ebreak, RVC_MASK_C_EBREAK, RVC_MATCH_C_EBREAK) -__RISCV_INSN_FUNCS(ebreak, RVG_MASK_EBREAK, RVG_MATCH_EBREAK) -__RISCV_INSN_FUNCS(sret, RVG_MASK_SRET, RVG_MATCH_SRET) -__RISCV_INSN_FUNCS(fence, RVG_MASK_FENCE, RVG_MATCH_FENCE); - /* special case to catch _any_ system instruction */ static __always_inline bool riscv_insn_is_system(u32 code) { @@ -275,18 +275,6 @@ static __always_inline bool riscv_insn_is_branch(u32 code) return (code & RV_INSN_OPCODE_MASK) == RVG_OPCODE_BRANCH; } -static __always_inline bool riscv_insn_is_c_jr(u32 code) -{ - return (code & RVC_MASK_C_JR) == RVC_MATCH_C_JR && - (code & RVC_INSN_J_RS1_MASK) != 0; -} - -static __always_inline bool riscv_insn_is_c_jalr(u32 code) -{ - return (code & RVC_MASK_C_JALR) == RVC_MATCH_C_JALR && - (code & RVC_INSN_J_RS1_MASK) != 0; -} - #define INSN_MATCH_LB 0x3 #define INSN_MASK_LB 0x707f #define INSN_MATCH_LH 0x1003 diff --git a/arch/riscv/tools/Makefile b/arch/riscv/tools/Makefile new file mode 100644 index 000000000000..5f40439c12e9 --- /dev/null +++ b/arch/riscv/tools/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 + +gen := arch/$(ARCH)/include/generated/asm +insn_tbl := $(src)/insn_tbl.sh +insn := $(src)/insn.tbl + +gen-y := $(gen)/insn_gen.h + +targets += $(addprefix ../../../,$(gen-y)) + +PHONY += insn + +insn: $(gen-y) + +# Create output directory if not already present +$(shell mkdir -p $(gen)) + +quiet_cmd_insn_tbl = INST_TBL $@ + cmd_insn_tbl = $(CONFIG_SHELL) $(insn_tbl) $< $@ + +$(gen)/insn_gen.h: $(insn) $(insn_tbl) FORCE + $(call if_changed,insn_tbl) diff --git a/arch/riscv/tools/insn.tbl b/arch/riscv/tools/insn.tbl new file mode 100644 index 000000000000..56797903b141 --- /dev/null +++ b/arch/riscv/tools/insn.tbl @@ -0,0 +1,1391 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# GENERATED WITH https://github.com/riscv-software-src/riscv-unified-db +# "bundle exec rake gen:insn_table" +# +# Each line of the instruction table should have the following format: +# NAME BASE FIXED_BITS [VARIABLE_LIST] +# NAME instruction name +# BASE instruction base size (common[,(32|64)]) +# FIXED_BITS bitfields of the fixed bits of an instruction concatenated with | +# each continuous grouping of fixed bits is in the format of bits[--][<<][!...] +# if the variable requires sign extension, surround the index to sign extend at in '-' +# if the variable requires left shifting, surround the left shift amount in '<' +# a "constraint" is an integer value that is invalid for this variable +# VARIABLE_LIST a variable sized list of all variables in the instruction definition +# in the form of name[~][_MASK useful to help check if arbitrary binary is +# - riscv_insn__MATCH useful to help check if arbitrary binary is +# - riscv_insn_ useful to construct +# - riscv_insn__ useful to extract from +# +# Each line of the instruction table should have the following format: +# +# NAME BASE FIXED_BITS [VARIABLE_LIST] +# +# NAME instruction name +# BASE instruction base size (common[,(32|64)]) +# FIXED_BITS bitfields of the fixed bits of an instruction concatenated with | +# each continuous grouping of fixed bits is in the format of bits[--][<<][!...] +# if the variable requires sign extension, surround the index to sign extend at in '-' +# if the variable requires left shifting, surround the left shift amount in '<' +# a "constraint" is an integer value that is invalid for this variable +# VARIABLE_LIST a variable sized list of all variables in the instruction definition +# in the form of name[~][&2 "usage: $0 BASE INFILE OUTFILE" >&2 + echo >&2 + echo >&2 " INFILE input instruction table" + echo >&2 " OUTFILE output header file" + exit 1 +} + +if [ $# -ne 2 ]; then + usage +fi + +infile="$1" +outfile="$2" + +file=$(readlink -f $0) + +echo "/* Auto-generated rv${base} header from script arch/${file#*arch/} */" > $outfile + +echo "#ifndef RISCV_INSN_GEN_H" >> $outfile +echo "#define RISCV_INSN_GEN_H" >> $outfile +echo >> $outfile + +printf "#include " >> $outfile +echo >> $outfile +echo "#define COMMA ," >> $outfile +echo "#define SEMICOLON ;" >> $outfile +echo "#define SINGLE_ARG(...) __VA_ARGS__" >> $outfile +echo >> $outfile + +grep -E "^[a-z\.0-9]+[[:space:]]+" "$infile" | { + while read name base fixed variables; do + echo "/* $name */" + + compressed_name=${name##c.*} + invalid_inst_functions="" + variable_params="" + constraints="" + match="" + mask="" + make="" + + # All compressed instructions start with "c." + size=${compressed_name:+32}; + size=${size:-16}; + + # Replace all . with _ + formatted_inst_name=$name + while [ ! ${formatted_inst_name##*.*} ]; do + prefix=${formatted_inst_name%.*} + suffix=${formatted_inst_name##*.} + contains_dot=${formatted_inst_name##*.*} + formatted_inst_name=${contains_dot:-${prefix}_${suffix}} + done + + # Collect all fixed bits of an instruction + OLD_IFS=$IFS + IFS='|' + for segment in $fixed; do + bits=${segment%<*} + offset=${segment#*<} + + len=${#bits} + + mask="${mask} | 0b" + + while [ $len -gt 0 ]; do + len=$((len - 1)) + mask=${mask}1 + done + + if [ ${offset} -gt 0 ]; then + s=" << ${offset}" + else + s="" + fi + + mask="${mask}${s}" + + match="${match} | 0b${bits}${s}" + done + IFS=$OLD_IFS + + # Instruction only appears in one base + only_base= + if [ "${base}" != "${base%32}" ]; then + echo "#if __riscv_xlen == 32" + only_base=32 + elif [ "${base}" != "${base%64}" ]; then + echo "#if __riscv_xlen == 64" + only_base=64 + fi + + # Standard name for the instruction parameter in generated functions + insn="_insn" + + for variable in ${variables}; do + variable_name="${variable%%[<~=!]*}" + parts="${variable#*=}" + insert_mask="" + sign_extend="" + left_shift="" + extract="" + insert="" + + # Standard name for the variable parameter in generated functions + var="_${variable_name}" + variable_params="${variable_params}u32 ${var}, " + + if [ "${variable}" != "${variable#*~}" ]; then + sign_extend="1" + fi + + if [ "${variable}" != "${variable#*<}" ]; then + left_shift="${variable#*<}" + left_shift="${left_shift%%[=<~!]*}" + else + left_shift="0" + fi + + if [ "${variable}" != "${variable#*!}" ]; then + raw_constraints="${variable#*!}" + raw_constraints="${raw_constraints%%[=<~!]**}" + + OLD_IFS=$IFS + IFS='!' + for constraint in $raw_constraints; do + constraints="${constraints}(riscv_insn_${formatted_inst_name}_extract_${variable_name}(${insn}) != ${constraint}) && " + done + IFS=$OLD_IFS + fi + + offset=0 + while true; do + part=${parts##*|} + + if [ "${part#*-}" = "${part}" ]; then + high="${part}" + low="${part}" + len=1 + else + high="${part%-*}" + low="${part#*-}" + len=$((high - low + 1)) + fi + + # Don't emit shift if 0 + first_shift=${low} + if [ "${first_shift}" = "0" ]; then + first_shift= + fi + + second_shift=$((offset + left_shift)) + if [ "${second_shift}" = "0" ]; then + second_shift= + fi + + extract="${extract} | ((${insn}${first_shift:+ >> }${first_shift} & GENMASK($((len - 1)), 0))${second_shift:+ << }${second_shift})" + insert_mask="${insert_mask} & ~GENMASK(${high}, ${low})" + insert="${insert} | (((${var}${second_shift:+ >> }${second_shift}) & GENMASK($((len - 1)), 0))${first_shift:+ << }${first_shift})" + offset=$((offset + len)) + + if [ "${parts}" = "${part}" ]; then + # Processed all parts of variable + break + fi + + parts=${parts%|*} + done + + extract="${extract# | }" + + if [ ${sign_extend} ]; then + extract="sign_extend32(${extract}, ${offset})" + type="s" + else + type="u" + fi + + echo "static __always_inline ${type}${size} riscv_insn_${formatted_inst_name}_extract_${variable_name}(u${size} ${insn})" + echo "{" + echo "\treturn ${extract};" + echo "}" + echo "static __always_inline void riscv_insn_${formatted_inst_name}_insert_${variable_name}(u${size} *${insn}, ${type}32 ${var})" + echo "{" + echo "\t*_insn &= ${insert_mask# & };" + echo "\t*_insn |= ${insert# | };" + echo "}" + + if [ "${only_base}" ]; then + invalid_inst_functions="${invalid_inst_functions}static __always_inline ${type}${size} riscv_insn_${formatted_inst_name}_extract_${variable_name}(u${size} ${insn}) {\n\tpanic(\"${name} is not supported on non ${only_base}-bit systems.\");\n}\n" + fi + + make="${make} riscv_insn_${formatted_inst_name}_insert_${variable_name}(&${insn}, ${var});\n" + done + + variable_params="${variable_params%, }" + variable_params="${variable_params:-void}" + + echo "#define riscv_insn_${formatted_inst_name}_MASK (${mask# | })" + echo "#define riscv_insn_${formatted_inst_name}_MATCH (${match# | })" + echo "static __always_inline u${size} riscv_insn_${formatted_inst_name}(${variable_params})" + echo "{" + echo "\tu${size} ${insn} = riscv_insn_${formatted_inst_name}_MATCH;" + echo "${make} return ${insn};" + echo "}" + + # Check against instructions that have a variable that may contain invalid values + if [ "$constraints" ]; then + echo "__RISCV_INSN_FUNCS_CONSTRAINED(${formatted_inst_name}, ${constraints% && });" + else + echo "__RISCV_INSN_FUNCS(${formatted_inst_name});" + fi + + # If common does not appear in the base, then this instruction only appears in one base + if [ "$base" = "${base#common}" ]; then + echo "#else" + echo "__RISCV_INSN_FUNCS_UNSUPPORTED(${formatted_inst_name});" + echo "${invalid_inst_functions%\\n}" + fi + + # Instruction has a base variant + if [ "$base" != "${base%[24]}" ]; then + echo "#endif" + fi + + echo + done + + echo "#endif /* RISCV_INST_GEN_H */" +} >> $outfile -- 2.52.0