xv6

port of xv6 to x86-64
git clone http://frotz.net/git/xv6.git
Log | Files | Refs | README | LICENSE

lapic.c (5489B)


      1 // The local APIC manages internal (non-I/O) interrupts.
      2 // See Chapter 8 & Appendix C of Intel processor manual volume 3.
      3 
      4 #include "types.h"
      5 #include "defs.h"
      6 #include "memlayout.h"
      7 #include "traps.h"
      8 #include "mmu.h"
      9 #include "x86.h"
     10 #include "param.h"
     11 #include "proc.h"
     12 
     13 // Local APIC registers, divided by 4 for use as uint[] indices.
     14 #define ID      (0x0020/4)   // ID
     15 #define VER     (0x0030/4)   // Version
     16 #define TPR     (0x0080/4)   // Task Priority
     17 #define EOI     (0x00B0/4)   // EOI
     18 #define SVR     (0x00F0/4)   // Spurious Interrupt Vector
     19   #define ENABLE     0x00000100   // Unit Enable
     20 #define ESR     (0x0280/4)   // Error Status
     21 #define ICRLO   (0x0300/4)   // Interrupt Command
     22   #define INIT       0x00000500   // INIT/RESET
     23   #define STARTUP    0x00000600   // Startup IPI
     24   #define DELIVS     0x00001000   // Delivery status
     25   #define ASSERT     0x00004000   // Assert interrupt (vs deassert)
     26   #define DEASSERT   0x00000000
     27   #define LEVEL      0x00008000   // Level triggered
     28   #define BCAST      0x00080000   // Send to all APICs, including self.
     29   #define BUSY       0x00001000
     30   #define FIXED      0x00000000
     31 #define ICRHI   (0x0310/4)   // Interrupt Command [63:32]
     32 #define TIMER   (0x0320/4)   // Local Vector Table 0 (TIMER)
     33   #define X1         0x0000000B   // divide counts by 1
     34   #define PERIODIC   0x00020000   // Periodic
     35 #define PCINT   (0x0340/4)   // Performance Counter LVT
     36 #define LINT0   (0x0350/4)   // Local Vector Table 1 (LINT0)
     37 #define LINT1   (0x0360/4)   // Local Vector Table 2 (LINT1)
     38 #define ERROR   (0x0370/4)   // Local Vector Table 3 (ERROR)
     39   #define MASKED     0x00010000   // Interrupt masked
     40 #define TICR    (0x0380/4)   // Timer Initial Count
     41 #define TCCR    (0x0390/4)   // Timer Current Count
     42 #define TDCR    (0x03E0/4)   // Timer Divide Configuration
     43 
     44 volatile uint *lapic;  // Initialized in mp.c
     45 
     46 static void
     47 lapicw(int index, int value)
     48 {
     49   lapic[index] = value;
     50   lapic[ID];  // wait for write to finish, by reading
     51 }
     52 //PAGEBREAK!
     53 
     54 void
     55 lapicinit(void)
     56 {
     57   if(!lapic) 
     58     return;
     59 
     60   // Enable local APIC; set spurious interrupt vector.
     61   lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
     62 
     63   // The timer repeatedly counts down at bus frequency
     64   // from lapic[TICR] and then issues an interrupt.  
     65   // If xv6 cared more about precise timekeeping,
     66   // TICR would be calibrated using an external time source.
     67   lapicw(TDCR, X1);
     68   lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
     69   lapicw(TICR, 10000000); 
     70 
     71   // Disable logical interrupt lines.
     72   lapicw(LINT0, MASKED);
     73   lapicw(LINT1, MASKED);
     74 
     75   // Disable performance counter overflow interrupts
     76   // on machines that provide that interrupt entry.
     77   if(((lapic[VER]>>16) & 0xFF) >= 4)
     78     lapicw(PCINT, MASKED);
     79 
     80   // Map error interrupt to IRQ_ERROR.
     81   lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
     82 
     83   // Clear error status register (requires back-to-back writes).
     84   lapicw(ESR, 0);
     85   lapicw(ESR, 0);
     86 
     87   // Ack any outstanding interrupts.
     88   lapicw(EOI, 0);
     89 
     90   // Send an Init Level De-Assert to synchronise arbitration ID's.
     91   lapicw(ICRHI, 0);
     92   lapicw(ICRLO, BCAST | INIT | LEVEL);
     93   while(lapic[ICRLO] & DELIVS)
     94     ;
     95 
     96   // Enable interrupts on the APIC (but not on the processor).
     97   lapicw(TPR, 0);
     98 }
     99 
    100 // This is only used during secondary processor startup.
    101 // cpu->id is the fast way to get the cpu number, once the
    102 // processor is fully started.
    103 int
    104 cpunum(void)
    105 {
    106   int n, id;
    107   // Cannot call cpu when interrupts are enabled:
    108   // result not guaranteed to last long enough to be used!
    109   // Would prefer to panic but even printing is chancy here:
    110   // almost everything, including cprintf and panic, calls cpu,
    111   // often indirectly through acquire and release.
    112   if(readeflags()&FL_IF){
    113     static int n;
    114     if(n++ == 0)
    115       cprintf("cpu called from %x with interrupts enabled\n",
    116         __builtin_return_address(0));
    117   }
    118 
    119   if(!lapic)
    120     return 0;
    121 
    122   id = lapic[ID]>>24;
    123   for (n = 0; n < ncpu; n++)
    124     if (id == cpus[n].apicid)
    125       return n;
    126 
    127   return 0;
    128 }
    129 
    130 // Acknowledge interrupt.
    131 void
    132 lapiceoi(void)
    133 {
    134   if(lapic)
    135     lapicw(EOI, 0);
    136 }
    137 
    138 // Spin for a given number of microseconds.
    139 // On real hardware would want to tune this dynamically.
    140 void
    141 microdelay(int us)
    142 {
    143 }
    144 
    145 #define IO_RTC  0x70
    146 
    147 // Start additional processor running entry code at addr.
    148 // See Appendix B of MultiProcessor Specification.
    149 void
    150 lapicstartap(uchar apicid, uint addr)
    151 {
    152   int i;
    153   ushort *wrv;
    154   
    155   // "The BSP must initialize CMOS shutdown code to 0AH
    156   // and the warm reset vector (DWORD based at 40:67) to point at
    157   // the AP startup code prior to the [universal startup algorithm]."
    158   outb(IO_RTC, 0xF);  // offset 0xF is shutdown code
    159   outb(IO_RTC+1, 0x0A);
    160   wrv = (ushort*)P2V((0x40<<4 | 0x67));  // Warm reset vector
    161   wrv[0] = 0;
    162   wrv[1] = addr >> 4;
    163 
    164   // "Universal startup algorithm."
    165   // Send INIT (level-triggered) interrupt to reset other CPU.
    166   lapicw(ICRHI, apicid<<24);
    167   lapicw(ICRLO, INIT | LEVEL | ASSERT);
    168   microdelay(200);
    169   lapicw(ICRLO, INIT | LEVEL);
    170   microdelay(100);    // should be 10ms, but too slow in Bochs!
    171   
    172   // Send startup IPI (twice!) to enter code.
    173   // Regular hardware is supposed to only accept a STARTUP
    174   // when it is in the halted state due to an INIT.  So the second
    175   // should be ignored, but it is part of the official Intel algorithm.
    176   // Bochs complains about the second one.  Too bad for Bochs.
    177   for(i = 0; i < 2; i++){
    178     lapicw(ICRHI, apicid<<24);
    179     lapicw(ICRLO, STARTUP | (addr>>12));
    180     microdelay(200);
    181   }
    182 }
    183 
    184