162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012,2013 - ARM Ltd 462306a36Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Derived from arch/arm/kvm/coproc.h 762306a36Sopenharmony_ci * Copyright (C) 2012 - Virtual Open Systems and Columbia University 862306a36Sopenharmony_ci * Authors: Christoffer Dall <c.dall@virtualopensystems.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#ifndef __ARM64_KVM_SYS_REGS_LOCAL_H__ 1262306a36Sopenharmony_ci#define __ARM64_KVM_SYS_REGS_LOCAL_H__ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/bsearch.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define reg_to_encoding(x) \ 1762306a36Sopenharmony_ci sys_reg((u32)(x)->Op0, (u32)(x)->Op1, \ 1862306a36Sopenharmony_ci (u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct sys_reg_params { 2162306a36Sopenharmony_ci u8 Op0; 2262306a36Sopenharmony_ci u8 Op1; 2362306a36Sopenharmony_ci u8 CRn; 2462306a36Sopenharmony_ci u8 CRm; 2562306a36Sopenharmony_ci u8 Op2; 2662306a36Sopenharmony_ci u64 regval; 2762306a36Sopenharmony_ci bool is_write; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define encoding_to_params(reg) \ 3162306a36Sopenharmony_ci ((struct sys_reg_params){ .Op0 = sys_reg_Op0(reg), \ 3262306a36Sopenharmony_ci .Op1 = sys_reg_Op1(reg), \ 3362306a36Sopenharmony_ci .CRn = sys_reg_CRn(reg), \ 3462306a36Sopenharmony_ci .CRm = sys_reg_CRm(reg), \ 3562306a36Sopenharmony_ci .Op2 = sys_reg_Op2(reg) }) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define esr_sys64_to_params(esr) \ 3862306a36Sopenharmony_ci ((struct sys_reg_params){ .Op0 = ((esr) >> 20) & 3, \ 3962306a36Sopenharmony_ci .Op1 = ((esr) >> 14) & 0x7, \ 4062306a36Sopenharmony_ci .CRn = ((esr) >> 10) & 0xf, \ 4162306a36Sopenharmony_ci .CRm = ((esr) >> 1) & 0xf, \ 4262306a36Sopenharmony_ci .Op2 = ((esr) >> 17) & 0x7, \ 4362306a36Sopenharmony_ci .is_write = !((esr) & 1) }) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define esr_cp1x_32_to_params(esr) \ 4662306a36Sopenharmony_ci ((struct sys_reg_params){ .Op1 = ((esr) >> 14) & 0x7, \ 4762306a36Sopenharmony_ci .CRn = ((esr) >> 10) & 0xf, \ 4862306a36Sopenharmony_ci .CRm = ((esr) >> 1) & 0xf, \ 4962306a36Sopenharmony_ci .Op2 = ((esr) >> 17) & 0x7, \ 5062306a36Sopenharmony_ci .is_write = !((esr) & 1) }) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct sys_reg_desc { 5362306a36Sopenharmony_ci /* Sysreg string for debug */ 5462306a36Sopenharmony_ci const char *name; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci enum { 5762306a36Sopenharmony_ci AA32_DIRECT, 5862306a36Sopenharmony_ci AA32_LO, 5962306a36Sopenharmony_ci AA32_HI, 6062306a36Sopenharmony_ci } aarch32_map; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* MRS/MSR instruction which accesses it. */ 6362306a36Sopenharmony_ci u8 Op0; 6462306a36Sopenharmony_ci u8 Op1; 6562306a36Sopenharmony_ci u8 CRn; 6662306a36Sopenharmony_ci u8 CRm; 6762306a36Sopenharmony_ci u8 Op2; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Trapped access from guest, if non-NULL. */ 7062306a36Sopenharmony_ci bool (*access)(struct kvm_vcpu *, 7162306a36Sopenharmony_ci struct sys_reg_params *, 7262306a36Sopenharmony_ci const struct sys_reg_desc *); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* 7562306a36Sopenharmony_ci * Initialization for vcpu. Return initialized value, or KVM 7662306a36Sopenharmony_ci * sanitized value for ID registers. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci u64 (*reset)(struct kvm_vcpu *, const struct sys_reg_desc *); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* Index into sys_reg[], or 0 if we don't need to save it. */ 8162306a36Sopenharmony_ci int reg; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Value (usually reset value), or write mask for idregs */ 8462306a36Sopenharmony_ci u64 val; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Custom get/set_user functions, fallback to generic if NULL */ 8762306a36Sopenharmony_ci int (*get_user)(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, 8862306a36Sopenharmony_ci u64 *val); 8962306a36Sopenharmony_ci int (*set_user)(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, 9062306a36Sopenharmony_ci u64 val); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Return mask of REG_* runtime visibility overrides */ 9362306a36Sopenharmony_ci unsigned int (*visibility)(const struct kvm_vcpu *vcpu, 9462306a36Sopenharmony_ci const struct sys_reg_desc *rd); 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define REG_HIDDEN (1 << 0) /* hidden from userspace and guest */ 9862306a36Sopenharmony_ci#define REG_HIDDEN_USER (1 << 1) /* hidden from userspace only */ 9962306a36Sopenharmony_ci#define REG_RAZ (1 << 2) /* RAZ from userspace and guest */ 10062306a36Sopenharmony_ci#define REG_USER_WI (1 << 3) /* WI from userspace only */ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic __printf(2, 3) 10362306a36Sopenharmony_ciinline void print_sys_reg_msg(const struct sys_reg_params *p, 10462306a36Sopenharmony_ci char *fmt, ...) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci va_list va; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci va_start(va, fmt); 10962306a36Sopenharmony_ci /* Look, we even formatted it for you to paste into the table! */ 11062306a36Sopenharmony_ci kvm_pr_unimpl("%pV { Op0(%2u), Op1(%2u), CRn(%2u), CRm(%2u), Op2(%2u), func_%s },\n", 11162306a36Sopenharmony_ci &(struct va_format){ fmt, &va }, 11262306a36Sopenharmony_ci p->Op0, p->Op1, p->CRn, p->CRm, p->Op2, p->is_write ? "write" : "read"); 11362306a36Sopenharmony_ci va_end(va); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic inline void print_sys_reg_instr(const struct sys_reg_params *p) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci /* GCC warns on an empty format string */ 11962306a36Sopenharmony_ci print_sys_reg_msg(p, "%s", ""); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic inline bool ignore_write(struct kvm_vcpu *vcpu, 12362306a36Sopenharmony_ci const struct sys_reg_params *p) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return true; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic inline bool read_zero(struct kvm_vcpu *vcpu, 12962306a36Sopenharmony_ci struct sys_reg_params *p) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci p->regval = 0; 13262306a36Sopenharmony_ci return true; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Reset functions */ 13662306a36Sopenharmony_cistatic inline u64 reset_unknown(struct kvm_vcpu *vcpu, 13762306a36Sopenharmony_ci const struct sys_reg_desc *r) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci BUG_ON(!r->reg); 14062306a36Sopenharmony_ci BUG_ON(r->reg >= NR_SYS_REGS); 14162306a36Sopenharmony_ci __vcpu_sys_reg(vcpu, r->reg) = 0x1de7ec7edbadc0deULL; 14262306a36Sopenharmony_ci return __vcpu_sys_reg(vcpu, r->reg); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic inline u64 reset_val(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci BUG_ON(!r->reg); 14862306a36Sopenharmony_ci BUG_ON(r->reg >= NR_SYS_REGS); 14962306a36Sopenharmony_ci __vcpu_sys_reg(vcpu, r->reg) = r->val; 15062306a36Sopenharmony_ci return __vcpu_sys_reg(vcpu, r->reg); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic inline unsigned int sysreg_visibility(const struct kvm_vcpu *vcpu, 15462306a36Sopenharmony_ci const struct sys_reg_desc *r) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci if (likely(!r->visibility)) 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return r->visibility(vcpu, r); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic inline bool sysreg_hidden(const struct kvm_vcpu *vcpu, 16362306a36Sopenharmony_ci const struct sys_reg_desc *r) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci return sysreg_visibility(vcpu, r) & REG_HIDDEN; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic inline bool sysreg_hidden_user(const struct kvm_vcpu *vcpu, 16962306a36Sopenharmony_ci const struct sys_reg_desc *r) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci if (likely(!r->visibility)) 17262306a36Sopenharmony_ci return false; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return r->visibility(vcpu, r) & (REG_HIDDEN | REG_HIDDEN_USER); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic inline bool sysreg_visible_as_raz(const struct kvm_vcpu *vcpu, 17862306a36Sopenharmony_ci const struct sys_reg_desc *r) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci return sysreg_visibility(vcpu, r) & REG_RAZ; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic inline bool sysreg_user_write_ignore(const struct kvm_vcpu *vcpu, 18462306a36Sopenharmony_ci const struct sys_reg_desc *r) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci return sysreg_visibility(vcpu, r) & REG_USER_WI; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic inline int cmp_sys_reg(const struct sys_reg_desc *i1, 19062306a36Sopenharmony_ci const struct sys_reg_desc *i2) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci BUG_ON(i1 == i2); 19362306a36Sopenharmony_ci if (!i1) 19462306a36Sopenharmony_ci return 1; 19562306a36Sopenharmony_ci else if (!i2) 19662306a36Sopenharmony_ci return -1; 19762306a36Sopenharmony_ci if (i1->Op0 != i2->Op0) 19862306a36Sopenharmony_ci return i1->Op0 - i2->Op0; 19962306a36Sopenharmony_ci if (i1->Op1 != i2->Op1) 20062306a36Sopenharmony_ci return i1->Op1 - i2->Op1; 20162306a36Sopenharmony_ci if (i1->CRn != i2->CRn) 20262306a36Sopenharmony_ci return i1->CRn - i2->CRn; 20362306a36Sopenharmony_ci if (i1->CRm != i2->CRm) 20462306a36Sopenharmony_ci return i1->CRm - i2->CRm; 20562306a36Sopenharmony_ci return i1->Op2 - i2->Op2; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic inline int match_sys_reg(const void *key, const void *elt) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci const unsigned long pval = (unsigned long)key; 21162306a36Sopenharmony_ci const struct sys_reg_desc *r = elt; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return pval - reg_to_encoding(r); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic inline const struct sys_reg_desc * 21762306a36Sopenharmony_cifind_reg(const struct sys_reg_params *params, const struct sys_reg_desc table[], 21862306a36Sopenharmony_ci unsigned int num) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci unsigned long pval = reg_to_encoding(params); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return __inline_bsearch((void *)pval, table, num, sizeof(table[0]), match_sys_reg); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ciconst struct sys_reg_desc *get_reg_by_id(u64 id, 22662306a36Sopenharmony_ci const struct sys_reg_desc table[], 22762306a36Sopenharmony_ci unsigned int num); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ciint kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); 23062306a36Sopenharmony_ciint kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); 23162306a36Sopenharmony_ciint kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg, 23262306a36Sopenharmony_ci const struct sys_reg_desc table[], unsigned int num); 23362306a36Sopenharmony_ciint kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg, 23462306a36Sopenharmony_ci const struct sys_reg_desc table[], unsigned int num); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci#define AA32(_x) .aarch32_map = AA32_##_x 23762306a36Sopenharmony_ci#define Op0(_x) .Op0 = _x 23862306a36Sopenharmony_ci#define Op1(_x) .Op1 = _x 23962306a36Sopenharmony_ci#define CRn(_x) .CRn = _x 24062306a36Sopenharmony_ci#define CRm(_x) .CRm = _x 24162306a36Sopenharmony_ci#define Op2(_x) .Op2 = _x 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci#define SYS_DESC(reg) \ 24462306a36Sopenharmony_ci .name = #reg, \ 24562306a36Sopenharmony_ci Op0(sys_reg_Op0(reg)), Op1(sys_reg_Op1(reg)), \ 24662306a36Sopenharmony_ci CRn(sys_reg_CRn(reg)), CRm(sys_reg_CRm(reg)), \ 24762306a36Sopenharmony_ci Op2(sys_reg_Op2(reg)) 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci#endif /* __ARM64_KVM_SYS_REGS_LOCAL_H__ */ 250