source: rtems/c/src/lib/libbsp/i386/shared/smp/smp-imps.c @ baf8f4d

4.115
Last change on this file since baf8f4d was baf8f4d, checked in by Sebastian Huber <sebastian.huber@…>, on 05/14/13 at 08:12:52

smp: Simplify main CPU initialization

Call _SMP_Handler_initialize() later and move bsp_smp_initialize() into
_SMP_Handler_initialize(). Change bsp_smp_initialize() prototype to
match integer types of calling context.

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