| /* |
| * Copyright 2019 The Hafnium Authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "debug_el1.h" |
| |
| #include "hf/check.h" |
| #include "hf/dlog.h" |
| #include "hf/panic.h" |
| #include "hf/types.h" |
| |
| #include "msr.h" |
| |
| /** |
| * Controls traps for Trace Filter. |
| */ |
| #define MDCR_EL2_TTRF (1u << 19) |
| |
| /** |
| * Controls traps for Debug ROM. |
| */ |
| #define MDCR_EL2_TDRA (1u << 11) |
| |
| /** |
| * Controls traps for OS-Related Register Access. |
| */ |
| #define MDCR_EL2_TDOSA (1u << 10) |
| |
| /** |
| * Controls traps for remaining Debug Registers not trapped by TDRA and TDOSA. |
| */ |
| #define MDCR_EL2_TDA (1u << 9) |
| |
| /** |
| * Controls traps for all debug exceptions (e.g., breakpoints). |
| */ |
| #define MDCR_EL2_TDE (1u << 8) |
| |
| /** |
| * Controls traps for debug events, i.e., breakpoints, watchpoints, and vector. |
| * catch exceptions. |
| */ |
| #define MDSCR_EL1_MDE (1u << 15) |
| |
| /** |
| * System register are identified by op0, op2, op1, crn, crm. The ISS encoding |
| * includes also rt and direction. Exclude them, @see D13.2.37 (D13-2977). |
| */ |
| #define ISS_SYSREG_MASK \ |
| (((1u << 22) - 1u) & /* Select the ISS bits*/ \ |
| ~(0x1fu << 5) & /* exclude rt */ \ |
| ~1u /* exclude direction */) |
| |
| #define GET_ISS_SYSREG(esr) (ISS_SYSREG_MASK & (esr)) |
| |
| /** |
| * Op0 from the ISS encoding in the ESR. |
| */ |
| #define ISS_OP0_MASK 0x300000 |
| #define ISS_OP0_SHIFT 20 |
| #define GET_ISS_OP0(esr) ((ISS_OP0_MASK & (esr)) >> ISS_OP0_SHIFT) |
| |
| /** |
| * Op1 from the ISS encoding in the ESR. |
| */ |
| #define ISS_OP1_MASK 0x1c000 |
| #define ISS_OP1_SHIFT 14 |
| #define GET_ISS_OP1(esr) ((ISS_OP1_MASK & (esr)) >> ISS_OP1_SHIFT) |
| |
| /** |
| * Direction (i.e., read (1) or write (0), is the first bit in the ISS/ESR. |
| */ |
| #define ISS_DIRECTION_MASK 1u |
| |
| /** |
| * Gets the direction of the system register access, read (1) or write (0). |
| */ |
| #define GET_ISS_DIRECTION(esr) (ISS_DIRECTION_MASK & (esr)) |
| |
| /** |
| * True if the ISS encoded in the esr indicates a read of the system register. |
| */ |
| #define ISS_IS_READ(esr) (ISS_DIRECTION_MASK & (esr)) |
| |
| /** |
| * Rt, which identifies the general purpose register used for the operation. |
| */ |
| #define ISS_RT_MASK 0x3e0 |
| #define ISS_RT_SHIFT 5 |
| #define GET_ISS_RT(esr) ((ISS_RT_MASK & (esr)) >> ISS_RT_SHIFT) |
| |
| /** |
| * Definitions of read-only debug registers' ISS signatures. |
| */ |
| #define EL1_DEBUG_REGISTERS_READ \ |
| X(MDRAR_EL1, 0x200400) \ |
| X(DBGAUTHSTATUS_EL1, 0x2c1c1c) \ |
| X(OSLSR_EL1, 0x280402) |
| |
| /** |
| * Definitions of readable and writeable debug registers' ISS signatures. |
| */ |
| #define EL1_DEBUG_REGISTERS_READ_WRITE \ |
| X(DBGCLAIMCLR_EL1, 0x2c1c12) \ |
| X(DBGCLAIMSET_EL1, 0x2c1c10) \ |
| X(DBGPRCR_EL1, 0x280408) \ |
| X(MDCCINT_EL1, 0x200004) \ |
| X(MDSCR_EL1, 0x240004) \ |
| X(OSDLR_EL1, 0x280406) \ |
| X(OSDTRRX_EL1, 0x240000) \ |
| X(OSDTRTX_EL1, 0x240006) \ |
| X(OSECCR_EL1, 0x24000c) \ |
| X(DBGBCR0_EL1, 0x2a0000) \ |
| X(DBGBCR1_EL1, 0x2a0002) \ |
| X(DBGBCR2_EL1, 0x2a0004) \ |
| X(DBGBCR3_EL1, 0x2a0006) \ |
| X(DBGBCR4_EL1, 0x2a0008) \ |
| X(DBGBCR5_EL1, 0x2a000a) \ |
| X(DBGBCR6_EL1, 0x2a000c) \ |
| X(DBGBCR7_EL1, 0x2a000e) \ |
| X(DBGBCR8_EL1, 0x2a0010) \ |
| X(DBGBCR9_EL1, 0x2a0012) \ |
| X(DBGBCR10_EL1, 0x2a0014) \ |
| X(DBGBCR11_EL1, 0x2a0016) \ |
| X(DBGBCR12_EL1, 0x2a0018) \ |
| X(DBGBCR13_EL1, 0x2a001a) \ |
| X(DBGBCR14_EL1, 0x2a001c) \ |
| X(DBGBCR15_EL1, 0x2a001e) \ |
| X(DBGBVR0_EL1, 0x280000) \ |
| X(DBGBVR1_EL1, 0x280002) \ |
| X(DBGBVR2_EL1, 0x280004) \ |
| X(DBGBVR3_EL1, 0x280006) \ |
| X(DBGBVR4_EL1, 0x280008) \ |
| X(DBGBVR5_EL1, 0x28000a) \ |
| X(DBGBVR6_EL1, 0x28000c) \ |
| X(DBGBVR7_EL1, 0x28000e) \ |
| X(DBGBVR8_EL1, 0x280010) \ |
| X(DBGBVR9_EL1, 0x280012) \ |
| X(DBGBVR10_EL1, 0x280014) \ |
| X(DBGBVR11_EL1, 0x280016) \ |
| X(DBGBVR12_EL1, 0x280018) \ |
| X(DBGBVR13_EL1, 0x28001a) \ |
| X(DBGBVR14_EL1, 0x28001c) \ |
| X(DBGBVR15_EL1, 0x28001e) \ |
| X(DBGWCR0_EL1, 0x2e0000) \ |
| X(DBGWCR1_EL1, 0x2e0002) \ |
| X(DBGWCR2_EL1, 0x2e0004) \ |
| X(DBGWCR3_EL1, 0x2e0006) \ |
| X(DBGWCR4_EL1, 0x2e0008) \ |
| X(DBGWCR5_EL1, 0x2e000a) \ |
| X(DBGWCR6_EL1, 0x2e000c) \ |
| X(DBGWCR7_EL1, 0x2e000e) \ |
| X(DBGWCR8_EL1, 0x2e0010) \ |
| X(DBGWCR9_EL1, 0x2e0012) \ |
| X(DBGWCR10_EL1, 0x2e0014) \ |
| X(DBGWCR11_EL1, 0x2e0016) \ |
| X(DBGWCR12_EL1, 0x2e0018) \ |
| X(DBGWCR13_EL1, 0x2e001a) \ |
| X(DBGWCR14_EL1, 0x2e001c) \ |
| X(DBGWCR15_EL1, 0x2e001e) \ |
| X(DBGWVR0_EL1, 0x2c0000) \ |
| X(DBGWVR1_EL1, 0x2c0002) \ |
| X(DBGWVR2_EL1, 0x2c0004) \ |
| X(DBGWVR3_EL1, 0x2c0006) \ |
| X(DBGWVR4_EL1, 0x2c0008) \ |
| X(DBGWVR5_EL1, 0x2c000a) \ |
| X(DBGWVR6_EL1, 0x2c000c) \ |
| X(DBGWVR7_EL1, 0x2c000e) \ |
| X(DBGWVR8_EL1, 0x2c0010) \ |
| X(DBGWVR9_EL1, 0x2c0012) \ |
| X(DBGWVR10_EL1, 0x2c0014) \ |
| X(DBGWVR11_EL1, 0x2c0016) \ |
| X(DBGWVR12_EL1, 0x2c0018) \ |
| X(DBGWVR13_EL1, 0x2c001a) \ |
| X(DBGWVR14_EL1, 0x2c001c) \ |
| X(DBGWVR15_EL1, 0x2c001e) |
| |
| /** |
| * Definitions of all debug registers' ISS signatures. |
| */ |
| #define EL1_DEBUG_REGISTERS \ |
| EL1_DEBUG_REGISTERS_READ \ |
| EL1_DEBUG_REGISTERS_READ_WRITE |
| |
| /** |
| * Returns the value for mdcr_el2 for the particular VM. |
| * For now, the primary VM has one value and all secondary VMs share a value. |
| */ |
| uintreg_t get_mdcr_el2_value(spci_vm_id_t vm_id) |
| { |
| if (vm_id == HF_PRIMARY_VM_ID) { |
| /* |
| * Trap primary VM accesses to debug registers to have fine |
| * grained control over system register accesses. |
| * Do not trap the Primary VM's debug events (!MDCR_EL2_TDE). |
| */ |
| return MDCR_EL2_TTRF | MDCR_EL2_TDRA | MDCR_EL2_TDOSA | |
| MDCR_EL2_TDA; |
| } |
| |
| /* |
| * Trap all secondary VM debug register accesses as well as debug |
| * event exceptions. |
| * Debug event exceptions should be disabled in secondary VMs, but trap |
| * them for additional security (MDCR_EL2_TDE). |
| */ |
| return MDCR_EL2_TTRF | MDCR_EL2_TDRA | MDCR_EL2_TDOSA | MDCR_EL2_TDA | |
| MDCR_EL2_TDE; |
| } |
| |
| /** |
| * Returns true if the ESR register shows an access to an EL1 debug register. |
| */ |
| bool is_debug_el1_register_access(uintreg_t esr_el2) |
| { |
| /* |
| * Architecture Reference Manual D12.2: op0 == 0b10 is for debug and |
| * trace system registers. op1 = 0x1 for trace, remaining are debug. |
| */ |
| return GET_ISS_OP0(esr_el2) == 0x2 && GET_ISS_OP1(esr_el2) != 0x1; |
| } |
| |
| /** |
| * Processes an access (msr, mrs) to an EL1 debug register. |
| * Returns true if the access was allowed and performed, false otherwise. |
| */ |
| bool debug_el1_process_access(struct vcpu *vcpu, spci_vm_id_t vm_id, |
| uintreg_t esr_el2) |
| { |
| /* |
| * For now, debug registers are not supported by secondary VMs. |
| * Disallow accesses to them. |
| */ |
| if (vm_id != HF_PRIMARY_VM_ID) { |
| return false; |
| } |
| |
| uintreg_t sys_register = GET_ISS_SYSREG(esr_el2); |
| uintreg_t rt_register = GET_ISS_RT(esr_el2); |
| uintreg_t value; |
| |
| CHECK(rt_register < NUM_GP_REGS); |
| |
| if (ISS_IS_READ(esr_el2)) { |
| switch (sys_register) { |
| #define X(reg_name, reg_sig) \ |
| case reg_sig: \ |
| value = read_msr(reg_name); \ |
| break; |
| EL1_DEBUG_REGISTERS |
| #undef X |
| default: |
| value = vcpu->regs.r[rt_register]; |
| dlog("Unsupported system register read 0x%x\n", |
| sys_register); |
| break; |
| } |
| vcpu->regs.r[rt_register] = value; |
| |
| } else { |
| value = vcpu->regs.r[rt_register]; |
| switch (sys_register) { |
| #define X(reg_name, reg_sig) \ |
| case reg_sig: \ |
| write_msr(reg_name, value); \ |
| break; |
| EL1_DEBUG_REGISTERS_READ_WRITE |
| #undef X |
| default: |
| dlog("Unsupported system register write 0x%x\n", |
| sys_register); |
| break; |
| } |
| } |
| |
| return true; |
| } |