target/mips: add Octeon CHORD and LLM COP2 helpers

Add the Octeon CHORD hardware register access path and the LLM 36-bit
and 64-bit read and write windows. Model both CHORD access forms,
including the RDHWR $30 path and the legacy DMFC2 alias.

Implement sparse backing storage for the two LLM sets so user-mode code
can save, restore, and probe the architectural state without allocating a
full hardware-sized backing array.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
Message-ID: <20260608-mips-octeon-missing-insns-v2-v16-13-daef7a0d8b04@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
This commit is contained in:
James Hilliard
2026-06-08 12:59:38 -06:00
committed by Philippe Mathieu-Daudé
parent 900c423717
commit e3c6d52fa0
8 changed files with 241 additions and 0 deletions

View File

@@ -26,6 +26,7 @@
#include "cpu.h"
#include "internal.h"
#include "qemu/module.h"
#include "qemu/qtree.h"
#include "system/qtest.h"
#include "hw/core/qdev-properties.h"
#include "hw/core/qdev-clock.h"
@@ -181,6 +182,57 @@ static bool mips_cpu_has_work(CPUState *cs)
#include "cpu-defs.c.inc"
static gint mips_octeon_u64_tree_compare(gconstpointer a, gconstpointer b,
gpointer user_data)
{
uint64_t av = *(const uint64_t *)a;
uint64_t bv = *(const uint64_t *)b;
return (av > bv) - (av < bv);
}
QTree *mips_octeon_llm_tree_new(void)
{
return q_tree_new_full(mips_octeon_u64_tree_compare,
NULL, g_free, g_free);
}
uint64_t mips_octeon_llm_load(QTree *tree, uint64_t addr)
{
uint64_t key = addr;
uint64_t *value = tree ? q_tree_lookup(tree, &key) : NULL;
return value ? *value : 0;
}
void mips_octeon_llm_store(QTree **treep, uint64_t addr, uint64_t value)
{
uint64_t *key;
uint64_t *stored;
if (!*treep) {
*treep = mips_octeon_llm_tree_new();
}
key = g_new(uint64_t, 1);
stored = g_new(uint64_t, 1);
*key = addr;
*stored = value;
q_tree_replace(*treep, key, stored);
}
static void mips_octeon_destroy_llm_state(MIPSOcteonCryptoState *crypto)
{
if (crypto->llm36) {
q_tree_destroy(crypto->llm36);
crypto->llm36 = NULL;
}
if (crypto->llm64) {
q_tree_destroy(crypto->llm64);
crypto->llm64 = NULL;
}
}
static void mips_cpu_reset_hold(Object *obj, ResetType type)
{
CPUState *cs = CPU(obj);
@@ -192,6 +244,7 @@ static void mips_cpu_reset_hold(Object *obj, ResetType type)
mcc->parent_phases.hold(obj, type);
}
mips_octeon_destroy_llm_state(&env->octeon_crypto);
memset(env, 0, offsetof(CPUMIPSState, end_reset_fields));
/* Reset registers to their default values */
@@ -246,6 +299,9 @@ static void mips_cpu_reset_hold(Object *obj, ResetType type)
env->active_fpu.fcr31 = env->cpu_model->CP1_fcr31;
env->msair = env->cpu_model->MSAIR;
env->insn_flags = env->cpu_model->insn_flags;
if (env->insn_flags & INSN_OCTEON) {
env->octeon_crypto.chord = 1;
}
#if defined(CONFIG_USER_ONLY)
env->CP0_Status = (MIPS_HFLAG_UM << CP0St_KSU);
@@ -262,6 +318,9 @@ static void mips_cpu_reset_hold(Object *obj, ResetType type)
* hardware registers.
*/
env->CP0_HWREna |= 0x0000000F;
if (env->insn_flags & INSN_OCTEON) {
env->CP0_HWREna |= 0x40000000u;
}
if (env->CP0_Config1 & (1 << CP0C1_FP)) {
env->CP0_Status |= (1 << CP0St_CU1);
}
@@ -417,6 +476,13 @@ static void mips_cpu_reset_hold(Object *obj, ResetType type)
#endif
}
static void mips_cpu_finalize(Object *obj)
{
MIPSCPU *cpu = MIPS_CPU(obj);
mips_octeon_destroy_llm_state(&cpu->env.octeon_crypto);
}
static void mips_cpu_disas_set_info(const CPUState *cs, disassemble_info *info)
{
const MIPSCPU *cpu = MIPS_CPU(cs);
@@ -645,6 +711,7 @@ static const TypeInfo mips_cpu_type_info = {
.instance_size = sizeof(MIPSCPU),
.instance_align = __alignof(MIPSCPU),
.instance_init = mips_cpu_initfn,
.instance_finalize = mips_cpu_finalize,
.abstract = true,
.class_size = sizeof(MIPSCPUClass),
.class_init = mips_cpu_class_init,

View File

@@ -11,6 +11,7 @@
#include "fpu/softfloat-types.h"
#include "hw/core/clock.h"
#include "mips-defs.h"
#include "qemu/qtree.h"
typedef struct CPUMIPSTLBContext CPUMIPSTLBContext;
@@ -559,6 +560,10 @@ typedef struct MIPSOcteonCryptoState {
uint16_t gfm_poly;
uint8_t aes_keylen;
uint8_t crc_len;
uint64_t chord;
uint64_t llm_data[2];
QTree *llm36;
QTree *llm64;
} MIPSOcteonCryptoState;
typedef struct CPUArchState {

View File

@@ -74,6 +74,14 @@ DEF_HELPER_2(octeon_cp2_mt_hsh_startmd5, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_hsh_startsha256, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_hsh_startsha, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_hsh_startsha512, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_llm_read_addr0, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_llm_write_addr0, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_llm_read64_addr0, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_llm_write64_addr0, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_llm_read_addr1, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_llm_write_addr1, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_llm_read64_addr1, void, env, i64)
DEF_HELPER_2(octeon_cp2_mt_llm_write64_addr1, void, env, i64)
/* microMIPS functions */
DEF_HELPER_4(lwm, void, env, tl, tl, i32)
@@ -245,6 +253,7 @@ DEF_HELPER_1(rdhwr_cc, tl, env)
DEF_HELPER_1(rdhwr_ccres, tl, env)
DEF_HELPER_1(rdhwr_performance, tl, env)
DEF_HELPER_1(rdhwr_xnp, tl, env)
DEF_HELPER_1(rdhwr_chord, tl, env)
DEF_HELPER_2(pmon, void, env, int)
DEF_HELPER_1(wait, void, env)

View File

@@ -93,6 +93,9 @@ extern const int mips_defs_number;
int mips_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
QTree *mips_octeon_llm_tree_new(void);
uint64_t mips_octeon_llm_load(QTree *tree, uint64_t addr);
void mips_octeon_llm_store(QTree **treep, uint64_t addr, uint64_t value);
#define USEG_LIMIT ((target_ulong)(int32_t)0x7FFFFFFFUL)
#define KSEG0_BASE ((target_ulong)(int32_t)0x80000000UL)

View File

@@ -131,6 +131,69 @@ static const VMStateDescription vmstate_octeon_multiplier_tc = {
}
};
typedef struct OcteonLLMTreePutData {
QEMUFile *f;
} OcteonLLMTreePutData;
static gboolean put_octeon_llm_tree_entry(gpointer key, gpointer value,
gpointer user_data)
{
OcteonLLMTreePutData *data = user_data;
qemu_put_be64(data->f, *(uint64_t *)key);
qemu_put_be64(data->f, *(uint64_t *)value);
return false;
}
static int put_octeon_llm_tree(QEMUFile *f, void *pv, size_t size,
const VMStateField *field, JSONWriter *vmdesc)
{
QTree *tree = *(QTree **)pv;
OcteonLLMTreePutData data = { .f = f };
uint32_t nnodes = tree ? q_tree_nnodes(tree) : 0;
qemu_put_be32(f, nnodes);
if (tree) {
q_tree_foreach(tree, put_octeon_llm_tree_entry, &data);
}
return 0;
}
static int get_octeon_llm_tree(QEMUFile *f, void *pv, size_t size,
const VMStateField *field)
{
QTree **treep = pv;
uint32_t nnodes = qemu_get_be32(f);
if (*treep) {
q_tree_destroy(*treep);
}
*treep = mips_octeon_llm_tree_new();
for (uint32_t i = 0; i < nnodes; i++) {
uint64_t addr = qemu_get_be64(f);
uint64_t value = qemu_get_be64(f);
mips_octeon_llm_store(treep, addr, value);
}
return 0;
}
static const VMStateInfo vmstate_info_octeon_llm_tree = {
.name = "octeon_llm_tree",
.get = get_octeon_llm_tree,
.put = put_octeon_llm_tree,
};
#define VMSTATE_OCTEON_LLM_TREE(_f, _s) { \
.name = stringify(_f), \
.version_id = 1, \
.info = &vmstate_info_octeon_llm_tree, \
.offset = vmstate_offset_pointer(_s, _f, QTree), \
}
/* MVP state */
static const VMStateDescription vmstate_mvp = {
@@ -301,6 +364,10 @@ static const VMStateDescription mips_vmstate_octeon_crypto = {
VMSTATE_UINT16(env.octeon_crypto.gfm_poly, MIPSCPU),
VMSTATE_UINT8(env.octeon_crypto.aes_keylen, MIPSCPU),
VMSTATE_UINT8(env.octeon_crypto.crc_len, MIPSCPU),
VMSTATE_UINT64(env.octeon_crypto.chord, MIPSCPU),
VMSTATE_UINT64_ARRAY(env.octeon_crypto.llm_data, MIPSCPU, 2),
VMSTATE_OCTEON_LLM_TREE(env.octeon_crypto.llm36, MIPSCPU),
VMSTATE_OCTEON_LLM_TREE(env.octeon_crypto.llm64, MIPSCPU),
VMSTATE_END_OF_LIST()
}
};

View File

@@ -16,6 +16,42 @@
#include "qemu/bitops.h"
#include "qemu/host-utils.h"
#define OCTEON_LLM_NARROW_MASK ((1ULL << 36) - 1)
static uint64_t octeon_llm_pack_narrow(uint64_t value)
{
value &= OCTEON_LLM_NARROW_MASK;
return value | ((uint64_t)(ctpop64(value) & 1) << 36);
}
static void octeon_llm_read(MIPSOcteonCryptoState *crypto, unsigned int set,
uint64_t addr, bool wide)
{
uint64_t value;
if (wide) {
value = mips_octeon_llm_load(crypto->llm64, addr);
} else {
value = octeon_llm_pack_narrow(
mips_octeon_llm_load(crypto->llm36, addr));
}
crypto->llm_data[set] = value;
}
static void octeon_llm_write(MIPSOcteonCryptoState *crypto, unsigned int set,
uint64_t addr, bool wide)
{
uint64_t value = crypto->llm_data[set];
if (wide) {
mips_octeon_llm_store(&crypto->llm64, addr, value);
} else {
mips_octeon_llm_store(&crypto->llm36, addr,
value & OCTEON_LLM_NARROW_MASK);
}
}
static uint32_t octeon_crc_reflect32_by_byte(uint32_t v)
{
return bswap32(revbit32(v));
@@ -2225,3 +2261,43 @@ void helper_octeon_cp2_mt_crc_write_var_reflect(CPUMIPSState *env,
octeon_crc_update_reflect(crypto, value, MIN(8U, crypto->crc_len & 0xf));
}
void helper_octeon_cp2_mt_llm_read_addr0(CPUMIPSState *env, uint64_t value)
{
octeon_llm_read(&env->octeon_crypto, 0, value, false);
}
void helper_octeon_cp2_mt_llm_write_addr0(CPUMIPSState *env, uint64_t value)
{
octeon_llm_write(&env->octeon_crypto, 0, value, false);
}
void helper_octeon_cp2_mt_llm_read64_addr0(CPUMIPSState *env, uint64_t value)
{
octeon_llm_read(&env->octeon_crypto, 0, value, true);
}
void helper_octeon_cp2_mt_llm_write64_addr0(CPUMIPSState *env, uint64_t value)
{
octeon_llm_write(&env->octeon_crypto, 0, value, true);
}
void helper_octeon_cp2_mt_llm_read_addr1(CPUMIPSState *env, uint64_t value)
{
octeon_llm_read(&env->octeon_crypto, 1, value, false);
}
void helper_octeon_cp2_mt_llm_write_addr1(CPUMIPSState *env, uint64_t value)
{
octeon_llm_write(&env->octeon_crypto, 1, value, false);
}
void helper_octeon_cp2_mt_llm_read64_addr1(CPUMIPSState *env, uint64_t value)
{
octeon_llm_read(&env->octeon_crypto, 1, value, true);
}
void helper_octeon_cp2_mt_llm_write64_addr1(CPUMIPSState *env, uint64_t value)
{
octeon_llm_write(&env->octeon_crypto, 1, value, true);
}

View File

@@ -255,6 +255,12 @@ target_ulong helper_rdhwr_xnp(CPUMIPSState *env)
return (env->CP0_Config5 >> CP0C5_XNP) & 1;
}
target_ulong helper_rdhwr_chord(CPUMIPSState *env)
{
check_hwrena(env, 30, GETPC());
return env->octeon_crypto.chord;
}
void helper_pmon(CPUMIPSState *env, int function)
{
function /= 2;

View File

@@ -10925,6 +10925,14 @@ void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel)
}
break;
#endif
case 30:
if (!(ctx->insn_flags & INSN_OCTEON)) {
gen_reserved_instruction(ctx);
break;
}
gen_helper_rdhwr_chord(t0, tcg_env);
gen_store_gpr(t0, rt);
break;
default: /* Invalid */
MIPS_INVAL("rdhwr");
gen_reserved_instruction(ctx);