Second RISC-V PR for 8.2
* Add support for the max CPU * Detect user choice in TCG * Clear CSR values at reset and sync MPSTATE with host * Fix the typo of inverted order of pmpaddr13 and pmpaddr14 * Split TCG/KVM accelerators from cpu.c * Add extension properties for all cpus * Replace GDB exit calls with proper shutdown * Support KVM_GET_REG_LIST * Remove RVG warning * Use env_archcpu for better performance * Deprecate capital 'Z' CPU properties * Fix vfwmaccbf16.vf -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEaukCtqfKh31tZZKWr3yVEwxTgBMFAmUncYAACgkQr3yVEwxT gBPQ3g/9Fi4uYRK7dymHHAQbOO9NPlmVPPSxmQ8fNUhoZUkbHfm56JEl42Xr02rA Lg2ORRQxJhAinANV8CotnbyLRHNCAvouCMCQEjHo1YEHzdXc0tQzp+rIOHT7v9rH 6OQpI6RuCjO+0LQPMgzJx8yokMw/9b0uma3+RkNKod1XsSySo6JvDkMZGGZZWuVX Que3TMHzc4513PWEwRS9NaAHqRdy/ax0aPu9khswTYBxeJ/mBTLvGj4wBq5wnS7+ JPvq0M5ScUMl4K5o884wsAzOdxRk8QZOMx3duMCbqXw0xFmYZj/EzcIeHdnXwuDB lcANd6LcESMNUb8iDBaFRjLnZ/gNiu20/P/LPWyTirfoZXzZ+h6WPnSeli36xtzO KKWtvS1YggCjsDvh9/PLYAvUGBcS/kUhIynN10YKnoKB+wSDxxyvBS1GU6c8czgc WDf3V4P3Z8oPKDA/24Qd9Uiho1Gq9FED4eBQPb9PuvkfboKE/g7lUp708XXDFVld hkJMsYROSRvk54RHITrD9Z+XFQ2TfC8wHLH0IwlyynQnc1sKvXaR6U1hZTAVtE4f yley/xCQ7OUV+hrx1sQLURcN6A+SPummOY5jdHiD29QcJnOZnkSy5j2KOlnHSa5i 6v/6EFCgxwr69N6Q6X34VDv6+DZqLO2dNncQCInYFfupRhQ7t1E= =SUon -----END PGP SIGNATURE----- Merge tag 'pull-riscv-to-apply-20231012-1' of https://github.com/alistair23/qemu into staging Second RISC-V PR for 8.2 * Add support for the max CPU * Detect user choice in TCG * Clear CSR values at reset and sync MPSTATE with host * Fix the typo of inverted order of pmpaddr13 and pmpaddr14 * Split TCG/KVM accelerators from cpu.c * Add extension properties for all cpus * Replace GDB exit calls with proper shutdown * Support KVM_GET_REG_LIST * Remove RVG warning * Use env_archcpu for better performance * Deprecate capital 'Z' CPU properties * Fix vfwmaccbf16.vf # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEaukCtqfKh31tZZKWr3yVEwxTgBMFAmUncYAACgkQr3yVEwxT # gBPQ3g/9Fi4uYRK7dymHHAQbOO9NPlmVPPSxmQ8fNUhoZUkbHfm56JEl42Xr02rA # Lg2ORRQxJhAinANV8CotnbyLRHNCAvouCMCQEjHo1YEHzdXc0tQzp+rIOHT7v9rH # 6OQpI6RuCjO+0LQPMgzJx8yokMw/9b0uma3+RkNKod1XsSySo6JvDkMZGGZZWuVX # Que3TMHzc4513PWEwRS9NaAHqRdy/ax0aPu9khswTYBxeJ/mBTLvGj4wBq5wnS7+ # JPvq0M5ScUMl4K5o884wsAzOdxRk8QZOMx3duMCbqXw0xFmYZj/EzcIeHdnXwuDB # lcANd6LcESMNUb8iDBaFRjLnZ/gNiu20/P/LPWyTirfoZXzZ+h6WPnSeli36xtzO # KKWtvS1YggCjsDvh9/PLYAvUGBcS/kUhIynN10YKnoKB+wSDxxyvBS1GU6c8czgc # WDf3V4P3Z8oPKDA/24Qd9Uiho1Gq9FED4eBQPb9PuvkfboKE/g7lUp708XXDFVld # hkJMsYROSRvk54RHITrD9Z+XFQ2TfC8wHLH0IwlyynQnc1sKvXaR6U1hZTAVtE4f # yley/xCQ7OUV+hrx1sQLURcN6A+SPummOY5jdHiD29QcJnOZnkSy5j2KOlnHSa5i # 6v/6EFCgxwr69N6Q6X34VDv6+DZqLO2dNncQCInYFfupRhQ7t1E= # =SUon # -----END PGP SIGNATURE----- # gpg: Signature made Thu 12 Oct 2023 00:09:36 EDT # gpg: using RSA key 6AE902B6A7CA877D6D659296AF7C95130C538013 # gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 6AE9 02B6 A7CA 877D 6D65 9296 AF7C 9513 0C53 8013 * tag 'pull-riscv-to-apply-20231012-1' of https://github.com/alistair23/qemu: (54 commits) target/riscv: Fix vfwmaccbf16.vf target/riscv: deprecate capital 'Z' CPU properties target/riscv: Use env_archcpu for better performance target/riscv/tcg: remove RVG warning target/riscv/kvm: support KVM_GET_REG_LIST target/riscv/kvm: improve 'init_multiext_cfg' error msg gdbstub: replace exit calls with proper shutdown for softmmu hw/char: riscv_htif: replace exit calls with proper shutdown hw/misc/sifive_test.c: replace exit calls with proper shutdown softmmu: pass the main loop status to gdb "Wxx" packet softmmu: add means to pass an exit code when requesting a shutdown target/riscv/tcg-cpu.c: add extension properties for all cpus target/riscv: add riscv_cpu_get_name() target/riscv/cpu: move priv spec functions to tcg-cpu.c target/riscv/cpu.c: export isa_edata_arr[] target/riscv/tcg: move riscv_cpu_add_misa_properties() to tcg-cpu.c target/riscv/cpu.c: make misa_ext_cfgs[] 'const' target/riscv/tcg: introduce tcg_cpu_instance_init() target/riscv/cpu.c: export set_misa() target/riscv/kvm: do not use riscv_cpu_add_misa_properties() ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
63011373ad
@ -2116,8 +2116,8 @@ static const char *csr_name(int csrno)
|
||||
case 0x03ba: return "pmpaddr10";
|
||||
case 0x03bb: return "pmpaddr11";
|
||||
case 0x03bc: return "pmpaddr12";
|
||||
case 0x03bd: return "pmpaddr14";
|
||||
case 0x03be: return "pmpaddr13";
|
||||
case 0x03bd: return "pmpaddr13";
|
||||
case 0x03be: return "pmpaddr14";
|
||||
case 0x03bf: return "pmpaddr15";
|
||||
case 0x0780: return "mtohost";
|
||||
case 0x0781: return "mfromhost";
|
||||
|
@ -327,6 +327,41 @@ QEMU's ``vhost`` feature, which would eliminate the high latency costs under
|
||||
which the 9p ``proxy`` backend currently suffers. However as of to date nobody
|
||||
has indicated plans for such kind of reimplementation unfortunately.
|
||||
|
||||
RISC-V 'any' CPU type ``-cpu any`` (since 8.2)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The 'any' CPU type was introduced back in 2018 and has been around since the
|
||||
initial RISC-V QEMU port. Its usage has always been unclear: users don't know
|
||||
what to expect from a CPU called 'any', and in fact the CPU does not do anything
|
||||
special that isn't already done by the default CPUs rv32/rv64.
|
||||
|
||||
After the introduction of the 'max' CPU type, RISC-V now has a good coverage
|
||||
of generic CPUs: rv32 and rv64 as default CPUs and 'max' as a feature complete
|
||||
CPU for both 32 and 64 bit builds. Users are then discouraged to use the 'any'
|
||||
CPU type starting in 8.2.
|
||||
|
||||
RISC-V CPU properties which start with capital 'Z' (since 8.2)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
All RISC-V CPU properties which start with capital 'Z' are being deprecated
|
||||
starting in 8.2. The reason is that they were wrongly added with capital 'Z'
|
||||
in the past. CPU properties were later added with lower-case names, which
|
||||
is the format we want to use from now on.
|
||||
|
||||
Users which try to use these deprecated properties will receive a warning
|
||||
recommending to switch to their stable counterparts:
|
||||
|
||||
- "Zifencei" should be replaced with "zifencei"
|
||||
- "Zicsr" should be replaced with "zicsr"
|
||||
- "Zihintntl" should be replaced with "zihintntl"
|
||||
- "Zihintpause" should be replaced with "zihintpause"
|
||||
- "Zawrs" should be replaced with "zawrs"
|
||||
- "Zfa" should be replaced with "zfa"
|
||||
- "Zfh" should be replaced with "zfh"
|
||||
- "Zfhmin" should be replaced with "zfhmin"
|
||||
- "Zve32f" should be replaced with "zve32f"
|
||||
- "Zve64f" should be replaced with "zve64f"
|
||||
- "Zve64d" should be replaced with "zve64d"
|
||||
|
||||
Block device options
|
||||
''''''''''''''''''''
|
||||
|
@ -1324,7 +1324,7 @@ static void handle_v_kill(GArray *params, void *user_ctx)
|
||||
gdb_put_packet("OK");
|
||||
error_report("QEMU: Terminated via GDBstub");
|
||||
gdb_exit(0);
|
||||
exit(0);
|
||||
gdb_qemu_exit(0);
|
||||
}
|
||||
|
||||
static const GdbCmdParseEntry gdb_v_commands_table[] = {
|
||||
@ -1843,7 +1843,8 @@ static int gdb_handle_packet(const char *line_buf)
|
||||
/* Kill the target */
|
||||
error_report("QEMU: Terminated via GDBstub");
|
||||
gdb_exit(0);
|
||||
exit(0);
|
||||
gdb_qemu_exit(0);
|
||||
break;
|
||||
case 'D':
|
||||
{
|
||||
static const GdbCmdParseEntry detach_cmd_desc = {
|
||||
|
@ -435,6 +435,12 @@ void gdb_exit(int code)
|
||||
qemu_chr_fe_deinit(&gdbserver_system_state.chr, true);
|
||||
}
|
||||
|
||||
void gdb_qemu_exit(int code)
|
||||
{
|
||||
qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN,
|
||||
code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory access
|
||||
*/
|
||||
|
@ -113,6 +113,12 @@ void gdb_exit(int code)
|
||||
gdb_put_packet(buf);
|
||||
gdbserver_state.allow_stop_reply = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void gdb_qemu_exit(int code)
|
||||
{
|
||||
exit(code);
|
||||
}
|
||||
|
||||
int gdb_handlesig(CPUState *cpu, int sig)
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/tswap.h"
|
||||
#include "sysemu/dma.h"
|
||||
#include "sysemu/runstate.h"
|
||||
|
||||
#define RISCV_DEBUG_HTIF 0
|
||||
#define HTIF_DEBUG(fmt, ...) \
|
||||
@ -206,7 +207,9 @@ static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written)
|
||||
g_free(sig_data);
|
||||
}
|
||||
|
||||
exit(exit_code);
|
||||
qemu_system_shutdown_request_with_code(
|
||||
SHUTDOWN_CAUSE_GUEST_SHUTDOWN, exit_code);
|
||||
return;
|
||||
} else {
|
||||
uint64_t syscall[8];
|
||||
cpu_physical_memory_read(payload, syscall, sizeof(syscall));
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_riscv.h"
|
||||
#include "kvm/kvm_riscv.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
#define APLIC_MAX_IDC (1UL << 14)
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "qemu/module.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/misc/sifive_test.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
@ -39,9 +40,13 @@ static void sifive_test_write(void *opaque, hwaddr addr,
|
||||
int code = (val64 >> 16) & 0xffff;
|
||||
switch (status) {
|
||||
case FINISHER_FAIL:
|
||||
exit(code);
|
||||
qemu_system_shutdown_request_with_code(
|
||||
SHUTDOWN_CAUSE_GUEST_PANIC, code);
|
||||
return;
|
||||
case FINISHER_PASS:
|
||||
exit(0);
|
||||
qemu_system_shutdown_request_with_code(
|
||||
SHUTDOWN_CAUSE_GUEST_SHUTDOWN, code);
|
||||
return;
|
||||
case FINISHER_RESET:
|
||||
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||
return;
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include "hw/riscv/virt.h"
|
||||
#include "hw/riscv/boot.h"
|
||||
#include "hw/riscv/numa.h"
|
||||
#include "kvm_riscv.h"
|
||||
#include "kvm/kvm_riscv.h"
|
||||
#include "hw/intc/riscv_aclint.h"
|
||||
#include "hw/intc/riscv_aplic.h"
|
||||
#include "hw/intc/riscv_imsic.h"
|
||||
|
@ -110,4 +110,13 @@ int use_gdb_syscalls(void);
|
||||
*/
|
||||
void gdb_exit(int code);
|
||||
|
||||
/**
|
||||
* gdb_qemu_exit: ask qemu to exit
|
||||
* @code: exit code reported
|
||||
*
|
||||
* This requests qemu to exit. This function is allowed to return as
|
||||
* the exit request might be processed asynchronously by qemu backend.
|
||||
*/
|
||||
void gdb_qemu_exit(int code);
|
||||
|
||||
#endif /* _SYSCALLS_H_ */
|
||||
|
@ -68,6 +68,8 @@ void qemu_system_wakeup_request(WakeupReason reason, Error **errp);
|
||||
void qemu_system_wakeup_enable(WakeupReason reason, bool enabled);
|
||||
void qemu_register_wakeup_notifier(Notifier *notifier);
|
||||
void qemu_register_wakeup_support(void);
|
||||
void qemu_system_shutdown_request_with_code(ShutdownCause reason,
|
||||
int exit_code);
|
||||
void qemu_system_shutdown_request(ShutdownCause reason);
|
||||
void qemu_system_powerdown_request(void);
|
||||
void qemu_register_powerdown_notifier(Notifier *notifier);
|
||||
|
@ -101,7 +101,7 @@ bool defaults_enabled(void);
|
||||
|
||||
void qemu_init(int argc, char **argv);
|
||||
int qemu_main_loop(void);
|
||||
void qemu_cleanup(void);
|
||||
void qemu_cleanup(int);
|
||||
|
||||
extern QemuOptsList qemu_legacy_drive_opts;
|
||||
extern QemuOptsList qemu_common_drive_opts;
|
||||
|
@ -35,7 +35,7 @@ int qemu_default_main(void)
|
||||
int status;
|
||||
|
||||
status = qemu_main_loop();
|
||||
qemu_cleanup();
|
||||
qemu_cleanup(status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -385,6 +385,7 @@ void vm_state_notify(bool running, RunState state)
|
||||
|
||||
static ShutdownCause reset_requested;
|
||||
static ShutdownCause shutdown_requested;
|
||||
static int shutdown_exit_code = EXIT_SUCCESS;
|
||||
static int shutdown_signal;
|
||||
static pid_t shutdown_pid;
|
||||
static int powerdown_requested;
|
||||
@ -664,6 +665,13 @@ void qemu_system_killed(int signal, pid_t pid)
|
||||
qemu_notify_event();
|
||||
}
|
||||
|
||||
void qemu_system_shutdown_request_with_code(ShutdownCause reason,
|
||||
int exit_code)
|
||||
{
|
||||
shutdown_exit_code = exit_code;
|
||||
qemu_system_shutdown_request(reason);
|
||||
}
|
||||
|
||||
void qemu_system_shutdown_request(ShutdownCause reason)
|
||||
{
|
||||
trace_qemu_system_shutdown_request(reason);
|
||||
@ -725,7 +733,9 @@ static bool main_loop_should_exit(int *status)
|
||||
if (shutdown_action == SHUTDOWN_ACTION_PAUSE) {
|
||||
vm_stop(RUN_STATE_SHUTDOWN);
|
||||
} else {
|
||||
if (request == SHUTDOWN_CAUSE_GUEST_PANIC &&
|
||||
if (shutdown_exit_code != EXIT_SUCCESS) {
|
||||
*status = shutdown_exit_code;
|
||||
} else if (request == SHUTDOWN_CAUSE_GUEST_PANIC &&
|
||||
panic_action == PANIC_ACTION_EXIT_FAILURE) {
|
||||
*status = EXIT_FAILURE;
|
||||
}
|
||||
@ -824,9 +834,9 @@ void qemu_init_subsystems(void)
|
||||
}
|
||||
|
||||
|
||||
void qemu_cleanup(void)
|
||||
void qemu_cleanup(int status)
|
||||
{
|
||||
gdb_exit(0);
|
||||
gdb_exit(status);
|
||||
|
||||
/*
|
||||
* cleaning up the migration object cancels any existing migration
|
||||
|
@ -30,6 +30,7 @@
|
||||
#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
|
||||
|
||||
#define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
|
||||
#define TYPE_RISCV_CPU_MAX RISCV_CPU_TYPE_NAME("max")
|
||||
#define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
|
||||
#define TYPE_RISCV_CPU_BASE64 RISCV_CPU_TYPE_NAME("rv64")
|
||||
#define TYPE_RISCV_CPU_BASE128 RISCV_CPU_TYPE_NAME("x-rv128")
|
||||
|
1034
target/riscv/cpu.c
1034
target/riscv/cpu.c
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,7 @@
|
||||
|
||||
#include "hw/core/cpu.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "exec/cpu-defs.h"
|
||||
#include "qemu/cpu-float.h"
|
||||
#include "qom/object.h"
|
||||
@ -42,7 +43,7 @@
|
||||
#define RV(x) ((target_ulong)1 << (x - 'A'))
|
||||
|
||||
/*
|
||||
* Consider updating misa_ext_info_arr[] and misa_ext_cfgs[]
|
||||
* Update misa_bits[], misa_ext_info_arr[] and misa_ext_cfgs[]
|
||||
* when adding new MISA bits here.
|
||||
*/
|
||||
#define RVI RV('I')
|
||||
@ -59,9 +60,12 @@
|
||||
#define RVJ RV('J')
|
||||
#define RVG RV('G')
|
||||
|
||||
extern const uint32_t misa_bits[];
|
||||
const char *riscv_get_misa_ext_name(uint32_t bit);
|
||||
const char *riscv_get_misa_ext_description(uint32_t bit);
|
||||
|
||||
#define CPU_CFG_OFFSET(_prop) offsetof(struct RISCVCPUConfig, _prop)
|
||||
|
||||
/* Privileged specification version */
|
||||
enum {
|
||||
PRIV_VERSION_1_10_0 = 0,
|
||||
@ -443,7 +447,6 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||
bool probe, uintptr_t retaddr);
|
||||
char *riscv_isa_string(RISCVCPU *cpu);
|
||||
void riscv_cpu_list(void);
|
||||
void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp);
|
||||
|
||||
#define cpu_list riscv_cpu_list
|
||||
#define cpu_mmu_index riscv_cpu_mmu_index
|
||||
@ -705,6 +708,33 @@ enum riscv_pmu_event_idx {
|
||||
RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS = 0x10021,
|
||||
};
|
||||
|
||||
/* used by tcg/tcg-cpu.c*/
|
||||
void isa_ext_update_enabled(RISCVCPU *cpu, uint32_t ext_offset, bool en);
|
||||
bool isa_ext_is_enabled(RISCVCPU *cpu, uint32_t ext_offset);
|
||||
void riscv_cpu_set_misa(CPURISCVState *env, RISCVMXL mxl, uint32_t ext);
|
||||
|
||||
typedef struct RISCVCPUMultiExtConfig {
|
||||
const char *name;
|
||||
uint32_t offset;
|
||||
bool enabled;
|
||||
} RISCVCPUMultiExtConfig;
|
||||
|
||||
extern const RISCVCPUMultiExtConfig riscv_cpu_extensions[];
|
||||
extern const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[];
|
||||
extern const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[];
|
||||
extern const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[];
|
||||
extern Property riscv_cpu_options[];
|
||||
|
||||
typedef struct isa_ext_data {
|
||||
const char *name;
|
||||
int min_version;
|
||||
int ext_enable_offset;
|
||||
} RISCVIsaExtData;
|
||||
extern const RISCVIsaExtData isa_edata_arr[];
|
||||
char *riscv_cpu_get_name(RISCVCPU *cpu);
|
||||
|
||||
void riscv_add_satp_mode_properties(Object *obj);
|
||||
|
||||
/* CSR function table */
|
||||
extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE];
|
||||
|
||||
|
@ -65,8 +65,7 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
|
||||
void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
|
||||
uint64_t *cs_base, uint32_t *pflags)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
RISCVCPU *cpu = env_archcpu(env);
|
||||
RISCVExtStatus fs, vs;
|
||||
uint32_t flags = 0;
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "cpu.h"
|
||||
#include "tcg/tcg-cpu.h"
|
||||
#include "pmu.h"
|
||||
#include "time_helper.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* QEMU KVM RISC-V specific function stubs
|
||||
*
|
||||
* Copyright (c) 2020 Huawei Technologies Co., Ltd
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "kvm_riscv.h"
|
||||
|
||||
void kvm_riscv_reset_vcpu(RISCVCPU *cpu)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level)
|
||||
{
|
||||
abort();
|
||||
}
|
@ -31,6 +31,7 @@
|
||||
#include "sysemu/kvm_int.h"
|
||||
#include "cpu.h"
|
||||
#include "trace.h"
|
||||
#include "hw/core/accel-cpu.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "exec/memattrs.h"
|
||||
#include "exec/address-spaces.h"
|
||||
@ -51,6 +52,8 @@ void riscv_kvm_aplic_request(void *opaque, int irq, int level)
|
||||
kvm_set_irq(kvm_state, irq, !!level);
|
||||
}
|
||||
|
||||
static bool cap_has_mp_state;
|
||||
|
||||
static uint64_t kvm_riscv_reg_id(CPURISCVState *env, uint64_t type,
|
||||
uint64_t idx)
|
||||
{
|
||||
@ -205,10 +208,8 @@ static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs)
|
||||
}
|
||||
}
|
||||
|
||||
#define CPUCFG(_prop) offsetof(struct RISCVCPUConfig, _prop)
|
||||
|
||||
#define KVM_EXT_CFG(_name, _prop, _reg_id) \
|
||||
{.name = _name, .offset = CPUCFG(_prop), \
|
||||
{.name = _name, .offset = CPU_CFG_OFFSET(_prop), \
|
||||
.kvm_reg_id = _reg_id}
|
||||
|
||||
static KVMCPUConfig kvm_multi_ext_cfgs[] = {
|
||||
@ -285,13 +286,13 @@ static void kvm_cpu_set_multi_ext_cfg(Object *obj, Visitor *v,
|
||||
|
||||
static KVMCPUConfig kvm_cbom_blocksize = {
|
||||
.name = "cbom_blocksize",
|
||||
.offset = CPUCFG(cbom_blocksize),
|
||||
.offset = CPU_CFG_OFFSET(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),
|
||||
.offset = CPU_CFG_OFFSET(cboz_blocksize),
|
||||
.kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicboz_block_size)
|
||||
};
|
||||
|
||||
@ -345,10 +346,58 @@ static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs)
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_cpu_add_kvm_unavail_prop(Object *obj, const char *prop_name)
|
||||
{
|
||||
/* Check if KVM created the property already */
|
||||
if (object_property_find(obj, prop_name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the default to disabled for every extension
|
||||
* unknown to KVM and error out if the user attempts
|
||||
* to enable any of them.
|
||||
*/
|
||||
object_property_add(obj, prop_name, "bool",
|
||||
NULL, cpu_set_cfg_unavailable,
|
||||
NULL, (void *)prop_name);
|
||||
}
|
||||
|
||||
static void riscv_cpu_add_kvm_unavail_prop_array(Object *obj,
|
||||
const RISCVCPUMultiExtConfig *array)
|
||||
{
|
||||
const RISCVCPUMultiExtConfig *prop;
|
||||
|
||||
g_assert(array);
|
||||
|
||||
for (prop = array; prop && prop->name; prop++) {
|
||||
riscv_cpu_add_kvm_unavail_prop(obj, prop->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj)
|
||||
{
|
||||
int i;
|
||||
|
||||
riscv_add_satp_mode_properties(cpu_obj);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kvm_misa_ext_cfgs); i++) {
|
||||
KVMCPUConfig *misa_cfg = &kvm_misa_ext_cfgs[i];
|
||||
int bit = misa_cfg->offset;
|
||||
@ -364,6 +413,11 @@ static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj)
|
||||
misa_cfg->description);
|
||||
}
|
||||
|
||||
for (i = 0; misa_bits[i] != 0; i++) {
|
||||
const char *ext_name = riscv_get_misa_ext_name(misa_bits[i]);
|
||||
riscv_cpu_add_kvm_unavail_prop(cpu_obj, ext_name);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) {
|
||||
KVMCPUConfig *multi_cfg = &kvm_multi_ext_cfgs[i];
|
||||
|
||||
@ -380,6 +434,10 @@ static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj)
|
||||
object_property_add(cpu_obj, "cboz_blocksize", "uint16",
|
||||
NULL, kvm_cpu_set_cbomz_blksize,
|
||||
NULL, &kvm_cboz_blocksize);
|
||||
|
||||
riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_extensions);
|
||||
riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_vendor_exts);
|
||||
riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_experimental_exts);
|
||||
}
|
||||
|
||||
static int kvm_riscv_get_regs_core(CPUState *cs)
|
||||
@ -713,7 +771,8 @@ static void kvm_riscv_read_cbomz_blksize(RISCVCPU *cpu, KVMScratchCPU *kvmcpu,
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
|
||||
static void kvm_riscv_read_multiext_legacy(RISCVCPU *cpu,
|
||||
KVMScratchCPU *kvmcpu)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
uint64_t val;
|
||||
@ -734,8 +793,8 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
|
||||
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);
|
||||
"error code: %s", multi_ext_cfg->name,
|
||||
strerrorname_np(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
@ -754,7 +813,100 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_riscv_init_user_properties(Object *cpu_obj)
|
||||
static int uint64_cmp(const void *a, const void *b)
|
||||
{
|
||||
uint64_t val1 = *(const uint64_t *)a;
|
||||
uint64_t val2 = *(const uint64_t *)b;
|
||||
|
||||
if (val1 < val2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (val1 > val2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
|
||||
{
|
||||
KVMCPUConfig *multi_ext_cfg;
|
||||
struct kvm_one_reg reg;
|
||||
struct kvm_reg_list rl_struct;
|
||||
struct kvm_reg_list *reglist;
|
||||
uint64_t val, reg_id, *reg_search;
|
||||
int i, ret;
|
||||
|
||||
rl_struct.n = 0;
|
||||
ret = ioctl(kvmcpu->cpufd, KVM_GET_REG_LIST, &rl_struct);
|
||||
|
||||
/*
|
||||
* If KVM_GET_REG_LIST isn't supported we'll get errno 22
|
||||
* (EINVAL). Use read_legacy() in this case.
|
||||
*/
|
||||
if (errno == EINVAL) {
|
||||
return kvm_riscv_read_multiext_legacy(cpu, kvmcpu);
|
||||
} else if (errno != E2BIG) {
|
||||
/*
|
||||
* E2BIG is an expected error message for the API since we
|
||||
* don't know the number of registers. The right amount will
|
||||
* be written in rl_struct.n.
|
||||
*
|
||||
* Error out if we get any other errno.
|
||||
*/
|
||||
error_report("Error when accessing get-reg-list, code: %s",
|
||||
strerrorname_np(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
reglist = g_malloc(sizeof(struct kvm_reg_list) +
|
||||
rl_struct.n * sizeof(uint64_t));
|
||||
reglist->n = rl_struct.n;
|
||||
ret = ioctl(kvmcpu->cpufd, KVM_GET_REG_LIST, reglist);
|
||||
if (ret) {
|
||||
error_report("Error when reading KVM_GET_REG_LIST, code %s ",
|
||||
strerrorname_np(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* sort reglist to use bsearch() */
|
||||
qsort(®list->reg, reglist->n, sizeof(uint64_t), uint64_cmp);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) {
|
||||
multi_ext_cfg = &kvm_multi_ext_cfgs[i];
|
||||
reg_id = kvm_riscv_reg_id(&cpu->env, KVM_REG_RISCV_ISA_EXT,
|
||||
multi_ext_cfg->kvm_reg_id);
|
||||
reg_search = bsearch(®_id, reglist->reg, reglist->n,
|
||||
sizeof(uint64_t), uint64_cmp);
|
||||
if (!reg_search) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reg.id = reg_id;
|
||||
reg.addr = (uint64_t)&val;
|
||||
ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
|
||||
if (ret != 0) {
|
||||
error_report("Unable to read ISA_EXT KVM register %s, "
|
||||
"error code: %s", multi_ext_cfg->name,
|
||||
strerrorname_np(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_init_kvm_registers(Object *cpu_obj)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cpu_obj);
|
||||
KVMScratchCPU kvmcpu;
|
||||
@ -763,7 +915,6 @@ void kvm_riscv_init_user_properties(Object *cpu_obj)
|
||||
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);
|
||||
@ -797,6 +948,24 @@ int kvm_arch_get_registers(CPUState *cs)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state)
|
||||
{
|
||||
if (cap_has_mp_state) {
|
||||
struct kvm_mp_state mp_state = {
|
||||
.mp_state = state
|
||||
};
|
||||
|
||||
int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state);
|
||||
if (ret) {
|
||||
fprintf(stderr, "%s: failed to sync MP_STATE %d/%s\n",
|
||||
__func__, ret, strerror(-ret));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_put_registers(CPUState *cs, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -816,6 +985,18 @@ int kvm_arch_put_registers(CPUState *cs, int level)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (KVM_PUT_RESET_STATE == level) {
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
if (cs->cpu_index == 0) {
|
||||
ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_RUNNABLE);
|
||||
} else {
|
||||
ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_STOPPED);
|
||||
}
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -928,6 +1109,7 @@ int kvm_arch_get_default_type(MachineState *ms)
|
||||
|
||||
int kvm_arch_init(MachineState *ms, KVMState *s)
|
||||
{
|
||||
cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1010,14 +1192,25 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
||||
void kvm_riscv_reset_vcpu(RISCVCPU *cpu)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
int i;
|
||||
|
||||
if (!kvm_enabled()) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < 32; i++) {
|
||||
env->gpr[i] = 0;
|
||||
}
|
||||
env->pc = cpu->env.kernel_addr;
|
||||
env->gpr[10] = kvm_arch_vcpu_id(CPU(cpu)); /* a0 */
|
||||
env->gpr[11] = cpu->env.fdt_addr; /* a1 */
|
||||
env->satp = 0;
|
||||
env->mie = 0;
|
||||
env->stvec = 0;
|
||||
env->sscratch = 0;
|
||||
env->sepc = 0;
|
||||
env->scause = 0;
|
||||
env->stval = 0;
|
||||
env->mip = 0;
|
||||
}
|
||||
|
||||
void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level)
|
||||
@ -1229,3 +1422,62 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
|
||||
|
||||
kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
|
||||
}
|
||||
|
||||
static void kvm_cpu_instance_init(CPUState *cs)
|
||||
{
|
||||
Object *obj = OBJECT(RISCV_CPU(cs));
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
|
||||
riscv_init_kvm_registers(obj);
|
||||
|
||||
kvm_riscv_add_cpu_user_properties(obj);
|
||||
|
||||
for (Property *prop = riscv_cpu_options; prop && prop->name; prop++) {
|
||||
/* Check if we have a specific KVM handler for the option */
|
||||
if (object_property_find(obj, prop->name)) {
|
||||
continue;
|
||||
}
|
||||
qdev_property_add_static(dev, prop);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelCPUClass *acc = ACCEL_CPU_CLASS(oc);
|
||||
|
||||
acc->cpu_instance_init = kvm_cpu_instance_init;
|
||||
}
|
||||
|
||||
static const TypeInfo kvm_cpu_accel_type_info = {
|
||||
.name = ACCEL_CPU_NAME("kvm"),
|
||||
|
||||
.parent = TYPE_ACCEL_CPU,
|
||||
.class_init = kvm_cpu_accel_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
static void kvm_cpu_accel_register_types(void)
|
||||
{
|
||||
type_register_static(&kvm_cpu_accel_type_info);
|
||||
}
|
||||
type_init(kvm_cpu_accel_register_types);
|
||||
|
||||
static void riscv_host_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
|
||||
#if defined(TARGET_RISCV32)
|
||||
env->misa_mxl_max = env->misa_mxl = MXL_RV32;
|
||||
#elif defined(TARGET_RISCV64)
|
||||
env->misa_mxl_max = env->misa_mxl = MXL_RV64;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const TypeInfo riscv_kvm_cpu_type_infos[] = {
|
||||
{
|
||||
.name = TYPE_RISCV_CPU_HOST,
|
||||
.parent = TYPE_RISCV_CPU,
|
||||
.instance_init = riscv_host_cpu_init,
|
||||
}
|
||||
};
|
||||
|
||||
DEFINE_TYPES(riscv_kvm_cpu_type_infos)
|
@ -19,7 +19,6 @@
|
||||
#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);
|
||||
void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
|
||||
@ -27,5 +26,6 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
|
||||
uint64_t aplic_base, uint64_t imsic_base,
|
||||
uint64_t guest_num);
|
||||
void riscv_kvm_aplic_request(void *opaque, int irq, int level);
|
||||
int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state);
|
||||
|
||||
#endif
|
1
target/riscv/kvm/meson.build
Normal file
1
target/riscv/kvm/meson.build
Normal file
@ -0,0 +1 @@
|
||||
riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm-cpu.c'))
|
@ -24,7 +24,6 @@ riscv_ss.add(files(
|
||||
'zce_helper.c',
|
||||
'vcrypto_helper.c'
|
||||
))
|
||||
riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c'))
|
||||
|
||||
riscv_system_ss = ss.source_set()
|
||||
riscv_system_ss.add(files(
|
||||
@ -38,5 +37,8 @@ riscv_system_ss.add(files(
|
||||
'riscv-qmp-cmds.c',
|
||||
))
|
||||
|
||||
subdir('tcg')
|
||||
subdir('kvm')
|
||||
|
||||
target_arch += {'riscv': riscv_ss}
|
||||
target_system_arch += {'riscv': riscv_system_ss}
|
||||
|
2
target/riscv/tcg/meson.build
Normal file
2
target/riscv/tcg/meson.build
Normal file
@ -0,0 +1,2 @@
|
||||
riscv_ss.add(when: 'CONFIG_TCG', if_true: files(
|
||||
'tcg-cpu.c'))
|
949
target/riscv/tcg/tcg-cpu.c
Normal file
949
target/riscv/tcg/tcg-cpu.c
Normal file
@ -0,0 +1,949 @@
|
||||
/*
|
||||
* riscv TCG cpu class initialization
|
||||
*
|
||||
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
|
||||
* Copyright (c) 2017-2018 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg-cpu.h"
|
||||
#include "cpu.h"
|
||||
#include "pmu.h"
|
||||
#include "time_helper.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/core/accel-cpu.h"
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
||||
/* Hash that stores user set extensions */
|
||||
static GHashTable *multi_ext_user_opts;
|
||||
|
||||
static bool cpu_cfg_ext_is_user_set(uint32_t ext_offset)
|
||||
{
|
||||
return g_hash_table_contains(multi_ext_user_opts,
|
||||
GUINT_TO_POINTER(ext_offset));
|
||||
}
|
||||
|
||||
static void riscv_cpu_synchronize_from_tb(CPUState *cs,
|
||||
const TranslationBlock *tb)
|
||||
{
|
||||
if (!(tb_cflags(tb) & CF_PCREL)) {
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
|
||||
|
||||
tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
|
||||
|
||||
if (xl == MXL_RV32) {
|
||||
env->pc = (int32_t) tb->pc;
|
||||
} else {
|
||||
env->pc = tb->pc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_restore_state_to_opc(CPUState *cs,
|
||||
const TranslationBlock *tb,
|
||||
const uint64_t *data)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
|
||||
target_ulong pc;
|
||||
|
||||
if (tb_cflags(tb) & CF_PCREL) {
|
||||
pc = (env->pc & TARGET_PAGE_MASK) | data[0];
|
||||
} else {
|
||||
pc = data[0];
|
||||
}
|
||||
|
||||
if (xl == MXL_RV32) {
|
||||
env->pc = (int32_t)pc;
|
||||
} else {
|
||||
env->pc = pc;
|
||||
}
|
||||
env->bins = data[1];
|
||||
}
|
||||
|
||||
static const struct TCGCPUOps riscv_tcg_ops = {
|
||||
.initialize = riscv_translate_init,
|
||||
.synchronize_from_tb = riscv_cpu_synchronize_from_tb,
|
||||
.restore_state_to_opc = riscv_restore_state_to_opc,
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
.tlb_fill = riscv_cpu_tlb_fill,
|
||||
.cpu_exec_interrupt = riscv_cpu_exec_interrupt,
|
||||
.do_interrupt = riscv_cpu_do_interrupt,
|
||||
.do_transaction_failed = riscv_cpu_do_transaction_failed,
|
||||
.do_unaligned_access = riscv_cpu_do_unaligned_access,
|
||||
.debug_excp_handler = riscv_cpu_debug_excp_handler,
|
||||
.debug_check_breakpoint = riscv_cpu_debug_check_breakpoint,
|
||||
.debug_check_watchpoint = riscv_cpu_debug_check_watchpoint,
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
};
|
||||
|
||||
static int cpu_cfg_ext_get_min_version(uint32_t ext_offset)
|
||||
{
|
||||
const RISCVIsaExtData *edata;
|
||||
|
||||
for (edata = isa_edata_arr; edata && edata->name; edata++) {
|
||||
if (edata->ext_enable_offset != ext_offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return edata->min_version;
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void cpu_cfg_ext_auto_update(RISCVCPU *cpu, uint32_t ext_offset,
|
||||
bool value)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
bool prev_val = isa_ext_is_enabled(cpu, ext_offset);
|
||||
int min_version;
|
||||
|
||||
if (prev_val == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_cfg_ext_is_user_set(ext_offset)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value && env->priv_ver != PRIV_VERSION_LATEST) {
|
||||
/* Do not enable it if priv_ver is older than min_version */
|
||||
min_version = cpu_cfg_ext_get_min_version(ext_offset);
|
||||
if (env->priv_ver < min_version) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
isa_ext_update_enabled(cpu, ext_offset, value);
|
||||
}
|
||||
|
||||
static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp)
|
||||
{
|
||||
if (riscv_has_ext(env, RVH) && env->priv_ver < PRIV_VERSION_1_12_0) {
|
||||
error_setg(errp, "H extension requires priv spec 1.12.0");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_cpu_validate_misa_mxl(RISCVCPU *cpu, Error **errp)
|
||||
{
|
||||
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
|
||||
CPUClass *cc = CPU_CLASS(mcc);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
|
||||
/* Validate that MISA_MXL is set properly. */
|
||||
switch (env->misa_mxl_max) {
|
||||
#ifdef TARGET_RISCV64
|
||||
case MXL_RV64:
|
||||
case MXL_RV128:
|
||||
cc->gdb_core_xml_file = "riscv-64bit-cpu.xml";
|
||||
break;
|
||||
#endif
|
||||
case MXL_RV32:
|
||||
cc->gdb_core_xml_file = "riscv-32bit-cpu.xml";
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
if (env->misa_mxl_max != env->misa_mxl) {
|
||||
error_setg(errp, "misa_mxl_max must be equal to misa_mxl");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_cpu_validate_priv_spec(RISCVCPU *cpu, Error **errp)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
int priv_version = -1;
|
||||
|
||||
if (cpu->cfg.priv_spec) {
|
||||
if (!g_strcmp0(cpu->cfg.priv_spec, "v1.12.0")) {
|
||||
priv_version = PRIV_VERSION_1_12_0;
|
||||
} else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) {
|
||||
priv_version = PRIV_VERSION_1_11_0;
|
||||
} else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) {
|
||||
priv_version = PRIV_VERSION_1_10_0;
|
||||
} else {
|
||||
error_setg(errp,
|
||||
"Unsupported privilege spec version '%s'",
|
||||
cpu->cfg.priv_spec);
|
||||
return;
|
||||
}
|
||||
|
||||
env->priv_ver = priv_version;
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg,
|
||||
Error **errp)
|
||||
{
|
||||
if (!is_power_of_2(cfg->vlen)) {
|
||||
error_setg(errp, "Vector extension VLEN must be power of 2");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg->vlen > RV_VLEN_MAX || cfg->vlen < 128) {
|
||||
error_setg(errp,
|
||||
"Vector extension implementation only supports VLEN "
|
||||
"in the range [128, %d]", RV_VLEN_MAX);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(cfg->elen)) {
|
||||
error_setg(errp, "Vector extension ELEN must be power of 2");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg->elen > 64 || cfg->elen < 8) {
|
||||
error_setg(errp,
|
||||
"Vector extension implementation only supports ELEN "
|
||||
"in the range [8, 64]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg->vext_spec) {
|
||||
if (!g_strcmp0(cfg->vext_spec, "v1.0")) {
|
||||
env->vext_ver = VEXT_VERSION_1_00_0;
|
||||
} else {
|
||||
error_setg(errp, "Unsupported vector spec version '%s'",
|
||||
cfg->vext_spec);
|
||||
return;
|
||||
}
|
||||
} else if (env->vext_ver == 0) {
|
||||
qemu_log("vector version is not specified, "
|
||||
"use the default value v1.0\n");
|
||||
|
||||
env->vext_ver = VEXT_VERSION_1_00_0;
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
const RISCVIsaExtData *edata;
|
||||
|
||||
/* Force disable extensions if priv spec version does not match */
|
||||
for (edata = isa_edata_arr; edata && edata->name; edata++) {
|
||||
if (isa_ext_is_enabled(cpu, edata->ext_enable_offset) &&
|
||||
(env->priv_ver < edata->min_version)) {
|
||||
isa_ext_update_enabled(cpu, edata->ext_enable_offset, false);
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx
|
||||
" because privilege spec version does not match",
|
||||
edata->name, env->mhartid);
|
||||
#else
|
||||
warn_report("disabling %s extension because "
|
||||
"privilege spec version does not match",
|
||||
edata->name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check consistency between chosen extensions while setting
|
||||
* cpu->cfg accordingly.
|
||||
*/
|
||||
void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Do some ISA extension error checking */
|
||||
if (riscv_has_ext(env, RVG) &&
|
||||
!(riscv_has_ext(env, RVI) && riscv_has_ext(env, RVM) &&
|
||||
riscv_has_ext(env, RVA) && riscv_has_ext(env, RVF) &&
|
||||
riscv_has_ext(env, RVD) &&
|
||||
cpu->cfg.ext_icsr && cpu->cfg.ext_ifencei)) {
|
||||
|
||||
if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_icsr)) &&
|
||||
!cpu->cfg.ext_icsr) {
|
||||
error_setg(errp, "RVG requires Zicsr but user set Zicsr to false");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_ifencei)) &&
|
||||
!cpu->cfg.ext_ifencei) {
|
||||
error_setg(errp, "RVG requires Zifencei but user set "
|
||||
"Zifencei to false");
|
||||
return;
|
||||
}
|
||||
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_icsr), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_ifencei), true);
|
||||
|
||||
env->misa_ext |= RVI | RVM | RVA | RVF | RVD;
|
||||
env->misa_ext_mask |= RVI | RVM | RVA | RVF | RVD;
|
||||
}
|
||||
|
||||
if (riscv_has_ext(env, RVI) && riscv_has_ext(env, RVE)) {
|
||||
error_setg(errp,
|
||||
"I and E extensions are incompatible");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!riscv_has_ext(env, RVI) && !riscv_has_ext(env, RVE)) {
|
||||
error_setg(errp,
|
||||
"Either I or E extension must be set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (riscv_has_ext(env, RVS) && !riscv_has_ext(env, RVU)) {
|
||||
error_setg(errp,
|
||||
"Setting S extension without U extension is illegal");
|
||||
return;
|
||||
}
|
||||
|
||||
if (riscv_has_ext(env, RVH) && !riscv_has_ext(env, RVI)) {
|
||||
error_setg(errp,
|
||||
"H depends on an I base integer ISA with 32 x registers");
|
||||
return;
|
||||
}
|
||||
|
||||
if (riscv_has_ext(env, RVH) && !riscv_has_ext(env, RVS)) {
|
||||
error_setg(errp, "H extension implicitly requires S-mode");
|
||||
return;
|
||||
}
|
||||
|
||||
if (riscv_has_ext(env, RVF) && !cpu->cfg.ext_icsr) {
|
||||
error_setg(errp, "F extension requires Zicsr");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((cpu->cfg.ext_zawrs) && !riscv_has_ext(env, RVA)) {
|
||||
error_setg(errp, "Zawrs extension requires A extension");
|
||||
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_auto_update(cpu, CPU_CFG_OFFSET(ext_zfhmin), true);
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zfhmin && !riscv_has_ext(env, RVF)) {
|
||||
error_setg(errp, "Zfh/Zfhmin extensions require F extension");
|
||||
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;
|
||||
}
|
||||
|
||||
if (riscv_has_ext(env, RVV)) {
|
||||
riscv_cpu_validate_v(env, &cpu->cfg, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The V vector extension depends on the Zve64d extension */
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64d), true);
|
||||
}
|
||||
|
||||
/* The Zve64d extension depends on the Zve64f extension */
|
||||
if (cpu->cfg.ext_zve64d) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64f), true);
|
||||
}
|
||||
|
||||
/* The Zve64f extension depends on the Zve32f extension */
|
||||
if (cpu->cfg.ext_zve64f) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve32f), true);
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zve64d && !riscv_has_ext(env, RVD)) {
|
||||
error_setg(errp, "Zve64d/V extensions require D extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zve32f && !riscv_has_ext(env, RVF)) {
|
||||
error_setg(errp, "Zve32f/Zve64f extensions require F extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zvfh) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvfhmin), true);
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zvfhmin && !cpu->cfg.ext_zve32f) {
|
||||
error_setg(errp, "Zvfh/Zvfhmin extensions require Zve32f extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zvfh && !cpu->cfg.ext_zfhmin) {
|
||||
error_setg(errp, "Zvfh extensions requires Zfhmin extension");
|
||||
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_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true);
|
||||
}
|
||||
|
||||
if ((cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinxmin) && !cpu->cfg.ext_zfinx) {
|
||||
error_setg(errp, "Zdinx/Zhinx/Zhinxmin extensions require Zfinx");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zfinx) {
|
||||
if (!cpu->cfg.ext_icsr) {
|
||||
error_setg(errp, "Zfinx extension requires Zicsr");
|
||||
return;
|
||||
}
|
||||
if (riscv_has_ext(env, RVF)) {
|
||||
error_setg(errp,
|
||||
"Zfinx cannot be supported together with F extension");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zce) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcb), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmp), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmt), true);
|
||||
if (riscv_has_ext(env, RVF) && env->misa_mxl_max == MXL_RV32) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true);
|
||||
}
|
||||
}
|
||||
|
||||
/* zca, zcd and zcf has a PRIV 1.12.0 restriction */
|
||||
if (riscv_has_ext(env, RVC) && env->priv_ver >= PRIV_VERSION_1_12_0) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true);
|
||||
if (riscv_has_ext(env, RVF) && env->misa_mxl_max == MXL_RV32) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true);
|
||||
}
|
||||
if (riscv_has_ext(env, RVD)) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcd), true);
|
||||
}
|
||||
}
|
||||
|
||||
if (env->misa_mxl_max != MXL_RV32 && cpu->cfg.ext_zcf) {
|
||||
error_setg(errp, "Zcf extension is only relevant to RV32");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!riscv_has_ext(env, RVF) && cpu->cfg.ext_zcf) {
|
||||
error_setg(errp, "Zcf extension requires F extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!riscv_has_ext(env, RVD) && cpu->cfg.ext_zcd) {
|
||||
error_setg(errp, "Zcd extension requires D extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((cpu->cfg.ext_zcf || cpu->cfg.ext_zcd || cpu->cfg.ext_zcb ||
|
||||
cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt) && !cpu->cfg.ext_zca) {
|
||||
error_setg(errp, "Zcf/Zcd/Zcb/Zcmp/Zcmt extensions require Zca "
|
||||
"extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zcd && (cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt)) {
|
||||
error_setg(errp, "Zcmp/Zcmt extensions are incompatible with "
|
||||
"Zcd extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zcmt && !cpu->cfg.ext_icsr) {
|
||||
error_setg(errp, "Zcmt extension requires Zicsr extension");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* In principle Zve*x would also suffice here, were they supported
|
||||
* in qemu
|
||||
*/
|
||||
if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkg || cpu->cfg.ext_zvkned ||
|
||||
cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed || cpu->cfg.ext_zvksh) &&
|
||||
!cpu->cfg.ext_zve32f) {
|
||||
error_setg(errp,
|
||||
"Vector crypto extensions require V or Zve* extensions");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((cpu->cfg.ext_zvbc || cpu->cfg.ext_zvknhb) && !cpu->cfg.ext_zve64f) {
|
||||
error_setg(
|
||||
errp,
|
||||
"Zvbc and Zvknhb extensions require V or Zve64{f,d} extensions");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zk) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkn), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkr), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkt), true);
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zkn) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkb), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkc), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkx), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkne), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zknd), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zknh), true);
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zks) {
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkb), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkc), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkx), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zksed), true);
|
||||
cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zksh), true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable isa extensions based on priv spec after we
|
||||
* validated and set everything we need.
|
||||
*/
|
||||
riscv_cpu_disable_priv_spec_isa_exts(cpu);
|
||||
}
|
||||
|
||||
static bool riscv_cpu_is_generic(Object *cpu_obj)
|
||||
{
|
||||
return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We'll get here via the following path:
|
||||
*
|
||||
* riscv_cpu_realize()
|
||||
* -> cpu_exec_realizefn()
|
||||
* -> tcg_cpu_realize() (via accel_cpu_common_realize())
|
||||
*/
|
||||
static bool tcg_cpu_realize(CPUState *cs, Error **errp)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) {
|
||||
g_autofree char *name = riscv_cpu_get_name(cpu);
|
||||
error_setg(errp, "'%s' CPU is not compatible with TCG acceleration",
|
||||
name);
|
||||
return false;
|
||||
}
|
||||
|
||||
riscv_cpu_validate_misa_mxl(cpu, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
riscv_cpu_validate_priv_spec(cpu, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
riscv_cpu_validate_misa_priv(env, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cpu->cfg.epmp && !cpu->cfg.pmp) {
|
||||
/*
|
||||
* Enhanced PMP should only be available
|
||||
* on harts with PMP support
|
||||
*/
|
||||
error_setg(errp, "Invalid configuration: EPMP requires PMP support");
|
||||
return false;
|
||||
}
|
||||
|
||||
riscv_cpu_validate_set_extensions(cpu, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
CPU(cs)->tcg_cflags |= CF_PCREL;
|
||||
|
||||
if (cpu->cfg.ext_sstc) {
|
||||
riscv_timer_init(cpu);
|
||||
}
|
||||
|
||||
if (cpu->cfg.pmu_num) {
|
||||
if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && cpu->cfg.ext_sscofpmf) {
|
||||
cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
riscv_pmu_timer_cb, cpu);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct RISCVCPUMisaExtConfig {
|
||||
target_ulong misa_bit;
|
||||
bool enabled;
|
||||
} RISCVCPUMisaExtConfig;
|
||||
|
||||
static void cpu_set_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
const RISCVCPUMisaExtConfig *misa_ext_cfg = opaque;
|
||||
target_ulong misa_bit = misa_ext_cfg->misa_bit;
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
bool generic_cpu = riscv_cpu_is_generic(obj);
|
||||
bool prev_val, value;
|
||||
|
||||
if (!visit_type_bool(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
prev_val = env->misa_ext & misa_bit;
|
||||
|
||||
if (value == prev_val) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (!generic_cpu) {
|
||||
g_autofree char *cpuname = riscv_cpu_get_name(cpu);
|
||||
error_setg(errp, "'%s' CPU does not allow enabling extensions",
|
||||
cpuname);
|
||||
return;
|
||||
}
|
||||
|
||||
env->misa_ext |= misa_bit;
|
||||
env->misa_ext_mask |= misa_bit;
|
||||
} else {
|
||||
env->misa_ext &= ~misa_bit;
|
||||
env->misa_ext_mask &= ~misa_bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void cpu_get_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
const RISCVCPUMisaExtConfig *misa_ext_cfg = opaque;
|
||||
target_ulong misa_bit = misa_ext_cfg->misa_bit;
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
bool value;
|
||||
|
||||
value = env->misa_ext & misa_bit;
|
||||
|
||||
visit_type_bool(v, name, &value, errp);
|
||||
}
|
||||
|
||||
#define MISA_CFG(_bit, _enabled) \
|
||||
{.misa_bit = _bit, .enabled = _enabled}
|
||||
|
||||
static const 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),
|
||||
};
|
||||
|
||||
/*
|
||||
* We do not support user choice tracking for MISA
|
||||
* extensions yet because, so far, we do not silently
|
||||
* change MISA bits during realize() (RVG enables MISA
|
||||
* bits but the user is warned about it).
|
||||
*/
|
||||
static void riscv_cpu_add_misa_properties(Object *cpu_obj)
|
||||
{
|
||||
bool use_def_vals = riscv_cpu_is_generic(cpu_obj);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(misa_ext_cfgs); i++) {
|
||||
const RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i];
|
||||
int bit = misa_cfg->misa_bit;
|
||||
const char *name = riscv_get_misa_ext_name(bit);
|
||||
const char *desc = riscv_get_misa_ext_description(bit);
|
||||
|
||||
/* Check if KVM already created the property */
|
||||
if (object_property_find(cpu_obj, name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
object_property_add(cpu_obj, name, "bool",
|
||||
cpu_get_misa_ext_cfg,
|
||||
cpu_set_misa_ext_cfg,
|
||||
NULL, (void *)misa_cfg);
|
||||
object_property_set_description(cpu_obj, name, desc);
|
||||
if (use_def_vals) {
|
||||
object_property_set_bool(cpu_obj, name, misa_cfg->enabled, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool cpu_ext_is_deprecated(const char *ext_name)
|
||||
{
|
||||
return isupper(ext_name[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* String will be allocated in the heap. Caller is responsible
|
||||
* for freeing it.
|
||||
*/
|
||||
static char *cpu_ext_to_lower(const char *ext_name)
|
||||
{
|
||||
char *ret = g_malloc0(strlen(ext_name) + 1);
|
||||
|
||||
strcpy(ret, ext_name);
|
||||
ret[0] = tolower(ret[0]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
const RISCVCPUMultiExtConfig *multi_ext_cfg = opaque;
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
bool generic_cpu = riscv_cpu_is_generic(obj);
|
||||
bool prev_val, value;
|
||||
|
||||
if (!visit_type_bool(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_ext_is_deprecated(multi_ext_cfg->name)) {
|
||||
g_autofree char *lower = cpu_ext_to_lower(multi_ext_cfg->name);
|
||||
|
||||
warn_report("CPU property '%s' is deprecated. Please use '%s' instead",
|
||||
multi_ext_cfg->name, lower);
|
||||
}
|
||||
|
||||
g_hash_table_insert(multi_ext_user_opts,
|
||||
GUINT_TO_POINTER(multi_ext_cfg->offset),
|
||||
(gpointer)value);
|
||||
|
||||
prev_val = isa_ext_is_enabled(cpu, multi_ext_cfg->offset);
|
||||
|
||||
if (value == prev_val) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value && !generic_cpu) {
|
||||
g_autofree char *cpuname = riscv_cpu_get_name(cpu);
|
||||
error_setg(errp, "'%s' CPU does not allow enabling extensions",
|
||||
cpuname);
|
||||
return;
|
||||
}
|
||||
|
||||
isa_ext_update_enabled(cpu, multi_ext_cfg->offset, value);
|
||||
}
|
||||
|
||||
static void cpu_get_multi_ext_cfg(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
const RISCVCPUMultiExtConfig *multi_ext_cfg = opaque;
|
||||
bool value = isa_ext_is_enabled(RISCV_CPU(obj), multi_ext_cfg->offset);
|
||||
|
||||
visit_type_bool(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void cpu_add_multi_ext_prop(Object *cpu_obj,
|
||||
const RISCVCPUMultiExtConfig *multi_cfg)
|
||||
{
|
||||
bool generic_cpu = riscv_cpu_is_generic(cpu_obj);
|
||||
bool deprecated_ext = cpu_ext_is_deprecated(multi_cfg->name);
|
||||
|
||||
object_property_add(cpu_obj, multi_cfg->name, "bool",
|
||||
cpu_get_multi_ext_cfg,
|
||||
cpu_set_multi_ext_cfg,
|
||||
NULL, (void *)multi_cfg);
|
||||
|
||||
if (!generic_cpu || deprecated_ext) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set def val directly instead of using
|
||||
* object_property_set_bool() to save the set()
|
||||
* callback hash for user inputs.
|
||||
*/
|
||||
isa_ext_update_enabled(RISCV_CPU(cpu_obj), multi_cfg->offset,
|
||||
multi_cfg->enabled);
|
||||
}
|
||||
|
||||
static void riscv_cpu_add_multiext_prop_array(Object *obj,
|
||||
const RISCVCPUMultiExtConfig *array)
|
||||
{
|
||||
const RISCVCPUMultiExtConfig *prop;
|
||||
|
||||
g_assert(array);
|
||||
|
||||
for (prop = array; prop && prop->name; prop++) {
|
||||
cpu_add_multi_ext_prop(obj, prop);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add CPU properties with user-facing flags.
|
||||
*
|
||||
* This will overwrite existing env->misa_ext values with the
|
||||
* defaults set via riscv_cpu_add_misa_properties().
|
||||
*/
|
||||
static void riscv_cpu_add_user_properties(Object *obj)
|
||||
{
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
riscv_add_satp_mode_properties(obj);
|
||||
#endif
|
||||
|
||||
riscv_cpu_add_misa_properties(obj);
|
||||
|
||||
riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_extensions);
|
||||
riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_vendor_exts);
|
||||
riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_experimental_exts);
|
||||
|
||||
riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_deprecated_exts);
|
||||
|
||||
for (Property *prop = riscv_cpu_options; prop && prop->name; prop++) {
|
||||
qdev_property_add_static(DEVICE(obj), prop);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The 'max' type CPU will have all possible ratified
|
||||
* non-vendor extensions enabled.
|
||||
*/
|
||||
static void riscv_init_max_cpu_extensions(Object *obj)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
const RISCVCPUMultiExtConfig *prop;
|
||||
|
||||
/* Enable RVG, RVJ and RVV that are disabled by default */
|
||||
riscv_cpu_set_misa(env, env->misa_mxl, env->misa_ext | RVG | RVJ | RVV);
|
||||
|
||||
for (prop = riscv_cpu_extensions; prop && prop->name; prop++) {
|
||||
isa_ext_update_enabled(cpu, prop->offset, true);
|
||||
}
|
||||
|
||||
/* set vector version */
|
||||
env->vext_ver = VEXT_VERSION_1_00_0;
|
||||
|
||||
/* Zfinx is not compatible with F. Disable it */
|
||||
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zfinx), false);
|
||||
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zdinx), false);
|
||||
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zhinx), false);
|
||||
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zhinxmin), false);
|
||||
|
||||
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zce), false);
|
||||
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcmp), false);
|
||||
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcmt), false);
|
||||
|
||||
if (env->misa_mxl != MXL_RV32) {
|
||||
isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcf), false);
|
||||
}
|
||||
}
|
||||
|
||||
static bool riscv_cpu_has_max_extensions(Object *cpu_obj)
|
||||
{
|
||||
return object_dynamic_cast(cpu_obj, TYPE_RISCV_CPU_MAX) != NULL;
|
||||
}
|
||||
|
||||
static void tcg_cpu_instance_init(CPUState *cs)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
Object *obj = OBJECT(cpu);
|
||||
|
||||
multi_ext_user_opts = g_hash_table_new(NULL, g_direct_equal);
|
||||
riscv_cpu_add_user_properties(obj);
|
||||
|
||||
if (riscv_cpu_has_max_extensions(obj)) {
|
||||
riscv_init_max_cpu_extensions(obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void tcg_cpu_init_ops(AccelCPUClass *accel_cpu, CPUClass *cc)
|
||||
{
|
||||
/*
|
||||
* All cpus use the same set of operations.
|
||||
*/
|
||||
cc->tcg_ops = &riscv_tcg_ops;
|
||||
}
|
||||
|
||||
static void tcg_cpu_class_init(CPUClass *cc)
|
||||
{
|
||||
cc->init_accel_cpu = tcg_cpu_init_ops;
|
||||
}
|
||||
|
||||
static void tcg_cpu_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelCPUClass *acc = ACCEL_CPU_CLASS(oc);
|
||||
|
||||
acc->cpu_class_init = tcg_cpu_class_init;
|
||||
acc->cpu_instance_init = tcg_cpu_instance_init;
|
||||
acc->cpu_target_realize = tcg_cpu_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo tcg_cpu_accel_type_info = {
|
||||
.name = ACCEL_CPU_NAME("tcg"),
|
||||
|
||||
.parent = TYPE_ACCEL_CPU,
|
||||
.class_init = tcg_cpu_accel_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void tcg_cpu_accel_register_types(void)
|
||||
{
|
||||
type_register_static(&tcg_cpu_accel_type_info);
|
||||
}
|
||||
type_init(tcg_cpu_accel_register_types);
|
27
target/riscv/tcg/tcg-cpu.h
Normal file
27
target/riscv/tcg/tcg-cpu.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* riscv TCG cpu class initialization
|
||||
*
|
||||
* Copyright (c) 2023 Ventana Micro Systems Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RISCV_TCG_CPU_H
|
||||
#define RISCV_TCG_CPU_H
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp);
|
||||
|
||||
#endif
|
@ -3361,7 +3361,7 @@ static uint32_t fwmaccbf16(uint16_t a, uint16_t b, uint32_t d, float_status *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)
|
||||
RVVCALL(OPFVF3, vfwmaccbf16_vf, WOP_UUU_H, H4, H2, fwmaccbf16)
|
||||
GEN_VEXT_VF(vfwmaccbf16_vf, 4)
|
||||
|
||||
static uint32_t fwnmacc16(uint16_t a, uint16_t b, uint32_t d, float_status *s)
|
||||
|
@ -501,6 +501,38 @@ class TuxRunBaselineTest(QemuSystemTest):
|
||||
|
||||
self.common_tuxrun(csums=sums)
|
||||
|
||||
def test_riscv32_maxcpu(self):
|
||||
"""
|
||||
:avocado: tags=arch:riscv32
|
||||
:avocado: tags=machine:virt
|
||||
:avocado: tags=cpu:max
|
||||
:avocado: tags=tuxboot:riscv32
|
||||
"""
|
||||
sums = { "Image" :
|
||||
"89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5",
|
||||
"fw_jump.elf" :
|
||||
"f2ef28a0b77826f79d085d3e4aa686f1159b315eff9099a37046b18936676985",
|
||||
"rootfs.ext4.zst" :
|
||||
"7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba" }
|
||||
|
||||
self.common_tuxrun(csums=sums)
|
||||
|
||||
def test_riscv64_maxcpu(self):
|
||||
"""
|
||||
:avocado: tags=arch:riscv64
|
||||
:avocado: tags=machine:virt
|
||||
:avocado: tags=cpu:max
|
||||
:avocado: tags=tuxboot:riscv64
|
||||
"""
|
||||
sums = { "Image" :
|
||||
"cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e",
|
||||
"fw_jump.elf" :
|
||||
"6e3373abcab4305fe151b564a4c71110d833c21f2c0a1753b7935459e36aedcf",
|
||||
"rootfs.ext4.zst" :
|
||||
"b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb" }
|
||||
|
||||
self.common_tuxrun(csums=sums)
|
||||
|
||||
def test_s390(self):
|
||||
"""
|
||||
:avocado: tags=arch:s390x
|
||||
|
Loading…
Reference in New Issue
Block a user