source: rtems/bsps/i386/pc386/start/smp-imps.c @ 85d6a760

5
Last change on this file since 85d6a760 was 85d6a760, checked in by Jan Sommer <jan.sommer@…>, on May 31, 2020 at 2:22:53 PM

bsp/pc386: Update GDT to work for SMP

Create a GS segment in the GDT for each processor for storing TLS.
This makes the GDT in startAP.S obsolete as all processors now share the
same GDT, which is passed to each AP at startup.

The correct segment for each processor is calculated in cpu_asm.S.

Update #3335

  • Property mode set to 100644
File size: 22.3 KB
Line 
1/*
2 * Author: Erich Boleyn  <erich@uruk.org>
3 *         http://www.uruk.org/~erich/
4 *
5 * Copyright (c) 1997-2011 Erich Boleyn.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/*
31 *  Source file implementing Intel MultiProcessor Specification (MPS)
32 *  version 1.1 and 1.4 SMP hardware control for Intel Architecture CPUs,
33 *  with hooks for running correctly on a standard PC without the hardware.
34 *
35 *  This file was created from information in the Intel MPS version 1.4
36 *  document, order number 242016-004, which can be ordered from the
37 *  Intel literature center.
38 *
39 *  General limitations of this code:
40 *
41 *   (1) : This code has never been tested on an MPS-compatible system with
42 *           486 CPUs, but is expected to work.
43 *   (2) : Presumes "int", "long", and "unsigned" are 32 bits in size, and
44 *       that 32-bit pointers and memory addressing is used uniformly.
45 */
46
47#define _SMP_IMPS_C
48
49/*
50 *  Includes here
51 */
52#if 0
53#define IMPS_DEBUG
54#endif
55
56#include <bsp/apic.h>
57#include <bsp/smp-imps.h>
58#include <bsp/irq.h>
59#include <rtems/score/smpimpl.h>
60
61/*
62 *  XXXXX  The following absolutely must be defined!!!
63 *
64 *  The "KERNEL_PRINT" could be made a null macro with no danger, of
65 *  course, but pretty much nothing would work without the other
66 *  ones defined.
67 */
68
69#if 0
70#define KERNEL_PRINT(x)       /* some kind of print function */
71#define CMOS_WRITE_BYTE(x,y)  /* write unsigned char "y" at CMOS loc "x" */
72#define CMOS_READ_BYTE(x)     /* read unsigned char at CMOS loc "x" */
73#define PHYS_TO_VIRTUAL(x)    /* convert physical address "x" to virtual */
74#define VIRTUAL_TO_PHYS(x)    /* convert virtual address "x" to physical */
75#define UDELAY(x)             /* delay roughly at least "x" microsecs */
76#define READ_MSR_LO(x)        /* Read MSR low function */
77#else
78#include <string.h>
79#include <unistd.h>
80#include <rtems.h>
81#include <rtems/bspIo.h>
82#include <rtems/score/cpu.h>
83#include <assert.h>
84
85extern void _pc386_delay(void);
86extern uint32_t* gdtdesc;
87
88/* #define KERNEL_PRINT(_format)       printk(_format) */
89
90static void CMOS_WRITE_BYTE(
91  unsigned int  offset,
92  unsigned char value
93)
94{
95  if ( offset < 128 ) {
96    outport_byte( 0x70, offset );
97    outport_byte( 0x71, value );
98  } else {
99    outport_byte( 0x72, offset );
100    outport_byte( 0x73, value );
101  }
102}
103
104static unsigned char CMOS_READ_BYTE(
105  unsigned int  offset
106)
107{
108  unsigned char value;
109  if ( offset < 128 ) {
110    outport_byte( 0x70, offset );
111    inport_byte( 0x71, value );
112  } else {
113    outport_byte( 0x72, offset );
114    inport_byte( 0x73, value );
115  }
116  return value;
117}
118
119#define PHYS_TO_VIRTUAL(_x)    _x
120#define VIRTUAL_TO_PHYS(_x)    _x
121static void UDELAY(int x)
122{ int _i = x;
123  while ( _i-- )
124    _pc386_delay();
125}
126
127#define READ_MSR_LO(_x) \
128  (unsigned int)(read_msr(_x) & 0xffffffff)
129
130static inline unsigned long long read_msr(unsigned int msr)
131{
132  unsigned long long value;
133
134  asm volatile("rdmsr" : "=A" (value) : "c" (msr));
135  return value;
136}
137#endif
138
139/*
140 *  Defines that are here so as not to be in the global header file.
141 */
142#define EBDA_SEG_ADDR       0x40E
143#define BIOS_RESET_VECTOR   0x467
144#define LAPIC_ADDR_DEFAULT  0xFEE00000uL
145#define IOAPIC_ADDR_DEFAULT 0xFEC00000uL
146#define CMOS_RESET_CODE     0xF
147#define CMOS_RESET_JUMP     0xa
148#define CMOS_BASE_MEMORY    0x15
149
150/*
151 *  Static defines here for SMP use.
152 */
153
154#define DEF_ENTRIES  23
155
156static struct {
157  imps_processor proc[2];
158  imps_bus bus[2];
159  imps_ioapic ioapic;
160  imps_interrupt intin[16];
161  imps_interrupt lintin[2];
162} defconfig = {
163  { { IMPS_BCT_PROCESSOR, 0, 0, 0, 0, 0},
164    { IMPS_BCT_PROCESSOR, 1, 0, 0, 0, 0} },
165  { { IMPS_BCT_BUS, 0, {'E', 'I', 'S', 'A', ' ', ' '}},
166    { 255, 1, {'P', 'C', 'I', ' ', ' ', ' '}} },
167  { IMPS_BCT_IOAPIC, 0, 0, IMPS_FLAG_ENABLED, IOAPIC_ADDR_DEFAULT },
168  { { IMPS_BCT_IO_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 0, 0xFF, 0},
169    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 1, 0xFF, 1},
170    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 0, 0xFF, 2},
171    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 3, 0xFF, 3},
172    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 4, 0xFF, 4},
173    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 5, 0xFF, 5},
174    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 6, 0xFF, 6},
175    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 7, 0xFF, 7},
176    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 8, 0xFF, 8},
177    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 9, 0xFF, 9},
178    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 10, 0xFF, 10},
179    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 11, 0xFF, 11},
180    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 12, 0xFF, 12},
181    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 13, 0xFF, 13},
182    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 14, 0xFF, 14},
183    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 15, 0xFF, 15} },
184  { { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 15, 0xFF, 0},
185    { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_NMI, 0, 0, 15, 0xFF, 1} }
186};
187
188/*
189 *  Exported globals here.
190 */
191
192volatile int imps_release_cpus = 0;
193int imps_enabled = 0;
194int imps_num_cpus = 1;
195unsigned char imps_cpu_apic_map[IMPS_MAX_CPUS];
196unsigned char imps_apic_cpu_map[IMPS_MAX_CPUS];
197
198/* now defined in getcpuid.c */
199extern unsigned imps_lapic_addr;
200
201static void secondary_cpu_initialize(void);
202
203/*
204 *  MPS checksum function
205 *
206 *  Function finished.
207 */
208static int
209get_checksum(unsigned start, int length)
210{
211  unsigned sum = 0;
212
213  while (length-- > 0) {
214    sum += *((unsigned char *) (start++));
215  }
216
217  return (sum&0xFF);
218}
219
220/*
221 *  APIC ICR write and status check function.
222 */
223static int
224send_ipi(unsigned int dst, unsigned int v)
225{
226  int to, send_status;
227
228  IMPS_LAPIC_WRITE(LAPIC_ICR+0x10, (dst << 24));
229  IMPS_LAPIC_WRITE(LAPIC_ICR, v);
230
231  /* Wait for send to finish */
232  to = 0;
233  do {
234    UDELAY(100);
235    send_status = IMPS_LAPIC_READ(LAPIC_ICR) & LAPIC_ICR_STATUS_PEND;
236  } while (send_status && (to++ < 1000));
237
238  return (to < 1000);
239}
240
241/*
242 *  Primary function for booting individual CPUs.
243 *
244 *  This must be modified to perform whatever OS-specific initialization
245 *  that is required.
246 */
247static int
248boot_cpu(imps_processor *proc)
249{
250  int apicid = proc->apic_id, success = 1;
251  unsigned bootaddr;
252  unsigned bios_reset_vector = PHYS_TO_VIRTUAL(BIOS_RESET_VECTOR);
253
254  /*
255   * Copy boot code for secondary CPUs here.  Find it in between
256   * "patch_code_start" and "patch_code_end" symbols.  The other CPUs
257   * will start there in 16-bit real mode under the 1MB boundary.
258   * "patch_code_start" should be placed at a 4K-aligned address
259   * under the 1MB boundary.
260   */
261
262  volatile uint32_t *reset;
263
264  bootaddr = (512-64)*1024;
265  reset= (volatile uint32_t *)bootaddr;
266
267  memcpy(
268    (char *) bootaddr,
269    _binary_appstart_bin_start,
270    (size_t)_binary_appstart_bin_size
271  );
272
273  /* Pass start function, stack region and gdtdescr to AP
274   * see startAP.S for location */
275  reset[1] = (uint32_t)secondary_cpu_initialize;
276  reset[2] = (uint32_t)_Per_CPU_Get_by_index(apicid)->interrupt_stack_high;
277  memcpy(
278        (char*) &reset[3],
279        &gdtdesc,
280        6);
281  /*
282   *  Generic CPU startup sequence starts here.
283   */
284
285  /* set BIOS reset vector */
286  CMOS_WRITE_BYTE(CMOS_RESET_CODE, CMOS_RESET_JUMP);
287  *((volatile unsigned *) bios_reset_vector) = ((bootaddr & 0xFF000) << 12);
288
289  /* clear the APIC error register */
290  IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
291  IMPS_LAPIC_READ(LAPIC_ESR);
292
293  /* assert INIT IPI */
294  send_ipi(
295    apicid,
296    LAPIC_ICR_TM_LEVEL | LAPIC_ICR_LEVELASSERT | LAPIC_ICR_DM_INIT
297  );
298  UDELAY(10000);
299
300  /* de-assert INIT IPI */
301  send_ipi(apicid, LAPIC_ICR_TM_LEVEL | LAPIC_ICR_DM_INIT);
302
303  UDELAY(10000);
304
305  /*
306   *  Send Startup IPIs if not an old pre-integrated APIC.
307   */
308
309  if (proc->apic_ver >= APIC_VER_NEW) {
310    int i;
311    for (i = 1; i <= 2; i++) {
312      send_ipi(apicid, LAPIC_ICR_DM_SIPI | ((bootaddr >> 12) & 0xFF));
313      UDELAY(1000);
314    }
315  }
316
317  /*
318   *  Wait until AP is in protected mode before starting the next AP
319   */
320  while (reset[2] != 0);
321
322  /*
323   *  Generic CPU startup sequence ends here, the rest is cleanup.
324   */
325
326  /* clear the APIC error register */
327  IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
328  IMPS_LAPIC_READ(LAPIC_ESR);
329
330  /* clean up BIOS reset vector */
331  CMOS_WRITE_BYTE(CMOS_RESET_CODE, 0);
332  *((volatile unsigned *) bios_reset_vector) = 0;
333
334  return success;
335}
336
337/*
338 *  read bios stuff and fill tables
339 */
340static void
341add_processor(imps_processor *proc)
342{
343  int apicid = proc->apic_id;
344
345  printk("  Processor [APIC id %d ver %d]: ", apicid, proc->apic_ver);
346  if (!(proc->flags & IMPS_FLAG_ENABLED)) {
347    printk("DISABLED\n");
348    return;
349  }
350  if (proc->flags & (IMPS_CPUFLAG_BOOT)) {
351    printk("#0  BootStrap Processor (BSP)\n");
352    return;
353  }
354  /* Setup the apic/cpu maps before booting the APs
355   * otherwise calls to _Get_current_processor can deliver
356   * wrong values if the BSP gets interrupted
357   */
358  imps_cpu_apic_map[imps_num_cpus] = apicid;
359  imps_apic_cpu_map[apicid] = imps_num_cpus;
360  if (boot_cpu(proc)) {
361
362    /*  XXXXX  add OS-specific setup for secondary CPUs here */
363
364    /* AP booted successfully, increase number of available cores */
365    imps_num_cpus++;
366    printk("#%d  Application Processor (AP)\n", imps_apic_cpu_map[apicid]);
367  }
368}
369
370
371static void
372add_bus(imps_bus *bus)
373{
374  char str[8];
375
376  memcpy(str, bus->bus_type, 6);
377  str[6] = 0;
378  printk("  Bus id %d is %s\n", bus->id, str);
379
380  /*  XXXXX  add OS-specific code here */
381}
382
383static void
384add_ioapic(imps_ioapic *ioapic)
385{
386  printk("  I/O APIC id %d ver %d, address: 0x%x  ",
387          ioapic->id, ioapic->ver, ioapic->addr);
388  if (!(ioapic->flags & IMPS_FLAG_ENABLED)) {
389    printk("DISABLED\n");
390    return;
391  }
392  printk("\n");
393
394  /*  XXXXX  add OS-specific code here */
395}
396
397static void
398imps_read_config_table(unsigned start, int count)
399{
400  while (count-- > 0) {
401    switch (*((unsigned char *)start)) {
402    case IMPS_BCT_PROCESSOR:
403      if ( imps_num_cpus < rtems_configuration_get_maximum_processors() ) {
404        if (_SMP_Should_start_processor((uint32_t) imps_num_cpus)) {
405          add_processor((imps_processor *)start);
406        }
407      } else
408        imps_num_cpus++;
409      start += 12;  /* 20 total */
410      break;
411    case IMPS_BCT_BUS:
412      add_bus((imps_bus *)start);
413      break;
414    case IMPS_BCT_IOAPIC:
415      add_ioapic((imps_ioapic *)start);
416      break;
417#if 0  /*  XXXXX  uncomment this if "add_io_interrupt" is implemented */
418    case IMPS_BCT_IO_INTERRUPT:
419      add_io_interrupt((imps_interrupt *)start);
420      break;
421#endif
422#if 0  /*  XXXXX  uncomment this if "add_local_interrupt" is implemented */
423    case IMPS_BCT_LOCAL_INTERRUPT:
424      add_local_interupt((imps_interrupt *)start);
425      break;
426#endif
427    default:
428      break;
429    }
430    start += 8;
431  }
432  if ( imps_num_cpus > rtems_configuration_get_maximum_processors() ) {
433    printk(
434      "WARNING!! Found more CPUs (%d) than configured for (%d)!!\n",
435      imps_num_cpus - 1,
436      rtems_configuration_get_maximum_processors()
437    );
438    imps_num_cpus = rtems_configuration_get_maximum_processors();
439    return;
440  }
441}
442
443static int
444imps_bad_bios(imps_fps *fps_ptr)
445{
446  int sum;
447  imps_cth *local_cth_ptr
448    = (imps_cth *) PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
449
450  if (fps_ptr->feature_info[0] > IMPS_FPS_DEFAULT_MAX) {
451    printk("    Invalid MP System Configuration type %d\n",
452            fps_ptr->feature_info[0]);
453    return 1;
454  }
455
456  if (fps_ptr->cth_ptr) {
457    sum = get_checksum((unsigned)local_cth_ptr,
458                                   local_cth_ptr->base_length);
459    if (local_cth_ptr->sig != IMPS_CTH_SIGNATURE || sum) {
460      printk(
461        "    Bad MP Config Table sig 0x%x and/or checksum 0x%x\n",
462        (unsigned)(fps_ptr->cth_ptr),
463        sum
464      );
465      return 1;
466    }
467    if (local_cth_ptr->spec_rev != fps_ptr->spec_rev) {
468      printk(
469        "    Bad MP Config Table sub-revision # %d\n",
470        local_cth_ptr->spec_rev
471      );
472      return 1;
473    }
474    if (local_cth_ptr->extended_length) {
475      sum = (get_checksum(((unsigned)local_cth_ptr)
476              + local_cth_ptr->base_length,
477              local_cth_ptr->extended_length)
478             + local_cth_ptr->extended_checksum) & 0xFF;
479      if (sum) {
480        printk("    Bad Extended MP Config Table checksum 0x%x\n", sum);
481        return 1;
482      }
483    }
484  } else if (!fps_ptr->feature_info[0]) {
485    printk("    Missing configuration information\n");
486    return 1;
487  }
488
489  return 0;
490}
491
492static void
493imps_read_bios(imps_fps *fps_ptr)
494{
495  int apicid;
496  unsigned cth_start, cth_count;
497  imps_cth *local_cth_ptr
498    = (imps_cth *)PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
499  char *str_ptr;
500
501  printk("Intel MultiProcessor Spec 1.%d BIOS support detected\n",
502          fps_ptr->spec_rev);
503
504  /*
505   *  Do all checking of errors which would definitely
506   *  lead to failure of the SMP boot here.
507   */
508  if (imps_bad_bios(fps_ptr)) {
509    printk("    Disabling MPS support\n");
510    return;
511  }
512
513  if (fps_ptr->feature_info[1] & IMPS_FPS_IMCRP_BIT) {
514    str_ptr = "IMCR and PIC";
515  } else {
516    str_ptr = "Virtual Wire";
517  }
518  if (fps_ptr->cth_ptr) {
519    imps_lapic_addr = local_cth_ptr->lapic_addr;
520  } else {
521    imps_lapic_addr = LAPIC_ADDR_DEFAULT;
522  }
523  printk("    APIC config: \"%s mode\"    Local APIC address: 0x%x\n",
524          str_ptr, imps_lapic_addr);
525  if (imps_lapic_addr != (READ_MSR_LO(0x1b) & 0xFFFFF000)) {
526    printk("Inconsistent Local APIC address, Disabling SMP support\n");
527    return;
528  }
529  imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
530
531  /*
532   *  Setup primary CPU.
533   */
534  apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
535  IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
536  apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
537  imps_cpu_apic_map[0] = apicid;
538  imps_apic_cpu_map[apicid] = 0;
539
540  if (fps_ptr->cth_ptr) {
541    char str1[16], str2[16];
542    memcpy(str1, local_cth_ptr->oem_id, 8);
543    str1[8] = 0;
544    memcpy(str2, local_cth_ptr->prod_id, 12);
545    str2[12] = 0;
546    printk("  OEM id: %s  Product id: %s\n", str1, str2);
547    cth_start = ((unsigned) local_cth_ptr) + sizeof(imps_cth);
548    cth_count = local_cth_ptr->entry_count;
549  } else {
550    *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) =  IOAPIC_ID;
551    defconfig.ioapic.id
552      = APIC_ID(*((volatile unsigned *)
553            (IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
554    *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) =  IOAPIC_VER;
555    defconfig.ioapic.ver
556      = APIC_VERSION(*((volatile unsigned *)
557           (IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
558    defconfig.proc[apicid].flags
559      = IMPS_FLAG_ENABLED|IMPS_CPUFLAG_BOOT;
560    defconfig.proc[!apicid].flags = IMPS_FLAG_ENABLED;
561    imps_num_cpus = 2;
562    if (fps_ptr->feature_info[0] == 1
563     || fps_ptr->feature_info[0] == 5) {
564      memcpy(defconfig.bus[0].bus_type, "ISA   ", 6);
565    }
566    if (fps_ptr->feature_info[0] == 4
567     || fps_ptr->feature_info[0] == 7) {
568      memcpy(defconfig.bus[0].bus_type, "MCA   ", 6);
569    }
570    if (fps_ptr->feature_info[0] > 4) {
571      defconfig.proc[0].apic_ver = 0x10;
572      defconfig.proc[1].apic_ver = 0x10;
573      defconfig.bus[1].type = IMPS_BCT_BUS;
574    }
575    if (fps_ptr->feature_info[0] == 2) {
576      defconfig.intin[2].type = 255;
577      defconfig.intin[13].type = 255;
578    }
579    if (fps_ptr->feature_info[0] == 7) {
580      defconfig.intin[0].type = 255;
581    }
582    cth_start = (unsigned) &defconfig;
583    cth_count = DEF_ENTRIES;
584  }
585  imps_read_config_table(cth_start, cth_count);
586
587  /* %%%%% ESB read extended entries here */
588
589  imps_enabled = 1;
590}
591
592/*
593 *  Given a region to check, this actually looks for the "MP Floating
594 *  Pointer Structure".  The return value indicates if the correct
595 *  signature and checksum for a floating pointer structure of the
596 *  appropriate spec revision was found.  If so, then do not search
597 *  further.
598 *
599 *  NOTE:  The memory scan will always be in the bottom 1 MB.
600 *
601 *  This function presumes that "start" will always be aligned to a 16-bit
602 *  boundary.
603 *
604 *  Function finished.
605 */
606static int
607imps_scan(unsigned start, unsigned length)
608{
609  printk("Scanning from 0x%x for %d bytes\n", start, length);
610
611  while (length > 0) {
612    imps_fps *fps_ptr = (imps_fps *) PHYS_TO_VIRTUAL(start);
613
614    if (fps_ptr->sig == IMPS_FPS_SIGNATURE
615     && fps_ptr->length == 1
616     && (fps_ptr->spec_rev == 1 || fps_ptr->spec_rev == 4)
617     && !get_checksum(start, 16)) {
618      printk("Found MP Floating Structure Pointer at %x\n", start);
619      imps_read_bios(fps_ptr);
620      return 1;
621    }
622
623    length -= 16;
624    start += 16;
625  }
626
627  return 0;
628}
629
630#if !defined(__rtems__)
631/*
632 *  This is the primary function to "force" SMP support, with
633 *  the assumption that you have consecutively numbered APIC ids.
634 */
635int
636imps_force(int ncpus)
637{
638  int apicid, i;
639  imps_processor p;
640
641  printk("Intel MultiProcessor \"Force\" Support\n");
642
643  imps_lapic_addr = (READ_MSR_LO(0x1b) & 0xFFFFF000);
644  imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
645
646  /*
647   *  Setup primary CPU.
648   */
649  apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
650  IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
651  apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
652  imps_cpu_apic_map[0] = apicid;
653  imps_apic_cpu_map[apicid] = 0;
654
655  p.type = 0;
656  p.apic_ver = 0x10;
657  p.signature = p.features = 0;
658
659  for (i = 0; i < ncpus; i++) {
660    if (apicid == i) {
661      p.flags = IMPS_FLAG_ENABLED | IMPS_CPUFLAG_BOOT;
662    } else {
663      p.flags = IMPS_FLAG_ENABLED;
664    }
665    p.apic_id = i;
666    add_processor(&p);
667  }
668
669  return imps_num_cpus;
670}
671#endif
672
673/*
674 *  This is the primary function for probing for MPS compatible hardware
675 *  and BIOS information.  Call this during the early stages of OS startup,
676 *  before memory can be messed up.
677 *
678 *  The probe looks for the "MP Floating Pointer Structure" at locations
679 *  listed at the top of page 4-2 of the spec.
680 *
681 *  Environment requirements from the OS to run:
682 *
683 *   (1) : A non-linear virtual to physical memory mapping is probably OK,
684 *       as (I think) the structures all fall within page boundaries,
685 *       but a linear mapping is recommended.  Currently assumes that
686 *       the mapping will remain identical over time (which should be
687 *       OK since it only accesses memory which shouldn't be munged
688 *       by the OS anyway).
689 *   (2) : The OS only consumes memory which the BIOS says is OK to use,
690 *       and not any of the BIOS standard areas (the areas 0x400 to
691 *       0x600, the EBDA, 0xE0000 to 0xFFFFF, and unreported physical
692 *       RAM).  Sometimes a small amount of physical RAM is not
693 *       reported by the BIOS, to be used to store MPS and other
694 *       information.
695 *   (3) : It must be possible to read the CMOS.
696 *   (4) : There must be between 512K and 640K of lower memory (this is a
697 *       sanity check).
698 *
699 *  Function finished.
700 */
701static int
702imps_probe(void)
703{
704  /*
705   *  Determine possible address of the EBDA
706   */
707  unsigned ebda_addr = *((unsigned short *)
708             PHYS_TO_VIRTUAL(EBDA_SEG_ADDR)) << 4;
709
710  /*
711   *  Determine amount of installed lower memory (not *available*
712   *  lower memory).
713   *
714   *  NOTE:  This should work reliably as long as we verify the
715   *         machine is at least a system that could possibly have
716   *         MPS compatibility to begin with.
717   */
718  unsigned mem_lower = ((CMOS_READ_BYTE(CMOS_BASE_MEMORY+1) << 8)
719            | CMOS_READ_BYTE(CMOS_BASE_MEMORY))       << 10;
720
721#ifdef IMPS_DEBUG
722  imps_enabled = 0;
723  imps_num_cpus = 1;
724#endif
725
726  /*
727   *  Sanity check : if this isn't reasonable, it is almost impossibly
728   *    unlikely to be an MPS compatible machine, so return failure.
729   */
730  if (mem_lower < 512*1024 || mem_lower > 640*1024) {
731    return 0;
732  }
733
734  if (ebda_addr > mem_lower - 1024
735   || ebda_addr + *((unsigned char *) PHYS_TO_VIRTUAL(ebda_addr))
736         * 1024 > mem_lower) {
737    ebda_addr = 0;
738  }
739
740  if (((ebda_addr && imps_scan(ebda_addr, 1024))
741   || (!ebda_addr && imps_scan(mem_lower - 1024, 1024))
742   || imps_scan(0xF0000, 0x10000)) && imps_enabled) {
743    return imps_num_cpus;
744  }
745
746  /*
747   *  If no BIOS info on MPS hardware is found, then return failure.
748   */
749
750  return 0;
751}
752
753/*
754 *  RTEMS SMP BSP Support
755 */
756static void smp_apic_ack(void)
757{
758  (void) IMPS_LAPIC_READ(LAPIC_SPIV);  /* dummy read */
759  IMPS_LAPIC_WRITE(LAPIC_EOI, 0 );     /* ACK the interrupt */
760}
761
762static void bsp_inter_processor_interrupt(void *arg)
763{
764  (void) arg;
765
766  smp_apic_ack();
767
768  _SMP_Inter_processor_interrupt_handler(_Per_CPU_Get());
769}
770
771static void ipi_install_irq(void)
772{
773  rtems_status_code status;
774
775  status = rtems_interrupt_handler_install(
776    16,
777    "smp-imps",
778    RTEMS_INTERRUPT_UNIQUE,
779    bsp_inter_processor_interrupt,
780    NULL
781  );
782  assert(status == RTEMS_SUCCESSFUL);
783}
784
785#ifdef __SSE__
786extern void enable_sse(void);
787#endif
788
789/* pc386 specific initialization */
790static void secondary_cpu_initialize(void)
791{
792  int apicid;
793
794  asm volatile( "lidt IDT_Descriptor" );
795
796  apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
797  IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
798
799#ifdef __SSE__
800  enable_sse();
801#endif
802
803  _SMP_Start_multitasking_on_secondary_processor( _Per_CPU_Get() );
804}
805
806uint32_t _CPU_SMP_Initialize( void )
807{
808  /* XXX need to deal with finding too many cores */
809
810  return (uint32_t) imps_probe();
811}
812
813void _CPU_SMP_Prepare_start_multitasking( void )
814{
815  /* Do nothing */
816}
817
818bool _CPU_SMP_Start_processor( uint32_t cpu_index )
819{
820  (void) cpu_index;
821
822  return true;
823}
824
825void _CPU_SMP_Finalize_initialization( uint32_t cpu_count )
826{
827  if ( cpu_count > 1 )
828    ipi_install_irq();
829}
830
831void _CPU_SMP_Send_interrupt( uint32_t target_processor_index )
832{
833  send_ipi( target_processor_index, 0x30 );
834}
Note: See TracBrowser for help on using the repository browser.