| /* |
| * Copyright (c) 2010, 2012-2013, 2016 ARM Limited |
| * All rights reserved |
| * |
| * The license below extends only to copyright in the software and shall |
| * not be construed as granting a license to any other intellectual |
| * property including but not limited to intellectual property relating |
| * to a hardware implementation of the functionality of the software |
| * licensed hereunder. You may use the software subject to the license |
| * terms below provided that you ensure that this notice is replicated |
| * unmodified and in its entirety in all distributions of the software, |
| * modified or unmodified, in source code or in binary form. |
| * |
| * Copyright (c) 2006 The Regents of The University of Michigan |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef __ARCH_ARM_INTERRUPT_HH__ |
| #define __ARCH_ARM_INTERRUPT_HH__ |
| |
| #include "arch/arm/faults.hh" |
| #include "arch/arm/isa_traits.hh" |
| #include "arch/arm/miscregs.hh" |
| #include "arch/arm/registers.hh" |
| #include "arch/arm/utility.hh" |
| #include "arch/generic/interrupts.hh" |
| #include "cpu/thread_context.hh" |
| #include "debug/Interrupt.hh" |
| #include "params/ArmInterrupts.hh" |
| |
| namespace ArmISA |
| { |
| |
| class Interrupts : public BaseInterrupts |
| { |
| private: |
| BaseCPU * cpu; |
| |
| bool interrupts[NumInterruptTypes]; |
| uint64_t intStatus; |
| |
| public: |
| |
| void |
| setCPU(BaseCPU * _cpu) |
| { |
| cpu = _cpu; |
| } |
| |
| typedef ArmInterruptsParams Params; |
| |
| const Params * |
| params() const |
| { |
| return dynamic_cast<const Params *>(_params); |
| } |
| |
| Interrupts(Params * p) : BaseInterrupts(p), cpu(NULL) |
| { |
| clearAll(); |
| } |
| |
| |
| void |
| post(int int_num, int index) |
| { |
| DPRINTF(Interrupt, "Interrupt %d:%d posted\n", int_num, index); |
| |
| if (int_num < 0 || int_num >= NumInterruptTypes) |
| panic("int_num out of bounds\n"); |
| |
| if (index != 0) |
| panic("No support for other interrupt indexes\n"); |
| |
| interrupts[int_num] = true; |
| intStatus |= ULL(1) << int_num; |
| } |
| |
| void |
| clear(int int_num, int index) |
| { |
| DPRINTF(Interrupt, "Interrupt %d:%d cleared\n", int_num, index); |
| |
| if (int_num < 0 || int_num >= NumInterruptTypes) |
| panic("int_num out of bounds\n"); |
| |
| if (index != 0) |
| panic("No support for other interrupt indexes\n"); |
| |
| interrupts[int_num] = false; |
| intStatus &= ~(ULL(1) << int_num); |
| } |
| |
| void |
| clearAll() |
| { |
| DPRINTF(Interrupt, "Interrupts all cleared\n"); |
| intStatus = 0; |
| memset(interrupts, 0, sizeof(interrupts)); |
| } |
| |
| enum InterruptMask { |
| INT_MASK_M, // masked (subject to PSTATE.{A,I,F} mask bit |
| INT_MASK_T, // taken regardless of mask |
| INT_MASK_P // pending |
| }; |
| |
| bool takeInt(ThreadContext *tc, InterruptTypes int_type) const; |
| |
| bool |
| checkInterrupts(ThreadContext *tc) const |
| { |
| HCR hcr = tc->readMiscReg(MISCREG_HCR); |
| |
| if (!(intStatus || hcr.va || hcr.vi || hcr.vf)) |
| return false; |
| |
| CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); |
| |
| bool isHypMode = currEL(tc) == EL2; |
| bool isSecure = inSecureState(tc); |
| bool allowVIrq = !cpsr.i && hcr.imo && !isSecure && !isHypMode; |
| bool allowVFiq = !cpsr.f && hcr.fmo && !isSecure && !isHypMode; |
| bool allowVAbort = !cpsr.a && hcr.amo && !isSecure && !isHypMode; |
| |
| if ( !(intStatus || (hcr.vi && allowVIrq) || (hcr.vf && allowVFiq) || |
| (hcr.va && allowVAbort)) ) |
| return false; |
| |
| bool take_irq = takeInt(tc, INT_IRQ); |
| bool take_fiq = takeInt(tc, INT_FIQ); |
| bool take_ea = takeInt(tc, INT_ABT); |
| |
| return ((interrupts[INT_IRQ] && take_irq) || |
| (interrupts[INT_FIQ] && take_fiq) || |
| (interrupts[INT_ABT] && take_ea) || |
| ((interrupts[INT_VIRT_IRQ] || hcr.vi) && allowVIrq) || |
| ((interrupts[INT_VIRT_FIQ] || hcr.vf) && allowVFiq) || |
| (hcr.va && allowVAbort) || |
| (interrupts[INT_RST]) || |
| (interrupts[INT_SEV]) |
| ); |
| } |
| |
| /** |
| * This function is used to check if a wfi operation should sleep. If there |
| * is an interrupt pending, even if it's masked, wfi doesn't sleep. |
| * @return any interrupts pending |
| */ |
| bool |
| checkWfiWake(HCR hcr, CPSR cpsr, SCR scr) const |
| { |
| uint64_t maskedIntStatus; |
| bool virtWake; |
| |
| maskedIntStatus = intStatus & ~((1 << INT_VIRT_IRQ) | |
| (1 << INT_VIRT_FIQ)); |
| virtWake = (hcr.vi || interrupts[INT_VIRT_IRQ]) && hcr.imo; |
| virtWake |= (hcr.vf || interrupts[INT_VIRT_FIQ]) && hcr.fmo; |
| virtWake |= hcr.va && hcr.amo; |
| virtWake &= (cpsr.mode != MODE_HYP) && !inSecureState(scr, cpsr); |
| return maskedIntStatus || virtWake; |
| } |
| |
| uint32_t |
| getISR(HCR hcr, CPSR cpsr, SCR scr) |
| { |
| bool useHcrMux; |
| CPSR isr = 0; // ARM ARM states ISR reg uses same bit possitions as CPSR |
| |
| useHcrMux = (cpsr.mode != MODE_HYP) && !inSecureState(scr, cpsr); |
| isr.i = (useHcrMux & hcr.imo) ? (interrupts[INT_VIRT_IRQ] || hcr.vi) |
| : interrupts[INT_IRQ]; |
| isr.f = (useHcrMux & hcr.fmo) ? (interrupts[INT_VIRT_FIQ] || hcr.vf) |
| : interrupts[INT_FIQ]; |
| isr.a = (useHcrMux & hcr.amo) ? hcr.va : interrupts[INT_ABT]; |
| return isr; |
| } |
| |
| /** |
| * Check the state of a particular interrupt, ignoring CPSR masks. |
| * |
| * This method is primarily used when running the target CPU in a |
| * hardware VM (e.g., KVM) to check if interrupts should be |
| * delivered upon guest entry. |
| * |
| * @param interrupt Interrupt type to check the state of. |
| * @return true if the interrupt is asserted, false otherwise. |
| */ |
| bool |
| checkRaw(InterruptTypes interrupt) const |
| { |
| if (interrupt >= NumInterruptTypes) |
| panic("Interrupt number out of range.\n"); |
| |
| return interrupts[interrupt]; |
| } |
| |
| Fault |
| getInterrupt(ThreadContext *tc) |
| { |
| assert(checkInterrupts(tc)); |
| |
| HCR hcr = tc->readMiscReg(MISCREG_HCR); |
| CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); |
| |
| // Calculate a few temp vars so we can work out if there's a pending |
| // virtual interrupt, and if its allowed to happen |
| // ARM ARM Issue C section B1.9.9, B1.9.11, and B1.9.13 |
| bool isHypMode = currEL(tc) == EL2; |
| bool isSecure = inSecureState(tc); |
| bool allowVIrq = !cpsr.i && hcr.imo && !isSecure && !isHypMode; |
| bool allowVFiq = !cpsr.f && hcr.fmo && !isSecure && !isHypMode; |
| bool allowVAbort = !cpsr.a && hcr.amo && !isSecure && !isHypMode; |
| |
| bool take_irq = takeInt(tc, INT_IRQ); |
| bool take_fiq = takeInt(tc, INT_FIQ); |
| bool take_ea = takeInt(tc, INT_ABT); |
| |
| if (interrupts[INT_IRQ] && take_irq) |
| return std::make_shared<Interrupt>(); |
| if ((interrupts[INT_VIRT_IRQ] || hcr.vi) && allowVIrq) |
| return std::make_shared<VirtualInterrupt>(); |
| if (interrupts[INT_FIQ] && take_fiq) |
| return std::make_shared<FastInterrupt>(); |
| if ((interrupts[INT_VIRT_FIQ] || hcr.vf) && allowVFiq) |
| return std::make_shared<VirtualFastInterrupt>(); |
| if (interrupts[INT_ABT] && take_ea) |
| return std::make_shared<SystemError>(); |
| if (hcr.va && allowVAbort) |
| return std::make_shared<VirtualDataAbort>( |
| 0, TlbEntry::DomainType::NoAccess, false, |
| ArmFault::AsynchronousExternalAbort); |
| if (interrupts[INT_RST]) |
| return std::make_shared<Reset>(); |
| if (interrupts[INT_SEV]) |
| return std::make_shared<ArmSev>(); |
| |
| panic("intStatus and interrupts not in sync\n"); |
| } |
| |
| void |
| updateIntrInfo(ThreadContext *tc) |
| { |
| ; // nothing to do |
| } |
| |
| void |
| serialize(CheckpointOut &cp) const |
| { |
| SERIALIZE_ARRAY(interrupts, NumInterruptTypes); |
| SERIALIZE_SCALAR(intStatus); |
| } |
| |
| void |
| unserialize(CheckpointIn &cp) |
| { |
| UNSERIALIZE_ARRAY(interrupts, NumInterruptTypes); |
| UNSERIALIZE_SCALAR(intStatus); |
| } |
| }; |
| } // namespace ARM_ISA |
| |
| #endif // __ARCH_ARM_INTERRUPT_HH__ |