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