David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 2 | /* |
| 3 | * Copyright (C) 2012,2013 - ARM Ltd |
| 4 | * Author: Marc Zyngier <marc.zyngier@arm.com> |
| 5 | * |
| 6 | * Derived from arch/arm/kvm/coproc.h |
| 7 | * Copyright (C) 2012 - Virtual Open Systems and Columbia University |
| 8 | * Authors: Christoffer Dall <c.dall@virtualopensystems.com> |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 9 | */ |
| 10 | |
| 11 | #ifndef __ARM64_KVM_SYS_REGS_LOCAL_H__ |
| 12 | #define __ARM64_KVM_SYS_REGS_LOCAL_H__ |
| 13 | |
| 14 | struct sys_reg_params { |
| 15 | u8 Op0; |
| 16 | u8 Op1; |
| 17 | u8 CRn; |
| 18 | u8 CRm; |
| 19 | u8 Op2; |
| 20 | u64 regval; |
| 21 | bool is_write; |
| 22 | bool is_aarch32; |
| 23 | bool is_32bit; /* Only valid if is_aarch32 is true */ |
| 24 | }; |
| 25 | |
| 26 | struct sys_reg_desc { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 27 | /* Sysreg string for debug */ |
| 28 | const char *name; |
| 29 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 30 | /* MRS/MSR instruction which accesses it. */ |
| 31 | u8 Op0; |
| 32 | u8 Op1; |
| 33 | u8 CRn; |
| 34 | u8 CRm; |
| 35 | u8 Op2; |
| 36 | |
| 37 | /* Trapped access from guest, if non-NULL. */ |
| 38 | bool (*access)(struct kvm_vcpu *, |
| 39 | struct sys_reg_params *, |
| 40 | const struct sys_reg_desc *); |
| 41 | |
| 42 | /* Initialization for vcpu. */ |
| 43 | void (*reset)(struct kvm_vcpu *, const struct sys_reg_desc *); |
| 44 | |
| 45 | /* Index into sys_reg[], or 0 if we don't need to save it. */ |
| 46 | int reg; |
| 47 | |
| 48 | /* Value (usually reset value) */ |
| 49 | u64 val; |
| 50 | |
| 51 | /* Custom get/set_user functions, fallback to generic if NULL */ |
| 52 | int (*get_user)(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, |
| 53 | const struct kvm_one_reg *reg, void __user *uaddr); |
| 54 | int (*set_user)(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, |
| 55 | const struct kvm_one_reg *reg, void __user *uaddr); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 56 | |
| 57 | /* Return mask of REG_* runtime visibility overrides */ |
| 58 | unsigned int (*visibility)(const struct kvm_vcpu *vcpu, |
| 59 | const struct sys_reg_desc *rd); |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 60 | }; |
| 61 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 62 | #define REG_HIDDEN_USER (1 << 0) /* hidden from userspace ioctls */ |
| 63 | #define REG_HIDDEN_GUEST (1 << 1) /* hidden from guest */ |
| 64 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 65 | static inline void print_sys_reg_instr(const struct sys_reg_params *p) |
| 66 | { |
| 67 | /* Look, we even formatted it for you to paste into the table! */ |
| 68 | kvm_pr_unimpl(" { Op0(%2u), Op1(%2u), CRn(%2u), CRm(%2u), Op2(%2u), func_%s },\n", |
| 69 | p->Op0, p->Op1, p->CRn, p->CRm, p->Op2, p->is_write ? "write" : "read"); |
| 70 | } |
| 71 | |
| 72 | static inline bool ignore_write(struct kvm_vcpu *vcpu, |
| 73 | const struct sys_reg_params *p) |
| 74 | { |
| 75 | return true; |
| 76 | } |
| 77 | |
| 78 | static inline bool read_zero(struct kvm_vcpu *vcpu, |
| 79 | struct sys_reg_params *p) |
| 80 | { |
| 81 | p->regval = 0; |
| 82 | return true; |
| 83 | } |
| 84 | |
| 85 | /* Reset functions */ |
| 86 | static inline void reset_unknown(struct kvm_vcpu *vcpu, |
| 87 | const struct sys_reg_desc *r) |
| 88 | { |
| 89 | BUG_ON(!r->reg); |
| 90 | BUG_ON(r->reg >= NR_SYS_REGS); |
| 91 | __vcpu_sys_reg(vcpu, r->reg) = 0x1de7ec7edbadc0deULL; |
| 92 | } |
| 93 | |
| 94 | static inline void reset_val(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) |
| 95 | { |
| 96 | BUG_ON(!r->reg); |
| 97 | BUG_ON(r->reg >= NR_SYS_REGS); |
| 98 | __vcpu_sys_reg(vcpu, r->reg) = r->val; |
| 99 | } |
| 100 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 101 | static inline bool sysreg_hidden_from_guest(const struct kvm_vcpu *vcpu, |
| 102 | const struct sys_reg_desc *r) |
| 103 | { |
| 104 | if (likely(!r->visibility)) |
| 105 | return false; |
| 106 | |
| 107 | return r->visibility(vcpu, r) & REG_HIDDEN_GUEST; |
| 108 | } |
| 109 | |
| 110 | static inline bool sysreg_hidden_from_user(const struct kvm_vcpu *vcpu, |
| 111 | const struct sys_reg_desc *r) |
| 112 | { |
| 113 | if (likely(!r->visibility)) |
| 114 | return false; |
| 115 | |
| 116 | return r->visibility(vcpu, r) & REG_HIDDEN_USER; |
| 117 | } |
| 118 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 119 | static inline int cmp_sys_reg(const struct sys_reg_desc *i1, |
| 120 | const struct sys_reg_desc *i2) |
| 121 | { |
| 122 | BUG_ON(i1 == i2); |
| 123 | if (!i1) |
| 124 | return 1; |
| 125 | else if (!i2) |
| 126 | return -1; |
| 127 | if (i1->Op0 != i2->Op0) |
| 128 | return i1->Op0 - i2->Op0; |
| 129 | if (i1->Op1 != i2->Op1) |
| 130 | return i1->Op1 - i2->Op1; |
| 131 | if (i1->CRn != i2->CRn) |
| 132 | return i1->CRn - i2->CRn; |
| 133 | if (i1->CRm != i2->CRm) |
| 134 | return i1->CRm - i2->CRm; |
| 135 | return i1->Op2 - i2->Op2; |
| 136 | } |
| 137 | |
| 138 | const struct sys_reg_desc *find_reg_by_id(u64 id, |
| 139 | struct sys_reg_params *params, |
| 140 | const struct sys_reg_desc table[], |
| 141 | unsigned int num); |
| 142 | |
| 143 | #define Op0(_x) .Op0 = _x |
| 144 | #define Op1(_x) .Op1 = _x |
| 145 | #define CRn(_x) .CRn = _x |
| 146 | #define CRm(_x) .CRm = _x |
| 147 | #define Op2(_x) .Op2 = _x |
| 148 | |
| 149 | #define SYS_DESC(reg) \ |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 150 | .name = #reg, \ |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 151 | Op0(sys_reg_Op0(reg)), Op1(sys_reg_Op1(reg)), \ |
| 152 | CRn(sys_reg_CRn(reg)), CRm(sys_reg_CRm(reg)), \ |
| 153 | Op2(sys_reg_Op2(reg)) |
| 154 | |
| 155 | #endif /* __ARM64_KVM_SYS_REGS_LOCAL_H__ */ |