xref: /cloud-hypervisor/devices/src/ioapic.rs (revision f6cd3bd86ded632da437b6dd6077f4237d2f71fe)
1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // Copyright © 2019 Intel Corporation
6 //
7 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
8 //
9 // Implementation of an intel 82093AA Input/Output Advanced Programmable Interrupt Controller
10 // See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification.
11 
12 use super::interrupt_controller::{Error, InterruptController};
13 use byteorder::{ByteOrder, LittleEndian};
14 use serde::{Deserialize, Serialize};
15 use std::result;
16 use std::sync::{Arc, Barrier};
17 use vm_device::interrupt::{
18     InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
19     MsiIrqGroupConfig, MsiIrqSourceConfig,
20 };
21 use vm_device::BusDevice;
22 use vm_memory::GuestAddress;
23 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
24 use vmm_sys_util::eventfd::EventFd;
25 
26 type Result<T> = result::Result<T, Error>;
27 
28 // I/O REDIRECTION TABLE REGISTER
29 //
30 // There are 24 I/O Redirection Table entry registers. Each register is a
31 // dedicated entry for each interrupt input signal. Each register is 64 bits
32 // split between two 32 bits registers as follow:
33 //
34 // 63-56: Destination Field - R/W
35 // 55-17: Reserved
36 // 16:    Interrupt Mask - R/W
37 // 15:    Trigger Mode - R/W
38 // 14:    Remote IRR - RO
39 // 13:    Interrupt Input Pin Polarity - R/W
40 // 12:    Delivery Status - RO
41 // 11:    Destination Mode - R/W
42 // 10-8:  Delivery Mode - R/W
43 // 7-0:   Interrupt Vector - R/W
44 pub type RedirectionTableEntry = u64;
45 
46 fn vector(entry: RedirectionTableEntry) -> u8 {
47     (entry & 0xffu64) as u8
48 }
49 fn delivery_mode(entry: RedirectionTableEntry) -> u8 {
50     ((entry >> 8) & 0x7u64) as u8
51 }
52 fn destination_mode(entry: RedirectionTableEntry) -> u8 {
53     ((entry >> 11) & 0x1u64) as u8
54 }
55 fn remote_irr(entry: RedirectionTableEntry) -> u8 {
56     ((entry >> 14) & 0x1u64) as u8
57 }
58 fn trigger_mode(entry: RedirectionTableEntry) -> u8 {
59     ((entry >> 15) & 0x1u64) as u8
60 }
61 fn interrupt_mask(entry: RedirectionTableEntry) -> u8 {
62     ((entry >> 16) & 0x1u64) as u8
63 }
64 fn destination_field(entry: RedirectionTableEntry) -> u8 {
65     // When the destination mode is physical, the destination field should only
66     // be defined through bits 56-59, as defined in the IOAPIC specification.
67     // But from the APIC specification, the APIC ID is always defined on 8 bits
68     // no matter which destination mode is selected. That's why we always
69     // retrieve the destination field based on bits 56-63.
70     ((entry >> 56) & 0xffu64) as u8
71 }
72 fn set_delivery_status(entry: &mut RedirectionTableEntry, val: u8) {
73     // Clear bit 12
74     *entry &= 0xffff_ffff_ffff_efff;
75     // Set it with the expected value
76     *entry |= u64::from(val & 0x1) << 12;
77 }
78 fn set_remote_irr(entry: &mut RedirectionTableEntry, val: u8) {
79     // Clear bit 14
80     *entry &= 0xffff_ffff_ffff_bfff;
81     // Set it with the expected value
82     *entry |= u64::from(val & 0x1) << 14;
83 }
84 
85 pub const NUM_IOAPIC_PINS: usize = 24;
86 const IOAPIC_VERSION_ID: u32 = 0x0017_0011;
87 
88 // Constants for IOAPIC direct register offset
89 const IOAPIC_REG_ID: u8 = 0x00;
90 const IOAPIC_REG_VERSION: u8 = 0x01;
91 const IOAPIC_REG_ARBITRATION_ID: u8 = 0x02;
92 
93 // Register offsets
94 const IOREGSEL_OFF: u8 = 0x0;
95 const IOWIN_OFF: u8 = 0x10;
96 const IOWIN_SCALE: u8 = 0x2;
97 const REG_MAX_OFFSET: u8 = IOWIN_OFF + (NUM_IOAPIC_PINS as u8 * 2) - 1;
98 
99 #[repr(u8)]
100 enum TriggerMode {
101     Edge = 0,
102     Level = 1,
103 }
104 
105 #[repr(u8)]
106 enum DeliveryMode {
107     Fixed = 0b000,
108     Lowest = 0b001,
109     Smi = 0b010,        // System management interrupt
110     RemoteRead = 0b011, // This is no longer supported by intel.
111     Nmi = 0b100,        // Non maskable interrupt
112     Init = 0b101,
113     Startup = 0b110,
114     External = 0b111,
115 }
116 
117 /// Given an offset that was read from/written to, return a tuple of the relevant IRQ and whether
118 /// the offset refers to the high bits of that register.
119 fn decode_irq_from_selector(selector: u8) -> (usize, bool) {
120     (
121         ((selector - IOWIN_OFF) / IOWIN_SCALE) as usize,
122         selector & 1 != 0,
123     )
124 }
125 
126 pub struct Ioapic {
127     id: String,
128     id_reg: u32,
129     reg_sel: u32,
130     reg_entries: [RedirectionTableEntry; NUM_IOAPIC_PINS],
131     used_entries: [bool; NUM_IOAPIC_PINS],
132     apic_address: GuestAddress,
133     interrupt_source_group: Arc<dyn InterruptSourceGroup>,
134 }
135 
136 #[derive(Serialize, Deserialize)]
137 pub struct IoapicState {
138     id_reg: u32,
139     reg_sel: u32,
140     reg_entries: [RedirectionTableEntry; NUM_IOAPIC_PINS],
141     used_entries: [bool; NUM_IOAPIC_PINS],
142     apic_address: u64,
143 }
144 
145 impl BusDevice for Ioapic {
146     fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
147         if data.len() != std::mem::size_of::<u32>() {
148             warn!("Invalid read size on IOAPIC: {}", data.len());
149             return;
150         }
151 
152         debug!("IOAPIC_R @ offset 0x{:x}", offset);
153 
154         let value: u32 = match offset as u8 {
155             IOREGSEL_OFF => self.reg_sel,
156             IOWIN_OFF => self.ioapic_read(),
157             _ => {
158                 error!("IOAPIC: failed reading at offset {}", offset);
159                 return;
160             }
161         };
162 
163         LittleEndian::write_u32(data, value);
164     }
165 
166     fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
167         if data.len() != std::mem::size_of::<u32>() {
168             warn!("Invalid write size on IOAPIC: {}", data.len());
169             return None;
170         }
171 
172         debug!("IOAPIC_W @ offset 0x{:x}", offset);
173 
174         let value = LittleEndian::read_u32(data);
175 
176         match offset as u8 {
177             IOREGSEL_OFF => self.reg_sel = value,
178             IOWIN_OFF => self.ioapic_write(value),
179             _ => {
180                 error!("IOAPIC: failed writing at offset {}", offset);
181             }
182         }
183         None
184     }
185 }
186 
187 impl Ioapic {
188     pub fn new(
189         id: String,
190         apic_address: GuestAddress,
191         interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
192         state: Option<IoapicState>,
193     ) -> Result<Ioapic> {
194         let interrupt_source_group = interrupt_manager
195             .create_group(MsiIrqGroupConfig {
196                 base: 0,
197                 count: NUM_IOAPIC_PINS as InterruptIndex,
198             })
199             .map_err(Error::CreateInterruptSourceGroup)?;
200 
201         let (id_reg, reg_sel, reg_entries, used_entries, apic_address) = if let Some(state) = &state
202         {
203             (
204                 state.id_reg,
205                 state.reg_sel,
206                 state.reg_entries,
207                 state.used_entries,
208                 GuestAddress(state.apic_address),
209             )
210         } else {
211             (
212                 0,
213                 0,
214                 [0x10000; NUM_IOAPIC_PINS],
215                 [false; NUM_IOAPIC_PINS],
216                 apic_address,
217             )
218         };
219 
220         // The IOAPIC is created with entries already masked. The guest will be
221         // in charge of unmasking them if/when necessary.
222         let ioapic = Ioapic {
223             id,
224             id_reg,
225             reg_sel,
226             reg_entries,
227             used_entries,
228             apic_address,
229             interrupt_source_group,
230         };
231 
232         // When restoring the Ioapic, we must enable used entries.
233         if state.is_some() {
234             for (irq, entry) in ioapic.used_entries.iter().enumerate() {
235                 if *entry {
236                     ioapic.update_entry(irq, false)?;
237                 }
238             }
239 
240             ioapic
241                 .interrupt_source_group
242                 .set_gsi()
243                 .map_err(Error::UpdateInterrupt)?;
244         }
245 
246         Ok(ioapic)
247     }
248 
249     fn ioapic_write(&mut self, val: u32) {
250         debug!("IOAPIC_W reg 0x{:x}, val 0x{:x}", self.reg_sel, val);
251 
252         match self.reg_sel as u8 {
253             IOAPIC_REG_VERSION => {
254                 if val == 0 {
255                     // Windows writes zero here (see #1791)
256                 } else {
257                     error!(
258                         "IOAPIC: invalid write to version register (0x{:x}): 0x{:x}",
259                         self.reg_sel, val
260                     );
261                 }
262             }
263             IOAPIC_REG_ID => self.id_reg = (val >> 24) & 0xf,
264             IOWIN_OFF..=REG_MAX_OFFSET => {
265                 let (index, is_high_bits) = decode_irq_from_selector(self.reg_sel as u8);
266                 if index > NUM_IOAPIC_PINS {
267                     warn!("IOAPIC index out of range: {}", index);
268                     return;
269                 }
270                 if is_high_bits {
271                     self.reg_entries[index] &= 0xffff_ffff;
272                     self.reg_entries[index] |= u64::from(val) << 32;
273                 } else {
274                     // Ensure not to override read-only bits:
275                     // - Delivery Status (bit 12)
276                     // - Remote IRR (bit 14)
277                     self.reg_entries[index] &= 0xffff_ffff_0000_5000;
278                     self.reg_entries[index] |= u64::from(val) & 0xffff_afff;
279                 }
280                 // The entry must be updated through the interrupt source
281                 // group.
282                 if let Err(e) = self.update_entry(index, true) {
283                     error!("Failed updating IOAPIC entry: {:?}", e);
284                 }
285                 // Store the information this IRQ is now being used.
286                 self.used_entries[index] = true;
287             }
288             _ => error!(
289                 "IOAPIC: invalid write to register offset 0x{:x}",
290                 self.reg_sel
291             ),
292         }
293     }
294 
295     fn ioapic_read(&self) -> u32 {
296         debug!("IOAPIC_R reg 0x{:x}", self.reg_sel);
297 
298         match self.reg_sel as u8 {
299             IOAPIC_REG_VERSION => IOAPIC_VERSION_ID,
300             IOAPIC_REG_ID | IOAPIC_REG_ARBITRATION_ID => (self.id_reg & 0xf) << 24,
301             IOWIN_OFF..=REG_MAX_OFFSET => {
302                 let (index, is_high_bits) = decode_irq_from_selector(self.reg_sel as u8);
303                 if index > NUM_IOAPIC_PINS {
304                     warn!("IOAPIC index out of range: {}", index);
305                     return 0;
306                 }
307                 if is_high_bits {
308                     (self.reg_entries[index] >> 32) as u32
309                 } else {
310                     (self.reg_entries[index] & 0xffff_ffff) as u32
311                 }
312             }
313             _ => {
314                 error!(
315                     "IOAPIC: invalid read from register offset 0x{:x}",
316                     self.reg_sel
317                 );
318                 0
319             }
320         }
321     }
322 
323     fn state(&self) -> IoapicState {
324         IoapicState {
325             id_reg: self.id_reg,
326             reg_sel: self.reg_sel,
327             reg_entries: self.reg_entries,
328             used_entries: self.used_entries,
329             apic_address: self.apic_address.0,
330         }
331     }
332 
333     fn update_entry(&self, irq: usize, set_gsi: bool) -> Result<()> {
334         let entry = self.reg_entries[irq];
335 
336         // Validate Destination Mode value, and retrieve Destination ID
337         let destination_mode = destination_mode(entry);
338         let destination_id = destination_field(entry);
339 
340         // When this bit is set, the message is directed to the processor with
341         // the lowest interrupt priority among processors that can receive the
342         // interrupt.
343         let redirection_hint: u8 = 1;
344 
345         // Generate MSI message address
346         let low_addr: u32 = self.apic_address.0 as u32
347             | u32::from(destination_id) << 12
348             | u32::from(redirection_hint) << 3
349             | u32::from(destination_mode) << 2;
350 
351         // Validate Trigger Mode value
352         let trigger_mode = trigger_mode(entry);
353         match trigger_mode {
354             x if (x == TriggerMode::Edge as u8) || (x == TriggerMode::Level as u8) => {}
355             _ => return Err(Error::InvalidTriggerMode),
356         }
357 
358         // Validate Delivery Mode value
359         let delivery_mode = delivery_mode(entry);
360         match delivery_mode {
361             x if (x == DeliveryMode::Fixed as u8)
362                 || (x == DeliveryMode::Lowest as u8)
363                 || (x == DeliveryMode::Smi as u8)
364                 || (x == DeliveryMode::RemoteRead as u8)
365                 || (x == DeliveryMode::Nmi as u8)
366                 || (x == DeliveryMode::Init as u8)
367                 || (x == DeliveryMode::Startup as u8)
368                 || (x == DeliveryMode::External as u8) => {}
369             _ => return Err(Error::InvalidDeliveryMode),
370         }
371 
372         // Generate MSI message data
373         let data: u32 = u32::from(trigger_mode) << 15
374             | u32::from(remote_irr(entry)) << 14
375             | u32::from(delivery_mode) << 8
376             | u32::from(vector(entry));
377 
378         let config = MsiIrqSourceConfig {
379             high_addr: 0x0,
380             low_addr,
381             data,
382             devid: 0,
383         };
384 
385         self.interrupt_source_group
386             .update(
387                 irq as InterruptIndex,
388                 InterruptSourceConfig::MsiIrq(config),
389                 interrupt_mask(entry) == 1,
390                 set_gsi,
391             )
392             .map_err(Error::UpdateInterrupt)?;
393 
394         Ok(())
395     }
396 }
397 
398 impl InterruptController for Ioapic {
399     // The ioapic must be informed about EOIs in order to deassert interrupts
400     // already sent.
401     fn end_of_interrupt(&mut self, vec: u8) {
402         for i in 0..NUM_IOAPIC_PINS {
403             let entry = &mut self.reg_entries[i];
404             // Clear Remote IRR bit
405             if vector(*entry) == vec && trigger_mode(*entry) == 1 {
406                 set_remote_irr(entry, 0);
407             }
408         }
409     }
410 
411     // This should be called anytime an interrupt needs to be injected into the
412     // running guest.
413     fn service_irq(&mut self, irq: usize) -> Result<()> {
414         let entry = &mut self.reg_entries[irq];
415 
416         self.interrupt_source_group
417             .trigger(irq as InterruptIndex)
418             .map_err(Error::TriggerInterrupt)?;
419         debug!("Interrupt successfully delivered");
420 
421         // If trigger mode is level sensitive, set the Remote IRR bit.
422         // It will be cleared when the EOI is received.
423         if trigger_mode(*entry) == 1 {
424             set_remote_irr(entry, 1);
425         }
426         // Clear the Delivery Status bit
427         set_delivery_status(entry, 0);
428 
429         Ok(())
430     }
431 
432     fn notifier(&self, irq: usize) -> Option<EventFd> {
433         self.interrupt_source_group.notifier(irq as InterruptIndex)
434     }
435 }
436 
437 impl Snapshottable for Ioapic {
438     fn id(&self) -> String {
439         self.id.clone()
440     }
441 
442     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
443         Snapshot::new_from_state(&self.state())
444     }
445 }
446 
447 impl Pausable for Ioapic {}
448 impl Transportable for Ioapic {}
449 impl Migratable for Ioapic {}
450