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