rstubs/arch/int/
lapic.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
//! Abstractions for the local APIC for timers, IPI and internal interrupt

use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};

use bitfield_struct::bitfield;

use crate::arch::int;
use crate::arch::io::{IOMem, VolatileUpdate};
use crate::arch::pit::Timer;
use crate::interrupts::Vector;

/// Abstracts the local APICs (which is integrated into every CPU core)
///
/// In modern (x86) PCs, every CPU core has its own Local APIC (LAPIC).
/// The LAPIC is the link between the local CPU core and the I/O APIC
/// (that takes care about external interrupt sources.
/// Interrupt messages received by the LAPIC will be passed to the
/// corresponding CPU core and trigger the interrupt handler on this core.
///
/// See <https://wiki.osdev.org/APIC>
#[derive(Debug)]
pub struct LApic {
    /// Base of the memory mapped lapic registers
    pub base: AtomicPtr<u32>,
    /// The number of ticks the timer does per millisecond.
    ///
    /// This is measured an set when the first timer is created.
    timer_ticks: AtomicUsize,
}

/// The LAPIC instance, that is different for each cpu core.
pub static LAPIC: LApic = LApic::new();

impl LApic {
    /// Default base address for the memory-mapped registers.
    pub const BASE: usize = 0xfee0_0000;

    /// Creates a new instance that has to be initialized with [Self::init].
    pub const fn new() -> Self {
        Self {
            base: AtomicPtr::new(Self::BASE as _),
            timer_ticks: AtomicUsize::new(0),
        }
    }

    /// Initialize the LAPIC fo the given cpu_id.
    pub fn init(&self, cpu_id: u8) {
        serial!(
            "lapic: v={:#x} @ {:?}",
            self.version(),
            self.base.load(Ordering::Acquire)
        );

        // use 255 as spurious vector, enable APIC and disable focus processor
        self.update(|v: SpuriousInt| {
            v.with_spurious_vector(0xff)
                .with_apic_enable(true)
                .with_focus_processor_checking(true)
        });

        // set flat delivery mode
        self.update(|v: DestFormat| v.with_model(DFR_MODEL_FLAT));

        // set task priority to 0 -> accept all interrupts
        self.update(|v: TaskPrio| v.with_task_prio(0).with_task_prio_sub(0));

        // reset logical destination ID
        assert!(cpu_id < 8, "LAPIC only supports 8 logical ids");
        self.update(|v: LogicalDst| v.with_lapic_id(1 << cpu_id));

        // Measure the timer frequency
        if self.timer_ticks.load(Ordering::Relaxed) == 0 {
            let ticks = self.measure_timer_ticks();
            self.timer_ticks.store(ticks, Ordering::Release);
            serial!("lapic: ticks {ticks}");
        }
    }

    /// Signalizes the LAPIC that the handling of the current interrupt
    /// finished. This function must be called at the end of interrupt
    /// handling before ireting.
    pub fn eoi(&self) {
        // dummy read
        self.read::<SpuriousInt>();
        // signal end of interrupt
        self.write(EndOfInt(0));
    }

    /// Get version number of local APIC.
    pub fn version(&self) -> u8 {
        self.read::<Version>().version() as _
    }

    /// Get the ID of the current core's LAPIC.
    pub fn id(&self) -> u8 {
        self.read::<Identification>().lapic_id() as _
    }

    /// Setup the LAPIC timer.
    pub fn timer(&self, us: usize, vector: u8, periodic: bool, masked: bool) {
        assert!(us != 0);

        let mut ticks = self.timer_ticks.load(Ordering::Acquire);
        assert!(ticks != 0, "LAPIC timer not measured yet");
        // Calculate the counter and divider
        let l_counter = (ticks as u64 * us as u64) / 1000;
        // Number of bits exceeding the 32 bit boundary
        let overlap = 32u32.saturating_sub(l_counter.leading_zeros());
        let divider = 1u8 << overlap;
        // convert to 32 bit
        let counter = (l_counter / divider as u64) as u32;
        self.raw_timer(counter, divider, vector, periodic, masked);
    }

    /// Setup the LAPIC timer with a raw counter and divider.
    fn raw_timer(&self, counter: u32, divider: u8, vector: u8, periodic: bool, masked: bool) {
        use TimerMode::*;
        // stop timer
        self.write(TimerInitCount(0));
        // set control register
        self.update(|v: TimerCtrl| {
            v.with_vector(vector)
                .with_timer_mode(if periodic { Periodic } else { OneShot })
                .with_masked(masked)
        });
        // set divider
        self.write(TimerDiv::new(divider));
        // start timer
        self.write(TimerInitCount(counter));
    }

