Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/smp/smp.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 "arch/i386-percpu.h"
0020 #include "kernel.h"
0021 #include "mem/mem.h"
0022 #include "smp/smp.h"
0023 #include "smp/apic.h"
0024 #include "smp/spinlock.h"
0025 #include "drivers/acpi/acpi.h"
0026 #include "drivers/acpi/acmacros.h"
0027 #include "drivers/acpi/acexcep.h"
0028 #include "util/printf.h"
0029 #include "util/perfmon.h"
0030 #include "sched/sched.h"
0031 
0032 //#define DEBUG_SMP 1
0033 
0034 volatile bool mp_enabled = 0;
0035 uint32 mp_num_cpus = 1;
0036 bool mp_apic_mode = 0;
0037 bool mp_ISA_PC = 0;
0038 
0039 mp_int_override mp_overrides[MAX_INT_OVERRIDES];
0040 uint32 mp_num_overrides = 0;
0041 
0042 uint32
0043 IRQ_to_GSI (uint32 src_bus, uint32 src_irq)
0044 {
0045   /* This is for ISA only */
0046   int i;
0047   for (i = 0; i < mp_num_overrides; i++) {
0048     if (src_bus == mp_overrides[i].src_bus &&
0049         src_irq == mp_overrides[i].src_IRQ)
0050       return mp_overrides[i].dest_GSI;
0051   }
0052   if (src_bus != mp_ISA_bus_id)
0053     panic ("IRQ_to_GSI only implemented for ISA bus.");
0054   /* assume identity-mapped if not overriden */
0055   return src_irq;
0056 }
0057 
0058 
0059 /* Mapping from CPU # to APIC ID */
0060 uint8 CPU_to_APIC[MAX_CPUS];
0061 uint8 APIC_to_CPU[MAX_CPUS];
0062 
0063 bool mp_ACPI_enabled = 0;
0064 
0065 /* I hard-code a check for re-routing of the timer IRQ, but this
0066  * should probably be folded into a more general interrupt routing
0067  * system. */
0068 uint32 mp_timer_IOAPIC_irq = 0;
0069 uint32 mp_timer_IOAPIC_id = 0;
0070 uint32 mp_ISA_bus_id = 0;
0071 
0072 
0073 /* ************************************************** */
0074 /* General initialization for SMP */
0075 
0076 /* Returns number of CPUs successfully booted. */
0077 int
0078 smp_init (void)
0079 {
0080   uint32 phys_id, log_dest;
0081   uint32 intel_mps_init(bool);
0082   uint32 acpi_early_init(void);
0083   void LAPIC_measure_timer(void);
0084   void LAPIC_init(void);
0085 
0086   mp_apic_mode = 1;
0087 
0088   LAPIC_init();
0089   
0090   phys_id = get_pcpu_id();
0091 
0092   /* setup a logical destination address */
0093   log_dest = 0x01000000 << phys_id;
0094   LAPIC_set_logical_destination(log_dest);
0095 
0096   /* Find out how fast the LAPIC can tick -- and correspondingly the
0097    * CPU bus frequency.  Also finds the RDTSC frequency. */
0098   LAPIC_measure_timer ();
0099 
0100 
0101 #ifdef NO_ACPI
0102   if (0);
0103 #else
0104   if ((acpi_early_init()) > 0) {
0105     /* ACPI succeeded */
0106     mp_ACPI_enabled = 1;
0107   }
0108 #endif
0109 
0110 #ifdef NO_INTEL_MPS
0111   else if (0);
0112 #else     
0113   else if ((intel_mps_init(FALSE)) > 0) {
0114     /* Intel MPS succeeded */
0115   }
0116 #endif
0117 
0118   else {
0119     /* unable to detect any kind of SMP configuration */
0120     mp_apic_mode = 0;
0121     com1_printf ("Disabling APIC mode -- assuming ISA bus-only\n");
0122     mp_ISA_PC = 1;            /* assume no PCI */
0123     mp_num_cpus = 1;
0124     return 1;                 /* assume uniprocessor */
0125   }
0126 
0127   return mp_num_cpus;
0128 }
0129 
0130 void
0131 smp_secondary_init (void)
0132 {
0133   void acpi_secondary_init(void);
0134   void IOAPIC_init(void);
0135 
0136   if (!mp_ISA_PC)               /* ISA PCs do not have IO APICs */
0137     IOAPIC_init();
0138 
0139   if(mp_ACPI_enabled)
0140     acpi_secondary_init();
0141 
0142   /* The global variable mp_enabled will be incremented in the PIT IRQ
0143    * handler, this permits the Application Processors to go ahead and
0144    * complete initialization after the kernel has entered a
0145    * multi-processing safe state. */
0146 }
0147 
0148 void
0149 smp_enable_scheduling (void)
0150 {
0151   LAPIC_enable_timer(0x3e,   /* enable LAPIC timer int: vector=0x3e */
0152                      FALSE,  /* one-shot mode. */
0153                      1);     /* set LAPIC timer divisor to 1 */
0154 
0155   LAPIC_start_timer(cpu_bus_freq / QUANTUM_HZ); /* quantum */
0156 
0157   sched_enabled = 1;
0158 }
0159 
0160 /* ************************************************** */
0161 
0162 /* General SMP initialization functions */
0163 
0164 
0165 /* A number of symbols defined in the boot-smp.S file: */
0166 extern uint8 patch_code_start[];        /* patch_code is what the AP boots */
0167 extern uint8 patch_code_end[];
0168 extern uint8 status_code[];     /* the AP writes a 1 into here when it's ready */
0169 extern uint8 ap_stack_ptr[];    /* we give the AP a stack pointer through this */
0170 
0171 /* For some reason, if this function is 'static', and -O is on, then
0172  * qemu fails. */
0173 uint32
0174 smp_boot_cpu (uint8 apic_id, uint8 APIC_version)
0175 {
0176   int success = 1;
0177   volatile int to;
0178   uint32 bootaddr, accept_status;
0179 
0180   /* Get a page for the AP's C stack */
0181   uint32 page_frame = (uint32) alloc_phys_frame ();
0182   uint32 *virt_addr = map_virtual_page (page_frame | 3);
0183 
0184   /* Set up the boot code for the APs */
0185 #define TEST_BOOTED(x) (*((volatile uint32 *)(x+status_code-patch_code_start)))
0186 #define STACK_PTR(x)   (*((volatile uint32 *)(x+ap_stack_ptr-patch_code_start)))
0187 
0188   /* The patch code is memcpyed into the hardcoded address MP_BOOTADDR */
0189   bootaddr = MP_BOOTADDR;       /* identity mapped */
0190   memcpy ((uint8 *) bootaddr, patch_code_start,
0191           patch_code_end - patch_code_start);
0192   /* The status code is reset to 0 */
0193   TEST_BOOTED (bootaddr) = 0;
0194   /* A temporary stack is allocated for the AP to be able to call C code */
0195   STACK_PTR (bootaddr) = (uint32) virt_addr;
0196   /* (FIXME: when does this get de-allocated?) */
0197 
0198   /* CPU startup sequence: officially it is supposed to proceed:
0199    * ASSERT INIT IPI, DE-ASSERT INIT IPI, STARTUP IPI, STARTUP IPI */
0200 
0201   /* clear APIC error register */
0202   accept_status = LAPIC_clear_error();
0203 
0204   /* assert INIT interprocessor interrupt */
0205   LAPIC_send_ipi (apic_id,
0206                   LAPIC_ICR_TM_LEVEL | LAPIC_ICR_LEVELASSERT | LAPIC_ICR_DM_INIT);
0207 
0208   /* de-assert INIT IPI */
0209   LAPIC_send_ipi (apic_id, LAPIC_ICR_TM_LEVEL | LAPIC_ICR_DM_INIT);
0210 
0211   tsc_delay_usec (10000);       /* wait 10 millisec */
0212 
0213   /* Send start-up IPIs if not old version */
0214   if (APIC_version >= APIC_VER_NEW) {
0215     int i;
0216     for (i = 1; i <= 2; i++) {
0217       /* Bochs starts it @ INIT-IPI and the AP goes into p-mode which is
0218        * bad if I then deliver a STARTUP-IPI because that loads the CS
0219        * register with MP_BOOTADDR>>4 which is of course invalid in p-mode.  So
0220        * I added the test. */
0221       if (TEST_BOOTED (bootaddr))
0222         break;
0223       LAPIC_send_ipi (apic_id, LAPIC_ICR_DM_SIPI | ((bootaddr >> 12) & 0xFF));
0224       tsc_delay_usec (200);     /* wait 200 microsec */
0225     }
0226   }
0227 #define LOOPS_TO_WAIT 10000000
0228   /* Check for successful start */
0229   to = 0;
0230   while (!TEST_BOOTED (bootaddr) && to++ < LOOPS_TO_WAIT) {
0231     tsc_delay_usec (200);       /* wait 200 microsec */
0232   }
0233   if (to >= LOOPS_TO_WAIT) {
0234     success = 0;
0235   }
0236 
0237   /* cleanup */
0238   accept_status = LAPIC_clear_error();
0239 
0240   return success;
0241 }
0242 
0243 
0244 /* ************************************************** */
0245 
0246 /* When the AP boots it will first start at the patch code in
0247  * boot-smp.S.  That code prepares the AP for the jump into C code,
0248  * namely, this function: */
0249 void
0250 ap_init (void)
0251 {
0252   int phys_id, log_dest;
0253   void LAPIC_init(void);
0254 
0255   /* Setup the LAPIC */
0256   LAPIC_init();
0257   LAPIC_set_task_priority(0x20); /* task priority = 0x20 */
0258 
0259   phys_id = get_pcpu_id ();
0260 
0261   /* setup a logical destination address */
0262   log_dest = 0x01000000 << phys_id;
0263   LAPIC_set_logical_destination(log_dest);
0264 
0265   asm volatile ("lidt idt_ptr");        /* Set the IDT */
0266 
0267   /* Spin-wait for all processors to come online, and the system to
0268    * enter MP mode. */
0269   while (!mp_enabled)
0270     asm volatile ("pause");
0271 
0272   LAPIC_enable_timer(0x3e, FALSE, 1);    /* vector=0x3e, one-shot, divisor=1 */
0273   LAPIC_start_timer(cpu_bus_freq / QUANTUM_HZ); /* quantum */
0274   
0275   /* The AP is now operating in an SMP environment so the kernel must
0276    * be locked before any shared resources are utilized. */
0277   lock_kernel ();
0278 
0279   /* Performance monitoring */
0280   perfmon_init ();
0281 
0282   /* Load the per-CPU TSS for this AP */
0283   hw_ltr (cpuTSS_selector[phys_id]);
0284 
0285   /* Initialize virtual machine per-processor infrastructure */
0286   { void vmx_processor_init (void); vmx_processor_init (); }
0287 
0288   /* The IDLE task runs in kernelspace, therefore it is capable of
0289    * unlocking the kernel and manually enabling interrupts.  This
0290    * makes it safe to use lock_kernel() above.  */
0291 
0292   /* If the IDLE task did not run in kernelspace, then we would need a
0293    * dummy TSS per-processor because it would not be possible to
0294    * protect the dummy TSS from simultaneous usage by multiple CPUs in
0295    * this case. */
0296 
0297   ltr (idleTSS_selector[phys_id]);
0298 
0299   /* task-switch to IDLE task */
0300   asm volatile ("jmp _sw_init_task":
0301                 :"D" (lookup_TSS (idleTSS_selector[phys_id])));
0302 
0303   /* never return */
0304 
0305   panic ("AP: unreachable");
0306 }
0307 
0308 /* 
0309  * Local Variables:
0310  * indent-tabs-mode: nil
0311  * mode: C
0312  * c-file-style: "gnu"
0313  * c-basic-offset: 2
0314  * End: 
0315  */
0316 
0317 /* vi: set et sw=2 sts=2: */