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