1 // Copyright 2017 The Fuchsia Authors 2 // 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file or at 5 // https://opensource.org/licenses/MIT 6 7 #pragma once 8 9 #include <bitmap/raw-bitmap.h> 10 #include <bitmap/storage.h> 11 #include <hypervisor/ktrace.h> 12 #include <hypervisor/state_invalidator.h> 13 #include <kernel/auto_lock.h> 14 #include <kernel/event.h> 15 #include <lib/ktrace.h> 16 17 namespace hypervisor { 18 19 enum class InterruptType { 20 INACTIVE, 21 VIRTUAL, 22 PHYSICAL 23 }; 24 static_assert( 25 static_cast<int>(InterruptType::INACTIVE) == 0 && 26 static_cast<int>(InterruptType::VIRTUAL) == 1 && 27 static_cast<int>(InterruptType::PHYSICAL) == 2, 28 "InterruptBitmap relies on these invariants."); 29 30 template <uint32_t N> 31 class InterruptBitmap { 32 public: Init()33 zx_status_t Init() { 34 return bitmap_.Reset(kNumBits); 35 } 36 Get(uint32_t vector)37 InterruptType Get(uint32_t vector) const { 38 if (vector >= N) { 39 DEBUG_ASSERT(false); 40 return InterruptType::INACTIVE; 41 } 42 size_t bitoff = vector * 2; 43 size_t first; 44 bool inactive = bitmap_.Scan(bitoff, bitoff + 2, false, &first); 45 if (inactive) { 46 return InterruptType::INACTIVE; 47 } 48 return bitoff == first ? InterruptType::VIRTUAL : InterruptType::PHYSICAL; 49 } 50 Set(uint32_t vector,InterruptType type)51 void Set(uint32_t vector, InterruptType type) { 52 if (vector >= N) { 53 DEBUG_ASSERT(false); 54 return; 55 } 56 size_t bitoff = vector * 2; 57 bitmap_.Clear(bitoff, bitoff + 2); 58 if (type != InterruptType::INACTIVE) { 59 auto state_bit = static_cast<size_t>(type) - 1; 60 bitmap_.SetOne(bitoff + state_bit); 61 } 62 } 63 Clear(uint32_t min,uint32_t max)64 void Clear(uint32_t min, uint32_t max) { 65 if (max < min || max >= N) { 66 DEBUG_ASSERT(false); 67 return; 68 } 69 bitmap_.Clear(min * 2, max * 2); 70 } 71 Scan(uint32_t * vector)72 InterruptType Scan(uint32_t* vector) { 73 size_t bitoff; 74 #if ARCH_ARM64 75 bool is_empty = bitmap_.Scan(0, kNumBits, false, &bitoff); 76 #elif ARCH_X86 77 bool is_empty = bitmap_.ReverseScan(0, kNumBits, false, &bitoff); 78 #endif 79 if (is_empty) { 80 return InterruptType::INACTIVE; 81 } 82 *vector = static_cast<uint32_t>(bitoff / 2); 83 if (bitoff % 2 == 0) { 84 return InterruptType::VIRTUAL; 85 } else { 86 return InterruptType::PHYSICAL; 87 } 88 } 89 90 private: 91 static constexpr uint32_t kNumBits = N * 2; 92 bitmap::RawBitmapGeneric<bitmap::FixedStorage<kNumBits>> bitmap_; 93 }; 94 95 // |N| is the maximum number of interrupts to be tracked. 96 template <uint32_t N> 97 class InterruptTracker { 98 public: Init()99 zx_status_t Init() { 100 event_init(&event_, false, EVENT_FLAG_AUTOUNSIGNAL); 101 AutoSpinLock lock(&lock_); 102 return bitmap_.Init(); 103 } 104 105 // Returns whether there are pending interrupts. Pending()106 bool Pending() { 107 uint32_t vector; 108 AutoSpinLock lock(&lock_); 109 return bitmap_.Scan(&vector) != InterruptType::INACTIVE; 110 } 111 112 // Clears all vectors in the range [min, max). Clear(uint32_t min,uint32_t max)113 void Clear(uint32_t min, uint32_t max) { 114 AutoSpinLock lock(&lock_); 115 bitmap_.Clear(min, max); 116 } 117 118 // Pops the specified vector, if it is pending. TryPop(uint32_t vector)119 InterruptType TryPop(uint32_t vector) { 120 AutoSpinLock lock(&lock_); 121 InterruptType type = bitmap_.Get(vector); 122 if (type != InterruptType::INACTIVE) { 123 bitmap_.Set(vector, InterruptType::INACTIVE); 124 } 125 return type; 126 } 127 128 // Pops the highest priority interrupt. Pop(uint32_t * vector)129 InterruptType Pop(uint32_t* vector) { 130 AutoSpinLock lock(&lock_); 131 InterruptType type = bitmap_.Scan(vector); 132 if (type != InterruptType::INACTIVE) { 133 bitmap_.Set(*vector, InterruptType::INACTIVE); 134 } 135 return type; 136 } 137 138 // Tracks the given interrupt. Track(uint32_t vector,InterruptType type)139 void Track(uint32_t vector, InterruptType type) { 140 AutoSpinLock lock(&lock_); 141 bitmap_.Set(vector, type); 142 } 143 144 // Tracks the given interrupt, and signals any waiters. Interrupt(uint32_t vector,InterruptType type,bool * signaled)145 void Interrupt(uint32_t vector, InterruptType type, bool* signaled) { 146 Track(vector, type); 147 int threads_unblocked = event_signal(&event_, true); 148 if (signaled != nullptr) { 149 *signaled = threads_unblocked > 0; 150 } 151 } 152 153 // Tracks the given virtual interrupt, and signals any waiters. VirtualInterrupt(uint32_t vector)154 void VirtualInterrupt(uint32_t vector) { 155 Interrupt(vector, hypervisor::InterruptType::VIRTUAL, nullptr); 156 } 157 158 // Waits for an interrupt. Wait(zx_time_t deadline,StateInvalidator * invalidator)159 zx_status_t Wait(zx_time_t deadline, StateInvalidator* invalidator) { 160 if (invalidator != nullptr) { 161 invalidator->Invalidate(); 162 } 163 ktrace_vcpu(TAG_VCPU_BLOCK, VCPU_INTERRUPT); 164 do { 165 zx_status_t status = event_wait_deadline(&event_, deadline, true); 166 if (status == ZX_ERR_TIMED_OUT) { 167 break; 168 } else if (status != ZX_OK) { 169 ktrace_vcpu(TAG_VCPU_UNBLOCK, VCPU_INTERRUPT); 170 return ZX_ERR_CANCELED; 171 } 172 } while (!Pending()); 173 ktrace_vcpu(TAG_VCPU_UNBLOCK, VCPU_INTERRUPT); 174 return ZX_OK; 175 } 176 177 private: 178 event_t event_; 179 SpinLock lock_; 180 InterruptBitmap<N> bitmap_ TA_GUARDED(lock_); 181 }; 182 183 } // namespace hypervisor 184