    /// Enable or disable the LAPIC timer.
    pub fn timer_enable(&self, enable: bool) {
        // set control register
        self.update(|v: TimerCtrl| v.with_masked(!enable));
    }

    /// Determines the LAPIC timer frequency.
    ///
    /// This function will calculate the number of LAPIC-timer ticks passing
    /// in the course of one millisecond. To do so, this function will rely
    /// on PIT timer functionality and measure the tick delta between start
    /// and end of waiting for a predefined period.
    ///
    /// For measurement, the LAPIC-timer single-shot mode (without interrupts)
    /// is used; after measurement, the timer is disabled again.
    ///
    /// Steps taken for precise measurement of LAPIC-timer ticks per ms:
    /// 1. Disable Interrupts to ensure measurement is not disturbed
    /// 2. Configure a timeout of 50 ms (nearly PIT's maximum possible delay)
    ///    Using a "large" value decreases the overhead induced by measurement and thereby increases the accuracy.
    /// 3. Now measure the number of passed LAPIC-timer ticks while waiting for the PIT
    ///    Note that configuring the PIT takes quite some time and therefore should be done prior to starting
    ///    LAPIC-timer measurement.
    /// 4. Restore previous state (disable PIT, LAPIC timer, restore interrupts)
    /// 5. Derive the ticks per millisecond (take care, the counter is counting towards zero)
    fn measure_timer_ticks(&self) -> usize {
        const MS: u16 = 50;
        const DIV: u8 = 1;

        // suppress interrupts until end of this function
        int::suppress(|| {
            // Configure pit
            let pit = Timer::new(MS * 1000);

            // Configure the timer to count every tick
            self.raw_timer(u32::MAX, DIV, 0, false, true);

            let TimerCount(start) = self.read();
            pit.wait(); // Wait 50ms
            let TimerCount(end) = self.read();

            // Disable timer
            self.raw_timer(0, DIV, 0, false, true);

            (start - end) as usize / MS as usize
        })
    }

    /// Check if the previously sent IPI has reached its destination.
    pub fn delivered(&self) -> bool {
        !self.read::<InterruptCmdL>().delivery_status()
    }

    /// Send an Inter-Processor Interrupt (IPI).
    pub fn send(&self, destination: Destination, vector: Vector) {
        let (dst, dst_type, dst_mode) = destination.raw();
        self.update(|h: InterruptCmdH| h.with_destination(dst));
        self.update(|l: InterruptCmdL| {
            l.with_vector(vector as _)
                .with_delivery_mode(DeliveryMode::Fixed)
                .with_destination_mode(dst_mode)
                .with_destination_type(dst_type)
                .with_level(true)
                .with_trigger_mode(false)
        });
    }

    /// Send an Init request IPI to another processor.
    pub fn send_init(&self, destination: Destination) {
        let (dst, dst_type, dst_mode) = destination.raw();
        self.update(|h: InterruptCmdH| h.with_destination(dst));
        self.update(|l: InterruptCmdL| {
            l.with_vector(0)
                .with_delivery_mode(DeliveryMode::Init)
                .with_destination_mode(dst_mode)
                .with_destination_type(dst_type)
                .with_level(true)
                .with_trigger_mode(false)
        });
    }

    /// Send a Startup IPI to another processor.
    pub fn send_startup(&self, destination: Destination, vector: u8) {
        let (dst, dst_type, dst_mode) = destination.raw();
        self.update(|h: InterruptCmdH| h.with_destination(dst));
        self.update(|l: InterruptCmdL| {
            l.with_vector(vector)
                .with_delivery_mode(DeliveryMode::Startup)
                .with_destination_mode(dst_mode)
                .with_destination_type(dst_type)
                .with_level(true)
                .with_trigger_mode(false)
        });
    }

    /// Read-modify-write a memory mapped register.
    fn update<T: IOMem>(&self, f: impl FnOnce(T) -> T) {
        unsafe {
            self.base
                .load(Ordering::Acquire)
                .add(T::OFFSET / 4)
                .cast::<T>()
                .update_volatile(f);
        }
    }

    /// Write a memory mapped register.
    fn write<T: IOMem>(&self, value: T) {
        unsafe {
            self.base
                .load(Ordering::Acquire)
                .add(T::OFFSET / 4)
                .cast::<T>()
                .write_volatile(value);
        };
    }

    /// Read a memory mapped register.
    fn read<T: IOMem>(&self) -> T {
        unsafe {
            self.base
                .load(Ordering::Acquire)
                .add(T::OFFSET / 4)
                .cast::<T>()
                .read_volatile()
        }
    }
}

