root/lapic.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lapicw
  2. lapicinit
  3. cpunum
  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 "types.h"
   5 #include "defs.h"
   6 #include "date.h"
   7 #include "memlayout.h"
   8 #include "traps.h"
   9 #include "mmu.h"
  10 #include "x86.h"
  11 
  12 // Local APIC registers, divided by 4 for use as uint[] indices.
  13 #define ID      (0x0020/4)   // ID
  14 #define VER     (0x0030/4)   // Version
  15 #define TPR     (0x0080/4)   // Task Priority
  16 #define EOI     (0x00B0/4)   // EOI
  17 #define SVR     (0x00F0/4)   // Spurious Interrupt Vector
  18   #define ENABLE     0x00000100   // Unit Enable
  19 #define ESR     (0x0280/4)   // Error Status
  20 #define ICRLO   (0x0300/4)   // Interrupt Command
  21   #define INIT       0x00000500   // INIT/RESET
  22   #define STARTUP    0x00000600   // Startup IPI
  23   #define DELIVS     0x00001000   // Delivery status
  24   #define ASSERT     0x00004000   // Assert interrupt (vs deassert)
  25   #define DEASSERT   0x00000000
  26   #define LEVEL      0x00008000   // Level triggered
  27   #define BCAST      0x00080000   // Send to all APICs, including self.
  28   #define BUSY       0x00001000
  29   #define FIXED      0x00000000
  30 #define ICRHI   (0x0310/4)   // Interrupt Command [63:32]
  31 #define TIMER   (0x0320/4)   // Local Vector Table 0 (TIMER)
  32   #define X1         0x0000000B   // divide counts by 1
  33   #define PERIODIC   0x00020000   // Periodic
  34 #define PCINT   (0x0340/4)   // Performance Counter LVT
  35 #define LINT0   (0x0350/4)   // Local Vector Table 1 (LINT0)
  36 #define LINT1   (0x0360/4)   // Local Vector Table 2 (LINT1)
  37 #define ERROR   (0x0370/4)   // Local Vector Table 3 (ERROR)
  38   #define MASKED     0x00010000   // Interrupt masked
  39 #define TICR    (0x0380/4)   // Timer Initial Count
  40 #define TCCR    (0x0390/4)   // Timer Current Count
  41 #define TDCR    (0x03E0/4)   // Timer Divide Configuration
  42 
  43 volatile uint *lapic;  // Initialized in mp.c
  44 
  45 static void
  46 lapicw(int index, int value)
  47 {
  48   lapic[index] = value;
  49   lapic[ID];  // wait for write to finish, by reading
  50 }
  51 //PAGEBREAK!
  52 
  53 void
  54 lapicinit(void)
  55 {
  56   if(!lapic) 
  57     return;
  58 
  59   // Enable local APIC; set spurious interrupt vector.
  60   lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
  61 
  62   // The timer repeatedly counts down at bus frequency
  63   // from lapic[TICR] and then issues an interrupt.  
  64   // If xv6 cared more about precise timekeeping,
  65   // TICR would be calibrated using an external time source.
  66   lapicw(TDCR, X1);
  67   lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
  68   lapicw(TICR, 10000000); 
  69 
  70   // Disable logical interrupt lines.
  71   lapicw(LINT0, MASKED);
  72   lapicw(LINT1, MASKED);
  73 
  74   // Disable performance counter overflow interrupts
  75   // on machines that provide that interrupt entry.
  76   if(((lapic[VER]>>16) & 0xFF) >= 4)
  77     lapicw(PCINT, MASKED);
  78 
  79   // Map error interrupt to IRQ_ERROR.
  80   lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
  81 
  82   // Clear error status register (requires back-to-back writes).
  83   lapicw(ESR, 0);
  84   lapicw(ESR, 0);
  85 
  86   // Ack any outstanding interrupts.
  87   lapicw(EOI, 0);
  88 
  89   // Send an Init Level De-Assert to synchronise arbitration ID's.
  90   lapicw(ICRHI, 0);
  91   lapicw(ICRLO, BCAST | INIT | LEVEL);
  92   while(lapic[ICRLO] & DELIVS)
  93     ;
  94 
  95   // Enable interrupts on the APIC (but not on the processor).
  96   lapicw(TPR, 0);
  97 }
  98 
  99 int
 100 cpunum(void)
 101 {
 102   // Cannot call cpu when interrupts are enabled:
 103   // result not guaranteed to last long enough to be used!
 104   // Would prefer to panic but even printing is chancy here:
 105   // almost everything, including cprintf and panic, calls cpu,
 106   // often indirectly through acquire and release.
 107   if(readeflags()&FL_IF){
 108     static int n;
 109     if(n++ == 0)
 110       cprintf("cpu called from %x with interrupts enabled\n",
 111         __builtin_return_address(0));
 112   }
 113 
 114   if(lapic)
 115     return lapic[ID]>>24;
 116   return 0;
 117 }
 118 
 119 // Acknowledge interrupt.
 120 void
 121 lapiceoi(void)
 122 {
 123   if(lapic)
 124     lapicw(EOI, 0);
 125 }
 126 
 127 // Spin for a given number of microseconds.
 128 // On real hardware would want to tune this dynamically.
 129 void
 130 microdelay(int us)
 131 {
 132 }
 133 
 134 #define CMOS_PORT    0x70
 135 #define CMOS_RETURN  0x71
 136 
 137 // Start additional processor running entry code at addr.
 138 // See Appendix B of MultiProcessor Specification.
 139 void
 140 lapicstartap(uchar apicid, uint addr)
 141 {
 142   int i;
 143   ushort *wrv;
 144   
 145   // "The BSP must initialize CMOS shutdown code to 0AH
 146   // and the warm reset vector (DWORD based at 40:67) to point at
 147   // the AP startup code prior to the [universal startup algorithm]."
 148   outb(CMOS_PORT, 0xF);  // offset 0xF is shutdown code
 149   outb(CMOS_PORT+1, 0x0A);
 150   wrv = (ushort*)P2V((0x40<<4 | 0x67));  // Warm reset vector
 151   wrv[0] = 0;
 152   wrv[1] = addr >> 4;
 153 
 154   // "Universal startup algorithm."
 155   // Send INIT (level-triggered) interrupt to reset other CPU.
 156   lapicw(ICRHI, apicid<<24);
 157   lapicw(ICRLO, INIT | LEVEL | ASSERT);
 158   microdelay(200);
 159   lapicw(ICRLO, INIT | LEVEL);
 160   microdelay(100);    // should be 10ms, but too slow in Bochs!
 161   
 162   // Send startup IPI (twice!) to enter code.
 163   // Regular hardware is supposed to only accept a STARTUP
 164   // when it is in the halted state due to an INIT.  So the second
 165   // should be ignored, but it is part of the official Intel algorithm.
 166   // Bochs complains about the second one.  Too bad for Bochs.
 167   for(i = 0; i < 2; i++){
 168     lapicw(ICRHI, apicid<<24);
 169     lapicw(ICRLO, STARTUP | (addr>>12));
 170     microdelay(200);
 171   }
 172 }
 173 
 174 #define CMOS_STATA   0x0a
 175 #define CMOS_STATB   0x0b
 176 #define CMOS_UIP    (1 << 7)        // RTC update in progress
 177 
 178 #define SECS    0x00
 179 #define MINS    0x02
 180 #define HOURS   0x04
 181 #define DAY     0x07
 182 #define MONTH   0x08
 183 #define YEAR    0x09
 184 
 185 static uint cmos_read(uint reg)
 186 {
 187   outb(CMOS_PORT,  reg);
 188   microdelay(200);
 189 
 190   return inb(CMOS_RETURN);
 191 }
 192 
 193 static void fill_rtcdate(struct rtcdate *r)
 194 {
 195   r->second = cmos_read(SECS);
 196   r->minute = cmos_read(MINS);
 197   r->hour   = cmos_read(HOURS);
 198   r->day    = cmos_read(DAY);
 199   r->month  = cmos_read(MONTH);
 200   r->year   = cmos_read(YEAR);
 201 }
 202 
 203 // qemu seems to use 24-hour GWT and the values are BCD encoded
 204 void cmostime(struct rtcdate *r)
 205 {
 206   struct rtcdate t1, t2;
 207   int sb, bcd;
 208 
 209   sb = cmos_read(CMOS_STATB);
 210 
 211   bcd = (sb & (1 << 2)) == 0;
 212 
 213   // make sure CMOS doesn't modify time while we read it
 214   for (;;) {
 215     fill_rtcdate(&t1);
 216     if (cmos_read(CMOS_STATA) & CMOS_UIP)
 217         continue;
 218     fill_rtcdate(&t2);
 219     if (memcmp(&t1, &t2, sizeof(t1)) == 0)
 220       break;
 221   }
 222 
 223   // convert
 224   if (bcd) {
 225 #define    CONV(x)     (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf))
 226     CONV(second);
 227     CONV(minute);
 228     CONV(hour  );
 229     CONV(day   );
 230     CONV(month );
 231     CONV(year  );
 232 #undef     CONV
 233   }
 234 
 235   *r = t1;
 236   r->year += 2000;
 237 }

/* [<][>][^][v][top][bottom][index][help] */