diff --git a/disas/meson.build b/disas/meson.build
index 3a480eb9f8..815523ab85 100644
--- a/disas/meson.build
+++ b/disas/meson.build
@@ -6,7 +6,11 @@ common_ss.add(when: 'CONFIG_M68K_DIS', if_true: files('m68k.c'))
common_ss.add(when: 'CONFIG_MICROBLAZE_DIS', if_true: files('microblaze.c'))
common_ss.add(when: 'CONFIG_MIPS_DIS', if_true: files('mips.c', 'nanomips.c'))
common_ss.add(when: 'CONFIG_NIOS2_DIS', if_true: files('nios2.c'))
-common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files('riscv.c'))
+common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files(
+ 'riscv.c',
+ 'riscv-xthead.c',
+ 'riscv-xventana.c'
+))
common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c'))
common_ss.add(when: 'CONFIG_SPARC_DIS', if_true: files('sparc.c'))
common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c'))
diff --git a/disas/riscv-xthead.c b/disas/riscv-xthead.c
new file mode 100644
index 0000000000..99da679d16
--- /dev/null
+++ b/disas/riscv-xthead.c
@@ -0,0 +1,707 @@
+/*
+ * QEMU RISC-V Disassembler for xthead.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "disas/riscv.h"
+#include "disas/riscv-xthead.h"
+
+typedef enum {
+ /* 0 is reserved for rv_op_illegal. */
+ /* XTheadBa */
+ rv_op_th_addsl = 1,
+ /* XTheadBb */
+ rv_op_th_srri,
+ rv_op_th_srriw,
+ rv_op_th_ext,
+ rv_op_th_extu,
+ rv_op_th_ff0,
+ rv_op_th_ff1,
+ rv_op_th_rev,
+ rv_op_th_revw,
+ rv_op_th_tstnbz,
+ /* XTheadBs */
+ rv_op_th_tst,
+ /* XTheadCmo */
+ rv_op_th_dcache_call,
+ rv_op_th_dcache_ciall,
+ rv_op_th_dcache_iall,
+ rv_op_th_dcache_cpa,
+ rv_op_th_dcache_cipa,
+ rv_op_th_dcache_ipa,
+ rv_op_th_dcache_cva,
+ rv_op_th_dcache_civa,
+ rv_op_th_dcache_iva,
+ rv_op_th_dcache_csw,
+ rv_op_th_dcache_cisw,
+ rv_op_th_dcache_isw,
+ rv_op_th_dcache_cpal1,
+ rv_op_th_dcache_cval1,
+ rv_op_th_icache_iall,
+ rv_op_th_icache_ialls,
+ rv_op_th_icache_ipa,
+ rv_op_th_icache_iva,
+ rv_op_th_l2cache_call,
+ rv_op_th_l2cache_ciall,
+ rv_op_th_l2cache_iall,
+ /* XTheadCondMov */
+ rv_op_th_mveqz,
+ rv_op_th_mvnez,
+ /* XTheadFMemIdx */
+ rv_op_th_flrd,
+ rv_op_th_flrw,
+ rv_op_th_flurd,
+ rv_op_th_flurw,
+ rv_op_th_fsrd,
+ rv_op_th_fsrw,
+ rv_op_th_fsurd,
+ rv_op_th_fsurw,
+ /* XTheadFmv */
+ rv_op_th_fmv_hw_x,
+ rv_op_th_fmv_x_hw,
+ /* XTheadMac */
+ rv_op_th_mula,
+ rv_op_th_mulah,
+ rv_op_th_mulaw,
+ rv_op_th_muls,
+ rv_op_th_mulsw,
+ rv_op_th_mulsh,
+ /* XTheadMemIdx */
+ rv_op_th_lbia,
+ rv_op_th_lbib,
+ rv_op_th_lbuia,
+ rv_op_th_lbuib,
+ rv_op_th_lhia,
+ rv_op_th_lhib,
+ rv_op_th_lhuia,
+ rv_op_th_lhuib,
+ rv_op_th_lwia,
+ rv_op_th_lwib,
+ rv_op_th_lwuia,
+ rv_op_th_lwuib,
+ rv_op_th_ldia,
+ rv_op_th_ldib,
+ rv_op_th_sbia,
+ rv_op_th_sbib,
+ rv_op_th_shia,
+ rv_op_th_shib,
+ rv_op_th_swia,
+ rv_op_th_swib,
+ rv_op_th_sdia,
+ rv_op_th_sdib,
+ rv_op_th_lrb,
+ rv_op_th_lrbu,
+ rv_op_th_lrh,
+ rv_op_th_lrhu,
+ rv_op_th_lrw,
+ rv_op_th_lrwu,
+ rv_op_th_lrd,
+ rv_op_th_srb,
+ rv_op_th_srh,
+ rv_op_th_srw,
+ rv_op_th_srd,
+ rv_op_th_lurb,
+ rv_op_th_lurbu,
+ rv_op_th_lurh,
+ rv_op_th_lurhu,
+ rv_op_th_lurw,
+ rv_op_th_lurwu,
+ rv_op_th_lurd,
+ rv_op_th_surb,
+ rv_op_th_surh,
+ rv_op_th_surw,
+ rv_op_th_surd,
+ /* XTheadMemPair */
+ rv_op_th_ldd,
+ rv_op_th_lwd,
+ rv_op_th_lwud,
+ rv_op_th_sdd,
+ rv_op_th_swd,
+ /* XTheadSync */
+ rv_op_th_sfence_vmas,
+ rv_op_th_sync,
+ rv_op_th_sync_i,
+ rv_op_th_sync_is,
+ rv_op_th_sync_s,
+} rv_xthead_op;
+
+const rv_opcode_data xthead_opcode_data[] = {
+ { "th.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 },
+ /* XTheadBa */
+ { "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ /* XTheadBb */
+ { "th.srri", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+ { "th.srriw", rv_codec_r2_imm5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+ { "th.ext", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 },
+ { "th.extu", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 },
+ { "th.ff0", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+ { "th.ff1", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+ { "th.rev", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+ { "th.revw", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+ { "th.tstnbz", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+ /* XTheadBs */
+ { "th.tst", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+ /* XTheadCmo */
+ { "th.dcache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ { "th.dcache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ { "th.dcache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ { "th.dcache.cpa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.dcache.cipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.dcache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.dcache.cva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.dcache.civa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.dcache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.dcache.csw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.dcache.cisw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.dcache.isw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.dcache.cpal1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.dcache.cval1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.icache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ { "th.icache.ialls", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ { "th.icache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.icache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+ { "th.l2cache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ { "th.l2cache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ { "th.l2cache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ /* XTheadCondMov */
+ { "th.mveqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+ { "th.mvnez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+ /* XTheadFMemIdx */
+ { "th.flrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.flrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.flurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.flurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.fsrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.fsrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.fsurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.fsurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ /* XTheadFmv */
+ { "th.fmv.hw.x", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+ { "th.fmv.x.hw", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+ /* XTheadMac */
+ { "th.mula", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+ { "th.mulaw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+ { "th.mulah", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+ { "th.muls", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+ { "th.mulsw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+ { "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+ /* XTheadMemIdx */
+ { "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 },
+ { "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lhib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lhuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lhuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lwia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lwib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lwuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lwuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.ldia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.ldib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.sbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.sbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.shia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.shib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.swia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.swib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.sdia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.sdib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
+ { "th.lrb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lrbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lrh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lrhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lrw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lrwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lrd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.srb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.srh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.srw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.srd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lurb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lurbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lurh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lurhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lurw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lurwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.lurd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.surb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.surh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.surw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ { "th.surd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
+ /* XTheadMemPair */
+ { "th.ldd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 },
+ { "th.lwd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 },
+ { "th.lwud", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 },
+ { "th.sdd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 },
+ { "th.swd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 },
+ /* XTheadSync */
+ { "th.sfence.vmas", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 },
+ { "th.sync", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ { "th.sync.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ { "th.sync.is", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+ { "th.sync.s", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+};
+
+void decode_xtheadba(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 1:
+ switch ((inst >> 25) & 0b1111111) {
+ case 0b0000000:
+ case 0b0000001:
+ case 0b0000010:
+ case 0b0000011: op = rv_op_th_addsl; break;
+ }
+ break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
+
+void decode_xtheadbb(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 1:
+ switch ((inst >> 25) & 0b1111111) {
+ case 0b0001010: op = rv_op_th_srriw; break;
+ case 0b1000000:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_tstnbz;
+ }
+ break;
+ case 0b1000001:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_rev;
+ }
+ break;
+ case 0b1000010:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_ff0;
+ }
+ break;
+ case 0b1000011:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_ff1;
+ }
+ break;
+ case 0b1000100:
+ case 0b1001000:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_revw;
+ }
+ break;
+ case 0b0000100:
+ case 0b0000101: op = rv_op_th_srri; break;
+ }
+ break;
+ case 2: op = rv_op_th_ext; break;
+ case 3: op = rv_op_th_extu; break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
+
+void decode_xtheadbs(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 1:
+ switch ((inst >> 26) & 0b111111) {
+ case 0b100010: op = rv_op_th_tst; break;
+ }
+ break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
+
+void decode_xtheadcmo(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 0:
+ switch ((inst >> 20 & 0b111111111111)) {
+ case 0b000000000001:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_dcache_call;
+ }
+ break;
+ case 0b000000000011:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_dcache_ciall;
+ }
+ break;
+ case 0b000000000010:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_dcache_iall;
+ }
+ break;
+ case 0b000000101001: op = rv_op_th_dcache_cpa; break;
+ case 0b000000101011: op = rv_op_th_dcache_cipa; break;
+ case 0b000000101010: op = rv_op_th_dcache_ipa; break;
+ case 0b000000100101: op = rv_op_th_dcache_cva; break;
+ case 0b000000100111: op = rv_op_th_dcache_civa; break;
+ case 0b000000100110: op = rv_op_th_dcache_iva; break;
+ case 0b000000100001: op = rv_op_th_dcache_csw; break;
+ case 0b000000100011: op = rv_op_th_dcache_cisw; break;
+ case 0b000000100010: op = rv_op_th_dcache_isw; break;
+ case 0b000000101000: op = rv_op_th_dcache_cpal1; break;
+ case 0b000000100100: op = rv_op_th_dcache_cval1; break;
+ case 0b000000010000:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_icache_iall;
+ }
+ break;
+ case 0b000000010001:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_icache_ialls;
+ }
+ break;
+ case 0b000000111000: op = rv_op_th_icache_ipa; break;
+ case 0b000000110000: op = rv_op_th_icache_iva; break;
+ case 0b000000010101:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_l2cache_call;
+ }
+ break;
+ case 0b000000010111:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_l2cache_ciall;
+ }
+ break;
+ case 0b000000010110:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_l2cache_iall;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
+
+void decode_xtheadcondmov(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 1:
+ switch ((inst >> 25) & 0b1111111) {
+ case 0b0100000: op = rv_op_th_mveqz; break;
+ case 0b0100001: op = rv_op_th_mvnez; break;
+ }
+ break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
+
+void decode_xtheadfmemidx(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 6:
+ switch ((inst >> 27) & 0b11111) {
+ case 8: op = rv_op_th_flrw; break;
+ case 10: op = rv_op_th_flurw; break;
+ case 12: op = rv_op_th_flrd; break;
+ case 14: op = rv_op_th_flurd; break;
+ }
+ break;
+ case 7:
+ switch ((inst >> 27) & 0b11111) {
+ case 8: op = rv_op_th_fsrw; break;
+ case 10: op = rv_op_th_fsurw; break;
+ case 12: op = rv_op_th_fsrd; break;
+ case 14: op = rv_op_th_fsurd; break;
+ }
+ break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
+
+void decode_xtheadfmv(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 1:
+ switch ((inst >> 25) & 0b1111111) {
+ case 0b1010000:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_fmv_hw_x;
+ }
+ break;
+ case 0b1100000:
+ if (((inst >> 20) & 0b11111) == 0) {
+ op = rv_op_th_fmv_x_hw;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
+
+void decode_xtheadmac(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 1:
+ switch ((inst >> 25) & 0b1111111) {
+ case 0b0010000: op = rv_op_th_mula; break;
+ case 0b0010001: op = rv_op_th_muls; break;
+ case 0b0010010: op = rv_op_th_mulaw; break;
+ case 0b0010011: op = rv_op_th_mulsw; break;
+ case 0b0010100: op = rv_op_th_mulah; break;
+ case 0b0010101: op = rv_op_th_mulsh; break;
+ }
+ break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
+
+void decode_xtheadmemidx(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 4:
+ switch ((inst >> 27) & 0b11111) {
+ case 0: op = rv_op_th_lrb; break;
+ case 1: op = rv_op_th_lbib; break;
+ case 2: op = rv_op_th_lurb; break;
+ case 3: op = rv_op_th_lbia; break;
+ case 4: op = rv_op_th_lrh; break;
+ case 5: op = rv_op_th_lhib; break;
+ case 6: op = rv_op_th_lurh; break;
+ case 7: op = rv_op_th_lhia; break;
+ case 8: op = rv_op_th_lrw; break;
+ case 9: op = rv_op_th_lwib; break;
+ case 10: op = rv_op_th_lurw; break;
+ case 11: op = rv_op_th_lwia; break;
+ case 12: op = rv_op_th_lrd; break;
+ case 13: op = rv_op_th_ldib; break;
+ case 14: op = rv_op_th_lurd; break;
+ case 15: op = rv_op_th_ldia; break;
+ case 16: op = rv_op_th_lrbu; break;
+ case 17: op = rv_op_th_lbuib; break;
+ case 18: op = rv_op_th_lurbu; break;
+ case 19: op = rv_op_th_lbuia; break;
+ case 20: op = rv_op_th_lrhu; break;
+ case 21: op = rv_op_th_lhuib; break;
+ case 22: op = rv_op_th_lurhu; break;
+ case 23: op = rv_op_th_lhuia; break;
+ case 24: op = rv_op_th_lrwu; break;
+ case 25: op = rv_op_th_lwuib; break;
+ case 26: op = rv_op_th_lurwu; break;
+ case 27: op = rv_op_th_lwuia; break;
+ }
+ break;
+ case 5:
+ switch ((inst >> 27) & 0b11111) {
+ case 0: op = rv_op_th_srb; break;
+ case 1: op = rv_op_th_sbib; break;
+ case 2: op = rv_op_th_surb; break;
+ case 3: op = rv_op_th_sbia; break;
+ case 4: op = rv_op_th_srh; break;
+ case 5: op = rv_op_th_shib; break;
+ case 6: op = rv_op_th_surh; break;
+ case 7: op = rv_op_th_shia; break;
+ case 8: op = rv_op_th_srw; break;
+ case 9: op = rv_op_th_swib; break;
+ case 10: op = rv_op_th_surw; break;
+ case 11: op = rv_op_th_swia; break;
+ case 12: op = rv_op_th_srd; break;
+ case 13: op = rv_op_th_sdib; break;
+ case 14: op = rv_op_th_surd; break;
+ case 15: op = rv_op_th_sdia; break;
+ }
+ break;
+ break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
+
+void decode_xtheadmempair(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 4:
+ switch ((inst >> 27) & 0b11111) {
+ case 28: op = rv_op_th_lwd; break;
+ case 30: op = rv_op_th_lwud; break;
+ case 31: op = rv_op_th_ldd; break;
+ }
+ break;
+ case 5:
+ switch ((inst >> 27) & 0b11111) {
+ case 28: op = rv_op_th_swd; break;
+ case 31: op = rv_op_th_sdd; break;
+ }
+ break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
+
+void decode_xtheadsync(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 2:
+ /* custom-0 */
+ switch ((inst >> 12) & 0b111) {
+ case 0:
+ switch ((inst >> 25) & 0b1111111) {
+ case 0b0000010: op = rv_op_th_sfence_vmas; break;
+ case 0b0000000:
+ switch ((inst >> 20) & 0b11111) {
+ case 0b11000: op = rv_op_th_sync; break;
+ case 0b11010: op = rv_op_th_sync_i; break;
+ case 0b11011: op = rv_op_th_sync_is; break;
+ case 0b11001: op = rv_op_th_sync_s; break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ /* custom-0 */
+ }
+ break;
+ }
+
+ dec->op = op;
+}
diff --git a/disas/riscv-xthead.h b/disas/riscv-xthead.h
new file mode 100644
index 0000000000..fcd42746e7
--- /dev/null
+++ b/disas/riscv-xthead.h
@@ -0,0 +1,28 @@
+/*
+ * QEMU disassembler -- RISC-V specific header (xthead*).
+ *
+ * Copyright (c) 2023 VRULL GmbH
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef DISAS_RISCV_XTHEAD_H
+#define DISAS_RISCV_XTHEAD_H
+
+#include "disas/riscv.h"
+
+extern const rv_opcode_data xthead_opcode_data[];
+
+void decode_xtheadba(rv_decode *, rv_isa);
+void decode_xtheadbb(rv_decode *, rv_isa);
+void decode_xtheadbs(rv_decode *, rv_isa);
+void decode_xtheadcmo(rv_decode *, rv_isa);
+void decode_xtheadcondmov(rv_decode *, rv_isa);
+void decode_xtheadfmemidx(rv_decode *, rv_isa);
+void decode_xtheadfmv(rv_decode *, rv_isa);
+void decode_xtheadmac(rv_decode *, rv_isa);
+void decode_xtheadmemidx(rv_decode *, rv_isa);
+void decode_xtheadmempair(rv_decode *, rv_isa);
+void decode_xtheadsync(rv_decode *, rv_isa);
+
+#endif /* DISAS_RISCV_XTHEAD_H */
diff --git a/disas/riscv-xventana.c b/disas/riscv-xventana.c
new file mode 100644
index 0000000000..a0224d1fb3
--- /dev/null
+++ b/disas/riscv-xventana.c
@@ -0,0 +1,41 @@
+/*
+ * QEMU RISC-V Disassembler for xventana.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "disas/riscv.h"
+#include "disas/riscv-xventana.h"
+
+typedef enum {
+ /* 0 is reserved for rv_op_illegal. */
+ ventana_op_vt_maskc = 1,
+ ventana_op_vt_maskcn = 2,
+} rv_ventana_op;
+
+const rv_opcode_data ventana_opcode_data[] = {
+ { "vt.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 },
+ { "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+ { "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+};
+
+void decode_xventanacondops(rv_decode *dec, rv_isa isa)
+{
+ rv_inst inst = dec->inst;
+ rv_opcode op = rv_op_illegal;
+
+ switch (((inst >> 0) & 0b11)) {
+ case 3:
+ switch (((inst >> 2) & 0b11111)) {
+ case 30:
+ switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {
+ case 6: op = ventana_op_vt_maskc; break;
+ case 7: op = ventana_op_vt_maskcn; break;
+ }
+ break;
+ }
+ break;
+ }
+
+ dec->op = op;
+}
diff --git a/disas/riscv-xventana.h b/disas/riscv-xventana.h
new file mode 100644
index 0000000000..72be9ffa16
--- /dev/null
+++ b/disas/riscv-xventana.h
@@ -0,0 +1,18 @@
+/*
+ * QEMU disassembler -- RISC-V specific header (xventana*).
+ *
+ * Copyright (c) 2023 VRULL GmbH
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef DISAS_RISCV_XVENTANA_H
+#define DISAS_RISCV_XVENTANA_H
+
+#include "disas/riscv.h"
+
+extern const rv_opcode_data ventana_opcode_data[];
+
+void decode_xventanacondops(rv_decode*, rv_isa);
+
+#endif /* DISAS_RISCV_XVENTANA_H */
diff --git a/disas/riscv.c b/disas/riscv.c
index 5005364aba..cd7b6e86a7 100644
--- a/disas/riscv.c
+++ b/disas/riscv.c
@@ -18,162 +18,17 @@
*/
#include "qemu/osdep.h"
+#include "qemu/bitops.h"
#include "disas/dis-asm.h"
#include "target/riscv/cpu_cfg.h"
+#include "disas/riscv.h"
-/* types */
-
-typedef uint64_t rv_inst;
-typedef uint16_t rv_opcode;
-
-/* enums */
+/* Vendor extensions */
+#include "disas/riscv-xthead.h"
+#include "disas/riscv-xventana.h"
typedef enum {
- rv32,
- rv64,
- rv128
-} rv_isa;
-
-typedef enum {
- rv_rm_rne = 0,
- rv_rm_rtz = 1,
- rv_rm_rdn = 2,
- rv_rm_rup = 3,
- rv_rm_rmm = 4,
- rv_rm_dyn = 7,
-} rv_rm;
-
-typedef enum {
- rv_fence_i = 8,
- rv_fence_o = 4,
- rv_fence_r = 2,
- rv_fence_w = 1,
-} rv_fence;
-
-typedef enum {
- rv_ireg_zero,
- rv_ireg_ra,
- rv_ireg_sp,
- rv_ireg_gp,
- rv_ireg_tp,
- rv_ireg_t0,
- rv_ireg_t1,
- rv_ireg_t2,
- rv_ireg_s0,
- rv_ireg_s1,
- rv_ireg_a0,
- rv_ireg_a1,
- rv_ireg_a2,
- rv_ireg_a3,
- rv_ireg_a4,
- rv_ireg_a5,
- rv_ireg_a6,
- rv_ireg_a7,
- rv_ireg_s2,
- rv_ireg_s3,
- rv_ireg_s4,
- rv_ireg_s5,
- rv_ireg_s6,
- rv_ireg_s7,
- rv_ireg_s8,
- rv_ireg_s9,
- rv_ireg_s10,
- rv_ireg_s11,
- rv_ireg_t3,
- rv_ireg_t4,
- rv_ireg_t5,
- rv_ireg_t6,
-} rv_ireg;
-
-typedef enum {
- rvc_end,
- rvc_rd_eq_ra,
- rvc_rd_eq_x0,
- rvc_rs1_eq_x0,
- rvc_rs2_eq_x0,
- rvc_rs2_eq_rs1,
- rvc_rs1_eq_ra,
- rvc_imm_eq_zero,
- rvc_imm_eq_n1,
- rvc_imm_eq_p1,
- rvc_csr_eq_0x001,
- rvc_csr_eq_0x002,
- rvc_csr_eq_0x003,
- rvc_csr_eq_0xc00,
- rvc_csr_eq_0xc01,
- rvc_csr_eq_0xc02,
- rvc_csr_eq_0xc80,
- rvc_csr_eq_0xc81,
- rvc_csr_eq_0xc82,
-} rvc_constraint;
-
-typedef enum {
- rv_codec_illegal,
- rv_codec_none,
- rv_codec_u,
- rv_codec_uj,
- rv_codec_i,
- rv_codec_i_sh5,
- rv_codec_i_sh6,
- rv_codec_i_sh7,
- rv_codec_i_csr,
- rv_codec_s,
- rv_codec_sb,
- rv_codec_r,
- rv_codec_r_m,
- rv_codec_r4_m,
- rv_codec_r_a,
- rv_codec_r_l,
- rv_codec_r_f,
- rv_codec_cb,
- rv_codec_cb_imm,
- rv_codec_cb_sh5,
- rv_codec_cb_sh6,
- rv_codec_ci,
- rv_codec_ci_sh5,
- rv_codec_ci_sh6,
- rv_codec_ci_16sp,
- rv_codec_ci_lwsp,
- rv_codec_ci_ldsp,
- rv_codec_ci_lqsp,
- rv_codec_ci_li,
- rv_codec_ci_lui,
- rv_codec_ci_none,
- rv_codec_ciw_4spn,
- rv_codec_cj,
- rv_codec_cj_jal,
- rv_codec_cl_lw,
- rv_codec_cl_ld,
- rv_codec_cl_lq,
- rv_codec_cr,
- rv_codec_cr_mv,
- rv_codec_cr_jalr,
- rv_codec_cr_jr,
- rv_codec_cs,
- rv_codec_cs_sw,
- rv_codec_cs_sd,
- rv_codec_cs_sq,
- rv_codec_css_swsp,
- rv_codec_css_sdsp,
- rv_codec_css_sqsp,
- rv_codec_k_bs,
- rv_codec_k_rnum,
- rv_codec_v_r,
- rv_codec_v_ldst,
- rv_codec_v_i,
- rv_codec_vsetvli,
- rv_codec_vsetivli,
- rv_codec_zcb_ext,
- rv_codec_zcb_mul,
- rv_codec_zcb_lb,
- rv_codec_zcb_lh,
- rv_codec_zcmp_cm_pushpop,
- rv_codec_zcmp_cm_mv,
- rv_codec_zcmt_jt,
-} rv_codec;
-
-typedef enum {
- rv_op_illegal = 0,
+ /* 0 is reserved for rv_op_illegal. */
rv_op_lui = 1,
rv_op_auipc = 2,
rv_op_jal = 3,
@@ -964,53 +819,51 @@ typedef enum {
rv_op_cm_jalt = 788,
rv_op_czero_eqz = 789,
rv_op_czero_nez = 790,
+ rv_op_fcvt_bf16_s = 791,
+ rv_op_fcvt_s_bf16 = 792,
+ rv_op_vfncvtbf16_f_f_w = 793,
+ rv_op_vfwcvtbf16_f_f_v = 794,
+ rv_op_vfwmaccbf16_vv = 795,
+ rv_op_vfwmaccbf16_vf = 796,
+ rv_op_flh = 797,
+ rv_op_fsh = 798,
+ rv_op_fmv_h_x = 799,
+ rv_op_fmv_x_h = 800,
+ rv_op_fli_s = 801,
+ rv_op_fli_d = 802,
+ rv_op_fli_q = 803,
+ rv_op_fli_h = 804,
+ rv_op_fminm_s = 805,
+ rv_op_fmaxm_s = 806,
+ rv_op_fminm_d = 807,
+ rv_op_fmaxm_d = 808,
+ rv_op_fminm_q = 809,
+ rv_op_fmaxm_q = 810,
+ rv_op_fminm_h = 811,
+ rv_op_fmaxm_h = 812,
+ rv_op_fround_s = 813,
+ rv_op_froundnx_s = 814,
+ rv_op_fround_d = 815,
+ rv_op_froundnx_d = 816,
+ rv_op_fround_q = 817,
+ rv_op_froundnx_q = 818,
+ rv_op_fround_h = 819,
+ rv_op_froundnx_h = 820,
+ rv_op_fcvtmod_w_d = 821,
+ rv_op_fmvh_x_d = 822,
+ rv_op_fmvp_d_x = 823,
+ rv_op_fmvh_x_q = 824,
+ rv_op_fmvp_q_x = 825,
+ rv_op_fleq_s = 826,
+ rv_op_fltq_s = 827,
+ rv_op_fleq_d = 828,
+ rv_op_fltq_d = 829,
+ rv_op_fleq_q = 830,
+ rv_op_fltq_q = 831,
+ rv_op_fleq_h = 832,
+ rv_op_fltq_h = 833,
} rv_op;
-/* structures */
-
-typedef struct {
- RISCVCPUConfig *cfg;
- uint64_t pc;
- uint64_t inst;
- int32_t imm;
- uint16_t op;
- uint8_t codec;
- uint8_t rd;
- uint8_t rs1;
- uint8_t rs2;
- uint8_t rs3;
- uint8_t rm;
- uint8_t pred;
- uint8_t succ;
- uint8_t aq;
- uint8_t rl;
- uint8_t bs;
- uint8_t rnum;
- uint8_t vm;
- uint32_t vzimm;
- uint8_t rlist;
-} rv_decode;
-
-typedef struct {
- const int op;
- const rvc_constraint *constraints;
-} rv_comp_data;
-
-enum {
- rvcd_imm_nz = 0x1
-};
-
-typedef struct {
- const char * const name;
- const rv_codec codec;
- const char * const format;
- const rv_comp_data *pseudo;
- const short decomp_rv32;
- const short decomp_rv64;
- const short decomp_rv128;
- const short decomp_data;
-} rv_opcode_data;
-
/* register names */
static const char rv_ireg_name_sym[32][5] = {
@@ -1034,78 +887,22 @@ static const char rv_vreg_name_sym[32][4] = {
"v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"
};
-/* instruction formats */
-
-#define rv_fmt_none "O\t"
-#define rv_fmt_rs1 "O\t1"
-#define rv_fmt_offset "O\to"
-#define rv_fmt_pred_succ "O\tp,s"
-#define rv_fmt_rs1_rs2 "O\t1,2"
-#define rv_fmt_rd_imm "O\t0,i"
-#define rv_fmt_rd_offset "O\t0,o"
-#define rv_fmt_rd_rs1_rs2 "O\t0,1,2"
-#define rv_fmt_frd_rs1 "O\t3,1"
-#define rv_fmt_frd_frs1 "O\t3,4"
-#define rv_fmt_rd_frs1 "O\t0,4"
-#define rv_fmt_rd_frs1_frs2 "O\t0,4,5"
-#define rv_fmt_frd_frs1_frs2 "O\t3,4,5"
-#define rv_fmt_rm_frd_frs1 "O\tr,3,4"
-#define rv_fmt_rm_frd_rs1 "O\tr,3,1"
-#define rv_fmt_rm_rd_frs1 "O\tr,0,4"
-#define rv_fmt_rm_frd_frs1_frs2 "O\tr,3,4,5"
-#define rv_fmt_rm_frd_frs1_frs2_frs3 "O\tr,3,4,5,6"
-#define rv_fmt_rd_rs1_imm "O\t0,1,i"
-#define rv_fmt_rd_rs1_offset "O\t0,1,i"
-#define rv_fmt_rd_offset_rs1 "O\t0,i(1)"
-#define rv_fmt_frd_offset_rs1 "O\t3,i(1)"
-#define rv_fmt_rd_csr_rs1 "O\t0,c,1"
-#define rv_fmt_rd_csr_zimm "O\t0,c,7"
-#define rv_fmt_rs2_offset_rs1 "O\t2,i(1)"
-#define rv_fmt_frs2_offset_rs1 "O\t5,i(1)"
-#define rv_fmt_rs1_rs2_offset "O\t1,2,o"
-#define rv_fmt_rs2_rs1_offset "O\t2,1,o"
-#define rv_fmt_aqrl_rd_rs2_rs1 "OAR\t0,2,(1)"
-#define rv_fmt_aqrl_rd_rs1 "OAR\t0,(1)"
-#define rv_fmt_rd "O\t0"
-#define rv_fmt_rd_zimm "O\t0,7"
-#define rv_fmt_rd_rs1 "O\t0,1"
-#define rv_fmt_rd_rs2 "O\t0,2"
-#define rv_fmt_rs1_offset "O\t1,o"
-#define rv_fmt_rs2_offset "O\t2,o"
-#define rv_fmt_rs1_rs2_bs "O\t1,2,b"
-#define rv_fmt_rd_rs1_rnum "O\t0,1,n"
-#define rv_fmt_ldst_vd_rs1_vm "O\tD,(1)m"
-#define rv_fmt_ldst_vd_rs1_rs2_vm "O\tD,(1),2m"
-#define rv_fmt_ldst_vd_rs1_vs2_vm "O\tD,(1),Fm"
-#define rv_fmt_vd_vs2_vs1 "O\tD,F,E"
-#define rv_fmt_vd_vs2_vs1_vl "O\tD,F,El"
-#define rv_fmt_vd_vs2_vs1_vm "O\tD,F,Em"
-#define rv_fmt_vd_vs2_rs1_vl "O\tD,F,1l"
-#define rv_fmt_vd_vs2_fs1_vl "O\tD,F,4l"
-#define rv_fmt_vd_vs2_rs1_vm "O\tD,F,1m"
-#define rv_fmt_vd_vs2_fs1_vm "O\tD,F,4m"
-#define rv_fmt_vd_vs2_imm_vl "O\tD,F,il"
-#define rv_fmt_vd_vs2_imm_vm "O\tD,F,im"
-#define rv_fmt_vd_vs2_uimm_vm "O\tD,F,um"
-#define rv_fmt_vd_vs1_vs2_vm "O\tD,E,Fm"
-#define rv_fmt_vd_rs1_vs2_vm "O\tD,1,Fm"
-#define rv_fmt_vd_fs1_vs2_vm "O\tD,4,Fm"
-#define rv_fmt_vd_vs1 "O\tD,E"
-#define rv_fmt_vd_rs1 "O\tD,1"
-#define rv_fmt_vd_fs1 "O\tD,4"
-#define rv_fmt_vd_imm "O\tD,i"
-#define rv_fmt_vd_vs2 "O\tD,F"
-#define rv_fmt_vd_vs2_vm "O\tD,Fm"
-#define rv_fmt_rd_vs2_vm "O\t0,Fm"
-#define rv_fmt_rd_vs2 "O\t0,F"
-#define rv_fmt_fd_vs2 "O\t3,F"
-#define rv_fmt_vd_vm "O\tDm"
-#define rv_fmt_vsetvli "O\t0,1,v"
-#define rv_fmt_vsetivli "O\t0,u,v"
-#define rv_fmt_rs1_rs2_zce_ldst "O\t2,i(1)"
-#define rv_fmt_push_rlist "O\tx,-i"
-#define rv_fmt_pop_rlist "O\tx,i"
-#define rv_fmt_zcmt_index "O\ti"
+/* The FLI.[HSDQ] numeric constants (0.0 for symbolic constants).
+ * The constants use the hex floating-point literal representation
+ * that is printed when using the printf %a format specifier,
+ * which matches the output that is generated by the disassembler.
+ */
+static const char rv_fli_name_const[32][9] =
+{
+ "0x1p+0", "min", "0x1p-16", "0x1p-15",
+ "0x1p-8", "0x1p-7", "0x1p-4", "0x1p-3",
+ "0x1p-2", "0x1.4p-2", "0x1.8p-2", "0x1.cp-2",
+ "0x1p-1", "0x1.4p-1", "0x1.8p-1", "0x1.cp-1",
+ "0x1p+0", "0x1.4p+0", "0x1.8p+0", "0x1.cp+0",
+ "0x1p+1", "0x1.4p+1", "0x1.8p+1", "0x1p+2",
+ "0x1p+3", "0x1p+4", "0x1p+7", "0x1p+8",
+ "0x1p+15", "0x1p+16", "inf", "nan"
+};
/* pseudo-instruction constraints */
@@ -1336,7 +1133,7 @@ static const rv_comp_data rvcp_fsgnjx_q[] = {
/* instruction metadata */
-const rv_opcode_data opcode_data[] = {
+const rv_opcode_data rvi_opcode_data[] = {
{ "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 },
{ "lui", rv_codec_u, rv_fmt_rd_imm, NULL, 0, 0, 0 },
{ "auipc", rv_codec_u, rv_fmt_rd_offset, NULL, 0, 0, 0 },
@@ -2168,6 +1965,49 @@ const rv_opcode_data opcode_data[] = {
{ "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index, NULL, 0 },
{ "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
{ "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+ { "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 },
+ { "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 },
+ { "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 },
+ { "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 },
+ { "flh", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 },
+ { "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 },
+ { "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 },
+ { "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+ { "fli.s", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
+ { "fli.d", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
+ { "fli.q", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
+ { "fli.h", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
+ { "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+ { "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+ { "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 },
+ { "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+ { "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 },
+ { "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
};
/* CSR names */
@@ -2643,6 +2483,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 3: op = rv_op_vloxei8_v; break;
}
break;
+ case 1: op = rv_op_flh; break;
case 2: op = rv_op_flw; break;
case 3: op = rv_op_fld; break;
case 4: op = rv_op_flq; break;
@@ -2846,6 +2687,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 3: op = rv_op_vsoxei8_v; break;
}
break;
+ case 1: op = rv_op_fsh; break;
case 2: op = rv_op_fsw; break;
case 3: op = rv_op_fsd; break;
case 4: op = rv_op_fsq; break;
@@ -3105,36 +2947,62 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
switch ((inst >> 12) & 0b111) {
case 0: op = rv_op_fmin_s; break;
case 1: op = rv_op_fmax_s; break;
+ case 2: op = rv_op_fminm_s; break;
+ case 3: op = rv_op_fmaxm_s; break;
}
break;
case 21:
switch ((inst >> 12) & 0b111) {
case 0: op = rv_op_fmin_d; break;
case 1: op = rv_op_fmax_d; break;
+ case 2: op = rv_op_fminm_d; break;
+ case 3: op = rv_op_fmaxm_d; break;
+ }
+ break;
+ case 22:
+ switch (((inst >> 12) & 0b111)) {
+ case 2: op = rv_op_fminm_h; break;
+ case 3: op = rv_op_fmaxm_h; break;
}
break;
case 23:
switch ((inst >> 12) & 0b111) {
case 0: op = rv_op_fmin_q; break;
case 1: op = rv_op_fmax_q; break;
+ case 2: op = rv_op_fminm_q; break;
+ case 3: op = rv_op_fmaxm_q; break;
}
break;
case 32:
switch ((inst >> 20) & 0b11111) {
case 1: op = rv_op_fcvt_s_d; break;
case 3: op = rv_op_fcvt_s_q; break;
+ case 4: op = rv_op_fround_s; break;
+ case 5: op = rv_op_froundnx_s; break;
+ case 6: op = rv_op_fcvt_s_bf16; break;
}
break;
case 33:
switch ((inst >> 20) & 0b11111) {
case 0: op = rv_op_fcvt_d_s; break;
case 3: op = rv_op_fcvt_d_q; break;
+ case 4: op = rv_op_fround_d; break;
+ case 5: op = rv_op_froundnx_d; break;
+ }
+ break;
+ case 34:
+ switch (((inst >> 20) & 0b11111)) {
+ case 4: op = rv_op_fround_h; break;
+ case 5: op = rv_op_froundnx_h; break;
+ case 8: op = rv_op_fcvt_bf16_s; break;
}
break;
case 35:
switch ((inst >> 20) & 0b11111) {
case 0: op = rv_op_fcvt_q_s; break;
case 1: op = rv_op_fcvt_q_d; break;
+ case 4: op = rv_op_fround_q; break;
+ case 5: op = rv_op_froundnx_q; break;
}
break;
case 44:
@@ -3157,6 +3025,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 0: op = rv_op_fle_s; break;
case 1: op = rv_op_flt_s; break;
case 2: op = rv_op_feq_s; break;
+ case 4: op = rv_op_fleq_s; break;
+ case 5: op = rv_op_fltq_s; break;
}
break;
case 81:
@@ -3164,6 +3034,14 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 0: op = rv_op_fle_d; break;
case 1: op = rv_op_flt_d; break;
case 2: op = rv_op_feq_d; break;
+ case 4: op = rv_op_fleq_d; break;
+ case 5: op = rv_op_fltq_d; break;
+ }
+ break;
+ case 82:
+ switch (((inst >> 12) & 0b111)) {
+ case 4: op = rv_op_fleq_h; break;
+ case 5: op = rv_op_fltq_h; break;
}
break;
case 83:
@@ -3171,6 +3049,18 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 0: op = rv_op_fle_q; break;
case 1: op = rv_op_flt_q; break;
case 2: op = rv_op_feq_q; break;
+ case 4: op = rv_op_fleq_q; break;
+ case 5: op = rv_op_fltq_q; break;
+ }
+ break;
+ case 89:
+ switch (((inst >> 12) & 0b111)) {
+ case 0: op = rv_op_fmvp_d_x; break;
+ }
+ break;
+ case 91:
+ switch (((inst >> 12) & 0b111)) {
+ case 0: op = rv_op_fmvp_q_x; break;
}
break;
case 96:
@@ -3187,6 +3077,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 1: op = rv_op_fcvt_wu_d; break;
case 2: op = rv_op_fcvt_l_d; break;
case 3: op = rv_op_fcvt_lu_d; break;
+ case 8: op = rv_op_fcvtmod_w_d; break;
}
break;
case 99:
@@ -3233,6 +3124,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_x_d; break;
case 1: op = rv_op_fclass_d; break;
+ case 8: op = rv_op_fmvh_x_d; break;
+ }
+ break;
+ case 114:
+ switch (((inst >> 17) & 0b11111000) |
+ ((inst >> 12) & 0b00000111)) {
+ case 0: op = rv_op_fmv_x_h; break;
}
break;
case 115:
@@ -3240,24 +3138,35 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_x_q; break;
case 1: op = rv_op_fclass_q; break;
+ case 8: op = rv_op_fmvh_x_q; break;
}
break;
case 120:
switch (((inst >> 17) & 0b11111000) |
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_s_x; break;
+ case 8: op = rv_op_fli_s; break;
}
break;
case 121:
switch (((inst >> 17) & 0b11111000) |
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_d_x; break;
+ case 8: op = rv_op_fli_d; break;
+ }
+ break;
+ case 122:
+ switch (((inst >> 17) & 0b11111000) |
+ ((inst >> 12) & 0b00000111)) {
+ case 0: op = rv_op_fmv_h_x; break;
+ case 8: op = rv_op_fli_h; break;
}
break;
case 123:
switch (((inst >> 17) & 0b11111000) |
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_q_x; break;
+ case 8: op = rv_op_fli_q; break;
}
break;
}
@@ -3350,6 +3259,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 10: op = rv_op_vfwcvt_f_xu_v; break;
case 11: op = rv_op_vfwcvt_f_x_v; break;
case 12: op = rv_op_vfwcvt_f_f_v; break;
+ case 13: op = rv_op_vfwcvtbf16_f_f_v; break;
case 14: op = rv_op_vfwcvt_rtz_xu_f_v; break;
case 15: op = rv_op_vfwcvt_rtz_x_f_v; break;
case 16: op = rv_op_vfncvt_xu_f_w; break;
@@ -3360,6 +3270,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 21: op = rv_op_vfncvt_rod_f_f_w; break;
case 22: op = rv_op_vfncvt_rtz_xu_f_w; break;
case 23: op = rv_op_vfncvt_rtz_x_f_w; break;
+ case 29: op = rv_op_vfncvtbf16_f_f_w; break;
}
break;
case 19:
@@ -3391,6 +3302,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 52: op = rv_op_vfwadd_wv; break;
case 54: op = rv_op_vfwsub_wv; break;
case 56: op = rv_op_vfwmul_vv; break;
+ case 59: op = rv_op_vfwmaccbf16_vv; break;
case 60: op = rv_op_vfwmacc_vv; break;
case 61: op = rv_op_vfwnmacc_vv; break;
case 62: op = rv_op_vfwmsac_vv; break;
@@ -3629,6 +3541,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 52: op = rv_op_vfwadd_wf; break;
case 54: op = rv_op_vfwsub_wf; break;
case 56: op = rv_op_vfwmul_vf; break;
+ case 59: op = rv_op_vfwmaccbf16_vf; break;
case 60: op = rv_op_vfwmacc_vf; break;
case 61: op = rv_op_vfwnmacc_vf; break;
case 62: op = rv_op_vfwmsac_vf; break;
@@ -4134,6 +4047,26 @@ static uint32_t operand_zcmp_rlist(rv_inst inst)
return ((inst << 56) >> 60);
}
+static uint32_t operand_imm6(rv_inst inst)
+{
+ return (inst << 38) >> 60;
+}
+
+static uint32_t operand_imm2(rv_inst inst)
+{
+ return (inst << 37) >> 62;
+}
+
+static uint32_t operand_immh(rv_inst inst)
+{
+ return (inst << 32) >> 58;
+}
+
+static uint32_t operand_imml(rv_inst inst)
+{
+ return (inst << 38) >> 58;
+}
+
static uint32_t calculate_stack_adj(rv_isa isa, uint32_t rlist, uint32_t spimm)
{
int xlen_bytes_log2 = isa == rv64 ? 3 : 2;
@@ -4157,6 +4090,7 @@ static uint32_t operand_tbl_index(rv_inst inst)
static void decode_inst_operands(rv_decode *dec, rv_isa isa)
{
+ const rv_opcode_data *opcode_data = dec->opcode_data;
rv_inst inst = dec->inst;
dec->codec = opcode_data[dec->op].codec;
switch (dec->codec) {
@@ -4496,6 +4430,42 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa)
break;
case rv_codec_zcmt_jt:
dec->imm = operand_tbl_index(inst);
+ break;
+ case rv_codec_fli:
+ dec->rd = operand_rd(inst);
+ dec->imm = operand_rs1(inst);
+ break;
+ case rv_codec_r2_imm5:
+ dec->rd = operand_rd(inst);
+ dec->rs1 = operand_rs1(inst);
+ dec->imm = operand_rs2(inst);
+ break;
+ case rv_codec_r2:
+ dec->rd = operand_rd(inst);
+ dec->rs1 = operand_rs1(inst);
+ break;
+ case rv_codec_r2_imm6:
+ dec->rd = operand_rd(inst);
+ dec->rs1 = operand_rs1(inst);
+ dec->imm = operand_imm6(inst);
+ break;
+ case rv_codec_r_imm2:
+ dec->rd = operand_rd(inst);
+ dec->rs1 = operand_rs1(inst);
+ dec->rs2 = operand_rs2(inst);
+ dec->imm = operand_imm2(inst);
+ break;
+ case rv_codec_r2_immhl:
+ dec->rd = operand_rd(inst);
+ dec->rs1 = operand_rs1(inst);
+ dec->imm = operand_immh(inst);
+ dec->imm1 = operand_imml(inst);
+ break;
+ case rv_codec_r2_imm2_imm5:
+ dec->rd = operand_rd(inst);
+ dec->rs1 = operand_rs1(inst);
+ dec->imm = sextract32(operand_rs2(inst), 0, 5);
+ dec->imm1 = operand_imm2(inst);
break;
};
}
@@ -4639,6 +4609,7 @@ static void append(char *s1, const char *s2, size_t n)
static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec)
{
+ const rv_opcode_data *opcode_data = dec->opcode_data;
char tmp[64];
const char *fmt;
@@ -4709,6 +4680,10 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec)
snprintf(tmp, sizeof(tmp), "%u", ((uint32_t)dec->imm & 0b11111));
append(buf, tmp, buflen);
break;
+ case 'j':
+ snprintf(tmp, sizeof(tmp), "%d", dec->imm1);
+ append(buf, tmp, buflen);
+ break;
case 'o':
snprintf(tmp, sizeof(tmp), "%d", dec->imm);
append(buf, tmp, buflen);
@@ -4869,6 +4844,9 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec)
append(buf, tmp, buflen);
break;
}
+ case 'h':
+ append(buf, rv_fli_name_const[dec->imm], buflen);
+ break;
default:
break;
}
@@ -4880,6 +4858,7 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec)
static void decode_inst_lift_pseudo(rv_decode *dec)
{
+ const rv_opcode_data *opcode_data = dec->opcode_data;
const rv_comp_data *comp_data = opcode_data[dec->op].pseudo;
if (!comp_data) {
return;
@@ -4898,6 +4877,7 @@ static void decode_inst_lift_pseudo(rv_decode *dec)
static void decode_inst_decompress_rv32(rv_decode *dec)
{
+ const rv_opcode_data *opcode_data = dec->opcode_data;
int decomp_op = opcode_data[dec->op].decomp_rv32;
if (decomp_op != rv_op_illegal) {
if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz)
@@ -4912,6 +4892,7 @@ static void decode_inst_decompress_rv32(rv_decode *dec)
static void decode_inst_decompress_rv64(rv_decode *dec)
{
+ const rv_opcode_data *opcode_data = dec->opcode_data;
int decomp_op = opcode_data[dec->op].decomp_rv64;
if (decomp_op != rv_op_illegal) {
if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz)
@@ -4926,6 +4907,7 @@ static void decode_inst_decompress_rv64(rv_decode *dec)
static void decode_inst_decompress_rv128(rv_decode *dec)
{
+ const rv_opcode_data *opcode_data = dec->opcode_data;
int decomp_op = opcode_data[dec->op].decomp_rv128;
if (decomp_op != rv_op_illegal) {
if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz)
@@ -4963,7 +4945,44 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst,
dec.pc = pc;
dec.inst = inst;
dec.cfg = cfg;
- decode_inst_opcode(&dec, isa);
+
+ static const struct {
+ bool (*guard_func)(const RISCVCPUConfig *);
+ const rv_opcode_data *opcode_data;
+ void (*decode_func)(rv_decode *, rv_isa);
+ } decoders[] = {
+ { always_true_p, rvi_opcode_data, decode_inst_opcode },
+ { has_xtheadba_p, xthead_opcode_data, decode_xtheadba },
+ { has_xtheadbb_p, xthead_opcode_data, decode_xtheadbb },
+ { has_xtheadbs_p, xthead_opcode_data, decode_xtheadbs },
+ { has_xtheadcmo_p, xthead_opcode_data, decode_xtheadcmo },
+ { has_xtheadcondmov_p, xthead_opcode_data, decode_xtheadcondmov },
+ { has_xtheadfmemidx_p, xthead_opcode_data, decode_xtheadfmemidx },
+ { has_xtheadfmv_p, xthead_opcode_data, decode_xtheadfmv },
+ { has_xtheadmac_p, xthead_opcode_data, decode_xtheadmac },
+ { has_xtheadmemidx_p, xthead_opcode_data, decode_xtheadmemidx },
+ { has_xtheadmempair_p, xthead_opcode_data, decode_xtheadmempair },
+ { has_xtheadsync_p, xthead_opcode_data, decode_xtheadsync },
+ { has_XVentanaCondOps_p, ventana_opcode_data, decode_xventanacondops },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) {
+ bool (*guard_func)(const RISCVCPUConfig *) = decoders[i].guard_func;
+ const rv_opcode_data *opcode_data = decoders[i].opcode_data;
+ void (*decode_func)(rv_decode *, rv_isa) = decoders[i].decode_func;
+
+ if (guard_func(cfg)) {
+ dec.opcode_data = opcode_data;
+ decode_func(&dec, isa);
+ if (dec.op != rv_op_illegal)
+ break;
+ }
+ }
+
+ if (dec.op == rv_op_illegal) {
+ dec.opcode_data = rvi_opcode_data;
+ }
+
decode_inst_operands(&dec, isa);
decode_inst_decompress(&dec, isa);
decode_inst_lift_pseudo(&dec);
diff --git a/disas/riscv.h b/disas/riscv.h
new file mode 100644
index 0000000000..9cf901fc1e
--- /dev/null
+++ b/disas/riscv.h
@@ -0,0 +1,302 @@
+/*
+ * QEMU disassembler -- RISC-V specific header.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef DISAS_RISCV_H
+#define DISAS_RISCV_H
+
+#include "qemu/osdep.h"
+#include "target/riscv/cpu_cfg.h"
+
+/* types */
+
+typedef uint64_t rv_inst;
+typedef uint16_t rv_opcode;
+
+/* enums */
+
+typedef enum {
+ rv32,
+ rv64,
+ rv128
+} rv_isa;
+
+typedef enum {
+ rv_rm_rne = 0,
+ rv_rm_rtz = 1,
+ rv_rm_rdn = 2,
+ rv_rm_rup = 3,
+ rv_rm_rmm = 4,
+ rv_rm_dyn = 7,
+} rv_rm;
+
+typedef enum {
+ rv_fence_i = 8,
+ rv_fence_o = 4,
+ rv_fence_r = 2,
+ rv_fence_w = 1,
+} rv_fence;
+
+typedef enum {
+ rv_ireg_zero,
+ rv_ireg_ra,
+ rv_ireg_sp,
+ rv_ireg_gp,
+ rv_ireg_tp,
+ rv_ireg_t0,
+ rv_ireg_t1,
+ rv_ireg_t2,
+ rv_ireg_s0,
+ rv_ireg_s1,
+ rv_ireg_a0,
+ rv_ireg_a1,
+ rv_ireg_a2,
+ rv_ireg_a3,
+ rv_ireg_a4,
+ rv_ireg_a5,
+ rv_ireg_a6,
+ rv_ireg_a7,
+ rv_ireg_s2,
+ rv_ireg_s3,
+ rv_ireg_s4,
+ rv_ireg_s5,
+ rv_ireg_s6,
+ rv_ireg_s7,
+ rv_ireg_s8,
+ rv_ireg_s9,
+ rv_ireg_s10,
+ rv_ireg_s11,
+ rv_ireg_t3,
+ rv_ireg_t4,
+ rv_ireg_t5,
+ rv_ireg_t6,
+} rv_ireg;
+
+typedef enum {
+ rvc_end,
+ rvc_rd_eq_ra,
+ rvc_rd_eq_x0,
+ rvc_rs1_eq_x0,
+ rvc_rs2_eq_x0,
+ rvc_rs2_eq_rs1,
+ rvc_rs1_eq_ra,
+ rvc_imm_eq_zero,
+ rvc_imm_eq_n1,
+ rvc_imm_eq_p1,
+ rvc_csr_eq_0x001,
+ rvc_csr_eq_0x002,
+ rvc_csr_eq_0x003,
+ rvc_csr_eq_0xc00,
+ rvc_csr_eq_0xc01,
+ rvc_csr_eq_0xc02,
+ rvc_csr_eq_0xc80,
+ rvc_csr_eq_0xc81,
+ rvc_csr_eq_0xc82,
+} rvc_constraint;
+
+typedef enum {
+ rv_codec_illegal,
+ rv_codec_none,
+ rv_codec_u,
+ rv_codec_uj,
+ rv_codec_i,
+ rv_codec_i_sh5,
+ rv_codec_i_sh6,
+ rv_codec_i_sh7,
+ rv_codec_i_csr,
+ rv_codec_s,
+ rv_codec_sb,
+ rv_codec_r,
+ rv_codec_r_m,
+ rv_codec_r4_m,
+ rv_codec_r_a,
+ rv_codec_r_l,
+ rv_codec_r_f,
+ rv_codec_cb,
+ rv_codec_cb_imm,
+ rv_codec_cb_sh5,
+ rv_codec_cb_sh6,
+ rv_codec_ci,
+ rv_codec_ci_sh5,
+ rv_codec_ci_sh6,
+ rv_codec_ci_16sp,
+ rv_codec_ci_lwsp,
+ rv_codec_ci_ldsp,
+ rv_codec_ci_lqsp,
+ rv_codec_ci_li,
+ rv_codec_ci_lui,
+ rv_codec_ci_none,
+ rv_codec_ciw_4spn,
+ rv_codec_cj,
+ rv_codec_cj_jal,
+ rv_codec_cl_lw,
+ rv_codec_cl_ld,
+ rv_codec_cl_lq,
+ rv_codec_cr,
+ rv_codec_cr_mv,
+ rv_codec_cr_jalr,
+ rv_codec_cr_jr,
+ rv_codec_cs,
+ rv_codec_cs_sw,
+ rv_codec_cs_sd,
+ rv_codec_cs_sq,
+ rv_codec_css_swsp,
+ rv_codec_css_sdsp,
+ rv_codec_css_sqsp,
+ rv_codec_k_bs,
+ rv_codec_k_rnum,
+ rv_codec_v_r,
+ rv_codec_v_ldst,
+ rv_codec_v_i,
+ rv_codec_vsetvli,
+ rv_codec_vsetivli,
+ rv_codec_zcb_ext,
+ rv_codec_zcb_mul,
+ rv_codec_zcb_lb,
+ rv_codec_zcb_lh,
+ rv_codec_zcmp_cm_pushpop,
+ rv_codec_zcmp_cm_mv,
+ rv_codec_zcmt_jt,
+ rv_codec_r2_imm5,
+ rv_codec_r2,
+ rv_codec_r2_imm6,
+ rv_codec_r_imm2,
+ rv_codec_r2_immhl,
+ rv_codec_r2_imm2_imm5,
+ rv_codec_fli,
+} rv_codec;
+
+/* structures */
+
+typedef struct {
+ const int op;
+ const rvc_constraint *constraints;
+} rv_comp_data;
+
+typedef struct {
+ const char * const name;
+ const rv_codec codec;
+ const char * const format;
+ const rv_comp_data *pseudo;
+ const short decomp_rv32;
+ const short decomp_rv64;
+ const short decomp_rv128;
+ const short decomp_data;
+} rv_opcode_data;
+
+typedef struct {
+ RISCVCPUConfig *cfg;
+ uint64_t pc;
+ uint64_t inst;
+ const rv_opcode_data *opcode_data;
+ int32_t imm;
+ int32_t imm1;
+ uint16_t op;
+ uint8_t codec;
+ uint8_t rd;
+ uint8_t rs1;
+ uint8_t rs2;
+ uint8_t rs3;
+ uint8_t rm;
+ uint8_t pred;
+ uint8_t succ;
+ uint8_t aq;
+ uint8_t rl;
+ uint8_t bs;
+ uint8_t rnum;
+ uint8_t vm;
+ uint32_t vzimm;
+ uint8_t rlist;
+} rv_decode;
+
+enum {
+ rv_op_illegal = 0
+};
+
+enum {
+ rvcd_imm_nz = 0x1
+};
+
+/* instruction formats */
+
+#define rv_fmt_none "O\t"
+#define rv_fmt_rs1 "O\t1"
+#define rv_fmt_offset "O\to"
+#define rv_fmt_pred_succ "O\tp,s"
+#define rv_fmt_rs1_rs2 "O\t1,2"
+#define rv_fmt_rd_imm "O\t0,i"
+#define rv_fmt_rd_offset "O\t0,o"
+#define rv_fmt_rd_rs1_rs2 "O\t0,1,2"
+#define rv_fmt_frd_rs1 "O\t3,1"
+#define rv_fmt_frd_rs1_rs2 "O\t3,1,2"
+#define rv_fmt_frd_frs1 "O\t3,4"
+#define rv_fmt_rd_frs1 "O\t0,4"
+#define rv_fmt_rd_frs1_frs2 "O\t0,4,5"
+#define rv_fmt_frd_frs1_frs2 "O\t3,4,5"
+#define rv_fmt_rm_frd_frs1 "O\tr,3,4"
+#define rv_fmt_rm_frd_rs1 "O\tr,3,1"
+#define rv_fmt_rm_rd_frs1 "O\tr,0,4"
+#define rv_fmt_rm_frd_frs1_frs2 "O\tr,3,4,5"
+#define rv_fmt_rm_frd_frs1_frs2_frs3 "O\tr,3,4,5,6"
+#define rv_fmt_rd_rs1_imm "O\t0,1,i"
+#define rv_fmt_rd_rs1_offset "O\t0,1,i"
+#define rv_fmt_rd_offset_rs1 "O\t0,i(1)"
+#define rv_fmt_frd_offset_rs1 "O\t3,i(1)"
+#define rv_fmt_rd_csr_rs1 "O\t0,c,1"
+#define rv_fmt_rd_csr_zimm "O\t0,c,7"
+#define rv_fmt_rs2_offset_rs1 "O\t2,i(1)"
+#define rv_fmt_frs2_offset_rs1 "O\t5,i(1)"
+#define rv_fmt_rs1_rs2_offset "O\t1,2,o"
+#define rv_fmt_rs2_rs1_offset "O\t2,1,o"
+#define rv_fmt_aqrl_rd_rs2_rs1 "OAR\t0,2,(1)"
+#define rv_fmt_aqrl_rd_rs1 "OAR\t0,(1)"
+#define rv_fmt_rd "O\t0"
+#define rv_fmt_rd_zimm "O\t0,7"
+#define rv_fmt_rd_rs1 "O\t0,1"
+#define rv_fmt_rd_rs2 "O\t0,2"
+#define rv_fmt_rs1_offset "O\t1,o"
+#define rv_fmt_rs2_offset "O\t2,o"
+#define rv_fmt_rs1_rs2_bs "O\t1,2,b"
+#define rv_fmt_rd_rs1_rnum "O\t0,1,n"
+#define rv_fmt_ldst_vd_rs1_vm "O\tD,(1)m"
+#define rv_fmt_ldst_vd_rs1_rs2_vm "O\tD,(1),2m"
+#define rv_fmt_ldst_vd_rs1_vs2_vm "O\tD,(1),Fm"
+#define rv_fmt_vd_vs2_vs1 "O\tD,F,E"
+#define rv_fmt_vd_vs2_vs1_vl "O\tD,F,El"
+#define rv_fmt_vd_vs2_vs1_vm "O\tD,F,Em"
+#define rv_fmt_vd_vs2_rs1_vl "O\tD,F,1l"
+#define rv_fmt_vd_vs2_fs1_vl "O\tD,F,4l"
+#define rv_fmt_vd_vs2_rs1_vm "O\tD,F,1m"
+#define rv_fmt_vd_vs2_fs1_vm "O\tD,F,4m"
+#define rv_fmt_vd_vs2_imm_vl "O\tD,F,il"
+#define rv_fmt_vd_vs2_imm_vm "O\tD,F,im"
+#define rv_fmt_vd_vs2_uimm_vm "O\tD,F,um"
+#define rv_fmt_vd_vs1_vs2_vm "O\tD,E,Fm"
+#define rv_fmt_vd_rs1_vs2_vm "O\tD,1,Fm"
+#define rv_fmt_vd_fs1_vs2_vm "O\tD,4,Fm"
+#define rv_fmt_vd_vs1 "O\tD,E"
+#define rv_fmt_vd_rs1 "O\tD,1"
+#define rv_fmt_vd_fs1 "O\tD,4"
+#define rv_fmt_vd_imm "O\tD,i"
+#define rv_fmt_vd_vs2 "O\tD,F"
+#define rv_fmt_vd_vs2_vm "O\tD,Fm"
+#define rv_fmt_rd_vs2_vm "O\t0,Fm"
+#define rv_fmt_rd_vs2 "O\t0,F"
+#define rv_fmt_fd_vs2 "O\t3,F"
+#define rv_fmt_vd_vm "O\tDm"
+#define rv_fmt_vsetvli "O\t0,1,v"
+#define rv_fmt_vsetivli "O\t0,u,v"
+#define rv_fmt_rs1_rs2_zce_ldst "O\t2,i(1)"
+#define rv_fmt_push_rlist "O\tx,-i"
+#define rv_fmt_pop_rlist "O\tx,i"
+#define rv_fmt_zcmt_index "O\ti"
+#define rv_fmt_rd_rs1_rs2_imm "O\t0,1,2,i"
+#define rv_fmt_frd_rs1_rs2_imm "O\t3,1,2,i"
+#define rv_fmt_rd_rs1_immh_imml "O\t0,1,i,j"
+#define rv_fmt_rd_rs1_immh_imml_addr "O\t0,(1),i,j"
+#define rv_fmt_rd2_imm "O\t0,2,(1),i"
+#define rv_fmt_fli "O\t3,h"
+
+#endif /* DISAS_RISCV_H */
diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst
index b33f45e5b3..f9a2eac544 100644
--- a/docs/system/riscv/virt.rst
+++ b/docs/system/riscv/virt.rst
@@ -93,6 +93,7 @@ The following machine-specific options are supported:
When this option is "on", ACLINT devices will be emulated instead of
SiFive CLINT. When not specified, this option is assumed to be "off".
+ This option is restricted to the TCG accelerator.
- aia=[none|aplic|aplic-imsic]
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index e4c2149175..6996d265e4 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -158,6 +158,9 @@ config SIFIVE_TEST
config SIFIVE_E_PRCI
bool
+config SIFIVE_E_AON
+ bool
+
config SIFIVE_U_OTP
bool
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 05877f61cc..892f8b91c5 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -30,6 +30,7 @@ system_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.
system_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c'))
system_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c'))
system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c'))
+system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c'))
system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c'))
system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c'))
diff --git a/hw/misc/sifive_e_aon.c b/hw/misc/sifive_e_aon.c
new file mode 100644
index 0000000000..4656457d0b
--- /dev/null
+++ b/hw/misc/sifive_e_aon.c
@@ -0,0 +1,319 @@
+/*
+ * SiFive HiFive1 AON (Always On Domain) for QEMU.
+ *
+ * Copyright (c) 2022 SiFive, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/timer.h"
+#include "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/registerfields.h"
+#include "hw/misc/sifive_e_aon.h"
+#include "qapi/visitor.h"
+#include "qapi/error.h"
+#include "sysemu/watchdog.h"
+#include "hw/qdev-properties.h"
+
+REG32(AON_WDT_WDOGCFG, 0x0)
+ FIELD(AON_WDT_WDOGCFG, SCALE, 0, 4)
+ FIELD(AON_WDT_WDOGCFG, RSVD0, 4, 4)
+ FIELD(AON_WDT_WDOGCFG, RSTEN, 8, 1)
+ FIELD(AON_WDT_WDOGCFG, ZEROCMP, 9, 1)
+ FIELD(AON_WDT_WDOGCFG, RSVD1, 10, 2)
+ FIELD(AON_WDT_WDOGCFG, EN_ALWAYS, 12, 1)
+ FIELD(AON_WDT_WDOGCFG, EN_CORE_AWAKE, 13, 1)
+ FIELD(AON_WDT_WDOGCFG, RSVD2, 14, 14)
+ FIELD(AON_WDT_WDOGCFG, IP0, 28, 1)
+ FIELD(AON_WDT_WDOGCFG, RSVD3, 29, 3)
+REG32(AON_WDT_WDOGCOUNT, 0x8)
+ FIELD(AON_WDT_WDOGCOUNT, VALUE, 0, 31)
+REG32(AON_WDT_WDOGS, 0x10)
+REG32(AON_WDT_WDOGFEED, 0x18)
+REG32(AON_WDT_WDOGKEY, 0x1c)
+REG32(AON_WDT_WDOGCMP0, 0x20)
+
+static void sifive_e_aon_wdt_update_wdogcount(SiFiveEAONState *r)
+{
+ int64_t now;
+ if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS) == 0 &&
+ FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE) == 0) {
+ return;
+ }
+
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ r->wdogcount += muldiv64(now - r->wdog_restart_time,
+ r->wdogclk_freq, NANOSECONDS_PER_SECOND);
+
+ /* Clean the most significant bit. */
+ r->wdogcount &= R_AON_WDT_WDOGCOUNT_VALUE_MASK;
+ r->wdog_restart_time = now;
+}
+
+static void sifive_e_aon_wdt_update_state(SiFiveEAONState *r)
+{
+ uint16_t wdogs;
+ bool cmp_signal = false;
+ sifive_e_aon_wdt_update_wdogcount(r);
+ wdogs = (uint16_t)(r->wdogcount >>
+ FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE));
+
+ if (wdogs >= r->wdogcmp0) {
+ cmp_signal = true;
+ if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, ZEROCMP) == 1) {
+ r->wdogcount = 0;
+ wdogs = 0;
+ }
+ }
+
+ if (cmp_signal) {
+ if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, RSTEN) == 1) {
+ watchdog_perform_action();
+ }
+ r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, IP0, 1);
+ }
+
+ qemu_set_irq(r->wdog_irq, FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, IP0));
+
+ if (wdogs < r->wdogcmp0 &&
+ (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS) == 1 ||
+ FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE) == 1)) {
+ int64_t next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ next += muldiv64((r->wdogcmp0 - wdogs) <<
+ FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE),
+ NANOSECONDS_PER_SECOND, r->wdogclk_freq);
+ timer_mod(r->wdog_timer, next);
+ } else {
+ timer_mod(r->wdog_timer, INT64_MAX);
+ }
+}
+
+/*
+ * Callback used when the timer set using timer_mod expires.
+ */
+static void sifive_e_aon_wdt_expired_cb(void *opaque)
+{
+ SiFiveEAONState *r = SIFIVE_E_AON(opaque);
+ sifive_e_aon_wdt_update_state(r);
+}
+
+static uint64_t
+sifive_e_aon_wdt_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ SiFiveEAONState *r = SIFIVE_E_AON(opaque);
+
+ switch (addr) {
+ case A_AON_WDT_WDOGCFG:
+ return r->wdogcfg;
+ case A_AON_WDT_WDOGCOUNT:
+ sifive_e_aon_wdt_update_wdogcount(r);
+ return r->wdogcount;
+ case A_AON_WDT_WDOGS:
+ sifive_e_aon_wdt_update_wdogcount(r);
+ return r->wdogcount >>
+ FIELD_EX32(r->wdogcfg,
+ AON_WDT_WDOGCFG,
+ SCALE);
+ case A_AON_WDT_WDOGFEED:
+ return 0;
+ case A_AON_WDT_WDOGKEY:
+ return r->wdogunlock;
+ case A_AON_WDT_WDOGCMP0:
+ return r->wdogcmp0;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n",
+ __func__, (int)addr);
+ }
+
+ return 0;
+}
+
+static void
+sifive_e_aon_wdt_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ SiFiveEAONState *r = SIFIVE_E_AON(opaque);
+ uint32_t value = val64;
+
+ switch (addr) {
+ case A_AON_WDT_WDOGCFG: {
+ uint8_t new_en_always;
+ uint8_t new_en_core_awake;
+ uint8_t old_en_always;
+ uint8_t old_en_core_awake;
+ if (r->wdogunlock == 0) {
+ return;
+ }
+
+ new_en_always = FIELD_EX32(value, AON_WDT_WDOGCFG, EN_ALWAYS);
+ new_en_core_awake = FIELD_EX32(value, AON_WDT_WDOGCFG, EN_CORE_AWAKE);
+ old_en_always = FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS);
+ old_en_core_awake = FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG,
+ EN_CORE_AWAKE);
+
+ if ((old_en_always ||
+ old_en_core_awake) == 1 &&
+ (new_en_always ||
+ new_en_core_awake) == 0) {
+ sifive_e_aon_wdt_update_wdogcount(r);
+ } else if ((old_en_always ||
+ old_en_core_awake) == 0 &&
+ (new_en_always ||
+ new_en_core_awake) == 1) {
+ r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ }
+ r->wdogcfg = value;
+ r->wdogunlock = 0;
+ break;
+ }
+ case A_AON_WDT_WDOGCOUNT:
+ if (r->wdogunlock == 0) {
+ return;
+ }
+ r->wdogcount = value & R_AON_WDT_WDOGCOUNT_VALUE_MASK;
+ r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ r->wdogunlock = 0;
+ break;
+ case A_AON_WDT_WDOGS:
+ return;
+ case A_AON_WDT_WDOGFEED:
+ if (r->wdogunlock == 0) {
+ return;
+ }
+ if (value == SIFIVE_E_AON_WDOGFEED) {
+ r->wdogcount = 0;
+ r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ }
+ r->wdogunlock = 0;
+ break;
+ case A_AON_WDT_WDOGKEY:
+ if (value == SIFIVE_E_AON_WDOGKEY) {
+ r->wdogunlock = 1;
+ }
+ break;
+ case A_AON_WDT_WDOGCMP0:
+ if (r->wdogunlock == 0) {
+ return;
+ }
+ r->wdogcmp0 = (uint16_t) value;
+ r->wdogunlock = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n",
+ __func__, (int)addr, (int)value);
+ }
+ sifive_e_aon_wdt_update_state(r);
+}
+
+static uint64_t
+sifive_e_aon_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ if (addr < SIFIVE_E_AON_RTC) {
+ return sifive_e_aon_wdt_read(opaque, addr, size);
+ } else if (addr < SIFIVE_E_AON_MAX) {
+ qemu_log_mask(LOG_UNIMP, "%s: Unimplemented read: addr=0x%x\n",
+ __func__, (int)addr);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n",
+ __func__, (int)addr);
+ }
+ return 0;
+}
+
+static void
+sifive_e_aon_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ if (addr < SIFIVE_E_AON_RTC) {
+ sifive_e_aon_wdt_write(opaque, addr, val64, size);
+ } else if (addr < SIFIVE_E_AON_MAX) {
+ qemu_log_mask(LOG_UNIMP, "%s: Unimplemented write: addr=0x%x\n",
+ __func__, (int)addr);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x\n",
+ __func__, (int)addr);
+ }
+}
+
+static const MemoryRegionOps sifive_e_aon_ops = {
+ .read = sifive_e_aon_read,
+ .write = sifive_e_aon_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ },
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void sifive_e_aon_reset(DeviceState *dev)
+{
+ SiFiveEAONState *r = SIFIVE_E_AON(dev);
+
+ r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, RSTEN, 0);
+ r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS, 0);
+ r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE, 0);
+ r->wdogcmp0 = 0xbeef;
+
+ sifive_e_aon_wdt_update_state(r);
+}
+
+static void sifive_e_aon_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ SiFiveEAONState *r = SIFIVE_E_AON(obj);
+
+ memory_region_init_io(&r->mmio, OBJECT(r), &sifive_e_aon_ops, r,
+ TYPE_SIFIVE_E_AON, SIFIVE_E_AON_MAX);
+ sysbus_init_mmio(sbd, &r->mmio);
+
+ /* watchdog timer */
+ r->wdog_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ sifive_e_aon_wdt_expired_cb, r);
+ r->wdogclk_freq = SIFIVE_E_LFCLK_DEFAULT_FREQ;
+ sysbus_init_irq(sbd, &r->wdog_irq);
+}
+
+static Property sifive_e_aon_properties[] = {
+ DEFINE_PROP_UINT64("wdogclk-frequency", SiFiveEAONState, wdogclk_freq,
+ SIFIVE_E_LFCLK_DEFAULT_FREQ),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sifive_e_aon_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->reset = sifive_e_aon_reset;
+ device_class_set_props(dc, sifive_e_aon_properties);
+}
+
+static const TypeInfo sifive_e_aon_info = {
+ .name = TYPE_SIFIVE_E_AON,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFiveEAONState),
+ .instance_init = sifive_e_aon_init,
+ .class_init = sifive_e_aon_class_init,
+};
+
+static void sifive_e_aon_register_types(void)
+{
+ type_register_static(&sifive_e_aon_info);
+}
+
+type_init(sifive_e_aon_register_types)
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 6528ebfa3a..b6a5eb4452 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -60,6 +60,7 @@ config SIFIVE_E
select SIFIVE_PLIC
select SIFIVE_UART
select SIFIVE_E_PRCI
+ select SIFIVE_E_AON
select UNIMP
config SIFIVE_U
diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
index 04939b60c3..0d37adc542 100644
--- a/hw/riscv/sifive_e.c
+++ b/hw/riscv/sifive_e.c
@@ -45,6 +45,7 @@
#include "hw/intc/riscv_aclint.h"
#include "hw/intc/sifive_plic.h"
#include "hw/misc/sifive_e_prci.h"
+#include "hw/misc/sifive_e_aon.h"
#include "chardev/char.h"
#include "sysemu/sysemu.h"
@@ -185,6 +186,8 @@ static void sifive_e_soc_init(Object *obj)
object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x1004, &error_abort);
object_initialize_child(obj, "riscv.sifive.e.gpio0", &s->gpio,
TYPE_SIFIVE_GPIO);
+ object_initialize_child(obj, "riscv.sifive.e.aon", &s->aon,
+ TYPE_SIFIVE_E_AON);
}
static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
@@ -223,10 +226,17 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus,
RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false);
- create_unimplemented_device("riscv.sifive.e.aon",
- memmap[SIFIVE_E_DEV_AON].base, memmap[SIFIVE_E_DEV_AON].size);
sifive_e_prci_create(memmap[SIFIVE_E_DEV_PRCI].base);
+ /* AON */
+
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->aon), errp)) {
+ return;
+ }
+
+ /* Map AON registers */
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->aon), 0, memmap[SIFIVE_E_DEV_AON].base);
+
/* GPIO */
if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) {
@@ -245,6 +255,9 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in(DEVICE(s->plic),
SIFIVE_E_GPIO0_IRQ0 + i));
}
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->aon), 0,
+ qdev_get_gpio_in(DEVICE(s->plic),
+ SIFIVE_E_AON_WDT_IRQ));
sifive_uart_create(sys_mem, memmap[SIFIVE_E_DEV_UART0].base,
serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART0_IRQ));
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 68ccd0bde1..d90286dc46 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -44,6 +44,7 @@
#include "chardev/char.h"
#include "sysemu/device_tree.h"
#include "sysemu/sysemu.h"
+#include "sysemu/tcg.h"
#include "sysemu/kvm.h"
#include "sysemu/tpm.h"
#include "hw/pci/pci.h"
@@ -243,13 +244,13 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket,
s->soc[socket].hartid_base + cpu);
qemu_fdt_add_subnode(ms->fdt, cpu_name);
- satp_mode_max = satp_mode_max_from_map(
- s->soc[socket].harts[cpu].cfg.satp_mode.map);
- sv_name = g_strdup_printf("riscv,%s",
- satp_mode_str(satp_mode_max, is_32_bit));
- qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type", sv_name);
- g_free(sv_name);
-
+ if (cpu_ptr->cfg.satp_mode.supported != 0) {
+ satp_mode_max = satp_mode_max_from_map(cpu_ptr->cfg.satp_mode.map);
+ sv_name = g_strdup_printf("riscv,%s",
+ satp_mode_str(satp_mode_max, is_32_bit));
+ qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type", sv_name);
+ g_free(sv_name);
+ }
name = riscv_isa_string(cpu_ptr);
qemu_fdt_setprop_string(ms->fdt, cpu_name, "riscv,isa", name);
@@ -776,7 +777,7 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,
g_free(clust_name);
- if (!kvm_enabled()) {
+ if (tcg_enabled()) {
if (s->have_aclint) {
create_fdt_socket_aclint(s, memmap, socket,
&intc_phandles[phandle_pos]);
@@ -1244,10 +1245,21 @@ static void virt_machine_done(Notifier *notifier, void *data)
target_ulong start_addr = memmap[VIRT_DRAM].base;
target_ulong firmware_end_addr, kernel_start_addr;
const char *firmware_name = riscv_default_firmware_name(&s->soc[0]);
- uint32_t fdt_load_addr;
+ uint64_t fdt_load_addr;
uint64_t kernel_entry = 0;
BlockBackend *pflash_blk0;
+ /* load/create device tree */
+ if (machine->dtb) {
+ machine->fdt = load_device_tree(machine->dtb, &s->fdt_size);
+ if (!machine->fdt) {
+ error_report("load_device_tree() failed");
+ exit(1);
+ }
+ } else {
+ create_fdt(s, memmap);
+ }
+
/*
* Only direct boot kernel is currently supported for KVM VM,
* so the "-bios" parameter is not supported when KVM is enabled.
@@ -1370,7 +1382,7 @@ static void virt_machine_init(MachineState *machine)
hart_count, &error_abort);
sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_fatal);
- if (!kvm_enabled()) {
+ if (tcg_enabled()) {
if (s->have_aclint) {
if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) {
/* Per-socket ACLINT MTIMER */
@@ -1508,17 +1520,6 @@ static void virt_machine_init(MachineState *machine)
}
virt_flash_map(s, system_memory);
- /* load/create device tree */
- if (machine->dtb) {
- machine->fdt = load_device_tree(machine->dtb, &s->fdt_size);
- if (!machine->fdt) {
- error_report("load_device_tree() failed");
- exit(1);
- }
- } else {
- create_fdt(s, memmap);
- }
-
s->machine_done.notify = virt_machine_done;
qemu_add_machine_init_done_notifier(&s->machine_done);
}
@@ -1682,12 +1683,13 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
#endif
- object_class_property_add_bool(oc, "aclint", virt_get_aclint,
- virt_set_aclint);
- object_class_property_set_description(oc, "aclint",
- "Set on/off to enable/disable "
- "emulating ACLINT devices");
-
+ if (tcg_enabled()) {
+ object_class_property_add_bool(oc, "aclint", virt_get_aclint,
+ virt_set_aclint);
+ object_class_property_set_description(oc, "aclint",
+ "Set on/off to enable/disable "
+ "emulating ACLINT devices");
+ }
object_class_property_add_str(oc, "aia", virt_get_aia,
virt_set_aia);
object_class_property_set_description(oc, "aia",
diff --git a/include/hw/misc/sifive_e_aon.h b/include/hw/misc/sifive_e_aon.h
new file mode 100644
index 0000000000..2ae1c4139c
--- /dev/null
+++ b/include/hw/misc/sifive_e_aon.h
@@ -0,0 +1,60 @@
+/*
+ * SiFive HiFive1 AON (Always On Domain) interface.
+ *
+ * Copyright (c) 2022 SiFive, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#ifndef HW_SIFIVE_AON_H
+#define HW_SIFIVE_AON_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_SIFIVE_E_AON "riscv.sifive.e.aon"
+OBJECT_DECLARE_SIMPLE_TYPE(SiFiveEAONState, SIFIVE_E_AON)
+
+#define SIFIVE_E_AON_WDOGKEY (0x51F15E)
+#define SIFIVE_E_AON_WDOGFEED (0xD09F00D)
+#define SIFIVE_E_LFCLK_DEFAULT_FREQ (32768)
+
+enum {
+ SIFIVE_E_AON_WDT = 0x0,
+ SIFIVE_E_AON_RTC = 0x40,
+ SIFIVE_E_AON_LFROSC = 0x70,
+ SIFIVE_E_AON_BACKUP = 0x80,
+ SIFIVE_E_AON_PMU = 0x100,
+ SIFIVE_E_AON_MAX = 0x150
+};
+
+struct SiFiveEAONState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion mmio;
+
+ /*< watchdog timer >*/
+ QEMUTimer *wdog_timer;
+ qemu_irq wdog_irq;
+ uint64_t wdog_restart_time;
+ uint64_t wdogclk_freq;
+
+ uint32_t wdogcfg;
+ uint16_t wdogcmp0;
+ uint32_t wdogcount;
+ uint8_t wdogunlock;
+};
+
+#endif
diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h
index b824a79e2d..31180a680e 100644
--- a/include/hw/riscv/sifive_e.h
+++ b/include/hw/riscv/sifive_e.h
@@ -22,6 +22,7 @@
#include "hw/riscv/riscv_hart.h"
#include "hw/riscv/sifive_cpu.h"
#include "hw/gpio/sifive_gpio.h"
+#include "hw/misc/sifive_e_aon.h"
#include "hw/boards.h"
#define TYPE_RISCV_E_SOC "riscv.sifive.e.soc"
@@ -35,6 +36,7 @@ typedef struct SiFiveESoCState {
/*< public >*/
RISCVHartArrayState cpus;
DeviceState *plic;
+ SiFiveEAONState aon;
SIFIVEGPIOState gpio;
MemoryRegion xip_mem;
MemoryRegion mask_rom;
@@ -76,9 +78,10 @@ enum {
};
enum {
- SIFIVE_E_UART0_IRQ = 3,
- SIFIVE_E_UART1_IRQ = 4,
- SIFIVE_E_GPIO0_IRQ0 = 8
+ SIFIVE_E_AON_WDT_IRQ = 1,
+ SIFIVE_E_UART0_IRQ = 3,
+ SIFIVE_E_UART1_IRQ = 4,
+ SIFIVE_E_GPIO0_IRQ0 = 8
};
#define SIFIVE_E_PLIC_HART_CONFIG "M"
diff --git a/linux-user/riscv/syscall32_nr.h b/linux-user/riscv/syscall32_nr.h
index 1327d7dffa..412e58e5b2 100644
--- a/linux-user/riscv/syscall32_nr.h
+++ b/linux-user/riscv/syscall32_nr.h
@@ -228,6 +228,7 @@
#define TARGET_NR_accept4 242
#define TARGET_NR_arch_specific_syscall 244
#define TARGET_NR_riscv_flush_icache (TARGET_NR_arch_specific_syscall + 15)
+#define TARGET_NR_riscv_hwprobe (TARGET_NR_arch_specific_syscall + 14)
#define TARGET_NR_prlimit64 261
#define TARGET_NR_fanotify_init 262
#define TARGET_NR_fanotify_mark 263
diff --git a/linux-user/riscv/syscall64_nr.h b/linux-user/riscv/syscall64_nr.h
index 6659751933..29e1eb2075 100644
--- a/linux-user/riscv/syscall64_nr.h
+++ b/linux-user/riscv/syscall64_nr.h
@@ -251,6 +251,7 @@
#define TARGET_NR_recvmmsg 243
#define TARGET_NR_arch_specific_syscall 244
#define TARGET_NR_riscv_flush_icache (TARGET_NR_arch_specific_syscall + 15)
+#define TARGET_NR_riscv_hwprobe (TARGET_NR_arch_specific_syscall + 14)
#define TARGET_NR_wait4 260
#define TARGET_NR_prlimit64 261
#define TARGET_NR_fanotify_init 262
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 9b9e3bd5e3..420bab7c68 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8983,6 +8983,147 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count)
}
#endif /* TARGET_NR_getdents64 */
+#if defined(TARGET_NR_riscv_hwprobe)
+
+#define RISCV_HWPROBE_KEY_MVENDORID 0
+#define RISCV_HWPROBE_KEY_MARCHID 1
+#define RISCV_HWPROBE_KEY_MIMPID 2
+
+#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3
+#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1 << 0)
+
+#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
+#define RISCV_HWPROBE_IMA_FD (1 << 0)
+#define RISCV_HWPROBE_IMA_C (1 << 1)
+
+#define RISCV_HWPROBE_KEY_CPUPERF_0 5
+#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
+#define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0)
+#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0)
+#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0)
+#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
+#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)
+
+struct riscv_hwprobe {
+ abi_llong key;
+ abi_ullong value;
+};
+
+static void risc_hwprobe_fill_pairs(CPURISCVState *env,
+ struct riscv_hwprobe *pair,
+ size_t pair_count)
+{
+ const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
+
+ for (; pair_count > 0; pair_count--, pair++) {
+ abi_llong key;
+ abi_ullong value;
+ __put_user(0, &pair->value);
+ __get_user(key, &pair->key);
+ switch (key) {
+ case RISCV_HWPROBE_KEY_MVENDORID:
+ __put_user(cfg->mvendorid, &pair->value);
+ break;
+ case RISCV_HWPROBE_KEY_MARCHID:
+ __put_user(cfg->marchid, &pair->value);
+ break;
+ case RISCV_HWPROBE_KEY_MIMPID:
+ __put_user(cfg->mimpid, &pair->value);
+ break;
+ case RISCV_HWPROBE_KEY_BASE_BEHAVIOR:
+ value = riscv_has_ext(env, RVI) &&
+ riscv_has_ext(env, RVM) &&
+ riscv_has_ext(env, RVA) ?
+ RISCV_HWPROBE_BASE_BEHAVIOR_IMA : 0;
+ __put_user(value, &pair->value);
+ break;
+ case RISCV_HWPROBE_KEY_IMA_EXT_0:
+ value = riscv_has_ext(env, RVF) &&
+ riscv_has_ext(env, RVD) ?
+ RISCV_HWPROBE_IMA_FD : 0;
+ value |= riscv_has_ext(env, RVC) ?
+ RISCV_HWPROBE_IMA_C : pair->value;
+ __put_user(value, &pair->value);
+ break;
+ case RISCV_HWPROBE_KEY_CPUPERF_0:
+ __put_user(RISCV_HWPROBE_MISALIGNED_FAST, &pair->value);
+ break;
+ default:
+ __put_user(-1, &pair->key);
+ break;
+ }
+ }
+}
+
+static int cpu_set_valid(abi_long arg3, abi_long arg4)
+{
+ int ret, i, tmp;
+ size_t host_mask_size, target_mask_size;
+ unsigned long *host_mask;
+
+ /*
+ * cpu_set_t represent CPU masks as bit masks of type unsigned long *.
+ * arg3 contains the cpu count.
+ */
+ tmp = (8 * sizeof(abi_ulong));
+ target_mask_size = ((arg3 + tmp - 1) / tmp) * sizeof(abi_ulong);
+ host_mask_size = (target_mask_size + (sizeof(*host_mask) - 1)) &
+ ~(sizeof(*host_mask) - 1);
+
+ host_mask = alloca(host_mask_size);
+
+ ret = target_to_host_cpu_mask(host_mask, host_mask_size,
+ arg4, target_mask_size);
+ if (ret != 0) {
+ return ret;
+ }
+
+ for (i = 0 ; i < host_mask_size / sizeof(*host_mask); i++) {
+ if (host_mask[i] != 0) {
+ return 0;
+ }
+ }
+ return -TARGET_EINVAL;
+}
+
+static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1,
+ abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5)
+{
+ int ret;
+ struct riscv_hwprobe *host_pairs;
+
+ /* flags must be 0 */
+ if (arg5 != 0) {
+ return -TARGET_EINVAL;
+ }
+
+ /* check cpu_set */
+ if (arg3 != 0) {
+ ret = cpu_set_valid(arg3, arg4);
+ if (ret != 0) {
+ return ret;
+ }
+ } else if (arg4 != 0) {
+ return -TARGET_EINVAL;
+ }
+
+ /* no pairs */
+ if (arg2 == 0) {
+ return 0;
+ }
+
+ host_pairs = lock_user(VERIFY_WRITE, arg1,
+ sizeof(*host_pairs) * (size_t)arg2, 0);
+ if (host_pairs == NULL) {
+ return -TARGET_EFAULT;
+ }
+ risc_hwprobe_fill_pairs(cpu_env, host_pairs, arg2);
+ unlock_user(host_pairs, arg1, sizeof(*host_pairs) * (size_t)arg2);
+ return 0;
+}
+#endif /* TARGET_NR_riscv_hwprobe */
+
#if defined(TARGET_NR_pivot_root) && defined(__NR_pivot_root)
_syscall2(int, pivot_root, const char *, new_root, const char *, put_old)
#endif
@@ -13665,6 +13806,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
return ret;
#endif
+#if defined(TARGET_NR_riscv_hwprobe)
+ case TARGET_NR_riscv_hwprobe:
+ return do_riscv_hwprobe(cpu_env, arg1, arg2, arg3, arg4, arg5);
+#endif
+
default:
qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
return -TARGET_ENOSYS;
diff --git a/meson.build b/meson.build
index 162e664c2b..5fcdb37a71 100644
--- a/meson.build
+++ b/meson.build
@@ -55,16 +55,11 @@ qapi_trace_events = []
bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin']
supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux']
-supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv', 'x86', 'x86_64',
+supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64',
'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64']
cpu = host_machine.cpu_family()
-# Unify riscv* to a single family.
-if cpu in ['riscv32', 'riscv64']
- cpu = 'riscv'
-endif
-
target_dirs = config_host['TARGET_DIRS'].split()
have_linux_user = false
have_bsd_user = false
@@ -99,6 +94,8 @@ elif cpu == 'x86'
host_arch = 'i386'
elif cpu == 'mips64'
host_arch = 'mips'
+elif cpu in ['riscv32', 'riscv64']
+ host_arch = 'riscv'
else
host_arch = cpu
endif
@@ -113,8 +110,10 @@ elif cpu in ['ppc', 'ppc64']
kvm_targets = ['ppc-softmmu', 'ppc64-softmmu']
elif cpu in ['mips', 'mips64']
kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu']
-elif cpu in ['riscv']
- kvm_targets = ['riscv32-softmmu', 'riscv64-softmmu']
+elif cpu in ['riscv32']
+ kvm_targets = ['riscv32-softmmu']
+elif cpu in ['riscv64']
+ kvm_targets = ['riscv64-softmmu']
else
kvm_targets = []
endif
diff --git a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
index 6a8425885c..7b6c67e0ae 100644
Binary files a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin and b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin differ
diff --git a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin
index 80bdbf2170..1b831b412c 100644
Binary files a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin and b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin differ
diff --git a/roms/opensbi b/roms/opensbi
index 6b5188ca14..2552799a1d 160000
--- a/roms/opensbi
+++ b/roms/opensbi
@@ -1 +1 @@
-Subproject commit 6b5188ca14e59ce7bf71afe4e7d3d557c3d31bf8
+Subproject commit 2552799a1df30a3dcd2321a8b75d61d06f5fb9fc
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 4035fe0e62..9339c0241d 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -34,16 +34,11 @@
#include "migration/vmstate.h"
#include "fpu/softfloat-helpers.h"
#include "sysemu/kvm.h"
+#include "sysemu/tcg.h"
#include "kvm_riscv.h"
#include "tcg/tcg.h"
/* RISC-V CPU definitions */
-
-#define RISCV_CPU_MARCHID ((QEMU_VERSION_MAJOR << 16) | \
- (QEMU_VERSION_MINOR << 8) | \
- (QEMU_VERSION_MICRO))
-#define RISCV_CPU_MIMPID RISCV_CPU_MARCHID
-
static const char riscv_single_letter_exts[] = "IEMAFDQCPVH";
struct isa_ext_data {
@@ -55,6 +50,17 @@ struct isa_ext_data {
#define ISA_EXT_DATA_ENTRY(_name, _min_ver, _prop) \
{#_name, _min_ver, offsetof(struct RISCVCPUConfig, _prop)}
+/*
+ * From vector_helper.c
+ * Note that vector data is stored in host-endian 64-bit chunks,
+ * so addressing bytes needs a host-endian fixup.
+ */
+#if HOST_BIG_ENDIAN
+#define BYTE(x) ((x) ^ 7)
+#else
+#define BYTE(x) (x)
+#endif
+
/*
* Here are the ordering rules of extension naming defined by RISC-V
* specification :
@@ -83,6 +89,8 @@ static const struct isa_ext_data isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_ifencei),
ISA_EXT_DATA_ENTRY(zihintpause, PRIV_VERSION_1_10_0, ext_zihintpause),
ISA_EXT_DATA_ENTRY(zawrs, PRIV_VERSION_1_12_0, ext_zawrs),
+ ISA_EXT_DATA_ENTRY(zfa, PRIV_VERSION_1_12_0, ext_zfa),
+ ISA_EXT_DATA_ENTRY(zfbfmin, PRIV_VERSION_1_12_0, ext_zfbfmin),
ISA_EXT_DATA_ENTRY(zfh, PRIV_VERSION_1_11_0, ext_zfh),
ISA_EXT_DATA_ENTRY(zfhmin, PRIV_VERSION_1_11_0, ext_zfhmin),
ISA_EXT_DATA_ENTRY(zfinx, PRIV_VERSION_1_12_0, ext_zfinx),
@@ -114,6 +122,8 @@ static const struct isa_ext_data isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(zve32f, PRIV_VERSION_1_10_0, ext_zve32f),
ISA_EXT_DATA_ENTRY(zve64f, PRIV_VERSION_1_10_0, ext_zve64f),
ISA_EXT_DATA_ENTRY(zve64d, PRIV_VERSION_1_10_0, ext_zve64d),
+ ISA_EXT_DATA_ENTRY(zvfbfmin, PRIV_VERSION_1_12_0, ext_zvfbfmin),
+ ISA_EXT_DATA_ENTRY(zvfbfwma, PRIV_VERSION_1_12_0, ext_zvfbfwma),
ISA_EXT_DATA_ENTRY(zvfh, PRIV_VERSION_1_12_0, ext_zvfh),
ISA_EXT_DATA_ENTRY(zvfhmin, PRIV_VERSION_1_12_0, ext_zvfhmin),
ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx),
@@ -183,6 +193,14 @@ const char * const riscv_fpr_regnames[] = {
"f30/ft10", "f31/ft11"
};
+const char * const riscv_rvv_regnames[] = {
+ "v0", "v1", "v2", "v3", "v4", "v5", "v6",
+ "v7", "v8", "v9", "v10", "v11", "v12", "v13",
+ "v14", "v15", "v16", "v17", "v18", "v19", "v20",
+ "v21", "v22", "v23", "v24", "v25", "v26", "v27",
+ "v28", "v29", "v30", "v31"
+};
+
static const char * const riscv_excp_names[] = {
"misaligned_fetch",
"fault_fetch",
@@ -412,6 +430,7 @@ static void rv64_thead_c906_cpu_init(Object *obj)
set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU);
env->priv_ver = PRIV_VERSION_1_11_0;
+ cpu->cfg.ext_zfa = true;
cpu->cfg.ext_zfh = true;
cpu->cfg.mmu = true;
cpu->cfg.ext_xtheadba = true;
@@ -444,6 +463,9 @@ static void rv64_veyron_v1_cpu_init(Object *obj)
/* Enable ISA extensions */
cpu->cfg.mmu = true;
+ cpu->cfg.ext_ifencei = true;
+ cpu->cfg.ext_icsr = true;
+ cpu->cfg.pmp = true;
cpu->cfg.ext_icbom = true;
cpu->cfg.cbom_blocksize = 64;
cpu->cfg.cboz_blocksize = 64;
@@ -608,7 +630,8 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags)
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
- int i;
+ int i, j;
+ uint8_t *p;
#if !defined(CONFIG_USER_ONLY)
if (riscv_has_ext(env, RVH)) {
@@ -692,6 +715,41 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags)
}
}
}
+ if (riscv_has_ext(env, RVV) && (flags & CPU_DUMP_VPU)) {
+ static const int dump_rvv_csrs[] = {
+ CSR_VSTART,
+ CSR_VXSAT,
+ CSR_VXRM,
+ CSR_VCSR,
+ CSR_VL,
+ CSR_VTYPE,
+ CSR_VLENB,
+ };
+ for (int i = 0; i < ARRAY_SIZE(dump_rvv_csrs); ++i) {
+ int csrno = dump_rvv_csrs[i];
+ target_ulong val = 0;
+ RISCVException res = riscv_csrrw_debug(env, csrno, &val, 0, 0);
+
+ /*
+ * Rely on the smode, hmode, etc, predicates within csr.c
+ * to do the filtering of the registers that are present.
+ */
+ if (res == RISCV_EXCP_NONE) {
+ qemu_fprintf(f, " %-8s " TARGET_FMT_lx "\n",
+ csr_ops[csrno].name, val);
+ }
+ }
+ uint16_t vlenb = cpu->cfg.vlen >> 3;
+
+ for (i = 0; i < 32; i++) {
+ qemu_fprintf(f, " %-8s ", riscv_rvv_regnames[i]);
+ p = (uint8_t *)env->vreg;
+ for (j = vlenb - 1 ; j >= 0; j--) {
+ qemu_fprintf(f, "%02x", *(p + i * vlenb + BYTE(j)));
+ }
+ qemu_fprintf(f, "\n");
+ }
+ }
}
static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
@@ -858,9 +916,10 @@ static void riscv_cpu_reset_hold(Object *obj)
static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
{
RISCVCPU *cpu = RISCV_CPU(s);
+ CPURISCVState *env = &cpu->env;
info->target_info = &cpu->cfg;
- switch (riscv_cpu_mxl(&cpu->env)) {
+ switch (env->xl) {
case MXL_RV32:
info->print_insn = print_insn_riscv32;
break;
@@ -1050,6 +1109,11 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
return;
}
+ if (cpu->cfg.ext_zfa && !riscv_has_ext(env, RVF)) {
+ error_setg(errp, "Zfa extension requires F extension");
+ return;
+ }
+
if (cpu->cfg.ext_zfh) {
cpu->cfg.ext_zfhmin = true;
}
@@ -1059,6 +1123,11 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
return;
}
+ if (cpu->cfg.ext_zfbfmin && !riscv_has_ext(env, RVF)) {
+ error_setg(errp, "Zfbfmin extension depends on F extension");
+ return;
+ }
+
if (riscv_has_ext(env, RVD) && !riscv_has_ext(env, RVF)) {
error_setg(errp, "D extension requires F extension");
return;
@@ -1109,6 +1178,21 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
return;
}
+ if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zfbfmin) {
+ error_setg(errp, "Zvfbfmin extension depends on Zfbfmin extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zve32f) {
+ error_setg(errp, "Zvfbfmin extension depends on Zve32f extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zvfbfwma && !cpu->cfg.ext_zvfbfmin) {
+ error_setg(errp, "Zvfbfwma extension depends on Zvfbfmin extension");
+ return;
+ }
+
/* Set the ISA extensions, checks should have happened above */
if (cpu->cfg.ext_zhinx) {
cpu->cfg.ext_zhinxmin = true;
@@ -1304,20 +1388,12 @@ static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp)
}
}
-static void riscv_cpu_realize(DeviceState *dev, Error **errp)
+static void riscv_cpu_realize_tcg(DeviceState *dev, Error **errp)
{
- CPUState *cs = CPU(dev);
RISCVCPU *cpu = RISCV_CPU(dev);
CPURISCVState *env = &cpu->env;
- RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
Error *local_err = NULL;
- cpu_exec_realizefn(cs, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- return;
- }
-
riscv_cpu_validate_misa_mxl(cpu, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -1352,7 +1428,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
}
#ifndef CONFIG_USER_ONLY
- cs->tcg_cflags |= CF_PCREL;
+ CPU(dev)->tcg_cflags |= CF_PCREL;
if (cpu->cfg.ext_sstc) {
riscv_timer_init(cpu);
@@ -1365,6 +1441,28 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
}
}
#endif
+}
+
+static void riscv_cpu_realize(DeviceState *dev, Error **errp)
+{
+ CPUState *cs = CPU(dev);
+ RISCVCPU *cpu = RISCV_CPU(dev);
+ RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+ Error *local_err = NULL;
+
+ cpu_exec_realizefn(cs, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (tcg_enabled()) {
+ riscv_cpu_realize_tcg(dev, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
riscv_cpu_finalize_features(cpu, &local_err);
if (local_err != NULL) {
@@ -1545,33 +1643,83 @@ static void cpu_get_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
visit_type_bool(v, name, &value, errp);
}
-static const RISCVCPUMisaExtConfig misa_ext_cfgs[] = {
- {.name = "a", .description = "Atomic instructions",
- .misa_bit = RVA, .enabled = true},
- {.name = "c", .description = "Compressed instructions",
- .misa_bit = RVC, .enabled = true},
- {.name = "d", .description = "Double-precision float point",
- .misa_bit = RVD, .enabled = true},
- {.name = "f", .description = "Single-precision float point",
- .misa_bit = RVF, .enabled = true},
- {.name = "i", .description = "Base integer instruction set",
- .misa_bit = RVI, .enabled = true},
- {.name = "e", .description = "Base integer instruction set (embedded)",
- .misa_bit = RVE, .enabled = false},
- {.name = "m", .description = "Integer multiplication and division",
- .misa_bit = RVM, .enabled = true},
- {.name = "s", .description = "Supervisor-level instructions",
- .misa_bit = RVS, .enabled = true},
- {.name = "u", .description = "User-level instructions",
- .misa_bit = RVU, .enabled = true},
- {.name = "h", .description = "Hypervisor",
- .misa_bit = RVH, .enabled = true},
- {.name = "x-j", .description = "Dynamic translated languages",
- .misa_bit = RVJ, .enabled = false},
- {.name = "v", .description = "Vector operations",
- .misa_bit = RVV, .enabled = false},
- {.name = "g", .description = "General purpose (IMAFD_Zicsr_Zifencei)",
- .misa_bit = RVG, .enabled = false},
+typedef struct misa_ext_info {
+ const char *name;
+ const char *description;
+} MISAExtInfo;
+
+#define MISA_INFO_IDX(_bit) \
+ __builtin_ctz(_bit)
+
+#define MISA_EXT_INFO(_bit, _propname, _descr) \
+ [MISA_INFO_IDX(_bit)] = {.name = _propname, .description = _descr}
+
+static const MISAExtInfo misa_ext_info_arr[] = {
+ MISA_EXT_INFO(RVA, "a", "Atomic instructions"),
+ MISA_EXT_INFO(RVC, "c", "Compressed instructions"),
+ MISA_EXT_INFO(RVD, "d", "Double-precision float point"),
+ MISA_EXT_INFO(RVF, "f", "Single-precision float point"),
+ MISA_EXT_INFO(RVI, "i", "Base integer instruction set"),
+ MISA_EXT_INFO(RVE, "e", "Base integer instruction set (embedded)"),
+ MISA_EXT_INFO(RVM, "m", "Integer multiplication and division"),
+ MISA_EXT_INFO(RVS, "s", "Supervisor-level instructions"),
+ MISA_EXT_INFO(RVU, "u", "User-level instructions"),
+ MISA_EXT_INFO(RVH, "h", "Hypervisor"),
+ MISA_EXT_INFO(RVJ, "x-j", "Dynamic translated languages"),
+ MISA_EXT_INFO(RVV, "v", "Vector operations"),
+ MISA_EXT_INFO(RVG, "g", "General purpose (IMAFD_Zicsr_Zifencei)"),
+};
+
+static int riscv_validate_misa_info_idx(uint32_t bit)
+{
+ int idx;
+
+ /*
+ * Our lowest valid input (RVA) is 1 and
+ * __builtin_ctz() is UB with zero.
+ */
+ g_assert(bit != 0);
+ idx = MISA_INFO_IDX(bit);
+
+ g_assert(idx < ARRAY_SIZE(misa_ext_info_arr));
+ return idx;
+}
+
+const char *riscv_get_misa_ext_name(uint32_t bit)
+{
+ int idx = riscv_validate_misa_info_idx(bit);
+ const char *val = misa_ext_info_arr[idx].name;
+
+ g_assert(val != NULL);
+ return val;
+}
+
+const char *riscv_get_misa_ext_description(uint32_t bit)
+{
+ int idx = riscv_validate_misa_info_idx(bit);
+ const char *val = misa_ext_info_arr[idx].description;
+
+ g_assert(val != NULL);
+ return val;
+}
+
+#define MISA_CFG(_bit, _enabled) \
+ {.misa_bit = _bit, .enabled = _enabled}
+
+static RISCVCPUMisaExtConfig misa_ext_cfgs[] = {
+ MISA_CFG(RVA, true),
+ MISA_CFG(RVC, true),
+ MISA_CFG(RVD, true),
+ MISA_CFG(RVF, true),
+ MISA_CFG(RVI, true),
+ MISA_CFG(RVE, false),
+ MISA_CFG(RVM, true),
+ MISA_CFG(RVS, true),
+ MISA_CFG(RVU, true),
+ MISA_CFG(RVH, true),
+ MISA_CFG(RVJ, false),
+ MISA_CFG(RVV, false),
+ MISA_CFG(RVG, false),
};
static void riscv_cpu_add_misa_properties(Object *cpu_obj)
@@ -1579,7 +1727,16 @@ static void riscv_cpu_add_misa_properties(Object *cpu_obj)
int i;
for (i = 0; i < ARRAY_SIZE(misa_ext_cfgs); i++) {
- const RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i];
+ RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i];
+ int bit = misa_cfg->misa_bit;
+
+ misa_cfg->name = riscv_get_misa_ext_name(bit);
+ misa_cfg->description = riscv_get_misa_ext_description(bit);
+
+ /* Check if KVM already created the property */
+ if (object_property_find(cpu_obj, misa_cfg->name)) {
+ continue;
+ }
object_property_add(cpu_obj, misa_cfg->name, "bool",
cpu_get_misa_ext_cfg,
@@ -1600,6 +1757,7 @@ static Property riscv_cpu_extensions[] = {
DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true),
DEFINE_PROP_BOOL("Zihintpause", RISCVCPU, cfg.ext_zihintpause, true),
DEFINE_PROP_BOOL("Zawrs", RISCVCPU, cfg.ext_zawrs, true),
+ DEFINE_PROP_BOOL("Zfa", RISCVCPU, cfg.ext_zfa, true),
DEFINE_PROP_BOOL("Zfh", RISCVCPU, cfg.ext_zfh, false),
DEFINE_PROP_BOOL("Zfhmin", RISCVCPU, cfg.ext_zfhmin, false),
DEFINE_PROP_BOOL("Zve32f", RISCVCPU, cfg.ext_zve32f, false),
@@ -1683,9 +1841,33 @@ static Property riscv_cpu_extensions[] = {
DEFINE_PROP_BOOL("x-zvfh", RISCVCPU, cfg.ext_zvfh, false),
DEFINE_PROP_BOOL("x-zvfhmin", RISCVCPU, cfg.ext_zvfhmin, false),
+ DEFINE_PROP_BOOL("x-zfbfmin", RISCVCPU, cfg.ext_zfbfmin, false),
+ DEFINE_PROP_BOOL("x-zvfbfmin", RISCVCPU, cfg.ext_zvfbfmin, false),
+ DEFINE_PROP_BOOL("x-zvfbfwma", RISCVCPU, cfg.ext_zvfbfwma, false),
+
DEFINE_PROP_END_OF_LIST(),
};
+
+#ifndef CONFIG_USER_ONLY
+static void cpu_set_cfg_unavailable(Object *obj, Visitor *v,
+ const char *name,
+ void *opaque, Error **errp)
+{
+ const char *propname = opaque;
+ bool value;
+
+ if (!visit_type_bool(v, name, &value, errp)) {
+ return;
+ }
+
+ if (value) {
+ error_setg(errp, "extension %s is not available with KVM",
+ propname);
+ }
+}
+#endif
+
/*
* Add CPU properties with user-facing flags.
*
@@ -1697,24 +1879,48 @@ static void riscv_cpu_add_user_properties(Object *obj)
Property *prop;
DeviceState *dev = DEVICE(obj);
+#ifndef CONFIG_USER_ONLY
+ riscv_add_satp_mode_properties(obj);
+
+ if (kvm_enabled()) {
+ kvm_riscv_init_user_properties(obj);
+ }
+#endif
+
riscv_cpu_add_misa_properties(obj);
for (prop = riscv_cpu_extensions; prop && prop->name; prop++) {
+#ifndef CONFIG_USER_ONLY
+ if (kvm_enabled()) {
+ /* Check if KVM created the property already */
+ if (object_property_find(obj, prop->name)) {
+ continue;
+ }
+
+ /*
+ * Set the default to disabled for every extension
+ * unknown to KVM and error out if the user attempts
+ * to enable any of them.
+ *
+ * We're giving a pass for non-bool properties since they're
+ * not related to the availability of extensions and can be
+ * safely ignored as is.
+ */
+ if (prop->info == &qdev_prop_bool) {
+ object_property_add(obj, prop->name, "bool",
+ NULL, cpu_set_cfg_unavailable,
+ NULL, (void *)prop->name);
+ continue;
+ }
+ }
+#endif
qdev_property_add_static(dev, prop);
}
-
-#ifndef CONFIG_USER_ONLY
- riscv_add_satp_mode_properties(obj);
-#endif
}
static Property riscv_cpu_properties[] = {
DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true),
- DEFINE_PROP_UINT32("mvendorid", RISCVCPU, cfg.mvendorid, 0),
- DEFINE_PROP_UINT64("marchid", RISCVCPU, cfg.marchid, RISCV_CPU_MARCHID),
- DEFINE_PROP_UINT64("mimpid", RISCVCPU, cfg.mimpid, RISCV_CPU_MIMPID),
-
#ifndef CONFIG_USER_ONLY
DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC),
#endif
@@ -1798,6 +2004,119 @@ static const struct TCGCPUOps riscv_tcg_ops = {
#endif /* !CONFIG_USER_ONLY */
};
+static bool riscv_cpu_is_dynamic(Object *cpu_obj)
+{
+ return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL;
+}
+
+static void cpu_set_mvendorid(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ bool dynamic_cpu = riscv_cpu_is_dynamic(obj);
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ uint32_t prev_val = cpu->cfg.mvendorid;
+ uint32_t value;
+
+ if (!visit_type_uint32(v, name, &value, errp)) {
+ return;
+ }
+
+ if (!dynamic_cpu && prev_val != value) {
+ error_setg(errp, "Unable to change %s mvendorid (0x%x)",
+ object_get_typename(obj), prev_val);
+ return;
+ }
+
+ cpu->cfg.mvendorid = value;
+}
+
+static void cpu_get_mvendorid(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ bool value = RISCV_CPU(obj)->cfg.mvendorid;
+
+ visit_type_bool(v, name, &value, errp);
+}
+
+static void cpu_set_mimpid(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ bool dynamic_cpu = riscv_cpu_is_dynamic(obj);
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ uint64_t prev_val = cpu->cfg.mimpid;
+ uint64_t value;
+
+ if (!visit_type_uint64(v, name, &value, errp)) {
+ return;
+ }
+
+ if (!dynamic_cpu && prev_val != value) {
+ error_setg(errp, "Unable to change %s mimpid (0x%" PRIu64 ")",
+ object_get_typename(obj), prev_val);
+ return;
+ }
+
+ cpu->cfg.mimpid = value;
+}
+
+static void cpu_get_mimpid(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ bool value = RISCV_CPU(obj)->cfg.mimpid;
+
+ visit_type_bool(v, name, &value, errp);
+}
+
+static void cpu_set_marchid(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ bool dynamic_cpu = riscv_cpu_is_dynamic(obj);
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ uint64_t prev_val = cpu->cfg.marchid;
+ uint64_t value, invalid_val;
+ uint32_t mxlen = 0;
+
+ if (!visit_type_uint64(v, name, &value, errp)) {
+ return;
+ }
+
+ if (!dynamic_cpu && prev_val != value) {
+ error_setg(errp, "Unable to change %s marchid (0x%" PRIu64 ")",
+ object_get_typename(obj), prev_val);
+ return;
+ }
+
+ switch (riscv_cpu_mxl(&cpu->env)) {
+ case MXL_RV32:
+ mxlen = 32;
+ break;
+ case MXL_RV64:
+ case MXL_RV128:
+ mxlen = 64;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ invalid_val = 1LL << (mxlen - 1);
+
+ if (value == invalid_val) {
+ error_setg(errp, "Unable to set marchid with MSB (%u) bit set "
+ "and the remaining bits zero", mxlen);
+ return;
+ }
+
+ cpu->cfg.marchid = value;
+}
+
+static void cpu_get_marchid(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ bool value = RISCV_CPU(obj)->cfg.marchid;
+
+ visit_type_bool(v, name, &value, errp);
+}
+
static void riscv_cpu_class_init(ObjectClass *c, void *data)
{
RISCVCPUClass *mcc = RISCV_CPU_CLASS(c);
@@ -1829,6 +2148,15 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
cc->gdb_get_dynamic_xml = riscv_gdb_get_dynamic_xml;
cc->tcg_ops = &riscv_tcg_ops;
+ object_class_property_add(c, "mvendorid", "uint32", cpu_get_mvendorid,
+ cpu_set_mvendorid, NULL, NULL);
+
+ object_class_property_add(c, "mimpid", "uint64", cpu_get_mimpid,
+ cpu_set_mimpid, NULL, NULL);
+
+ object_class_property_add(c, "marchid", "uint64", cpu_get_marchid,
+ cpu_set_marchid, NULL, NULL);
+
device_class_set_props(dc, riscv_cpu_properties);
}
@@ -1840,8 +2168,7 @@ static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str,
int i;
for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) {
- if (cpu->env.priv_ver >= isa_edata_arr[i].min_version &&
- isa_ext_is_enabled(cpu, &isa_edata_arr[i])) {
+ if (isa_ext_is_enabled(cpu, &isa_edata_arr[i])) {
new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL);
g_free(old);
old = new;
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 7adb8706ac..6ea22e0eea 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -41,7 +41,10 @@
#define RV(x) ((target_ulong)1 << (x - 'A'))
-/* Consider updating misa_ext_cfgs[] when adding new MISA bits here */
+/*
+ * Consider updating misa_ext_info_arr[] and misa_ext_cfgs[]
+ * when adding new MISA bits here.
+ */
#define RVI RV('I')
#define RVE RV('E') /* E and I are mutually exclusive */
#define RVM RV('M')
@@ -56,6 +59,8 @@
#define RVJ RV('J')
#define RVG RV('G')
+const char *riscv_get_misa_ext_name(uint32_t bit);
+const char *riscv_get_misa_ext_description(uint32_t bit);
/* Privileged specification version */
enum {
@@ -500,6 +505,7 @@ FIELD(TB_FLAGS, ITRIGGER, 22, 1)
/* Virtual mode enabled */
FIELD(TB_FLAGS, VIRT_ENABLED, 23, 1)
FIELD(TB_FLAGS, PRIV, 24, 2)
+FIELD(TB_FLAGS, AXL, 26, 2)
#ifdef TARGET_RISCV32
#define riscv_cpu_mxl(env) ((void)(env), MXL_RV32)
@@ -516,13 +522,20 @@ static inline const RISCVCPUConfig *riscv_cpu_cfg(CPURISCVState *env)
return &env_archcpu(env)->cfg;
}
-#if defined(TARGET_RISCV32)
-#define cpu_recompute_xl(env) ((void)(env), MXL_RV32)
-#else
-static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env)
+#if !defined(CONFIG_USER_ONLY)
+static inline int cpu_address_mode(CPURISCVState *env)
+{
+ int mode = env->priv;
+
+ if (mode == PRV_M && get_field(env->mstatus, MSTATUS_MPRV)) {
+ mode = get_field(env->mstatus, MSTATUS_MPP);
+ }
+ return mode;
+}
+
+static inline RISCVMXL cpu_get_xl(CPURISCVState *env, target_ulong mode)
{
RISCVMXL xl = env->misa_mxl;
-#if !defined(CONFIG_USER_ONLY)
/*
* When emulating a 32-bit-only cpu, use RV32.
* When emulating a 64-bit cpu, and MXL has been reduced to RV32,
@@ -530,7 +543,7 @@ static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env)
* back to RV64 for lower privs.
*/
if (xl != MXL_RV32) {
- switch (env->priv) {
+ switch (mode) {
case PRV_M:
break;
case PRV_U:
@@ -541,11 +554,38 @@ static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env)
break;
}
}
-#endif
return xl;
}
#endif
+#if defined(TARGET_RISCV32)
+#define cpu_recompute_xl(env) ((void)(env), MXL_RV32)
+#else
+static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ return cpu_get_xl(env, env->priv);
+#else
+ return env->misa_mxl;
+#endif
+}
+#endif
+
+#if defined(TARGET_RISCV32)
+#define cpu_address_xl(env) ((void)(env), MXL_RV32)
+#else
+static inline RISCVMXL cpu_address_xl(CPURISCVState *env)
+{
+#ifdef CONFIG_USER_ONLY
+ return env->xl;
+#else
+ int mode = cpu_address_mode(env);
+
+ return cpu_get_xl(env, mode);
+#endif
+}
+#endif
+
static inline int riscv_cpu_xlen(CPURISCVState *env)
{
return 16 << env->xl;
diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
index c4a627d335..2bd9510ba3 100644
--- a/target/riscv/cpu_cfg.h
+++ b/target/riscv/cpu_cfg.h
@@ -75,6 +75,8 @@ struct RISCVCPUConfig {
bool ext_svpbmt;
bool ext_zdinx;
bool ext_zawrs;
+ bool ext_zfa;
+ bool ext_zfbfmin;
bool ext_zfh;
bool ext_zfhmin;
bool ext_zfinx;
@@ -84,6 +86,8 @@ struct RISCVCPUConfig {
bool ext_zve64f;
bool ext_zve64d;
bool ext_zmmul;
+ bool ext_zvfbfmin;
+ bool ext_zvfbfwma;
bool ext_zvfh;
bool ext_zvfhmin;
bool ext_smaia;
@@ -133,4 +137,41 @@ struct RISCVCPUConfig {
};
typedef struct RISCVCPUConfig RISCVCPUConfig;
+
+/* Helper functions to test for extensions. */
+
+static inline bool always_true_p(const RISCVCPUConfig *cfg __attribute__((__unused__)))
+{
+ return true;
+}
+
+static inline bool has_xthead_p(const RISCVCPUConfig *cfg)
+{
+ return cfg->ext_xtheadba || cfg->ext_xtheadbb ||
+ cfg->ext_xtheadbs || cfg->ext_xtheadcmo ||
+ cfg->ext_xtheadcondmov ||
+ cfg->ext_xtheadfmemidx || cfg->ext_xtheadfmv ||
+ cfg->ext_xtheadmac || cfg->ext_xtheadmemidx ||
+ cfg->ext_xtheadmempair || cfg->ext_xtheadsync;
+}
+
+#define MATERIALISE_EXT_PREDICATE(ext) \
+ static inline bool has_ ## ext ## _p(const RISCVCPUConfig *cfg) \
+ { \
+ return cfg->ext_ ## ext ; \
+ }
+
+MATERIALISE_EXT_PREDICATE(xtheadba)
+MATERIALISE_EXT_PREDICATE(xtheadbb)
+MATERIALISE_EXT_PREDICATE(xtheadbs)
+MATERIALISE_EXT_PREDICATE(xtheadcmo)
+MATERIALISE_EXT_PREDICATE(xtheadcondmov)
+MATERIALISE_EXT_PREDICATE(xtheadfmemidx)
+MATERIALISE_EXT_PREDICATE(xtheadfmv)
+MATERIALISE_EXT_PREDICATE(xtheadmac)
+MATERIALISE_EXT_PREDICATE(xtheadmemidx)
+MATERIALISE_EXT_PREDICATE(xtheadmempair)
+MATERIALISE_EXT_PREDICATE(xtheadsync)
+MATERIALISE_EXT_PREDICATE(XVentanaCondOps)
+
#endif
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index a944f25694..9f611d89bb 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -47,7 +47,8 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
if (mode == PRV_M && get_field(status, MSTATUS_MPRV)) {
mode = get_field(env->mstatus, MSTATUS_MPP);
- virt = get_field(env->mstatus, MSTATUS_MPV);
+ virt = get_field(env->mstatus, MSTATUS_MPV) &&
+ (mode != PRV_M);
if (virt) {
status = env->vsstatus;
}
@@ -134,6 +135,7 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
flags = FIELD_DP32(flags, TB_FLAGS, FS, fs);
flags = FIELD_DP32(flags, TB_FLAGS, VS, vs);
flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl);
+ flags = FIELD_DP32(flags, TB_FLAGS, AXL, cpu_address_xl(env));
if (env->cur_pmmask != 0) {
flags = FIELD_DP32(flags, TB_FLAGS, PM_MASK_ENABLED, 1);
}
@@ -147,13 +149,16 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
void riscv_cpu_update_mask(CPURISCVState *env)
{
target_ulong mask = 0, base = 0;
+ RISCVMXL xl = env->xl;
/*
* TODO: Current RVJ spec does not specify
* how the extension interacts with XLEN.
*/
#ifndef CONFIG_USER_ONLY
+ int mode = cpu_address_mode(env);
+ xl = cpu_get_xl(env, mode);
if (riscv_has_ext(env, RVJ)) {
- switch (env->priv) {
+ switch (mode) {
case PRV_M:
if (env->mmte & M_PM_ENABLE) {
mask = env->mpmmask;
@@ -177,7 +182,7 @@ void riscv_cpu_update_mask(CPURISCVState *env)
}
}
#endif
- if (env->xl == MXL_RV32) {
+ if (xl == MXL_RV32) {
env->cur_pmmask = mask & UINT32_MAX;
env->cur_pmbase = base & UINT32_MAX;
} else {
@@ -1277,7 +1282,6 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
if (ret == TRANSLATE_G_STAGE_FAIL) {
first_stage_error = false;
two_stage_indirect_error = true;
- access_type = MMU_DATA_LOAD;
}
qemu_log_mask(CPU_LOG_MMU,
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 58499b5afc..ea7585329e 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -1311,11 +1311,9 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
}
if (xl != MXL_RV32 || env->debugger) {
- /*
- * RV32: MPV and GVA are not in mstatus. The current plan is to
- * add them to mstatush. For now, we just don't support it.
- */
- mask |= MSTATUS_MPV | MSTATUS_GVA;
+ if (riscv_has_ext(env, RVH)) {
+ mask |= MSTATUS_MPV | MSTATUS_GVA;
+ }
if ((val & MSTATUS64_UXL) != 0) {
mask |= MSTATUS64_UXL;
}
@@ -1323,10 +1321,6 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
mstatus = (mstatus & ~mask) | (val & mask);
- if (xl > MXL_RV32) {
- /* SXL field is for now read only */
- mstatus = set_field(mstatus, MSTATUS64_SXL, xl);
- }
env->mstatus = mstatus;
/*
@@ -1335,8 +1329,9 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
*/
if (env->debugger) {
env->xl = cpu_recompute_xl(env);
- riscv_cpu_update_mask(env);
}
+
+ riscv_cpu_update_mask(env);
return RISCV_EXCP_NONE;
}
@@ -1351,7 +1346,7 @@ static RISCVException write_mstatush(CPURISCVState *env, int csrno,
target_ulong val)
{
uint64_t valh = (uint64_t)val << 32;
- uint64_t mask = MSTATUS_MPV | MSTATUS_GVA;
+ uint64_t mask = riscv_has_ext(env, RVH) ? MSTATUS_MPV | MSTATUS_GVA : 0;
env->mstatus = (env->mstatus & ~mask) | (valh & mask);
@@ -3639,7 +3634,7 @@ static RISCVException write_mpmmask(CPURISCVState *env, int csrno,
uint64_t mstatus;
env->mpmmask = val;
- if ((env->priv == PRV_M) && (env->mmte & M_PM_ENABLE)) {
+ if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) {
env->cur_pmmask = val;
}
env->mmte |= EXT_STATUS_DIRTY;
@@ -3667,8 +3662,11 @@ static RISCVException write_spmmask(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
env->spmmask = val;
- if ((env->priv == PRV_S) && (env->mmte & S_PM_ENABLE)) {
+ if ((cpu_address_mode(env) == PRV_S) && (env->mmte & S_PM_ENABLE)) {
env->cur_pmmask = val;
+ if (cpu_get_xl(env, PRV_S) == MXL_RV32) {
+ env->cur_pmmask &= UINT32_MAX;
+ }
}
env->mmte |= EXT_STATUS_DIRTY;
@@ -3695,8 +3693,11 @@ static RISCVException write_upmmask(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
env->upmmask = val;
- if ((env->priv == PRV_U) && (env->mmte & U_PM_ENABLE)) {
+ if ((cpu_address_mode(env) == PRV_U) && (env->mmte & U_PM_ENABLE)) {
env->cur_pmmask = val;
+ if (cpu_get_xl(env, PRV_U) == MXL_RV32) {
+ env->cur_pmmask &= UINT32_MAX;
+ }
}
env->mmte |= EXT_STATUS_DIRTY;
@@ -3719,7 +3720,7 @@ static RISCVException write_mpmbase(CPURISCVState *env, int csrno,
uint64_t mstatus;
env->mpmbase = val;
- if ((env->priv == PRV_M) && (env->mmte & M_PM_ENABLE)) {
+ if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) {
env->cur_pmbase = val;
}
env->mmte |= EXT_STATUS_DIRTY;
@@ -3747,8 +3748,11 @@ static RISCVException write_spmbase(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
env->spmbase = val;
- if ((env->priv == PRV_S) && (env->mmte & S_PM_ENABLE)) {
+ if ((cpu_address_mode(env) == PRV_S) && (env->mmte & S_PM_ENABLE)) {
env->cur_pmbase = val;
+ if (cpu_get_xl(env, PRV_S) == MXL_RV32) {
+ env->cur_pmbase &= UINT32_MAX;
+ }
}
env->mmte |= EXT_STATUS_DIRTY;
@@ -3775,8 +3779,11 @@ static RISCVException write_upmbase(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
env->upmbase = val;
- if ((env->priv == PRV_U) && (env->mmte & U_PM_ENABLE)) {
+ if ((cpu_address_mode(env) == PRV_U) && (env->mmte & U_PM_ENABLE)) {
env->cur_pmbase = val;
+ if (cpu_get_xl(env, PRV_U) == MXL_RV32) {
+ env->cur_pmbase &= UINT32_MAX;
+ }
}
env->mmte |= EXT_STATUS_DIRTY;
diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c
index 5dd14d8390..871a70a316 100644
--- a/target/riscv/fpu_helper.c
+++ b/target/riscv/fpu_helper.c
@@ -252,6 +252,14 @@ uint64_t helper_fmin_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
float32_minimum_number(frs1, frs2, &env->fp_status));
}
+uint64_t helper_fminm_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
+{
+ float32 frs1 = check_nanbox_s(env, rs1);
+ float32 frs2 = check_nanbox_s(env, rs2);
+ float32 ret = float32_min(frs1, frs2, &env->fp_status);
+ return nanbox_s(env, ret);
+}
+
uint64_t helper_fmax_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float32 frs1 = check_nanbox_s(env, rs1);
@@ -261,6 +269,14 @@ uint64_t helper_fmax_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
float32_maximum_number(frs1, frs2, &env->fp_status));
}
+uint64_t helper_fmaxm_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
+{
+ float32 frs1 = check_nanbox_s(env, rs1);
+ float32 frs2 = check_nanbox_s(env, rs2);
+ float32 ret = float32_max(frs1, frs2, &env->fp_status);
+ return nanbox_s(env, ret);
+}
+
uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t rs1)
{
float32 frs1 = check_nanbox_s(env, rs1);
@@ -274,6 +290,13 @@ target_ulong helper_fle_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
return float32_le(frs1, frs2, &env->fp_status);
}
+target_ulong helper_fleq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
+{
+ float32 frs1 = check_nanbox_s(env, rs1);
+ float32 frs2 = check_nanbox_s(env, rs2);
+ return float32_le_quiet(frs1, frs2, &env->fp_status);
+}
+
target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float32 frs1 = check_nanbox_s(env, rs1);
@@ -281,6 +304,13 @@ target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
return float32_lt(frs1, frs2, &env->fp_status);
}
+target_ulong helper_fltq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
+{
+ float32 frs1 = check_nanbox_s(env, rs1);
+ float32 frs2 = check_nanbox_s(env, rs2);
+ return float32_lt_quiet(frs1, frs2, &env->fp_status);
+}
+
target_ulong helper_feq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float32 frs1 = check_nanbox_s(env, rs1);
@@ -338,6 +368,30 @@ target_ulong helper_fclass_s(CPURISCVState *env, uint64_t rs1)
return fclass_s(frs1);
}
+uint64_t helper_fround_s(CPURISCVState *env, uint64_t rs1)
+{
+ float_status *fs = &env->fp_status;
+ uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact;
+ float32 frs1 = check_nanbox_s(env, rs1);
+
+ frs1 = float32_round_to_int(frs1, fs);
+
+ /* Restore the original NX flag. */
+ uint16_t flags = get_float_exception_flags(fs);
+ flags &= ~float_flag_inexact;
+ flags |= nx_old;
+ set_float_exception_flags(flags, fs);
+
+ return nanbox_s(env, frs1);
+}
+
+uint64_t helper_froundnx_s(CPURISCVState *env, uint64_t rs1)
+{
+ float32 frs1 = check_nanbox_s(env, rs1);
+ frs1 = float32_round_to_int(frs1, &env->fp_status);
+ return nanbox_s(env, frs1);
+}
+
uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
{
return float64_add(frs1, frs2, &env->fp_status);
@@ -365,6 +419,11 @@ uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
float64_minimum_number(frs1, frs2, &env->fp_status);
}
+uint64_t helper_fminm_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ return float64_min(frs1, frs2, &env->fp_status);
+}
+
uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
{
return env->priv_ver < PRIV_VERSION_1_11_0 ?
@@ -372,6 +431,11 @@ uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
float64_maximum_number(frs1, frs2, &env->fp_status);
}
+uint64_t helper_fmaxm_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ return float64_max(frs1, frs2, &env->fp_status);
+}
+
uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1)
{
return nanbox_s(env, float64_to_float32(rs1, &env->fp_status));
@@ -393,11 +457,21 @@ target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
return float64_le(frs1, frs2, &env->fp_status);
}
+target_ulong helper_fleq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ return float64_le_quiet(frs1, frs2, &env->fp_status);
+}
+
target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
{
return float64_lt(frs1, frs2, &env->fp_status);
}
+target_ulong helper_fltq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+ return float64_lt_quiet(frs1, frs2, &env->fp_status);
+}
+
target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
{
return float64_eq_quiet(frs1, frs2, &env->fp_status);
@@ -408,6 +482,11 @@ target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1)
return float64_to_int32(frs1, &env->fp_status);
}
+uint64_t helper_fcvtmod_w_d(CPURISCVState *env, uint64_t value)
+{
+ return float64_to_int32_modulo(value, float_round_to_zero, &env->fp_status);
+}
+
target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1)
{
return (int32_t)float64_to_uint32(frs1, &env->fp_status);
@@ -448,6 +527,27 @@ target_ulong helper_fclass_d(uint64_t frs1)
return fclass_d(frs1);
}
+uint64_t helper_fround_d(CPURISCVState *env, uint64_t frs1)
+{
+ float_status *fs = &env->fp_status;
+ uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact;
+
+ frs1 = float64_round_to_int(frs1, fs);
+
+ /* Restore the original NX flag. */
+ uint16_t flags = get_float_exception_flags(fs);
+ flags &= ~float_flag_inexact;
+ flags |= nx_old;
+ set_float_exception_flags(flags, fs);
+
+ return frs1;
+}
+
+uint64_t helper_froundnx_d(CPURISCVState *env, uint64_t frs1)
+{
+ return float64_round_to_int(frs1, &env->fp_status);
+}
+
uint64_t helper_fadd_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float16 frs1 = check_nanbox_h(env, rs1);
@@ -485,6 +585,14 @@ uint64_t helper_fmin_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
float16_minimum_number(frs1, frs2, &env->fp_status));
}
+uint64_t helper_fminm_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
+{
+ float16 frs1 = check_nanbox_h(env, rs1);
+ float16 frs2 = check_nanbox_h(env, rs2);
+ float16 ret = float16_min(frs1, frs2, &env->fp_status);
+ return nanbox_h(env, ret);
+}
+
uint64_t helper_fmax_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float16 frs1 = check_nanbox_h(env, rs1);
@@ -494,6 +602,14 @@ uint64_t helper_fmax_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
float16_maximum_number(frs1, frs2, &env->fp_status));
}
+uint64_t helper_fmaxm_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
+{
+ float16 frs1 = check_nanbox_h(env, rs1);
+ float16 frs2 = check_nanbox_h(env, rs2);
+ float16 ret = float16_max(frs1, frs2, &env->fp_status);
+ return nanbox_h(env, ret);
+}
+
uint64_t helper_fsqrt_h(CPURISCVState *env, uint64_t rs1)
{
float16 frs1 = check_nanbox_h(env, rs1);
@@ -507,6 +623,13 @@ target_ulong helper_fle_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
return float16_le(frs1, frs2, &env->fp_status);
}
+target_ulong helper_fleq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
+{
+ float16 frs1 = check_nanbox_h(env, rs1);
+ float16 frs2 = check_nanbox_h(env, rs2);
+ return float16_le_quiet(frs1, frs2, &env->fp_status);
+}
+
target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float16 frs1 = check_nanbox_h(env, rs1);
@@ -514,6 +637,13 @@ target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
return float16_lt(frs1, frs2, &env->fp_status);
}
+target_ulong helper_fltq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
+{
+ float16 frs1 = check_nanbox_h(env, rs1);
+ float16 frs2 = check_nanbox_h(env, rs2);
+ return float16_lt_quiet(frs1, frs2, &env->fp_status);
+}
+
target_ulong helper_feq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float16 frs1 = check_nanbox_h(env, rs1);
@@ -527,6 +657,30 @@ target_ulong helper_fclass_h(CPURISCVState *env, uint64_t rs1)
return fclass_h(frs1);
}
+uint64_t helper_fround_h(CPURISCVState *env, uint64_t rs1)
+{
+ float_status *fs = &env->fp_status;
+ uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact;
+ float16 frs1 = check_nanbox_h(env, rs1);
+
+ frs1 = float16_round_to_int(frs1, fs);
+
+ /* Restore the original NX flag. */
+ uint16_t flags = get_float_exception_flags(fs);
+ flags &= ~float_flag_inexact;
+ flags |= nx_old;
+ set_float_exception_flags(flags, fs);
+
+ return nanbox_h(env, frs1);
+}
+
+uint64_t helper_froundnx_h(CPURISCVState *env, uint64_t rs1)
+{
+ float16 frs1 = check_nanbox_s(env, rs1);
+ frs1 = float16_round_to_int(frs1, &env->fp_status);
+ return nanbox_h(env, frs1);
+}
+
target_ulong helper_fcvt_w_h(CPURISCVState *env, uint64_t rs1)
{
float16 frs1 = check_nanbox_h(env, rs1);
@@ -593,3 +747,15 @@ uint64_t helper_fcvt_d_h(CPURISCVState *env, uint64_t rs1)
float16 frs1 = check_nanbox_h(env, rs1);
return float16_to_float64(frs1, true, &env->fp_status);
}
+
+uint64_t helper_fcvt_bf16_s(CPURISCVState *env, uint64_t rs1)
+{
+ float32 frs1 = check_nanbox_s(env, rs1);
+ return nanbox_h(env, float32_to_bfloat16(frs1, &env->fp_status));
+}
+
+uint64_t helper_fcvt_s_bf16(CPURISCVState *env, uint64_t rs1)
+{
+ float16 frs1 = check_nanbox_h(env, rs1);
+ return nanbox_s(env, bfloat16_to_float32(frs1, &env->fp_status));
+}
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index 98e97810fd..c95adaf08a 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -25,10 +25,14 @@ DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fminm_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmaxm_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_RWG, i64, env, i64)
DEF_HELPER_FLAGS_3(fle_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(fleq_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
DEF_HELPER_FLAGS_3(flt_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(fltq_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
DEF_HELPER_FLAGS_3(feq_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
DEF_HELPER_FLAGS_2(fcvt_w_s, TCG_CALL_NO_RWG, tl, env, i64)
DEF_HELPER_FLAGS_2(fcvt_wu_s, TCG_CALL_NO_RWG, tl, env, i64)
@@ -39,6 +43,8 @@ DEF_HELPER_FLAGS_2(fcvt_s_wu, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_2(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_2(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG_SE, tl, env, i64)
+DEF_HELPER_FLAGS_2(fround_s, TCG_CALL_NO_RWG_SE, i64, env, i64)
+DEF_HELPER_FLAGS_2(froundnx_s, TCG_CALL_NO_RWG_SE, i64, env, i64)
/* Floating Point - Double Precision */
DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
@@ -46,14 +52,19 @@ DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fminm_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmaxm_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_2(fcvt_s_d, TCG_CALL_NO_RWG, i64, env, i64)
DEF_HELPER_FLAGS_2(fcvt_d_s, TCG_CALL_NO_RWG, i64, env, i64)
DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_RWG, i64, env, i64)
DEF_HELPER_FLAGS_3(fle_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(fleq_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
DEF_HELPER_FLAGS_3(flt_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(fltq_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
DEF_HELPER_FLAGS_3(feq_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
DEF_HELPER_FLAGS_2(fcvt_w_d, TCG_CALL_NO_RWG, tl, env, i64)
+DEF_HELPER_FLAGS_2(fcvtmod_w_d, TCG_CALL_NO_RWG, i64, env, i64)
DEF_HELPER_FLAGS_2(fcvt_wu_d, TCG_CALL_NO_RWG, tl, env, i64)
DEF_HELPER_FLAGS_2(fcvt_l_d, TCG_CALL_NO_RWG, tl, env, i64)
DEF_HELPER_FLAGS_2(fcvt_lu_d, TCG_CALL_NO_RWG, tl, env, i64)
@@ -62,6 +73,8 @@ DEF_HELPER_FLAGS_2(fcvt_d_wu, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_2(fcvt_d_l, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_2(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64)
+DEF_HELPER_FLAGS_2(fround_d, TCG_CALL_NO_RWG_SE, i64, env, i64)
+DEF_HELPER_FLAGS_2(froundnx_d, TCG_CALL_NO_RWG_SE, i64, env, i64)
/* Bitmanip */
DEF_HELPER_FLAGS_2(clmul, TCG_CALL_NO_RWG_SE, tl, tl, tl)
@@ -78,10 +91,14 @@ DEF_HELPER_FLAGS_3(fsub_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fmul_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fdiv_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fmin_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fminm_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(fmax_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmaxm_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_2(fsqrt_h, TCG_CALL_NO_RWG, i64, env, i64)
DEF_HELPER_FLAGS_3(fle_h, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(fleq_h, TCG_CALL_NO_RWG, tl, env, i64, i64)
DEF_HELPER_FLAGS_3(flt_h, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(fltq_h, TCG_CALL_NO_RWG, tl, env, i64, i64)
DEF_HELPER_FLAGS_3(feq_h, TCG_CALL_NO_RWG, tl, env, i64, i64)
DEF_HELPER_FLAGS_2(fcvt_s_h, TCG_CALL_NO_RWG, i64, env, i64)
DEF_HELPER_FLAGS_2(fcvt_h_s, TCG_CALL_NO_RWG, i64, env, i64)
@@ -96,6 +113,8 @@ DEF_HELPER_FLAGS_2(fcvt_h_wu, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_2(fcvt_h_l, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_2(fcvt_h_lu, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_2(fclass_h, TCG_CALL_NO_RWG_SE, tl, env, i64)
+DEF_HELPER_FLAGS_2(fround_h, TCG_CALL_NO_RWG_SE, i64, env, i64)
+DEF_HELPER_FLAGS_2(froundnx_h, TCG_CALL_NO_RWG_SE, i64, env, i64)
/* Cache-block operations */
DEF_HELPER_2(cbo_clean_flush, void, env, tl)
@@ -1153,3 +1172,13 @@ DEF_HELPER_FLAGS_3(sm4ks, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl)
/* Zce helper */
DEF_HELPER_FLAGS_2(cm_jalt, TCG_CALL_NO_WG, tl, env, i32)
+
+/* BF16 functions */
+DEF_HELPER_FLAGS_2(fcvt_bf16_s, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_s_bf16, TCG_CALL_NO_RWG, i64, env, i64)
+
+DEF_HELPER_5(vfncvtbf16_f_f_w, void, ptr, ptr, ptr, env, i32)
+DEF_HELPER_5(vfwcvtbf16_f_f_v, void, ptr, ptr, ptr, env, i32)
+
+DEF_HELPER_6(vfwmaccbf16_vv, void, ptr, ptr, ptr, ptr, env, i32)
+DEF_HELPER_6(vfwmaccbf16_vf, void, ptr, ptr, i64, ptr, env, i32)
diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
index 73d5d1b045..e341fa9213 100644
--- a/target/riscv/insn32.decode
+++ b/target/riscv/insn32.decode
@@ -821,6 +821,32 @@ binvi 01101. ........... 001 ..... 0010011 @sh
bset 0010100 .......... 001 ..... 0110011 @r
bseti 00101. ........... 001 ..... 0010011 @sh
+# *** Zfa Standard Extension ***
+fli_s 1111000 00001 ..... 000 ..... 1010011 @r2
+fli_d 1111001 00001 ..... 000 ..... 1010011 @r2
+fli_h 1111010 00001 ..... 000 ..... 1010011 @r2
+fminm_s 0010100 ..... ..... 010 ..... 1010011 @r
+fmaxm_s 0010100 ..... ..... 011 ..... 1010011 @r
+fminm_d 0010101 ..... ..... 010 ..... 1010011 @r
+fmaxm_d 0010101 ..... ..... 011 ..... 1010011 @r
+fminm_h 0010110 ..... ..... 010 ..... 1010011 @r
+fmaxm_h 0010110 ..... ..... 011 ..... 1010011 @r
+fround_s 0100000 00100 ..... ... ..... 1010011 @r2_rm
+froundnx_s 0100000 00101 ..... ... ..... 1010011 @r2_rm
+fround_d 0100001 00100 ..... ... ..... 1010011 @r2_rm
+froundnx_d 0100001 00101 ..... ... ..... 1010011 @r2_rm
+fround_h 0100010 00100 ..... ... ..... 1010011 @r2_rm
+froundnx_h 0100010 00101 ..... ... ..... 1010011 @r2_rm
+fcvtmod_w_d 1100001 01000 ..... 001 ..... 1010011 @r2
+fmvh_x_d 1110001 00001 ..... 000 ..... 1010011 @r2
+fmvp_d_x 1011001 ..... ..... 000 ..... 1010011 @r
+fleq_s 1010000 ..... ..... 100 ..... 1010011 @r
+fltq_s 1010000 ..... ..... 101 ..... 1010011 @r
+fleq_d 1010001 ..... ..... 100 ..... 1010011 @r
+fltq_d 1010001 ..... ..... 101 ..... 1010011 @r
+fleq_h 1010010 ..... ..... 100 ..... 1010011 @r
+fltq_h 1010010 ..... ..... 101 ..... 1010011 @r
+
# *** RV32 Zfh Extension ***
flh ............ ..... 001 ..... 0000111 @i
fsh ....... ..... ..... 001 ..... 0100111 @s
@@ -908,3 +934,15 @@ sm4ks .. 11010 ..... ..... 000 ..... 0110011 @k_aes
# *** RV32 Zicond Standard Extension ***
czero_eqz 0000111 ..... ..... 101 ..... 0110011 @r
czero_nez 0000111 ..... ..... 111 ..... 0110011 @r
+
+# *** Zfbfmin Standard Extension ***
+fcvt_bf16_s 0100010 01000 ..... ... ..... 1010011 @r2_rm
+fcvt_s_bf16 0100000 00110 ..... ... ..... 1010011 @r2_rm
+
+# *** Zvfbfmin Standard Extension ***
+vfncvtbf16_f_f_w 010010 . ..... 11101 001 ..... 1010111 @r2_vm
+vfwcvtbf16_f_f_v 010010 . ..... 01101 001 ..... 1010111 @r2_vm
+
+# *** Zvfbfwma Standard Extension ***
+vfwmaccbf16_vv 111011 . ..... ..... 001 ..... 1010111 @r_vm
+vfwmaccbf16_vf 111011 . ..... ..... 101 ..... 1010111 @r_vm
diff --git a/target/riscv/insn_trans/trans_rvbf16.c.inc b/target/riscv/insn_trans/trans_rvbf16.c.inc
new file mode 100644
index 0000000000..911bc29908
--- /dev/null
+++ b/target/riscv/insn_trans/trans_rvbf16.c.inc
@@ -0,0 +1,175 @@
+/*
+ * RISC-V translation routines for the BF16 Standard Extensions.
+ *
+ * Copyright (c) 2020-2023 PLCT Lab
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#define REQUIRE_ZFBFMIN(ctx) do { \
+ if (!ctx->cfg_ptr->ext_zfbfmin) { \
+ return false; \
+ } \
+} while (0)
+
+#define REQUIRE_ZVFBFMIN(ctx) do { \
+ if (!ctx->cfg_ptr->ext_zvfbfmin) { \
+ return false; \
+ } \
+} while (0)
+
+#define REQUIRE_ZVFBFWMA(ctx) do { \
+ if (!ctx->cfg_ptr->ext_zvfbfwma) { \
+ return false; \
+ } \
+} while (0)
+
+static bool trans_fcvt_bf16_s(DisasContext *ctx, arg_fcvt_bf16_s *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFBFMIN(ctx);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+
+ gen_set_rm(ctx, a->rm);
+ gen_helper_fcvt_bf16_s(dest, cpu_env, src1);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fcvt_s_bf16(DisasContext *ctx, arg_fcvt_s_bf16 *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFBFMIN(ctx);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+
+ gen_set_rm(ctx, a->rm);
+ gen_helper_fcvt_s_bf16(dest, cpu_env, src1);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_vfncvtbf16_f_f_w(DisasContext *ctx, arg_vfncvtbf16_f_f_w *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZVFBFMIN(ctx);
+
+ if (opfv_narrow_check(ctx, a) && (ctx->sew == MO_16)) {
+ uint32_t data = 0;
+ TCGLabel *over = gen_new_label();
+
+ gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over);
+ tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
+
+ data = FIELD_DP32(data, VDATA, VM, a->vm);
+ data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul);
+ data = FIELD_DP32(data, VDATA, VTA, ctx->vta);
+ data = FIELD_DP32(data, VDATA, VMA, ctx->vma);
+ tcg_gen_gvec_3_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0),
+ vreg_ofs(ctx, a->rs2), cpu_env,
+ ctx->cfg_ptr->vlen / 8,
+ ctx->cfg_ptr->vlen / 8, data,
+ gen_helper_vfncvtbf16_f_f_w);
+ mark_vs_dirty(ctx);
+ gen_set_label(over);
+ return true;
+ }
+ return false;
+}
+
+static bool trans_vfwcvtbf16_f_f_v(DisasContext *ctx, arg_vfwcvtbf16_f_f_v *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZVFBFMIN(ctx);
+
+ if (opfv_widen_check(ctx, a) && (ctx->sew == MO_16)) {
+ uint32_t data = 0;
+ TCGLabel *over = gen_new_label();
+
+ gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over);
+ tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
+
+ data = FIELD_DP32(data, VDATA, VM, a->vm);
+ data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul);
+ data = FIELD_DP32(data, VDATA, VTA, ctx->vta);
+ data = FIELD_DP32(data, VDATA, VMA, ctx->vma);
+ tcg_gen_gvec_3_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0),
+ vreg_ofs(ctx, a->rs2), cpu_env,
+ ctx->cfg_ptr->vlen / 8,
+ ctx->cfg_ptr->vlen / 8, data,
+ gen_helper_vfwcvtbf16_f_f_v);
+ mark_vs_dirty(ctx);
+ gen_set_label(over);
+ return true;
+ }
+ return false;
+}
+
+static bool trans_vfwmaccbf16_vv(DisasContext *ctx, arg_vfwmaccbf16_vv *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZVFBFWMA(ctx);
+
+ if (require_rvv(ctx) && vext_check_isa_ill(ctx) && (ctx->sew == MO_16) &&
+ vext_check_dss(ctx, a->rd, a->rs1, a->rs2, a->vm)) {
+ uint32_t data = 0;
+ TCGLabel *over = gen_new_label();
+
+ gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over);
+ tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
+
+ data = FIELD_DP32(data, VDATA, VM, a->vm);
+ data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul);
+ data = FIELD_DP32(data, VDATA, VTA, ctx->vta);
+ data = FIELD_DP32(data, VDATA, VMA, ctx->vma);
+ tcg_gen_gvec_4_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0),
+ vreg_ofs(ctx, a->rs1),
+ vreg_ofs(ctx, a->rs2), cpu_env,
+ ctx->cfg_ptr->vlen / 8,
+ ctx->cfg_ptr->vlen / 8, data,
+ gen_helper_vfwmaccbf16_vv);
+ mark_vs_dirty(ctx);
+ gen_set_label(over);
+ return true;
+ }
+ return false;
+}
+
+static bool trans_vfwmaccbf16_vf(DisasContext *ctx, arg_vfwmaccbf16_vf *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZVFBFWMA(ctx);
+
+ if (require_rvv(ctx) && (ctx->sew == MO_16) && vext_check_isa_ill(ctx) &&
+ vext_check_ds(ctx, a->rd, a->rs2, a->vm)) {
+ uint32_t data = 0;
+
+ gen_set_rm(ctx, RISCV_FRM_DYN);
+ data = FIELD_DP32(data, VDATA, VM, a->vm);
+ data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul);
+ data = FIELD_DP32(data, VDATA, VTA, ctx->vta);
+ data = FIELD_DP32(data, VDATA, VMA, ctx->vma);
+ return opfvf_trans(a->rd, a->rs1, a->rs2, data,
+ gen_helper_vfwmaccbf16_vf, ctx);
+ }
+
+ return false;
+}
diff --git a/target/riscv/insn_trans/trans_rvzfa.c.inc b/target/riscv/insn_trans/trans_rvzfa.c.inc
new file mode 100644
index 0000000000..2c715af3e5
--- /dev/null
+++ b/target/riscv/insn_trans/trans_rvzfa.c.inc
@@ -0,0 +1,521 @@
+/*
+ * RISC-V translation routines for the Zfa Standard Extension.
+ *
+ * Copyright (c) 2023 Christoph Müllner, christoph.muellner@vrull.eu
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#define REQUIRE_ZFA(ctx) do { \
+ if (!ctx->cfg_ptr->ext_zfa) { \
+ return false; \
+ } \
+} while (0)
+
+#define REQUIRE_ZFH(ctx) do { \
+ if (!ctx->cfg_ptr->ext_zfh) { \
+ return false; \
+ } \
+} while (0)
+
+static bool trans_fli_s(DisasContext *ctx, arg_fli_s *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVF);
+
+ /* Values below are NaN-boxed to avoid a gen_nanbox_s(). */
+ static const uint64_t fli_s_table[] = {
+ 0xffffffffbf800000, /* -1.0 */
+ 0xffffffff00800000, /* minimum positive normal */
+ 0xffffffff37800000, /* 1.0 * 2^-16 */
+ 0xffffffff38000000, /* 1.0 * 2^-15 */
+ 0xffffffff3b800000, /* 1.0 * 2^-8 */
+ 0xffffffff3c000000, /* 1.0 * 2^-7 */
+ 0xffffffff3d800000, /* 1.0 * 2^-4 */
+ 0xffffffff3e000000, /* 1.0 * 2^-3 */
+ 0xffffffff3e800000, /* 0.25 */
+ 0xffffffff3ea00000, /* 0.3125 */
+ 0xffffffff3ec00000, /* 0.375 */
+ 0xffffffff3ee00000, /* 0.4375 */
+ 0xffffffff3f000000, /* 0.5 */
+ 0xffffffff3f200000, /* 0.625 */
+ 0xffffffff3f400000, /* 0.75 */
+ 0xffffffff3f600000, /* 0.875 */
+ 0xffffffff3f800000, /* 1.0 */
+ 0xffffffff3fa00000, /* 1.25 */
+ 0xffffffff3fc00000, /* 1.5 */
+ 0xffffffff3fe00000, /* 1.75 */
+ 0xffffffff40000000, /* 2.0 */
+ 0xffffffff40200000, /* 2.5 */
+ 0xffffffff40400000, /* 3 */
+ 0xffffffff40800000, /* 4 */
+ 0xffffffff41000000, /* 8 */
+ 0xffffffff41800000, /* 16 */
+ 0xffffffff43000000, /* 2^7 */
+ 0xffffffff43800000, /* 2^8 */
+ 0xffffffff47000000, /* 2^15 */
+ 0xffffffff47800000, /* 2^16 */
+ 0xffffffff7f800000, /* +inf */
+ 0xffffffff7fc00000, /* Canonical NaN */
+ };
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ tcg_gen_movi_i64(dest, fli_s_table[a->rs1]);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fli_d(DisasContext *ctx, arg_fli_d *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVD);
+
+ static const uint64_t fli_d_table[] = {
+ 0xbff0000000000000, /* -1.0 */
+ 0x0010000000000000, /* minimum positive normal */
+ 0x3ef0000000000000, /* 1.0 * 2^-16 */
+ 0x3f00000000000000, /* 1.0 * 2^-15 */
+ 0x3f70000000000000, /* 1.0 * 2^-8 */
+ 0x3f80000000000000, /* 1.0 * 2^-7 */
+ 0x3fb0000000000000, /* 1.0 * 2^-4 */
+ 0x3fc0000000000000, /* 1.0 * 2^-3 */
+ 0x3fd0000000000000, /* 0.25 */
+ 0x3fd4000000000000, /* 0.3125 */
+ 0x3fd8000000000000, /* 0.375 */
+ 0x3fdc000000000000, /* 0.4375 */
+ 0x3fe0000000000000, /* 0.5 */
+ 0x3fe4000000000000, /* 0.625 */
+ 0x3fe8000000000000, /* 0.75 */
+ 0x3fec000000000000, /* 0.875 */
+ 0x3ff0000000000000, /* 1.0 */
+ 0x3ff4000000000000, /* 1.25 */
+ 0x3ff8000000000000, /* 1.5 */
+ 0x3ffc000000000000, /* 1.75 */
+ 0x4000000000000000, /* 2.0 */
+ 0x4004000000000000, /* 2.5 */
+ 0x4008000000000000, /* 3 */
+ 0x4010000000000000, /* 4 */
+ 0x4020000000000000, /* 8 */
+ 0x4030000000000000, /* 16 */
+ 0x4060000000000000, /* 2^7 */
+ 0x4070000000000000, /* 2^8 */
+ 0x40e0000000000000, /* 2^15 */
+ 0x40f0000000000000, /* 2^16 */
+ 0x7ff0000000000000, /* +inf */
+ 0x7ff8000000000000, /* Canonical NaN */
+ };
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ tcg_gen_movi_i64(dest, fli_d_table[a->rs1]);
+ gen_set_fpr_d(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fli_h(DisasContext *ctx, arg_fli_h *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_ZFH(ctx);
+
+ /* Values below are NaN-boxed to avoid a gen_nanbox_h(). */
+ static const uint64_t fli_h_table[] = {
+ 0xffffffffffffbc00, /* -1.0 */
+ 0xffffffffffff0400, /* minimum positive normal */
+ 0xffffffffffff0100, /* 1.0 * 2^-16 */
+ 0xffffffffffff0200, /* 1.0 * 2^-15 */
+ 0xffffffffffff1c00, /* 1.0 * 2^-8 */
+ 0xffffffffffff2000, /* 1.0 * 2^-7 */
+ 0xffffffffffff2c00, /* 1.0 * 2^-4 */
+ 0xffffffffffff3000, /* 1.0 * 2^-3 */
+ 0xffffffffffff3400, /* 0.25 */
+ 0xffffffffffff3500, /* 0.3125 */
+ 0xffffffffffff3600, /* 0.375 */
+ 0xffffffffffff3700, /* 0.4375 */
+ 0xffffffffffff3800, /* 0.5 */
+ 0xffffffffffff3900, /* 0.625 */
+ 0xffffffffffff3a00, /* 0.75 */
+ 0xffffffffffff3b00, /* 0.875 */
+ 0xffffffffffff3c00, /* 1.0 */
+ 0xffffffffffff3d00, /* 1.25 */
+ 0xffffffffffff3e00, /* 1.5 */
+ 0xffffffffffff3f00, /* 1.75 */
+ 0xffffffffffff4000, /* 2.0 */
+ 0xffffffffffff4100, /* 2.5 */
+ 0xffffffffffff4200, /* 3 */
+ 0xffffffffffff4400, /* 4 */
+ 0xffffffffffff4800, /* 8 */
+ 0xffffffffffff4c00, /* 16 */
+ 0xffffffffffff5800, /* 2^7 */
+ 0xffffffffffff5c00, /* 2^8 */
+ 0xffffffffffff7800, /* 2^15 */
+ 0xffffffffffff7c00, /* 2^16 */
+ 0xffffffffffff7c00, /* +inf */
+ 0xffffffffffff7e00, /* Canonical NaN */
+ };
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ tcg_gen_movi_i64(dest, fli_h_table[a->rs1]);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fminm_s(DisasContext *ctx, arg_fminm_s *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVF);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
+
+ gen_helper_fminm_s(dest, cpu_env, src1, src2);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fmaxm_s(DisasContext *ctx, arg_fmaxm_s *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVF);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
+
+ gen_helper_fmaxm_s(dest, cpu_env, src1, src2);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fminm_d(DisasContext *ctx, arg_fminm_d *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVD);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
+
+ gen_helper_fminm_d(dest, cpu_env, src1, src2);
+ gen_set_fpr_d(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fmaxm_d(DisasContext *ctx, arg_fmaxm_d *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVD);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
+
+ gen_helper_fmaxm_d(dest, cpu_env, src1, src2);
+ gen_set_fpr_d(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fminm_h(DisasContext *ctx, arg_fminm_h *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_ZFH(ctx);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
+
+ gen_helper_fminm_h(dest, cpu_env, src1, src2);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fmaxm_h(DisasContext *ctx, arg_fmaxm_h *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_ZFH(ctx);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
+
+ gen_helper_fmaxm_h(dest, cpu_env, src1, src2);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fround_s(DisasContext *ctx, arg_fround_s *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVF);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+
+ gen_set_rm(ctx, a->rm);
+ gen_helper_fround_s(dest, cpu_env, src1);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_froundnx_s(DisasContext *ctx, arg_froundnx_s *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVF);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+
+ gen_set_rm(ctx, a->rm);
+ gen_helper_froundnx_s(dest, cpu_env, src1);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fround_d(DisasContext *ctx, arg_fround_d *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVD);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
+
+ gen_set_rm(ctx, a->rm);
+ gen_helper_fround_d(dest, cpu_env, src1);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_froundnx_d(DisasContext *ctx, arg_froundnx_d *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVD);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
+
+ gen_set_rm(ctx, a->rm);
+ gen_helper_froundnx_d(dest, cpu_env, src1);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_fround_h(DisasContext *ctx, arg_fround_h *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_ZFH(ctx);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+
+ gen_set_rm(ctx, a->rm);
+ gen_helper_fround_h(dest, cpu_env, src1);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+static bool trans_froundnx_h(DisasContext *ctx, arg_froundnx_h *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_ZFH(ctx);
+
+ TCGv_i64 dest = dest_fpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+
+ gen_set_rm(ctx, a->rm);
+ gen_helper_froundnx_h(dest, cpu_env, src1);
+ gen_set_fpr_hs(ctx, a->rd, dest);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+bool trans_fcvtmod_w_d(DisasContext *ctx, arg_fcvtmod_w_d *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVD);
+
+ TCGv dst = dest_gpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
+ TCGv_i64 t1 = tcg_temp_new_i64();
+
+ /* Rounding mode is RTZ. */
+ gen_set_rm(ctx, RISCV_FRM_RTZ);
+ gen_helper_fcvtmod_w_d(t1, cpu_env, src1);
+ tcg_gen_trunc_i64_tl(dst, t1);
+ gen_set_gpr(ctx, a->rd, dst);
+
+ return true;
+}
+
+bool trans_fmvh_x_d(DisasContext *ctx, arg_fmvh_x_d *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVD);
+ REQUIRE_32BIT(ctx);
+
+ TCGv dst = dest_gpr(ctx, a->rd);
+ TCGv_i64 t1 = tcg_temp_new_i64();
+ tcg_gen_sari_i64(t1, cpu_fpr[a->rs1], 32);
+ tcg_gen_trunc_i64_tl(dst, t1);
+ gen_set_gpr(ctx, a->rd, dst);
+ return true;
+}
+
+bool trans_fmvp_d_x(DisasContext *ctx, arg_fmvp_d_x *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVD);
+ REQUIRE_32BIT(ctx);
+
+ TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE);
+ TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE);
+ tcg_gen_concat_tl_i64(cpu_fpr[a->rd], src1, src2);
+
+ mark_fs_dirty(ctx);
+ return true;
+}
+
+bool trans_fleq_s(DisasContext *ctx, arg_fleq_s *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVF);
+
+ TCGv dest = dest_gpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
+
+ gen_helper_fleq_s(dest, cpu_env, src1, src2);
+ gen_set_gpr(ctx, a->rd, dest);
+ return true;
+}
+
+bool trans_fltq_s(DisasContext *ctx, arg_fltq_s *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVF);
+
+ TCGv dest = dest_gpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
+
+ gen_helper_fltq_s(dest, cpu_env, src1, src2);
+ gen_set_gpr(ctx, a->rd, dest);
+ return true;
+}
+
+bool trans_fleq_d(DisasContext *ctx, arg_fleq_d *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVD);
+
+ TCGv dest = dest_gpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
+
+ gen_helper_fltq_s(dest, cpu_env, src1, src2);
+ gen_set_gpr(ctx, a->rd, dest);
+ return true;
+}
+
+bool trans_fltq_d(DisasContext *ctx, arg_fltq_d *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_EXT(ctx, RVD);
+
+ TCGv dest = dest_gpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
+
+ gen_helper_fltq_s(dest, cpu_env, src1, src2);
+ gen_set_gpr(ctx, a->rd, dest);
+ return true;
+}
+
+bool trans_fleq_h(DisasContext *ctx, arg_fleq_h *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_ZFH(ctx);
+
+ TCGv dest = dest_gpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
+
+ gen_helper_fleq_h(dest, cpu_env, src1, src2);
+ gen_set_gpr(ctx, a->rd, dest);
+ return true;
+}
+
+bool trans_fltq_h(DisasContext *ctx, arg_fltq_h *a)
+{
+ REQUIRE_FPU;
+ REQUIRE_ZFA(ctx);
+ REQUIRE_ZFH(ctx);
+
+ TCGv dest = dest_gpr(ctx, a->rd);
+ TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
+ TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
+
+ gen_helper_fltq_h(dest, cpu_env, src1, src2);
+ gen_set_gpr(ctx, a->rd, dest);
+ return true;
+}
diff --git a/target/riscv/insn_trans/trans_rvzfh.c.inc b/target/riscv/insn_trans/trans_rvzfh.c.inc
index 74dde37ff7..8b1e2519bb 100644
--- a/target/riscv/insn_trans/trans_rvzfh.c.inc
+++ b/target/riscv/insn_trans/trans_rvzfh.c.inc
@@ -28,8 +28,8 @@
} \
} while (0)
-#define REQUIRE_ZFHMIN(ctx) do { \
- if (!ctx->cfg_ptr->ext_zfhmin) { \
+#define REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx) do { \
+ if (!ctx->cfg_ptr->ext_zfhmin && !ctx->cfg_ptr->ext_zfbfmin) { \
return false; \
} \
} while (0)
@@ -46,7 +46,7 @@ static bool trans_flh(DisasContext *ctx, arg_flh *a)
TCGv t0;
REQUIRE_FPU;
- REQUIRE_ZFHMIN(ctx);
+ REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx);
decode_save_opc(ctx);
t0 = get_gpr(ctx, a->rs1, EXT_NONE);
@@ -69,7 +69,7 @@ static bool trans_fsh(DisasContext *ctx, arg_fsh *a)
TCGv t0;
REQUIRE_FPU;
- REQUIRE_ZFHMIN(ctx);
+ REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx);
decode_save_opc(ctx);
t0 = get_gpr(ctx, a->rs1, EXT_NONE);
@@ -574,7 +574,7 @@ static bool trans_fcvt_h_wu(DisasContext *ctx, arg_fcvt_h_wu *a)
static bool trans_fmv_x_h(DisasContext *ctx, arg_fmv_x_h *a)
{
REQUIRE_FPU;
- REQUIRE_ZFHMIN(ctx);
+ REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx);
TCGv dest = dest_gpr(ctx, a->rd);
@@ -594,7 +594,7 @@ static bool trans_fmv_x_h(DisasContext *ctx, arg_fmv_x_h *a)
static bool trans_fmv_h_x(DisasContext *ctx, arg_fmv_h_x *a)
{
REQUIRE_FPU;
- REQUIRE_ZFHMIN(ctx);
+ REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx);
TCGv t0 = get_gpr(ctx, a->rs1, EXT_ZERO);
diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c
index 0f932a5b96..9d8a8982f9 100644
--- a/target/riscv/kvm.c
+++ b/target/riscv/kvm.c
@@ -22,8 +22,10 @@
#include
#include "qemu/timer.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
+#include "qapi/visitor.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "sysemu/kvm_int.h"
@@ -99,12 +101,280 @@ static uint64_t kvm_riscv_reg_id(CPURISCVState *env, uint64_t type,
#define KVM_RISCV_SET_TIMER(cs, env, name, reg) \
do { \
- int ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(env, time), ®); \
+ int ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(env, name), ®); \
if (ret) { \
abort(); \
} \
} while (0)
+typedef struct KVMCPUConfig {
+ const char *name;
+ const char *description;
+ target_ulong offset;
+ int kvm_reg_id;
+ bool user_set;
+ bool supported;
+} KVMCPUConfig;
+
+#define KVM_MISA_CFG(_bit, _reg_id) \
+ {.offset = _bit, .kvm_reg_id = _reg_id}
+
+/* KVM ISA extensions */
+static KVMCPUConfig kvm_misa_ext_cfgs[] = {
+ KVM_MISA_CFG(RVA, KVM_RISCV_ISA_EXT_A),
+ KVM_MISA_CFG(RVC, KVM_RISCV_ISA_EXT_C),
+ KVM_MISA_CFG(RVD, KVM_RISCV_ISA_EXT_D),
+ KVM_MISA_CFG(RVF, KVM_RISCV_ISA_EXT_F),
+ KVM_MISA_CFG(RVH, KVM_RISCV_ISA_EXT_H),
+ KVM_MISA_CFG(RVI, KVM_RISCV_ISA_EXT_I),
+ KVM_MISA_CFG(RVM, KVM_RISCV_ISA_EXT_M),
+};
+
+static void kvm_cpu_set_misa_ext_cfg(Object *obj, Visitor *v,
+ const char *name,
+ void *opaque, Error **errp)
+{
+ KVMCPUConfig *misa_ext_cfg = opaque;
+ target_ulong misa_bit = misa_ext_cfg->offset;
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ CPURISCVState *env = &cpu->env;
+ bool value, host_bit;
+
+ if (!visit_type_bool(v, name, &value, errp)) {
+ return;
+ }
+
+ host_bit = env->misa_ext_mask & misa_bit;
+
+ if (value == host_bit) {
+ return;
+ }
+
+ if (!value) {
+ misa_ext_cfg->user_set = true;
+ return;
+ }
+
+ /*
+ * Forbid users to enable extensions that aren't
+ * available in the hart.
+ */
+ error_setg(errp, "Enabling MISA bit '%s' is not allowed: it's not "
+ "enabled in the host", misa_ext_cfg->name);
+}
+
+static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs)
+{
+ CPURISCVState *env = &cpu->env;
+ uint64_t id, reg;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(kvm_misa_ext_cfgs); i++) {
+ KVMCPUConfig *misa_cfg = &kvm_misa_ext_cfgs[i];
+ target_ulong misa_bit = misa_cfg->offset;
+
+ if (!misa_cfg->user_set) {
+ continue;
+ }
+
+ /* If we're here we're going to disable the MISA bit */
+ reg = 0;
+ id = kvm_riscv_reg_id(env, KVM_REG_RISCV_ISA_EXT,
+ misa_cfg->kvm_reg_id);
+ ret = kvm_set_one_reg(cs, id, ®);
+ if (ret != 0) {
+ /*
+ * We're not checking for -EINVAL because if the bit is about
+ * to be disabled, it means that it was already enabled by
+ * KVM. We determined that by fetching the 'isa' register
+ * during init() time. Any error at this point is worth
+ * aborting.
+ */
+ error_report("Unable to set KVM reg %s, error %d",
+ misa_cfg->name, ret);
+ exit(EXIT_FAILURE);
+ }
+ env->misa_ext &= ~misa_bit;
+ }
+}
+
+#define CPUCFG(_prop) offsetof(struct RISCVCPUConfig, _prop)
+
+#define KVM_EXT_CFG(_name, _prop, _reg_id) \
+ {.name = _name, .offset = CPUCFG(_prop), \
+ .kvm_reg_id = _reg_id}
+
+static KVMCPUConfig kvm_multi_ext_cfgs[] = {
+ KVM_EXT_CFG("zicbom", ext_icbom, KVM_RISCV_ISA_EXT_ZICBOM),
+ KVM_EXT_CFG("zicboz", ext_icboz, KVM_RISCV_ISA_EXT_ZICBOZ),
+ KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE),
+ KVM_EXT_CFG("zbb", ext_zbb, KVM_RISCV_ISA_EXT_ZBB),
+ KVM_EXT_CFG("ssaia", ext_ssaia, KVM_RISCV_ISA_EXT_SSAIA),
+ KVM_EXT_CFG("sstc", ext_sstc, KVM_RISCV_ISA_EXT_SSTC),
+ KVM_EXT_CFG("svinval", ext_svinval, KVM_RISCV_ISA_EXT_SVINVAL),
+ KVM_EXT_CFG("svpbmt", ext_svpbmt, KVM_RISCV_ISA_EXT_SVPBMT),
+};
+
+static void *kvmconfig_get_cfg_addr(RISCVCPU *cpu, KVMCPUConfig *kvmcfg)
+{
+ return (void *)&cpu->cfg + kvmcfg->offset;
+}
+
+static void kvm_cpu_cfg_set(RISCVCPU *cpu, KVMCPUConfig *multi_ext,
+ uint32_t val)
+{
+ bool *ext_enabled = kvmconfig_get_cfg_addr(cpu, multi_ext);
+
+ *ext_enabled = val;
+}
+
+static uint32_t kvm_cpu_cfg_get(RISCVCPU *cpu,
+ KVMCPUConfig *multi_ext)
+{
+ bool *ext_enabled = kvmconfig_get_cfg_addr(cpu, multi_ext);
+
+ return *ext_enabled;
+}
+
+static void kvm_cpu_set_multi_ext_cfg(Object *obj, Visitor *v,
+ const char *name,
+ void *opaque, Error **errp)
+{
+ KVMCPUConfig *multi_ext_cfg = opaque;
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ bool value, host_val;
+
+ if (!visit_type_bool(v, name, &value, errp)) {
+ return;
+ }
+
+ host_val = kvm_cpu_cfg_get(cpu, multi_ext_cfg);
+
+ /*
+ * Ignore if the user is setting the same value
+ * as the host.
+ */
+ if (value == host_val) {
+ return;
+ }
+
+ if (!multi_ext_cfg->supported) {
+ /*
+ * Error out if the user is trying to enable an
+ * extension that KVM doesn't support. Ignore
+ * option otherwise.
+ */
+ if (value) {
+ error_setg(errp, "KVM does not support disabling extension %s",
+ multi_ext_cfg->name);
+ }
+
+ return;
+ }
+
+ multi_ext_cfg->user_set = true;
+ kvm_cpu_cfg_set(cpu, multi_ext_cfg, value);
+}
+
+static KVMCPUConfig kvm_cbom_blocksize = {
+ .name = "cbom_blocksize",
+ .offset = CPUCFG(cbom_blocksize),
+ .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicbom_block_size)
+};
+
+static KVMCPUConfig kvm_cboz_blocksize = {
+ .name = "cboz_blocksize",
+ .offset = CPUCFG(cboz_blocksize),
+ .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicboz_block_size)
+};
+
+static void kvm_cpu_set_cbomz_blksize(Object *obj, Visitor *v,
+ const char *name,
+ void *opaque, Error **errp)
+{
+ KVMCPUConfig *cbomz_cfg = opaque;
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ uint16_t value, *host_val;
+
+ if (!visit_type_uint16(v, name, &value, errp)) {
+ return;
+ }
+
+ host_val = kvmconfig_get_cfg_addr(cpu, cbomz_cfg);
+
+ if (value != *host_val) {
+ error_report("Unable to set %s to a different value than "
+ "the host (%u)",
+ cbomz_cfg->name, *host_val);
+ exit(EXIT_FAILURE);
+ }
+
+ cbomz_cfg->user_set = true;
+}
+
+static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs)
+{
+ CPURISCVState *env = &cpu->env;
+ uint64_t id, reg;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) {
+ KVMCPUConfig *multi_ext_cfg = &kvm_multi_ext_cfgs[i];
+
+ if (!multi_ext_cfg->user_set) {
+ continue;
+ }
+
+ id = kvm_riscv_reg_id(env, KVM_REG_RISCV_ISA_EXT,
+ multi_ext_cfg->kvm_reg_id);
+ reg = kvm_cpu_cfg_get(cpu, multi_ext_cfg);
+ ret = kvm_set_one_reg(cs, id, ®);
+ if (ret != 0) {
+ error_report("Unable to %s extension %s in KVM, error %d",
+ reg ? "enable" : "disable",
+ multi_ext_cfg->name, ret);
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(kvm_misa_ext_cfgs); i++) {
+ KVMCPUConfig *misa_cfg = &kvm_misa_ext_cfgs[i];
+ int bit = misa_cfg->offset;
+
+ misa_cfg->name = riscv_get_misa_ext_name(bit);
+ misa_cfg->description = riscv_get_misa_ext_description(bit);
+
+ object_property_add(cpu_obj, misa_cfg->name, "bool",
+ NULL,
+ kvm_cpu_set_misa_ext_cfg,
+ NULL, misa_cfg);
+ object_property_set_description(cpu_obj, misa_cfg->name,
+ misa_cfg->description);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) {
+ KVMCPUConfig *multi_cfg = &kvm_multi_ext_cfgs[i];
+
+ object_property_add(cpu_obj, multi_cfg->name, "bool",
+ NULL,
+ kvm_cpu_set_multi_ext_cfg,
+ NULL, multi_cfg);
+ }
+
+ object_property_add(cpu_obj, "cbom_blocksize", "uint16",
+ NULL, kvm_cpu_set_cbomz_blksize,
+ NULL, &kvm_cbom_blocksize);
+
+ object_property_add(cpu_obj, "cboz_blocksize", "uint16",
+ NULL, kvm_cpu_set_cbomz_blksize,
+ NULL, &kvm_cboz_blocksize);
+}
+
static int kvm_riscv_get_regs_core(CPUState *cs)
{
int ret = 0;
@@ -309,6 +579,191 @@ static void kvm_riscv_put_regs_timer(CPUState *cs)
env->kvm_timer_dirty = false;
}
+typedef struct KVMScratchCPU {
+ int kvmfd;
+ int vmfd;
+ int cpufd;
+} KVMScratchCPU;
+
+/*
+ * Heavily inspired by kvm_arm_create_scratch_host_vcpu()
+ * from target/arm/kvm.c.
+ */
+static bool kvm_riscv_create_scratch_vcpu(KVMScratchCPU *scratch)
+{
+ int kvmfd = -1, vmfd = -1, cpufd = -1;
+
+ kvmfd = qemu_open_old("/dev/kvm", O_RDWR);
+ if (kvmfd < 0) {
+ goto err;
+ }
+ do {
+ vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0);
+ } while (vmfd == -1 && errno == EINTR);
+ if (vmfd < 0) {
+ goto err;
+ }
+ cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0);
+ if (cpufd < 0) {
+ goto err;
+ }
+
+ scratch->kvmfd = kvmfd;
+ scratch->vmfd = vmfd;
+ scratch->cpufd = cpufd;
+
+ return true;
+
+ err:
+ if (cpufd >= 0) {
+ close(cpufd);
+ }
+ if (vmfd >= 0) {
+ close(vmfd);
+ }
+ if (kvmfd >= 0) {
+ close(kvmfd);
+ }
+
+ return false;
+}
+
+static void kvm_riscv_destroy_scratch_vcpu(KVMScratchCPU *scratch)
+{
+ close(scratch->cpufd);
+ close(scratch->vmfd);
+ close(scratch->kvmfd);
+}
+
+static void kvm_riscv_init_machine_ids(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
+{
+ CPURISCVState *env = &cpu->env;
+ struct kvm_one_reg reg;
+ int ret;
+
+ reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
+ KVM_REG_RISCV_CONFIG_REG(mvendorid));
+ reg.addr = (uint64_t)&cpu->cfg.mvendorid;
+ ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
+ if (ret != 0) {
+ error_report("Unable to retrieve mvendorid from host, error %d", ret);
+ }
+
+ reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
+ KVM_REG_RISCV_CONFIG_REG(marchid));
+ reg.addr = (uint64_t)&cpu->cfg.marchid;
+ ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
+ if (ret != 0) {
+ error_report("Unable to retrieve marchid from host, error %d", ret);
+ }
+
+ reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
+ KVM_REG_RISCV_CONFIG_REG(mimpid));
+ reg.addr = (uint64_t)&cpu->cfg.mimpid;
+ ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
+ if (ret != 0) {
+ error_report("Unable to retrieve mimpid from host, error %d", ret);
+ }
+}
+
+static void kvm_riscv_init_misa_ext_mask(RISCVCPU *cpu,
+ KVMScratchCPU *kvmcpu)
+{
+ CPURISCVState *env = &cpu->env;
+ struct kvm_one_reg reg;
+ int ret;
+
+ reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
+ KVM_REG_RISCV_CONFIG_REG(isa));
+ reg.addr = (uint64_t)&env->misa_ext_mask;
+ ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
+
+ if (ret) {
+ error_report("Unable to fetch ISA register from KVM, "
+ "error %d", ret);
+ kvm_riscv_destroy_scratch_vcpu(kvmcpu);
+ exit(EXIT_FAILURE);
+ }
+
+ env->misa_ext = env->misa_ext_mask;
+}
+
+static void kvm_riscv_read_cbomz_blksize(RISCVCPU *cpu, KVMScratchCPU *kvmcpu,
+ KVMCPUConfig *cbomz_cfg)
+{
+ CPURISCVState *env = &cpu->env;
+ struct kvm_one_reg reg;
+ int ret;
+
+ reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
+ cbomz_cfg->kvm_reg_id);
+ reg.addr = (uint64_t)kvmconfig_get_cfg_addr(cpu, cbomz_cfg);
+ ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
+ if (ret != 0) {
+ error_report("Unable to read KVM reg %s, error %d",
+ cbomz_cfg->name, ret);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
+{
+ CPURISCVState *env = &cpu->env;
+ uint64_t val;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) {
+ KVMCPUConfig *multi_ext_cfg = &kvm_multi_ext_cfgs[i];
+ struct kvm_one_reg reg;
+
+ reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_ISA_EXT,
+ multi_ext_cfg->kvm_reg_id);
+ reg.addr = (uint64_t)&val;
+ ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
+ if (ret != 0) {
+ if (errno == EINVAL) {
+ /* Silently default to 'false' if KVM does not support it. */
+ multi_ext_cfg->supported = false;
+ val = false;
+ } else {
+ error_report("Unable to read ISA_EXT KVM register %s, "
+ "error %d", multi_ext_cfg->name, ret);
+ kvm_riscv_destroy_scratch_vcpu(kvmcpu);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ multi_ext_cfg->supported = true;
+ }
+
+ kvm_cpu_cfg_set(cpu, multi_ext_cfg, val);
+ }
+
+ if (cpu->cfg.ext_icbom) {
+ kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize);
+ }
+
+ if (cpu->cfg.ext_icboz) {
+ kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize);
+ }
+}
+
+void kvm_riscv_init_user_properties(Object *cpu_obj)
+{
+ RISCVCPU *cpu = RISCV_CPU(cpu_obj);
+ KVMScratchCPU kvmcpu;
+
+ if (!kvm_riscv_create_scratch_vcpu(&kvmcpu)) {
+ return;
+ }
+
+ kvm_riscv_add_cpu_user_properties(cpu_obj);
+ kvm_riscv_init_machine_ids(cpu, &kvmcpu);
+ kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu);
+ kvm_riscv_init_multiext_cfg(cpu, &kvmcpu);
+
+ kvm_riscv_destroy_scratch_vcpu(&kvmcpu);
+}
+
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
KVM_CAP_LAST_INFO
};
@@ -394,23 +849,49 @@ void kvm_arch_init_irq_routing(KVMState *s)
{
}
+static int kvm_vcpu_set_machine_ids(RISCVCPU *cpu, CPUState *cs)
+{
+ CPURISCVState *env = &cpu->env;
+ uint64_t id;
+ int ret;
+
+ id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
+ KVM_REG_RISCV_CONFIG_REG(mvendorid));
+ ret = kvm_set_one_reg(cs, id, &cpu->cfg.mvendorid);
+ if (ret != 0) {
+ return ret;
+ }
+
+ id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
+ KVM_REG_RISCV_CONFIG_REG(marchid));
+ ret = kvm_set_one_reg(cs, id, &cpu->cfg.marchid);
+ if (ret != 0) {
+ return ret;
+ }
+
+ id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
+ KVM_REG_RISCV_CONFIG_REG(mimpid));
+ ret = kvm_set_one_reg(cs, id, &cpu->cfg.mimpid);
+
+ return ret;
+}
+
int kvm_arch_init_vcpu(CPUState *cs)
{
int ret = 0;
- target_ulong isa;
RISCVCPU *cpu = RISCV_CPU(cs);
- CPURISCVState *env = &cpu->env;
- uint64_t id;
qemu_add_vm_change_state_handler(kvm_riscv_vm_state_change, cs);
- id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
- KVM_REG_RISCV_CONFIG_REG(isa));
- ret = kvm_get_one_reg(cs, id, &isa);
- if (ret) {
- return ret;
+ if (!object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) {
+ ret = kvm_vcpu_set_machine_ids(cpu, cs);
+ if (ret != 0) {
+ return ret;
+ }
}
- env->misa_ext = isa;
+
+ kvm_riscv_update_cpu_misa_ext(cpu, cs);
+ kvm_riscv_update_cpu_cfg_isa_ext(cpu, cs);
return ret;
}
diff --git a/target/riscv/kvm_riscv.h b/target/riscv/kvm_riscv.h
index ed281bdce0..e3ba935808 100644
--- a/target/riscv/kvm_riscv.h
+++ b/target/riscv/kvm_riscv.h
@@ -19,6 +19,7 @@
#ifndef QEMU_KVM_RISCV_H
#define QEMU_KVM_RISCV_H
+void kvm_riscv_init_user_properties(Object *cpu_obj);
void kvm_riscv_reset_vcpu(RISCVCPU *cpu);
void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level);
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index f563dc3981..9cdb9cdd06 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -335,7 +335,8 @@ target_ulong helper_mret(CPURISCVState *env)
riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC());
}
- target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV);
+ target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV) &&
+ (prev_priv != PRV_M);
mstatus = set_field(mstatus, MSTATUS_MIE,
get_field(mstatus, MSTATUS_MPIE));
mstatus = set_field(mstatus, MSTATUS_MPIE, 1);
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 8a33da811e..697df1be9e 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -64,6 +64,7 @@ typedef struct DisasContext {
target_ulong priv_ver;
RISCVMXL misa_mxl_max;
RISCVMXL xl;
+ RISCVMXL address_xl;
uint32_t misa_ext;
uint32_t opcode;
RISCVExtStatus mstatus_fs;
@@ -121,29 +122,6 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext)
return ctx->misa_ext & ext;
}
-static bool always_true_p(DisasContext *ctx __attribute__((__unused__)))
-{
- return true;
-}
-
-static bool has_xthead_p(DisasContext *ctx __attribute__((__unused__)))
-{
- return ctx->cfg_ptr->ext_xtheadba || ctx->cfg_ptr->ext_xtheadbb ||
- ctx->cfg_ptr->ext_xtheadbs || ctx->cfg_ptr->ext_xtheadcmo ||
- ctx->cfg_ptr->ext_xtheadcondmov ||
- ctx->cfg_ptr->ext_xtheadfmemidx || ctx->cfg_ptr->ext_xtheadfmv ||
- ctx->cfg_ptr->ext_xtheadmac || ctx->cfg_ptr->ext_xtheadmemidx ||
- ctx->cfg_ptr->ext_xtheadmempair || ctx->cfg_ptr->ext_xtheadsync;
-}
-
-#define MATERIALISE_EXT_PREDICATE(ext) \
- static bool has_ ## ext ## _p(DisasContext *ctx) \
- { \
- return ctx->cfg_ptr->ext_ ## ext ; \
- }
-
-MATERIALISE_EXT_PREDICATE(XVentanaCondOps);
-
#ifdef TARGET_RISCV32
#define get_xl(ctx) MXL_RV32
#elif defined(CONFIG_USER_ONLY)
@@ -152,6 +130,14 @@ MATERIALISE_EXT_PREDICATE(XVentanaCondOps);
#define get_xl(ctx) ((ctx)->xl)
#endif
+#ifdef TARGET_RISCV32
+#define get_address_xl(ctx) MXL_RV32
+#elif defined(CONFIG_USER_ONLY)
+#define get_address_xl(ctx) MXL_RV64
+#else
+#define get_address_xl(ctx) ((ctx)->address_xl)
+#endif
+
/* The word size for this machine mode. */
static inline int __attribute__((unused)) get_xlen(DisasContext *ctx)
{
@@ -598,12 +584,13 @@ static TCGv get_address(DisasContext *ctx, int rs1, int imm)
tcg_gen_addi_tl(addr, src1, imm);
if (ctx->pm_mask_enabled) {
tcg_gen_andc_tl(addr, addr, pm_mask);
- } else if (get_xl(ctx) == MXL_RV32) {
+ } else if (get_address_xl(ctx) == MXL_RV32) {
tcg_gen_ext32u_tl(addr, addr);
}
if (ctx->pm_base_enabled) {
tcg_gen_or_tl(addr, addr, pm_base);
}
+
return addr;
}
@@ -1104,10 +1091,12 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc)
#include "insn_trans/trans_rvzicond.c.inc"
#include "insn_trans/trans_rvzawrs.c.inc"
#include "insn_trans/trans_rvzicbo.c.inc"
+#include "insn_trans/trans_rvzfa.c.inc"
#include "insn_trans/trans_rvzfh.c.inc"
#include "insn_trans/trans_rvk.c.inc"
#include "insn_trans/trans_privileged.c.inc"
#include "insn_trans/trans_svinval.c.inc"
+#include "insn_trans/trans_rvbf16.c.inc"
#include "decode-xthead.c.inc"
#include "insn_trans/trans_xthead.c.inc"
#include "insn_trans/trans_xventanacondops.c.inc"
@@ -1134,7 +1123,7 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode)
* that are tested in-order until a decoder matches onto the opcode.
*/
static const struct {
- bool (*guard_func)(DisasContext *);
+ bool (*guard_func)(const RISCVCPUConfig *);
bool (*decode_func)(DisasContext *, uint32_t);
} decoders[] = {
{ always_true_p, decode_insn32 },
@@ -1163,7 +1152,7 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode)
ctx->opcode = opcode32;
for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) {
- if (decoders[i].guard_func(ctx) &&
+ if (decoders[i].guard_func(ctx->cfg_ptr) &&
decoders[i].decode_func(ctx, opcode32)) {
return;
}
@@ -1200,6 +1189,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX);
ctx->misa_mxl_max = env->misa_mxl_max;
ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL);
+ ctx->address_xl = FIELD_EX32(tb_flags, TB_FLAGS, AXL);
ctx->cs = cs;
ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c
index 1e06e7447c..71bb9b4457 100644
--- a/target/riscv/vector_helper.c
+++ b/target/riscv/vector_helper.c
@@ -3554,6 +3554,17 @@ RVVCALL(OPFVF3, vfwmacc_vf_w, WOP_UUU_W, H8, H4, fwmacc32)
GEN_VEXT_VF(vfwmacc_vf_h, 4)
GEN_VEXT_VF(vfwmacc_vf_w, 8)
+static uint32_t fwmaccbf16(uint16_t a, uint16_t b, uint32_t d, float_status *s)
+{
+ return float32_muladd(bfloat16_to_float32(a, s),
+ bfloat16_to_float32(b, s), d, 0, s);
+}
+
+RVVCALL(OPFVV3, vfwmaccbf16_vv, WOP_UUU_H, H4, H2, H2, fwmaccbf16)
+GEN_VEXT_VV_ENV(vfwmaccbf16_vv, 4)
+RVVCALL(OPFVF3, vfwmaccbf16_vf, WOP_UUU_H, H4, H2, fwmacc16)
+GEN_VEXT_VF(vfwmaccbf16_vf, 4)
+
static uint32_t fwnmacc16(uint16_t a, uint16_t b, uint32_t d, float_status *s)
{
return float32_muladd(float16_to_float32(a, true, s),
@@ -4535,6 +4546,9 @@ RVVCALL(OPFVV1, vfwcvt_f_f_v_w, WOP_UU_W, H8, H4, float32_to_float64)
GEN_VEXT_V_ENV(vfwcvt_f_f_v_h, 4)
GEN_VEXT_V_ENV(vfwcvt_f_f_v_w, 8)
+RVVCALL(OPFVV1, vfwcvtbf16_f_f_v, WOP_UU_H, H4, H2, bfloat16_to_float32)
+GEN_VEXT_V_ENV(vfwcvtbf16_f_f_v, 4)
+
/* Narrowing Floating-Point/Integer Type-Convert Instructions */
/* (TD, T2, TX2) */
#define NOP_UU_B uint8_t, uint16_t, uint32_t
@@ -4581,6 +4595,9 @@ RVVCALL(OPFVV1, vfncvt_f_f_w_w, NOP_UU_W, H4, H8, float64_to_float32)
GEN_VEXT_V_ENV(vfncvt_f_f_w_h, 2)
GEN_VEXT_V_ENV(vfncvt_f_f_w_w, 4)
+RVVCALL(OPFVV1, vfncvtbf16_f_f_w, NOP_UU_H, H2, H4, float32_to_bfloat16)
+GEN_VEXT_V_ENV(vfncvtbf16_f_f_w, 2)
+
/*
* Vector Reduction Operations
*/
diff --git a/tests/avocado/riscv_opensbi.py b/tests/avocado/riscv_opensbi.py
index e02f0d404a..bfff9cc3c3 100644
--- a/tests/avocado/riscv_opensbi.py
+++ b/tests/avocado/riscv_opensbi.py
@@ -6,7 +6,6 @@
# later. See the COPYING file in the top-level directory.
from avocado_qemu import QemuSystemTest
-from avocado import skip
from avocado_qemu import wait_for_console_pattern
class RiscvOpenSBI(QemuSystemTest):
@@ -21,7 +20,6 @@ class RiscvOpenSBI(QemuSystemTest):
wait_for_console_pattern(self, 'Platform Name')
wait_for_console_pattern(self, 'Boot HART MEDELEG')
- @skip("requires OpenSBI fix to work")
def test_riscv32_spike(self):
"""
:avocado: tags=arch:riscv32
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 74630f6672..b071d400b3 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -234,6 +234,9 @@ qtests_s390x = \
'cpu-plug-test',
'migration-test']
+qtests_riscv32 = \
+ (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : [])
+
qos_test_ss = ss.source_set()
qos_test_ss.add(
'ac97-test.c',
diff --git a/tests/qtest/sifive-e-aon-watchdog-test.c b/tests/qtest/sifive-e-aon-watchdog-test.c
new file mode 100644
index 0000000000..1f313d16ad
--- /dev/null
+++ b/tests/qtest/sifive-e-aon-watchdog-test.c
@@ -0,0 +1,450 @@
+/*
+ * QTest testcase for the watchdog timer of HiFive 1 rev b.
+ *
+ * Copyright (c) 2023 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/timer.h"
+#include "qemu/bitops.h"
+#include "libqtest.h"
+#include "hw/registerfields.h"
+#include "hw/misc/sifive_e_aon.h"
+
+FIELD(AON_WDT_WDOGCFG, SCALE, 0, 4)
+FIELD(AON_WDT_WDOGCFG, RSVD0, 4, 4)
+FIELD(AON_WDT_WDOGCFG, RSTEN, 8, 1)
+FIELD(AON_WDT_WDOGCFG, ZEROCMP, 9, 1)
+FIELD(AON_WDT_WDOGCFG, RSVD1, 10, 2)
+FIELD(AON_WDT_WDOGCFG, EN_ALWAYS, 12, 1)
+FIELD(AON_WDT_WDOGCFG, EN_CORE_AWAKE, 13, 1)
+FIELD(AON_WDT_WDOGCFG, RSVD2, 14, 14)
+FIELD(AON_WDT_WDOGCFG, IP0, 28, 1)
+FIELD(AON_WDT_WDOGCFG, RSVD3, 29, 3)
+
+#define WDOG_BASE (0x10000000)
+#define WDOGCFG (0x0)
+#define WDOGCOUNT (0x8)
+#define WDOGS (0x10)
+#define WDOGFEED (0x18)
+#define WDOGKEY (0x1c)
+#define WDOGCMP0 (0x20)
+
+#define SIFIVE_E_AON_WDOGKEY (0x51F15E)
+#define SIFIVE_E_AON_WDOGFEED (0xD09F00D)
+#define SIFIVE_E_LFCLK_DEFAULT_FREQ (32768)
+
+static void test_init(QTestState *qts)
+{
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0);
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, 0);
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF);
+}
+
+static void test_wdogcount(void)
+{
+ uint64_t tmp;
+ QTestState *qts = qtest_init("-machine sifive_e");
+
+ test_init(qts);
+
+ tmp = qtest_readl(qts, WDOG_BASE + WDOGCOUNT);
+ qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xBEEF);
+ g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == tmp);
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xBEEF);
+ g_assert(0xBEEF == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xAAAAAAAA);
+ g_assert(0x2AAAAAAA == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xAAAAAAAA);
+ g_assert(0x2AAAAAAA == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGFEED, SIFIVE_E_AON_WDOGFEED);
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
+
+ qtest_quit(qts);
+}
+
+static void test_wdogcfg(void)
+{
+ uint32_t tmp_cfg;
+ QTestState *qts = qtest_init("-machine sifive_e");
+
+ test_init(qts);
+
+ tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, 0xFFFFFFFF);
+ g_assert(qtest_readl(qts, WDOG_BASE + WDOGCFG) == tmp_cfg);
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, 0xFFFFFFFF);
+ g_assert(0xFFFFFFFF == qtest_readl(qts, WDOG_BASE + WDOGCFG));
+
+ tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(15 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, SCALE));
+ g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, RSTEN));
+ g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, ZEROCMP));
+ g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
+ g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
+ g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, IP0));
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, 0);
+ tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, SCALE));
+ g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, RSTEN));
+ g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, ZEROCMP));
+ g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
+ g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
+ g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, IP0));
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCFG));
+
+ qtest_quit(qts);
+}
+
+static void test_wdogcmp0(void)
+{
+ uint32_t tmp;
+ QTestState *qts = qtest_init("-machine sifive_e");
+
+ test_init(qts);
+
+ tmp = qtest_readl(qts, WDOG_BASE + WDOGCMP0);
+ qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF);
+ g_assert(qtest_readl(qts, WDOG_BASE + WDOGCMP0) == tmp);
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF);
+ g_assert(0xBEEF == qtest_readl(qts, WDOG_BASE + WDOGCMP0));
+
+ qtest_quit(qts);
+}
+
+static void test_wdogkey(void)
+{
+ QTestState *qts = qtest_init("-machine sifive_e");
+
+ test_init(qts);
+
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY));
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, 0xFFFF);
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY));
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ g_assert(1 == qtest_readl(qts, WDOG_BASE + WDOGKEY));
+
+ qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xAAAAAAAA);
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY));
+
+ qtest_quit(qts);
+}
+
+static void test_wdogfeed(void)
+{
+ QTestState *qts = qtest_init("-machine sifive_e");
+
+ test_init(qts);
+
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGFEED));
+
+ qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xFFFF);
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGFEED));
+
+ qtest_quit(qts);
+}
+
+static void test_scaled_wdogs(void)
+{
+ uint32_t cfg;
+ uint32_t fake_count = 0x12345678;
+ QTestState *qts = qtest_init("-machine sifive_e");
+
+ test_init(qts);
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCOUNT, fake_count);
+ g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == fake_count);
+ g_assert((uint16_t)qtest_readl(qts, WDOG_BASE + WDOGS) ==
+ (uint16_t)fake_count);
+
+ for (int i = 0; i < 16; i++) {
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, i);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+ g_assert((uint16_t)qtest_readl(qts, WDOG_BASE + WDOGS) ==
+ (uint16_t)(fake_count >>
+ FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)));
+ }
+
+ qtest_quit(qts);
+}
+
+static void test_watchdog(void)
+{
+ uint32_t cfg;
+ QTestState *qts = qtest_init("-machine sifive_e");
+
+ test_init(qts);
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCMP0, SIFIVE_E_LFCLK_DEFAULT_FREQ);
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 0);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+
+ qtest_clock_step(qts, NANOSECONDS_PER_SECOND);
+
+ g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) ==
+ SIFIVE_E_LFCLK_DEFAULT_FREQ);
+ g_assert(qtest_readl(qts, WDOG_BASE + WDOGS) ==
+ SIFIVE_E_LFCLK_DEFAULT_FREQ);
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ qtest_quit(qts);
+}
+
+static void test_scaled_watchdog(void)
+{
+ uint32_t cfg;
+ QTestState *qts = qtest_init("-machine sifive_e");
+
+ test_init(qts);
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCMP0, 10);
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 15);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+
+ qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 10);
+
+ g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) ==
+ SIFIVE_E_LFCLK_DEFAULT_FREQ * 10);
+
+ g_assert(10 == qtest_readl(qts, WDOG_BASE + WDOGS));
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ qtest_quit(qts);
+}
+
+static void test_periodic_int(void)
+{
+ uint32_t cfg;
+ QTestState *qts = qtest_init("-machine sifive_e");
+
+ test_init(qts);
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCMP0, SIFIVE_E_LFCLK_DEFAULT_FREQ);
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 0);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, ZEROCMP, 1);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+
+ qtest_clock_step(qts, NANOSECONDS_PER_SECOND);
+
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGS));
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ qtest_clock_step(qts, NANOSECONDS_PER_SECOND);
+
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
+ g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGS));
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ qtest_quit(qts);
+}
+
+static void test_enable_disable(void)
+{
+ uint32_t cfg;
+ QTestState *qts = qtest_init("-machine sifive_e");
+
+ test_init(qts);
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCMP0, 10);
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 15);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+
+ qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 2);
+
+ g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) ==
+ SIFIVE_E_LFCLK_DEFAULT_FREQ * 2);
+ g_assert(2 == qtest_readl(qts, WDOG_BASE + WDOGS));
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 0);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+
+ qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 8);
+
+ g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) ==
+ SIFIVE_E_LFCLK_DEFAULT_FREQ * 2);
+ g_assert(2 == qtest_readl(qts, WDOG_BASE + WDOGS));
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+
+ qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 8);
+
+ g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) ==
+ SIFIVE_E_LFCLK_DEFAULT_FREQ * 10);
+ g_assert(10 == qtest_readl(qts, WDOG_BASE + WDOGS));
+
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
+ g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0);
+ cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0);
+ qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
+ qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
+ cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
+ g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char *argv[])
+{
+ g_test_init(&argc, &argv, NULL);
+ qtest_add_func("/sifive-e-aon-watchdog-test/wdogcount",
+ test_wdogcount);
+ qtest_add_func("/sifive-e-aon-watchdog-test/wdogcfg",
+ test_wdogcfg);
+ qtest_add_func("/sifive-e-aon-watchdog-test/wdogcmp0",
+ test_wdogcmp0);
+ qtest_add_func("/sifive-e-aon-watchdog-test/wdogkey",
+ test_wdogkey);
+ qtest_add_func("/sifive-e-aon-watchdog-test/wdogfeed",
+ test_wdogfeed);
+ qtest_add_func("/sifive-e-aon-watchdog-test/scaled_wdogs",
+ test_scaled_wdogs);
+ qtest_add_func("/sifive-e-aon-watchdog-test/watchdog",
+ test_watchdog);
+ qtest_add_func("/sifive-e-aon-watchdog-test/scaled_watchdog",
+ test_scaled_watchdog);
+ qtest_add_func("/sifive-e-aon-watchdog-test/periodic_int",
+ test_periodic_int);
+ qtest_add_func("/sifive-e-aon-watchdog-test/enable_disable",
+ test_enable_disable);
+ return g_test_run();
+}
diff --git a/tests/tcg/riscv64/Makefile.target b/tests/tcg/riscv64/Makefile.target
index 42993549cb..a7e390c384 100644
--- a/tests/tcg/riscv64/Makefile.target
+++ b/tests/tcg/riscv64/Makefile.target
@@ -12,3 +12,9 @@ run-test-noc: QEMU_OPTS += -cpu rv64,c=false
TESTS += test-aes
run-test-aes: QEMU_OPTS += -cpu rv64,zk=on
+
+# Test for fcvtmod
+TESTS += test-fcvtmod
+test-fcvtmod: CFLAGS += -march=rv64imafdc
+test-fcvtmod: LDFLAGS += -static
+run-test-fcvtmod: QEMU_OPTS += -cpu rv64,d=true,Zfa=true
diff --git a/tests/tcg/riscv64/test-fcvtmod.c b/tests/tcg/riscv64/test-fcvtmod.c
new file mode 100644
index 0000000000..f050579974
--- /dev/null
+++ b/tests/tcg/riscv64/test-fcvtmod.c
@@ -0,0 +1,345 @@
+#include
+#include
+#include
+
+#define FFLAG_NX_SHIFT 0 /* inexact */
+#define FFLAG_UF_SHIFT 1 /* underflow */
+#define FFLAG_OF_SHIFT 2 /* overflow */
+#define FFLAG_DZ_SHIFT 3 /* divide by zero */
+#define FFLAG_NV_SHIFT 4 /* invalid operation */
+
+#define FFLAG_NV (1UL << FFLAG_NV_SHIFT)
+#define FFLAG_DZ (1UL << FFLAG_DZ_SHIFT)
+#define FFLAG_OF (1UL << FFLAG_OF_SHIFT)
+#define FFLAG_UF (1UL << FFLAG_UF_SHIFT)
+#define FFLAG_NX (1UL << FFLAG_NX_SHIFT)
+
+typedef struct fp64_fcvt_fcvtmod_testcase {
+ const char* name;
+ union {
+ uint64_t inp_lu;
+ double inp_lf;
+ };
+ uint64_t exp_fcvt;
+ uint8_t exp_fcvt_fflags;
+ uint64_t exp_fcvtmod;
+ uint8_t exp_fcvtmod_fflags;
+} fp64_fcvt_fcvtmod_testcase_t;
+
+void print_fflags(uint8_t fflags)
+{
+ int set = 0;
+
+ if (fflags == 0) {
+ printf("-");
+ return;
+ }
+
+ if (fflags & FFLAG_NV) {
+ printf("%sFFLAG_NV", set ? " | " : "");
+ set = 1;
+ }
+ if (fflags & FFLAG_DZ) {
+ printf("%sFFLAG_DZ", set ? " | " : "");
+ set = 1;
+ }
+ if (fflags & FFLAG_OF) {
+ printf("%sFFLAG_OF", set ? " | " : "");
+ set = 1;
+ }
+ if (fflags & FFLAG_UF) {
+ printf("%sFFLAG_UF", set ? " | " : "");
+ set = 1;
+ }
+ if (fflags & FFLAG_NX) {
+ printf("%sFFLAG_NX", set ? " | " : "");
+ set = 1;
+ }
+}
+
+/* Clear all FP flags. */
+static inline void clear_fflags()
+{
+ __asm__ __volatile__("fsflags zero");
+}
+
+/* Read all FP flags. */
+static inline uint8_t get_fflags()
+{
+ uint64_t v;
+ __asm__ __volatile__("frflags %0" : "=r"(v));
+ return (uint8_t)v;
+}
+
+/* Move input value (without conversations) into an FP register. */
+static inline double do_fmv_d_x(uint64_t inp)
+{
+ double fpr;
+ __asm__ __volatile__("fmv.d.x %0, %1" : "=f"(fpr) : "r"(inp));
+ return fpr;
+}
+
+static inline uint64_t do_fcvt_w_d(uint64_t inp, uint8_t *fflags)
+{
+ uint64_t ret;
+ double fpr = do_fmv_d_x(inp);
+
+ clear_fflags();
+
+ __asm__ __volatile__("fcvt.w.d %0, %1, rtz" : "=r"(ret) : "f"(fpr));
+
+ *fflags = get_fflags();
+
+ return ret;
+}
+
+static inline uint64_t do_fcvtmod_w_d(uint64_t inp, uint8_t *fflags)
+{
+ uint64_t ret;
+ double fpr = do_fmv_d_x(inp);
+
+ clear_fflags();
+
+ /* fcvtmod.w.d rd, rs1, rtz = 1100001 01000 rs1 001 rd 1010011 */
+ asm(".insn r 0x53, 0x1, 0x61, %0, %1, f8" : "=r"(ret) : "f"(fpr));
+
+ *fflags = get_fflags();
+
+ return ret;
+}
+
+static const fp64_fcvt_fcvtmod_testcase_t tests[] = {
+ /* Zero (exp=0, frac=0) */
+ { .name = "+0.0",
+ .inp_lf = 0x0p0,
+ .exp_fcvt = 0x0000000000000000,
+ .exp_fcvt_fflags = 0,
+ .exp_fcvtmod = 0x0000000000000000,
+ .exp_fcvtmod_fflags = 0 },
+ { .name = "-0.0",
+ .inp_lf = -0x0p0,
+ .exp_fcvt = 0x0000000000000000,
+ .exp_fcvt_fflags = 0,
+ .exp_fcvtmod = 0x0000000000000000,
+ .exp_fcvtmod_fflags = 0 },
+
+ /* Subnormal: exp=0 frac!=0 */
+ { .name = "Subnormal frac=1",
+ .inp_lu = 0x0000000000000001,
+ .exp_fcvt = 0x0000000000000000,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+ { .name = "Subnormal frac=0xf..f",
+ .inp_lu = 0x0000ffffffffffff,
+ .exp_fcvt = 0x0000000000000000,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+ { .name = "Neg subnormal frac=1",
+ .inp_lu = 0x0000000000000001,
+ .exp_fcvt = 0x0000000000000000,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+ { .name = "Neg subnormal frac=0xf..f",
+ .inp_lu = 0x8000ffffffffffff,
+ .exp_fcvt = 0x0000000000000000,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+
+ /* Infinity: exp=0x7ff, frac=0 */
+ { .name = "+INF",
+ .inp_lu = 0x7ff0000000000000,
+ .exp_fcvt = 0x000000007fffffff, /* int32 max */
+ .exp_fcvt_fflags = FFLAG_NV,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NV },
+ { .name = "-INF",
+ .inp_lu = 0xfff0000000000000,
+ .exp_fcvt = 0xffffffff80000000, /* int32 min */
+ .exp_fcvt_fflags = FFLAG_NV,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NV },
+
+ /* NaN: exp=7ff, frac!=0 */
+ { .name = "canonical NaN",
+ .inp_lu = 0x7ff8000000000000,
+ .exp_fcvt = 0x000000007fffffff, /* int32 max */
+ .exp_fcvt_fflags = FFLAG_NV,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NV },
+ { .name = "non-canonical NaN",
+ .inp_lu = 0x7ff8000000100000,
+ .exp_fcvt = 0x000000007fffffff, /* int32 min */
+ .exp_fcvt_fflags = FFLAG_NV,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NV },
+
+ /* Normal numbers: exp!=0, exp!=7ff */
+ { .name = "+smallest normal value",
+ .inp_lu = 0x0010000000000000,
+ .exp_fcvt = 0,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+ { .name = "-smallest normal value",
+ .inp_lu = 0x8010000000000000,
+ .exp_fcvt = 0,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+
+ { .name = "+0.5",
+ .inp_lf = 0x1p-1,
+ .exp_fcvt = 0,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+ { .name = "-0.5",
+ .inp_lf = -0x1p-1,
+ .exp_fcvt = 0,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+
+ { .name = "+value just below 1.0",
+ .inp_lu = 0x3fefffffffffffff,
+ .exp_fcvt = 0,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+ { .name = "-value just above -1.0",
+ .inp_lu = 0xbfefffffffffffff,
+ .exp_fcvt = 0,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+
+ { .name = "+1.0",
+ .inp_lf = 0x1p0,
+ .exp_fcvt = 0x0000000000000001,
+ .exp_fcvt_fflags = 0,
+ .exp_fcvtmod = 0x0000000000000001,
+ .exp_fcvtmod_fflags = 0 },
+ { .name = "-1.0",
+ .inp_lf = -0x1p0,
+ .exp_fcvt = 0xffffffffffffffff,
+ .exp_fcvt_fflags = 0,
+ .exp_fcvtmod = 0xffffffffffffffff,
+ .exp_fcvtmod_fflags = 0 },
+
+ { .name = "+1.5",
+ .inp_lu = 0x3ff8000000000000,
+ .exp_fcvt = 1,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 1,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+ { .name = "-1.5",
+ .inp_lu = 0xbff8000000000000,
+ .exp_fcvt = 0xffffffffffffffff,
+ .exp_fcvt_fflags = FFLAG_NX,
+ .exp_fcvtmod = 0xffffffffffffffff,
+ .exp_fcvtmod_fflags = FFLAG_NX },
+
+ { .name = "+max int32 (2147483647)",
+ .inp_lu = 0x41dfffffffc00000,
+ .exp_fcvt = 0x000000007fffffff,
+ .exp_fcvt_fflags = 0,
+ .exp_fcvtmod = 0x000000007fffffff,
+ .exp_fcvtmod_fflags = 0 },
+ { .name = "+max int32 +1 (2147483648)",
+ .inp_lf = 0x1p31,
+ .exp_fcvt = 0x000000007fffffff,
+ .exp_fcvt_fflags = FFLAG_NV,
+ .exp_fcvtmod = (uint64_t)-2147483648l, /* int32 min */
+ .exp_fcvtmod_fflags = FFLAG_NV },
+ { .name = "+max int32 +2 (2147483649)",
+ .inp_lu = 0x41e0000000200000,
+ .exp_fcvt = 0x000000007fffffff,
+ .exp_fcvt_fflags = FFLAG_NV,
+ .exp_fcvtmod = (uint64_t)-2147483647l, /* int32 min +1 */
+ .exp_fcvtmod_fflags = FFLAG_NV },
+
+ { .name = "-max int32 (-2147483648)",
+ .inp_lf = -0x1p31,
+ .exp_fcvt = 0xffffffff80000000,
+ .exp_fcvt_fflags = 0,
+ .exp_fcvtmod = 0xffffffff80000000,
+ .exp_fcvtmod_fflags = 0 },
+ { .name = "-max int32 -1 (-2147483649)",
+ .inp_lf = -0x1.00000002p+31,
+ .exp_fcvt = 0xffffffff80000000,
+ .exp_fcvt_fflags = FFLAG_NV,
+ .exp_fcvtmod = 2147483647, /* int32 max */
+ .exp_fcvtmod_fflags = FFLAG_NV },
+ { .name = "-max int32 -2 (-2147483650)",
+ .inp_lf = -0x1.00000004p+31,
+ .exp_fcvt = 0xffffffff80000000,
+ .exp_fcvt_fflags = FFLAG_NV,
+ .exp_fcvtmod = 2147483646, /* int32 max -1 */
+ .exp_fcvtmod_fflags = FFLAG_NV },
+};
+
+int run_fcvtmod_tests()
+{
+ uint64_t act_fcvt;
+ uint8_t act_fcvt_fflags;
+ uint64_t act_fcvtmod;
+ uint8_t act_fcvtmod_fflags;
+
+ for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
+ const fp64_fcvt_fcvtmod_testcase_t *t = &tests[i];
+
+ act_fcvt = do_fcvt_w_d(t->inp_lu, &act_fcvt_fflags);
+ int fcvt_correct = act_fcvt == t->exp_fcvt &&
+ act_fcvt_fflags == t->exp_fcvt_fflags;
+ act_fcvtmod = do_fcvtmod_w_d(t->inp_lu, &act_fcvtmod_fflags);
+ int fcvtmod_correct = act_fcvtmod == t->exp_fcvtmod &&
+ act_fcvtmod_fflags == t->exp_fcvtmod_fflags;
+
+ if (fcvt_correct && fcvtmod_correct) {
+ continue;
+ }
+
+ printf("Test %zu (%s) failed!\n", i, t->name);
+
+ double fpr = do_fmv_d_x(t->inp_lu);
+ printf("inp_lu: 0x%016lx == %lf\n", t->inp_lu, fpr);
+ printf("inp_lf: %lf\n", t->inp_lf);
+
+ uint32_t sign = (t->inp_lu >> 63);
+ uint32_t exp = (uint32_t)(t->inp_lu >> 52) & 0x7ff;
+ uint64_t frac = t->inp_lu & 0xfffffffffffffull; /* significand */
+ int true_exp = exp - 1023;
+ int shift = true_exp - 52;
+ uint64_t true_frac = frac | 1ull << 52;
+
+ printf("sign=%d, exp=0x%03x, frac=0x%012lx\n", sign, exp, frac);
+ printf("true_exp=%d, shift=%d, true_frac=0x%016lx\n", true_exp, shift, true_frac);
+
+ if (!fcvt_correct) {
+ printf("act_fcvt: 0x%016lx == %li\n", act_fcvt, act_fcvt);
+ printf("exp_fcvt: 0x%016lx == %li\n", t->exp_fcvt, t->exp_fcvt);
+ printf("act_fcvt_fflags: "); print_fflags(act_fcvt_fflags); printf("\n");
+ printf("exp_fcvt_fflags: "); print_fflags(t->exp_fcvt_fflags); printf("\n");
+ }
+
+ if (!fcvtmod_correct) {
+ printf("act_fcvtmod: 0x%016lx == %li\n", act_fcvtmod, act_fcvtmod);
+ printf("exp_fcvtmod: 0x%016lx == %li\n", t->exp_fcvtmod, t->exp_fcvtmod);
+ printf("act_fcvtmod_fflags: "); print_fflags(act_fcvtmod_fflags); printf("\n");
+ printf("exp_fcvtmod_fflags: "); print_fflags(t->exp_fcvtmod_fflags); printf("\n");
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int main()
+{
+ return run_fcvtmod_tests();
+}