Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/smp/apic.c need to be fixed.

0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 #include "arch/i386.h"
0019 #include "kernel.h"
0020 #include "mem/mem.h"
0021 #include "smp/smp.h"
0022 #include "smp/apic.h"
0023 #include "smp/spinlock.h"
0024 #include "util/printf.h"
0025 
0026 #define APIC_BROADCAST_ID 0xFF
0027 //#define MAX_CPUS          APIC_BROADCAST_ID
0028 
0029 #define LAPIC_ADDR_DEFAULT  0xFEE00000uL
0030 #define IOAPIC_ADDR_DEFAULT 0xFEC00000uL
0031 
0032 uint32 mp_LAPIC_addr = LAPIC_ADDR_DEFAULT;
0033 #define MP_LAPIC_READ(x)   (*((volatile uint32 *) (mp_LAPIC_addr+(x))))
0034 #define MP_LAPIC_WRITE(x,y) (*((volatile uint32 *) (mp_LAPIC_addr+(x))) = (y))
0035 
0036 uint32 mp_IOAPIC_addr = IOAPIC_ADDR_DEFAULT;
0037 mp_IOAPIC_info mp_IOAPICs[MAX_IOAPICS];
0038 uint32 mp_num_IOAPICs = 0;
0039 #define MP_IOAPIC_READ(x)   (*((volatile uint32 *) (mp_IOAPIC_addr+(x))))
0040 #define MP_IOAPIC_WRITE(x,y) (*((volatile uint32 *) (mp_IOAPIC_addr+(x))) = (y))
0041 
0042 /* ************************************************** */
0043 /* Local APIC programming */
0044 
0045 /* Send Interprocessor Interrupt -- prods the Local APIC to deliver an
0046  * interprocessor interrupt, 'v' specifies the vector but also
0047  * specifies flags according to the Intel System Programming Manual --
0048  * also see apic.h and the LAPIC_ICR_* constants. */
0049 int
0050 LAPIC_send_ipi (uint32 dest, uint32 v)
0051 {
0052   int timeout, send_status;
0053 
0054   /* It is a bad idea to have interrupts enabled while twiddling the
0055    * LAPIC. */
0056   asm volatile ("pushfl");
0057   asm volatile ("cli");
0058 
0059   MP_LAPIC_WRITE (LAPIC_ICR + 0x10, dest << 24);
0060   MP_LAPIC_WRITE (LAPIC_ICR, v);
0061 
0062   /* Give it a thousand iterations to see if the interrupt was
0063    * successfully delivered. */
0064   timeout = 0;
0065   do {
0066     send_status = MP_LAPIC_READ (LAPIC_ICR) & LAPIC_ICR_STATUS_PEND;
0067   } while (send_status && (timeout++ < 1000));
0068 
0069   asm volatile ("popfl");
0070 
0071   return (timeout < 1000);
0072 }
0073 
0074 void
0075 LAPIC_init(void) 
0076 {
0077   MP_LAPIC_WRITE (LAPIC_TPR, 0x00);      /* task priority = 0x0 */
0078   MP_LAPIC_WRITE (LAPIC_LVTT, 0x10000);  /* disable timer int */
0079   MP_LAPIC_WRITE (LAPIC_LVTPC, 0x10000); /* disable perf ctr int */
0080   MP_LAPIC_WRITE (LAPIC_LVT0, 0x08700); /* enable normal external ints */
0081   MP_LAPIC_WRITE (LAPIC_LVT1, 0x00400); /* enable NMI */
0082   MP_LAPIC_WRITE (LAPIC_LVTE, 0x10000); /* disable error ints */
0083   MP_LAPIC_WRITE (LAPIC_SPIV, 0x0010F); /* enable APIC: spurious vector = 0xF */
0084 
0085   /* be sure: */
0086   MP_LAPIC_WRITE (LAPIC_LVT1, 0x00400); /* enable NMI */
0087   MP_LAPIC_WRITE (LAPIC_LVTE, 0x10000); /* disable error ints */
0088 }
0089 
0090 void 
0091 LAPIC_set_task_priority(uint8 prio)
0092 {
0093   MP_LAPIC_WRITE (LAPIC_TPR, prio);
0094 }
0095 
0096 uint32
0097 LAPIC_clear_error(void) 
0098 {
0099   MP_LAPIC_WRITE (LAPIC_ESR, 0);
0100   return MP_LAPIC_READ (LAPIC_ESR);
0101 }
0102 
0103 uint8
0104 LAPIC_get_physical_ID (void)
0105 {
0106   if (mp_ISA_PC)
0107     return 0;
0108   else
0109     return (MP_LAPIC_READ (LAPIC_ID) >> 0x18) & 0xF;
0110 }
0111 
0112 void
0113 send_eoi (void)
0114 {
0115   if (mp_apic_mode) {
0116     MP_LAPIC_WRITE (LAPIC_EOI, 0);      /* send to LAPIC */
0117   } else {
0118     outb (0x20, 0x20);          /* send to 8259A PIC */
0119   }
0120 }
0121 
0122 void
0123 LAPIC_start_timer (uint32 count)
0124 {
0125   MP_LAPIC_WRITE (LAPIC_TICR, count);
0126 }
0127 
0128 void
0129 LAPIC_set_logical_destination(uint32 log_dest) {
0130   MP_LAPIC_WRITE (LAPIC_LDR, log_dest); /* write to logical destination reg */
0131   MP_LAPIC_WRITE (LAPIC_DFR, -1);       /* use 'flat model' destination format */
0132 }
0133 
0134 void
0135 LAPIC_enable_timer(uint8 vec, bool periodic, uint8 divisor) 
0136 {
0137   uint8 div_flag = 0xB;         /* default: div-by-1 */
0138 
0139   switch(divisor) {
0140   case 1: div_flag = 0xB; break;
0141   case 2: div_flag = 0x0; break;
0142   case 4: div_flag = 0x1; break;
0143   case 8: div_flag = 0x2; break;
0144   case 16: div_flag = 0x3; break;
0145   case 32: div_flag = 0x8; break;
0146   case 64: div_flag = 0x9; break;
0147   case 128: div_flag = 0xA; break;
0148   default: break;
0149   }
0150 
0151   if (periodic)
0152     MP_LAPIC_WRITE (LAPIC_LVTT, (1<<17) | (uint32)vec); 
0153   else
0154     MP_LAPIC_WRITE (LAPIC_LVTT, (uint32)vec); 
0155   MP_LAPIC_WRITE (LAPIC_TDCR, div_flag); 
0156 }
0157 
0158 void
0159 LAPIC_disable_timer(void)
0160 {
0161   MP_LAPIC_WRITE (LAPIC_LVTT, (1<<16));
0162 }
0163 
0164 
0165 /* CPU bus frequency measurement code -- the primary usage of this
0166  * code is for the purpose of programming the Local APIC timer because
0167  * it operates at the speed of the CPU bus.  So, in order to get an
0168  * idea of what that means in clock time, we need to compare the
0169  * frequency of the Local APIC to the frequency of the Programmable
0170  * Interval Timer. */
0171 
0172 /* In essence this boils down to counting how many ticks the Local
0173  * APIC can speed through for a single interval between PIT
0174  * interrupts. */
0175 
0176 /* NB: The local APIC is perhaps the most finely grained timer
0177  * commonly available on the PC architecture.  Expect precision on the
0178  * order of 100 nanoseconds, or better.  In fact, this may cause a
0179  * problem with integer overflow on the 32-bit architecture. */
0180 
0181 extern volatile uint32 tick;    /* defined in interrupt_handler.c */
0182 static void
0183 smp_setup_LAPIC_timer_int (void)
0184 {
0185   /* Temporary handler for the PIT-generated timer IRQ */
0186   tick++;
0187 
0188   outb (0x20, 0x20);            /* EOI -- still using PIC */
0189 
0190   /* Cheat a little here: the C compiler will insert 
0191    *   leave
0192    *   ret
0193    * but we want to fix the stack up and IRET so I just
0194    * stick that in right here. */
0195   asm volatile ("leave");
0196   asm volatile ("iret");
0197 }
0198 
0199 static void
0200 smp_LAPIC_timer_irq_handler (void)
0201 {
0202   /* Temporary handler for the LAPIC-generated timer interrupt */
0203   /* just EOI and ignore it */
0204   MP_LAPIC_WRITE (LAPIC_EOI, 0);        /* send to LAPIC -- this int came from LAPIC */
0205   asm volatile ("leave");
0206   asm volatile ("iret");
0207 }
0208 
0209 /* CPU BUS FREQUENCY -- IN HERTZ */
0210 uint32 cpu_bus_freq = 0;
0211 uint64 tsc_freq = 0;
0212 
0213 /* Use the PIT to find how fast the LAPIC ticks (and correspondingly,
0214  * the bus speed of the processor) */
0215 void
0216 LAPIC_measure_timer (void)
0217 {
0218   idt_descriptor old_timer, old_3f;
0219   uint32 value, start, count;
0220   uint32 tsc_start_hi, tsc_start_lo, tsc_value_hi, tsc_value_lo;
0221 
0222   /* I want to enable interrupts but I don't want the normal
0223    * interrupt-handling architecture to kick-in just yet.  Here's an
0224    * idea: mask off every single entry in the IDT! */
0225   disable_idt ();
0226 
0227   /* Now save the handlers for vectors 0x20 and 0x3F */
0228   get_idt_descriptor (0x20, &old_timer);
0229   get_idt_descriptor (0x3f, &old_3f);
0230   /* And use them for our temporary handlers */
0231   set_idt_descriptor_by_addr (0x20, (void *) &smp_setup_LAPIC_timer_int, 0x3);
0232   set_idt_descriptor_by_addr (0x3f, (void *) &smp_LAPIC_timer_irq_handler,
0233                               0x3);
0234 
0235   MP_LAPIC_WRITE (LAPIC_LVTT, 0x3f);    /* enable LAPIC timer int: vector=0x3f */
0236   MP_LAPIC_WRITE (LAPIC_TDCR, 0x0B);    /* set LAPIC timer divisor to 1 */
0237 
0238   /* Timing code: */
0239   value = tick;
0240   asm volatile ("sti");
0241   /* Wait until the PIT fires: */
0242   while (tick == value)
0243     asm volatile ("pause");
0244   start = tick;
0245   MP_LAPIC_WRITE (LAPIC_TICR, 0xFFFFFFFF);      /* write large value to Initial Count Reg. */
0246   asm volatile ("rdtsc":"=a" (tsc_start_lo), "=d" (tsc_start_hi));      /* store timestamp */
0247   /* LAPIC begins counting down, wait until the PIT fires again: */
0248   while (tick == start)
0249     asm volatile ("pause");
0250   asm volatile ("cli");
0251   asm volatile ("rdtsc":"=a" (tsc_value_lo), "=d" (tsc_value_hi));      /* store timestamp */
0252   MP_LAPIC_WRITE (LAPIC_LVTT, 0x10000); /* disable timer int */
0253   count = MP_LAPIC_READ (LAPIC_TCCR);   /* read the remaining count */
0254 
0255   /* Restore the original handlers: */
0256   set_idt_descriptor (0x20, &old_timer);
0257   set_idt_descriptor (0x3f, &old_3f);
0258 
0259   cpu_bus_freq = (0xFFFFFFFF - count) * HZ;
0260   com1_printf ("CPU bus frequency = 0x%X\n", cpu_bus_freq);
0261   tsc_freq = (((uint64) tsc_value_hi) << 32) | ((uint64) tsc_value_lo);
0262   tsc_freq -= ((uint64) tsc_start_hi) << 32;
0263   tsc_freq -= (uint64) tsc_start_lo;
0264   tsc_freq *= HZ;
0265   com1_printf ("TSC frequency = 0x%X 0x%X\n", (uint32) (tsc_freq >> 32),
0266                (uint32) tsc_freq);
0267 
0268   /* Put the IDT back in shape */
0269   enable_idt ();
0270 }
0271 
0272 /* End CPU Bus Frequency measurement code */
0273 
0274 /* ************************************************** */
0275 /* IO-APIC programming */
0276 
0277 void
0278 IOAPIC_init (void)
0279 {
0280   int i, j;
0281 
0282   outb (0xFF, 0x21);            /* Mask interrupts in Master/Slave 8259A PIC */
0283   outb (0xFF, 0xA1);
0284 
0285   /* To get to a consistent state, first disable all IO-APIC
0286    * redirection entries. */
0287 
0288   for (i = 0; i < mp_num_IOAPICs; i++) {
0289     for (j = 0; j < mp_IOAPICs[i].numGSIs; j++) {
0290       IOAPIC_write64 (IOAPIC_REDIR + (j * 2), 0x0000000000010000LL);
0291     }
0292   }
0293 
0294   /* Map timer IRQ to vector 0x20 */
0295   IOAPIC_map_GSI (IRQ_to_GSI (mp_ISA_bus_id, 0), 0x20, 0xFF00000000000800LL);
0296 
0297 #ifndef NO_IMCR_REDIRECT
0298   outb (0x70, 0x22);            /* Re-direct IMCR to use IO-APIC */
0299   outb (0x01, 0x23);            /* (for some motherboards) */
0300 #endif
0301 
0302 }
0303 
0304 uint64
0305 IOAPIC_read64 (uint8 reg)
0306 {
0307   uint32 high, low;
0308   uint64 retval;
0309   MP_IOAPIC_WRITE (IOAPIC_REGSEL, reg + 1);
0310   high = MP_IOAPIC_READ (IOAPIC_RW);
0311   MP_IOAPIC_WRITE (IOAPIC_REGSEL, reg);
0312   low = MP_IOAPIC_READ (IOAPIC_RW);
0313   retval = (uint64) high << 32;
0314   retval |= (uint64) low;
0315   return retval;
0316 }
0317 
0318 void
0319 IOAPIC_write64 (uint8 reg, uint64 v)
0320 {
0321   /* First, disable the entry by setting the mask bit */
0322   MP_IOAPIC_WRITE (IOAPIC_REGSEL, reg);
0323   MP_IOAPIC_WRITE (IOAPIC_RW, 0x10000);
0324   /* Write to the upper half */
0325   MP_IOAPIC_WRITE (IOAPIC_REGSEL, reg + 1);
0326   MP_IOAPIC_WRITE (IOAPIC_RW, (uint32) (v >> 32));
0327   /* Write to the lower half */
0328   MP_IOAPIC_WRITE (IOAPIC_REGSEL, reg);
0329   MP_IOAPIC_WRITE (IOAPIC_RW, (uint32) (v & 0xFFFFFFFF));
0330 }
0331 
0332 uint32
0333 IOAPIC_num_entries(void)
0334 {
0335   MP_IOAPIC_WRITE (IOAPIC_REGSEL, 0x1);
0336   return APIC_MAXREDIR (MP_IOAPIC_READ (IOAPIC_RW)) + 1;
0337 }
0338 
0339 mp_IOAPIC_info *
0340 IOAPIC_lookup (uint8 id)
0341 {
0342   int i;
0343   for (i = 0; i < mp_num_IOAPICs; i++) {
0344     if (mp_IOAPICs[i].id == id)
0345       return &mp_IOAPICs[i];
0346   }
0347   panic ("IOAPIC_lookup failed.");
0348 }
0349 
0350 int
0351 IOAPIC_map_GSI (uint32 GSI, uint8 vector, uint64 flags)
0352 {
0353   int i;
0354   uint32 old_addr = 0;
0355 
0356   if (mp_ISA_PC)
0357     return -1;
0358 
0359   /* First, figure out which IOAPIC */
0360   for (i = 0; i < mp_num_IOAPICs; i++) {
0361     if (mp_IOAPICs[i].startGSI <= GSI &&
0362         GSI < mp_IOAPICs[i].startGSI + mp_IOAPICs[i].numGSIs) {
0363       old_addr = mp_IOAPIC_addr;
0364       /* set address for macros to use: */
0365       mp_IOAPIC_addr = mp_IOAPICs[i].address;
0366       /* set GSI relative to startGSI: */
0367       GSI -= mp_IOAPICs[i].startGSI;
0368       break;
0369     }
0370   }
0371   if (!old_addr)
0372     return -1;                  /* not found */
0373 
0374   IOAPIC_write64 (IOAPIC_REDIR + (GSI * 2), flags | vector);
0375 
0376   /* clean up */
0377   mp_IOAPIC_addr = old_addr;
0378   return GSI;
0379 }
0380 
0381 int
0382 IOAPIC_get_GSI_mapping (uint32 GSI, uint8 *vector, uint64 *flags)
0383 {
0384   int i;
0385   uint32 old_addr = 0;
0386 
0387   if (mp_ISA_PC)
0388     return -1;
0389 
0390   /* First, figure out which IOAPIC */
0391   for (i = 0; i < mp_num_IOAPICs; i++) {
0392     if (mp_IOAPICs[i].startGSI <= GSI &&
0393         GSI < mp_IOAPICs[i].startGSI + mp_IOAPICs[i].numGSIs) {
0394       old_addr = mp_IOAPIC_addr;
0395       /* set address for macros to use: */
0396       mp_IOAPIC_addr = mp_IOAPICs[i].address;
0397       /* set GSI relative to startGSI: */
0398       GSI -= mp_IOAPICs[i].startGSI;
0399       break;
0400     }
0401   }
0402   if (!old_addr)
0403     return -1;                  /* not found */
0404 
0405   u64 entry = IOAPIC_read64 (IOAPIC_REDIR + (GSI * 2));
0406 
0407   if (flags)
0408     *flags = entry & (~0xFFULL);
0409   if (vector)
0410     *vector = (u8) (entry & (0xFFULL));
0411 
0412   /* clean up */
0413   mp_IOAPIC_addr = old_addr;
0414   return GSI;
0415 }
0416 
0417 void
0418 IOAPIC_dump_entries (void)
0419 {
0420   int i, j;
0421   u32 old_addr = mp_IOAPIC_addr;
0422   for (i=0;i<mp_num_IOAPICs;i++) {
0423     mp_IOAPIC_addr = mp_IOAPICs[i].address;
0424     for (j=0; j<mp_IOAPICs[i].numGSIs; j++) {
0425       logger_printf ("IOAPIC[%d][%d]=0x%llX\n",
0426                      i, j, IOAPIC_read64 (IOAPIC_REDIR + (j * 2)));
0427     }
0428   }
0429   mp_IOAPIC_addr = old_addr;
0430 }
0431 
0432 /* 
0433  * Local Variables:
0434  * indent-tabs-mode: nil
0435  * mode: C
0436  * c-file-style: "gnu"
0437  * c-basic-offset: 2
0438  * End: 
0439  */
0440 
0441 /* vi: set et sw=2 sts=2: */