/// Destination for an inter-processor interrupt
pub enum Destination {
    /// Physical ID
    Physical(u8),
    /// Logical ID bit mask
    Group(u8),
    /// Including self
    All,
    /// Excluding self
    Others,
}
impl Destination {
    /// Returns a tuple with (destination, destination_type, destination_mode)
    const fn raw(self) -> (u8, u8, bool) {
        match self {
            Self::Physical(dst) => (dst, 0, false),
            Self::Group(dst) => (dst, 0, true),
            Self::All => (0, 2, false),
            Self::Others => (0, 3, false),
        }
    }
}

/// Register for reconfiguring the base address
#[bitfield(u64)]
struct APICBaseAddr {
    __: u8,
    /// Indicates if the processor is the bootstrap processor.
    bootstrap_cpu: bool,
    #[bits(2)]
    __: (),
    /// Enable the local APIC.
    enable: bool,
    #[bits(40)]
    /// Specifies the base address of the APIC registers.
    base_addr: u64,
    #[bits(12)]
    __: (),
}

/// Local APIC ID Register.
#[bitfield(u32)]
struct Identification {
    #[bits(24)]
    __: (),
    /// At power up, system hardware assigns a unique APIC ID to each local APIC.
    /// In MP systems, the local APIC ID is also used as a processor ID.
    ///
    /// Note: This ID is hierarchically structured and might not be consecutive.
    lapic_id: u8,
}
impl IOMem for Identification {
    const OFFSET: usize = 0x020;
}

/// Local APIC Version Register, RO
#[bitfield(u32)]
struct Version {
    /// The version numbers of the local APIC:
    /// - 0x00-0x10: 82489DX discrete APIC
    /// - 0x10-0x15: Integrated APIC
    version: u8,
    __: u8,
    /// The number LVT entries minus 1.
    max_lvt_entry: u8,
    /// Indicates whether software can inhibit the broadcast of EOI message
    suppress_eoi_broadcast: bool,
    #[bits(7)]
    __: (),
}
impl IOMem for Version {
    const OFFSET: usize = 0x030;
}

/// Task Priority Register, R/W
#[bitfield(u32)]
struct TaskPrio {
    #[bits(4)]
    task_prio_sub: u8,
    #[bits(4)]
    task_prio: u8,
    #[bits(24)]
    __: (),
}
impl IOMem for TaskPrio {
    const OFFSET: usize = 0x080;
}

/// EOI Register, WO
#[repr(transparent)]
struct EndOfInt(u32);
impl IOMem for EndOfInt {
    const OFFSET: usize = 0x0b0;
}

/// Logical Destination Register.
#[bitfield(u32)]
struct LogicalDst {
    #[bits(24)]
    __: u32,
    /// Configures on which logical id this LAPIC will listen.
    lapic_id: u8,
}
impl IOMem for LogicalDst {
    const OFFSET: usize = 0x0d0;
}

const DFR_MODEL_CLUSTER: u8 = 0x0;
const DFR_MODEL_FLAT: u8 = 0xf;

/// Destination Format Register.
#[bitfield(u32)]
struct DestFormat {
    #[bits(28)]
    __: (),
    /// Model (Flat vs. Cluster)
    #[bits(4)]
    model: u8,
}
impl IOMem for DestFormat {
    const OFFSET: usize = 0x0e0;
}

/// Spurious Interrupt Vector Register.
#[bitfield(u32)]
struct SpuriousInt {
    /// Determines the vector number to be delivered to the processor when
    /// the local APIC generates a spurious vector.
    spurious_vector: u8,
    /// Allows software to temporarily enable/disable the local APIC.
    apic_enable: bool,
    /// Determines if focus processor checking is enabled when using the
    /// lowest-priority delivery mode.
    focus_processor_checking: bool,
    #[bits(22)]
    __: (),
}
impl IOMem for SpuriousInt {
    const OFFSET: usize = 0x0f0;
}

/// Interrupt Command Register 1, R/W
#[bitfield(u32)]
struct InterruptCmdL {
    /// Interrupt vector in the IDT will be activated when
    /// the corresponding external interrupt triggers.
    pub vector: u8,
    /// The delivery mode denotes the way the interrupts will be delivered
    /// to the local CPU cores, respectively to their local APICs.
    #[bits(3)]
    pub delivery_mode: DeliveryMode,
    /// The destination mode.
    ///
    /// Clear for a physical destination, or set for a logical destination.
    pub destination_mode: bool,
    /// Delivery status.
    ///
    /// Cleared when the interrupt has been accepted by the target.
    /// You should usually wait until this bit clears after sending an interrupt.
    pub delivery_status: bool,
    __: bool,
    /// The polarity denotes when an interrupt should be issued.
    ///
    /// Clear for INIT level de-assert, otherwise set.
    pub level: bool,
    /// The trigger mode states whether the interrupt signaling is level or edge triggered.
    ///
    ///  Set for INIT level de-assert, otherwise clear.
    pub trigger_mode: bool,
    #[bits(2)]
    __: (),
    /// Destination type.
    ///
    /// If this is > 0 then the destination field is ignored.
    /// 1 will always send the interrupt to itself,
    /// 2 will send it to all processors,
    /// and 3 will send it to all processors aside from the current one.
    ///
    /// It is best to avoid using modes 1, 2 and 3, and stick with 0.
    #[bits(2)]
    pub destination_type: u8,
    #[bits(12)]
    __: (),
}
impl IOMem for InterruptCmdL {
    const OFFSET: usize = 0x300;
}

