Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/arch/i386/percpu.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 /* Support for per-CPU variables, partially inspired by the Linux
0019  * implementation.  All per-CPU variables are declared in a special
0020  * non-loaded section so they all gain distinct addresses.  Then space
0021  * is allocated after the number of CPUs is determined.  A segment
0022  * descriptor for each space is setup, and each CPU loads a spare
0023  * segment register with it (like %fs).  Per-CPU variable access is
0024  * done, via macros, relative to that segment override. */
0025 
0026 /* Per-CPU variable initialization functions are arranged so that a
0027  * null-terminated array of pointers to these functions are placed in
0028  * a special section beginning at _percpu_ctor_list.  This is inspired
0029  * by the way C++ does global constructors. */
0030 
0031 #include <kernel.h>
0032 #include <util/debug.h>
0033 #include <mem/physical.h>
0034 #include <mem/virtual.h>
0035 #include <arch/i386-percpu.h>
0036 #include <smp/apic.h>
0037 
0038 //#define DEBUG_PERCPU
0039 
0040 #ifdef DEBUG_PERCPU
0041 #define DLOG(fmt,...) DLOG_PREFIX("percpu",fmt,##__VA_ARGS__)
0042 #else
0043 #define DLOG(fmt,...) ;
0044 #endif
0045 
0046 uint global_pcpu_id = 0;
0047 
0048 /* Establish a contiguous sequence of unique ID numbers, one for each
0049  * logical processor in the system. */
0050 DEF_PER_CPU (uint, pcpu_id);
0051 INIT_PER_CPU (pcpu_id) {
0052   percpu_write (pcpu_id, global_pcpu_id++);
0053 }
0054 extern uint
0055 get_pcpu_id (void)
0056 {
0057   return percpu_read (pcpu_id);
0058 }
0059 
0060 extern s8 _percpu_pages_plus_one;
0061 extern void (*_percpu_ctor_list)();
0062 
0063 u8 *percpu_virt[MAX_CPUS];
0064 
0065 /* should only be used on one processor at a time */
0066 extern void
0067 percpu_per_cpu_init (void)
0068 {
0069   descriptor *ad = (descriptor *)KERN_GDT;
0070   int i, pages = (int) &_percpu_pages_plus_one;
0071   int limit = pages * 0x1000 - 1;
0072 
0073   /* workaround: GCC will eliminate this if-statement when the check
0074    * is (pages == 0) because it thinks it knows the address of a
0075    * symbol can never be zero.  So, check (pages + 1 == 1) and then
0076    * subtract one. */
0077   if (pages == 1) {
0078     DLOG ("no per-CPU variables in system");
0079     /* fall back to the usual data segment */
0080     asm volatile ("movw %%ds, %%ax\n"
0081                   "movw %%ax, %%"PER_CPU_SEG_STR"\n"
0082                   "movzwl %%ax, %%eax\n"
0083                   "movl %%eax, %%"PER_CPU_DBG_STR"":::"eax");
0084     return;
0085   }
0086 
0087   pages--;
0088 
0089   for (i=1; i<256; i++)
0090     if (ad[i].fPresent == 0)
0091       break;
0092   if (i == 256) panic ("out of GDT");
0093 
0094   uint start_frame = alloc_phys_frames (pages);
0095   if (start_frame == -1) panic ("out of physical RAM");
0096   uint start_virt = (uint) map_contiguous_virtual_pages (start_frame | 3, pages);
0097   if (start_virt == 0) panic ("out of virtual RAM");
0098 
0099   DLOG ("LAPIC_get_physical_ID=0x%X", LAPIC_get_physical_ID ());
0100   /* global_pcpu_id will be the current ID until it is incremented in
0101    * the pcpu_id initialization function */
0102   percpu_virt[global_pcpu_id] = (u8 *) start_virt;
0103 
0104   descriptor seg = {
0105     .pBase0 = start_virt & 0xFFFF,
0106     .pBase1 = (start_virt >> 16) & 0xFF,
0107     .pBase2 = (start_virt >> 24) & 0xFF,
0108     .uLimit0 = limit & 0xFFFF,
0109     .uType  = 0x12,             /* writeable */
0110     .uDPL   = 0,
0111     .fPresent = 1,
0112     .uLimit1 = (limit >> 16) & 0xF,
0113     .f = 0,
0114     .f0 = 0,
0115     .fX = 1,
0116     .fGranularity = 0
0117   };
0118 
0119   memcpy (&ad[i], &seg, sizeof (seg));
0120 
0121   i <<= 3;
0122 
0123   asm volatile ("mov %0, %%"PER_CPU_SEG_STR"\n"
0124                 "mov %0, %%"PER_CPU_DBG_STR :: "r" (i));
0125 
0126   /* invoke initialization functions */
0127   void (**ctor) ();
0128   for (ctor = &_percpu_ctor_list; *ctor; ctor++)
0129     (*ctor) ();
0130 
0131   DLOG ("init n=%d percpu_pages=%d segsel=0x%X start_frame=%p start_virt=%p",
0132         percpu_read (pcpu_id), pages, i, start_frame, start_virt);
0133 }
0134 
0135 /*
0136  * Local Variables:
0137  * indent-tabs-mode: nil
0138  * mode: C
0139  * c-file-style: "gnu"
0140  * c-basic-offset: 2
0141  * End:
0142  */
0143 
0144 /* vi: set et sw=2 sts=2: */