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