/// Delivery mode specifies the type of interrupt sent to the CPU.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
enum DeliveryMode {
    /// "ordinary" interrupt; send to ALL cores listed in the destination bit mask
    Fixed = 0,
    /// "ordinary" interrupt; send to the lowest priority core from destination mask
    LowestPriority = 1,
    /// System Management Interrupt; vector number required to be 0
    SysManagement = 2,
    /// Non-Maskable Interrupt, vector number ignored, only edge triggered
    NonMaskable = 4,
    /// Initialization interrupt (always treated as edge triggered)
    Init = 5,
    /// Dedicated Startup-Interrupt (SIPI)
    Startup = 6,
}
impl DeliveryMode {
    const fn from_bits(value: u32) -> Self {
        match value {
            1 => Self::LowestPriority,
            2 => Self::SysManagement,
            4 => Self::NonMaskable,
            5 => Self::Init,
            6 => Self::Startup,
            _ => Self::Fixed,
        }
    }
    const fn into_bits(self) -> u32 {
        self as _
    }
}

/// Interrupt Command Register 2, R/W
#[bitfield(u32)]
struct InterruptCmdH {
    #[bits(24)]
    __: (),
    /// The meaning of destination depends on the destination mode:
    /// For the logical destination mode, destination holds a bit mask made up
    /// of the cores that are candidates for receiving the interrupt.
    /// In the single-core case, this value is `1`, in the multi-core case,
    /// the `n` low-order bits needs to be set (with `n` being the number of CPU cores).
    /// Setting the `n` low-order bits marks all available cores as candidates for receiving
    /// interrupts and thereby balancing the number of interrupts between the cores.
    destination: u8,
}
impl IOMem for InterruptCmdH {
    const OFFSET: usize = 0x310;
}

// ------------------- LAPIC Timer -------------------

/// LApic timer control register, R/W
#[bitfield(u32)]
struct TimerCtrl {
    /// This vector in the IDT will be activated for the timer interrupt.
    vector: u8,
    #[bits(4)]
    __: (),
    /// Whether the interrupt delivery is pending (true) or idle.
    delivery_status: bool,
    #[bits(3)]
    __: (),
    /// Mask (disables) or unmask timer interrupts.
    masked: bool,
    /// Timer mode.
    #[bits(2)]
    timer_mode: TimerMode,
    #[bits(13)]
    __: (),
}
impl IOMem for TimerCtrl {
    const OFFSET: usize = 0x320;
}

/// Timer mode.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum TimerMode {
    /// Send a single interrupt after reaching 0.
    OneShot = 0,
    /// Send a periodic interrupt, restarting with the [TimerInitCount] value.
    Periodic = 1,
    /// Uses the TSC_DEADLINE MSR to program the timer instead of the [TimerInitCount] and [TimerCount].
    Deadline = 2,
}
impl TimerMode {
    const fn from_bits(value: u8) -> Self {
        match value {
            1 => Self::Periodic,
            2 => Self::Deadline,
            _ => Self::OneShot,
        }
    }
    const fn into_bits(self) -> u8 {
        self as _
    }
}

/// LApic timer initial counter register, R/W
#[repr(transparent)]
struct TimerInitCount(u32);
impl IOMem for TimerInitCount {
    const OFFSET: usize = 0x380;
}

/// LApic timer current counter register, RO
#[repr(transparent)]
struct TimerCount(u32);
impl IOMem for TimerCount {
    const OFFSET: usize = 0x390;
}

/// LApic timer divide configuration register, RW
#[repr(transparent)]
struct TimerDiv(u32);
impl TimerDiv {
    const fn new(div: u8) -> Self {
        assert!(div != 0 && div.is_power_of_two());
        let marks = [
            0xb, // divides by   1
            0x0, // divides by   2
            0x1, // divides by   4
            0x2, // divides by   8
            0x3, // divides by  16
            0x8, // divides by  32
            0x9, // divides by  64
            0xa, // divides by 128
        ];
        let trail = div.trailing_zeros();
        assert!(trail < marks.len() as u32);
        Self(marks[trail as usize])
    }
}

impl IOMem for TimerDiv {
    const OFFSET: usize = 0x3e0;
}