root/lapic.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. lapicw
  2. lapicinit
  3. lapicid
  4. lapiceoi
  5. microdelay
  6. lapicstartap
  7. cmos_read
  8. fill_rtcdate
  9. cmostime

   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 "param.h"
   5 #include "types.h"
   6 #include "defs.h"
   7 #include "date.h"
   8 #include "memlayout.h"
   9 #include "traps.h"
  10 #include "mmu.h"
  11 #include "x86.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 //PAGEBREAK!
  47 static void
  48 lapicw(int index, int value)
     /* [previous][next][first][last][top][bottom][index][help]  */
  49 {
  50   lapic[index] = value;
  51   lapic[ID];  // wait for write to finish, by reading
  52 }
  53 
  54 void
  55 lapicinit(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  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 int
 101 lapicid(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 102 {
 103   if (!lapic)
 104     return 0;
 105   return lapic[ID] >> 24;
 106 }
 107 
 108 // Acknowledge interrupt.
 109 void
 110 lapiceoi(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 111 {
 112   if(lapic)
 113     lapicw(EOI, 0);
 114 }
 115 
 116 // Spin for a given number of microseconds.
 117 // On real hardware would want to tune this dynamically.
 118 void
 119 microdelay(int us)
     /* [previous][next][first][last][top][bottom][index][help]  */
 120 {
 121 }
 122 
 123 #define CMOS_PORT    0x70
 124 #define CMOS_RETURN  0x71
 125 
 126 // Start additional processor running entry code at addr.
 127 // See Appendix B of MultiProcessor Specification.
 128 void
 129 lapicstartap(uchar apicid, uint addr)
     /* [previous][next][first][last][top][bottom][index][help]  */
 130 {
 131   int i;
 132   ushort *wrv;
 133 
 134   // "The BSP must initialize CMOS shutdown code to 0AH
 135   // and the warm reset vector (DWORD based at 40:67) to point at
 136   // the AP startup code prior to the [universal startup algorithm]."
 137   outb(CMOS_PORT, 0xF);  // offset 0xF is shutdown code
 138   outb(CMOS_PORT+1, 0x0A);
 139   wrv = (ushort*)P2V((0x40<<4 | 0x67));  // Warm reset vector
 140   wrv[0] = 0;
 141   wrv[1] = addr >> 4;
 142 
 143   // "Universal startup algorithm."
 144   // Send INIT (level-triggered) interrupt to reset other CPU.
 145   lapicw(ICRHI, apicid<<24);
 146   lapicw(ICRLO, INIT | LEVEL | ASSERT);
 147   microdelay(200);
 148   lapicw(ICRLO, INIT | LEVEL);
 149   microdelay(100);    // should be 10ms, but too slow in Bochs!
 150 
 151   // Send startup IPI (twice!) to enter code.
 152   // Regular hardware is supposed to only accept a STARTUP
 153   // when it is in the halted state due to an INIT.  So the second
 154   // should be ignored, but it is part of the official Intel algorithm.
 155   // Bochs complains about the second one.  Too bad for Bochs.
 156   for(i = 0; i < 2; i++){
 157     lapicw(ICRHI, apicid<<24);
 158     lapicw(ICRLO, STARTUP | (addr>>12));
 159     microdelay(200);
 160   }
 161 }
 162 
 163 #define CMOS_STATA   0x0a
 164 #define CMOS_STATB   0x0b
 165 #define CMOS_UIP    (1 << 7)        // RTC update in progress
 166 
 167 #define SECS    0x00
 168 #define MINS    0x02
 169 #define HOURS   0x04
 170 #define DAY     0x07
 171 #define MONTH   0x08
 172 #define YEAR    0x09
 173 
 174 static uint
 175 cmos_read(uint reg)
     /* [previous][next][first][last][top][bottom][index][help]  */
 176 {
 177   outb(CMOS_PORT,  reg);
 178   microdelay(200);
 179 
 180   return inb(CMOS_RETURN);
 181 }
 182 
 183 static void
 184 fill_rtcdate(struct rtcdate *r)
     /* [previous][next][first][last][top][bottom][index][help]  */
 185 {
 186   r->second = cmos_read(SECS);
 187   r->minute = cmos_read(MINS);
 188   r->hour   = cmos_read(HOURS);
 189   r->day    = cmos_read(DAY);
 190   r->month  = cmos_read(MONTH);
 191   r->year   = cmos_read(YEAR);
 192 }
 193 
 194 // qemu seems to use 24-hour GWT and the values are BCD encoded
 195 void
 196 cmostime(struct rtcdate *r)
     /* [previous][next][first][last][top][bottom][index][help]  */
 197 {
 198   struct rtcdate t1, t2;
 199   int sb, bcd;
 200 
 201   sb = cmos_read(CMOS_STATB);
 202 
 203   bcd = (sb & (1 << 2)) == 0;
 204 
 205   // make sure CMOS doesn't modify time while we read it
 206   for(;;) {
 207     fill_rtcdate(&t1);
 208     if(cmos_read(CMOS_STATA) & CMOS_UIP)
 209         continue;
 210     fill_rtcdate(&t2);
 211     if(memcmp(&t1, &t2, sizeof(t1)) == 0)
 212       break;
 213   }
 214 
 215   // convert
 216   if(bcd) {
 217 #define    CONV(x)     (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf))
 218     CONV(second);
 219     CONV(minute);
 220     CONV(hour  );
 221     CONV(day   );
 222     CONV(month );
 223     CONV(year  );
 224 #undef     CONV
 225   }
 226 
 227   *r = t1;
 228   r->year += 2000;
 229 }

/* [previous][next][first][last][top][bottom][index][help]  */