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

4.115
Last change on this file since 53e008b was 53e008b, checked in by Sebastian Huber <sebastian.huber@…>, on 04/10/14 at 13:48:05

score: SMP initialization changes

Add and use _CPU_SMP_Start_processor(). Add and use
_CPU_SMP_Finalize_initialization(). This makes most
_CPU_SMP_Initialize() functions a bit simpler since we can calculate the
minimum value of the count of processors requested by the application
configuration and the count of physically or virtually available
processors in the high-level code.

The CPU port has now the ability to signal a processor start failure.
With the support for clustered/partitioned scheduling the presence of
particular processors can be configured to be optional or mandatory.
There will be a fatal error only in case mandatory processors are not
present.

The CPU port may use a timeout to monitor the start of a processor.

  • Property mode set to 100644
File size: 21.6 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 <libcpu/cpu.h>
83#include <assert.h>
84
85extern void _pc386_delay(void);
86
87/* #define KERNEL_PRINT(_format)       printk(_format) */
88
89static void CMOS_WRITE_BYTE(
90  unsigned int  offset,
91  unsigned char value
92)
93{
94  if ( offset < 128 ) {
95    outport_byte( 0x70, offset );
96    outport_byte( 0x71, value );
97  } else {
98    outport_byte( 0x72, offset );
99    outport_byte( 0x73, value );
100  }
101}
102
103static unsigned char CMOS_READ_BYTE(
104  unsigned int  offset
105)
106{
107  unsigned char value;
108  if ( offset < 128 ) {
109    outport_byte( 0x70, offset );
110    inport_byte( 0x71, value );
111  } else {
112    outport_byte( 0x72, offset );
113    inport_byte( 0x73, value );
114  }
115  return value;
116}
117
118#define PHYS_TO_VIRTUAL(_x)    _x
119#define VIRTUAL_TO_PHYS(_x)    _x
120static void UDELAY(int x)
121{ int _i = x;
122  while ( _i-- )
123    _pc386_delay();
124}
125
126#define READ_MSR_LO(_x) \
127  (unsigned int)(read_msr(_x) & 0xffffffff)
128
129static inline unsigned long long read_msr(unsigned int msr)
130{
131  unsigned long long value;
132
133  asm volatile("rdmsr" : "=A" (value) : "c" (msr));
134  return value;
135}
136#endif
137
138/*
139 *  Defines that are here so as not to be in the global header file.
140 */
141#define EBDA_SEG_ADDR       0x40E
142#define BIOS_RESET_VECTOR   0x467
143#define LAPIC_ADDR_DEFAULT  0xFEE00000uL
144#define IOAPIC_ADDR_DEFAULT 0xFEC00000uL
145#define CMOS_RESET_CODE     0xF
146#define CMOS_RESET_JUMP     0xa
147#define CMOS_BASE_MEMORY    0x15
148
149/*
150 *  Static defines here for SMP use.
151 */
152
153#define DEF_ENTRIES  23
154
155static struct {
156  imps_processor proc[2];
157  imps_bus bus[2];
158  imps_ioapic ioapic;
159  imps_interrupt intin[16];
160  imps_interrupt lintin[2];
161} defconfig = {
162  { { IMPS_BCT_PROCESSOR, 0, 0, 0, 0, 0},
163    { IMPS_BCT_PROCESSOR, 1, 0, 0, 0, 0} },
164  { { IMPS_BCT_BUS, 0, {'E', 'I', 'S', 'A', ' ', ' '}},
165    { 255, 1, {'P', 'C', 'I', ' ', ' ', ' '}} },
166  { IMPS_BCT_IOAPIC, 0, 0, IMPS_FLAG_ENABLED, IOAPIC_ADDR_DEFAULT },
167  { { IMPS_BCT_IO_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 0, 0xFF, 0},
168    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 1, 0xFF, 1},
169    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 0, 0xFF, 2},
170    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 3, 0xFF, 3},
171    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 4, 0xFF, 4},
172    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 5, 0xFF, 5},
173    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 6, 0xFF, 6},
174    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 7, 0xFF, 7},
175    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 8, 0xFF, 8},
176    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 9, 0xFF, 9},
177    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 10, 0xFF, 10},
178    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 11, 0xFF, 11},
179    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 12, 0xFF, 12},
180    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 13, 0xFF, 13},
181    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 14, 0xFF, 14},
182    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 15, 0xFF, 15} },
183  { { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 15, 0xFF, 0},
184    { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_NMI, 0, 0, 15, 0xFF, 1} }
185};
186
187/*
188 *  Exported globals here.
189 */
190
191volatile int imps_release_cpus = 0;
192int imps_enabled = 0;
193int imps_num_cpus = 1;
194unsigned char imps_cpu_apic_map[IMPS_MAX_CPUS];
195unsigned char imps_apic_cpu_map[IMPS_MAX_CPUS];
196
197/* now defined in getcpuid.c */
198extern unsigned imps_lapic_addr;
199
200static void secondary_cpu_initialize(void);
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 */
246static int
247boot_cpu(imps_processor *proc)
248{
249  int apicid = proc->apic_id, success = 1;
250  unsigned bootaddr;
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)secondary_cpu_initialize;
273  reset[2] = (uint32_t)_Per_CPU_Get_by_index(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  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   *  Generic CPU startup sequence ends here, the rest is cleanup.
313   */
314
315  /* clear the APIC error register */
316  IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
317  IMPS_LAPIC_READ(LAPIC_ESR);
318
319  /* clean up BIOS reset vector */
320  CMOS_WRITE_BYTE(CMOS_RESET_CODE, 0);
321  *((volatile unsigned *) bios_reset_vector) = 0;
322
323  printk("\n");
324
325  return success;
326}
327
328/*
329 *  read bios stuff and fill tables
330 */
331static void
332add_processor(imps_processor *proc)
333{
334  int apicid = proc->apic_id;
335
336  printk("  Processor [APIC id %d ver %d]: ", apicid, proc->apic_ver);
337  if (!(proc->flags & IMPS_FLAG_ENABLED)) {
338    printk("DISABLED\n");
339    return;
340  }
341  if (proc->flags & (IMPS_CPUFLAG_BOOT)) {
342    printk("#0  BootStrap Processor (BSP)\n");
343    return;
344  }
345  if (boot_cpu(proc)) {
346
347    /*  XXXXX  add OS-specific setup for secondary CPUs here */
348
349    imps_cpu_apic_map[imps_num_cpus] = apicid;
350    imps_apic_cpu_map[apicid] = imps_num_cpus;
351    imps_num_cpus++;
352  }
353}
354
355
356static void
357add_bus(imps_bus *bus)
358{
359  char str[8];
360
361  memcpy(str, bus->bus_type, 6);
362  str[6] = 0;
363  printk("  Bus id %d is %s\n", bus->id, str);
364
365  /*  XXXXX  add OS-specific code here */
366}
367
368static void
369add_ioapic(imps_ioapic *ioapic)
370{
371  printk("  I/O APIC id %d ver %d, address: 0x%x  ",
372          ioapic->id, ioapic->ver, ioapic->addr);
373  if (!(ioapic->flags & IMPS_FLAG_ENABLED)) {
374    printk("DISABLED\n");
375    return;
376  }
377  printk("\n");
378
379  /*  XXXXX  add OS-specific code here */
380}
381
382static void
383imps_read_config_table(unsigned start, int count)
384{
385  while (count-- > 0) {
386    switch (*((unsigned char *)start)) {
387    case IMPS_BCT_PROCESSOR:
388      if ( imps_num_cpus < rtems_configuration_get_maximum_processors() ) {
389        add_processor((imps_processor *)start);
390      } else
391        imps_num_cpus++;
392      start += 12;  /* 20 total */
393      break;
394    case IMPS_BCT_BUS:
395      add_bus((imps_bus *)start);
396      break;
397    case IMPS_BCT_IOAPIC:
398      add_ioapic((imps_ioapic *)start);
399      break;
400#if 0  /*  XXXXX  uncomment this if "add_io_interrupt" is implemented */
401    case IMPS_BCT_IO_INTERRUPT:
402      add_io_interrupt((imps_interrupt *)start);
403      break;
404#endif
405#if 0  /*  XXXXX  uncomment this if "add_local_interrupt" is implemented */
406    case IMPS_BCT_LOCAL_INTERRUPT:
407      add_local_interupt((imps_interrupt *)start);
408      break;
409#endif
410    default:
411      break;
412    }
413    start += 8;
414  }
415  if ( imps_num_cpus > rtems_configuration_get_maximum_processors() ) {
416    printk(
417      "WARNING!! Found more CPUs (%d) than configured for (%d)!!\n",
418      imps_num_cpus - 1,
419      rtems_configuration_get_maximum_processors()
420    );
421    imps_num_cpus = rtems_configuration_get_maximum_processors();
422    return;
423  }
424}
425
426static int
427imps_bad_bios(imps_fps *fps_ptr)
428{
429  int sum;
430  imps_cth *local_cth_ptr
431    = (imps_cth *) PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
432
433  if (fps_ptr->feature_info[0] > IMPS_FPS_DEFAULT_MAX) {
434    printk("    Invalid MP System Configuration type %d\n",
435            fps_ptr->feature_info[0]);
436    return 1;
437  }
438
439  if (fps_ptr->cth_ptr) {
440    sum = get_checksum((unsigned)local_cth_ptr,
441                                   local_cth_ptr->base_length);
442    if (local_cth_ptr->sig != IMPS_CTH_SIGNATURE || sum) {
443      printk(
444        "    Bad MP Config Table sig 0x%x and/or checksum 0x%x\n",
445        (unsigned)(fps_ptr->cth_ptr),
446        sum
447      );
448      return 1;
449    }
450    if (local_cth_ptr->spec_rev != fps_ptr->spec_rev) {
451      printk(
452        "    Bad MP Config Table sub-revision # %d\n",
453        local_cth_ptr->spec_rev
454      );
455      return 1;
456    }
457    if (local_cth_ptr->extended_length) {
458      sum = (get_checksum(((unsigned)local_cth_ptr)
459              + local_cth_ptr->base_length,
460              local_cth_ptr->extended_length)
461             + local_cth_ptr->extended_checksum) & 0xFF;
462      if (sum) {
463        printk("    Bad Extended MP Config Table checksum 0x%x\n", sum);
464        return 1;
465      }
466    }
467  } else if (!fps_ptr->feature_info[0]) {
468    printk("    Missing configuration information\n");
469    return 1;
470  }
471
472  return 0;
473}
474
475static void
476imps_read_bios(imps_fps *fps_ptr)
477{
478  int apicid;
479  unsigned cth_start, cth_count;
480  imps_cth *local_cth_ptr
481    = (imps_cth *)PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
482  char *str_ptr;
483
484  printk("Intel MultiProcessor Spec 1.%d BIOS support detected\n",
485          fps_ptr->spec_rev);
486
487  /*
488   *  Do all checking of errors which would definitely
489   *  lead to failure of the SMP boot here.
490   */
491  if (imps_bad_bios(fps_ptr)) {
492    printk("    Disabling MPS support\n");
493    return;
494  }
495
496  if (fps_ptr->feature_info[1] & IMPS_FPS_IMCRP_BIT) {
497    str_ptr = "IMCR and PIC";
498  } else {
499    str_ptr = "Virtual Wire";
500  }
501  if (fps_ptr->cth_ptr) {
502    imps_lapic_addr = local_cth_ptr->lapic_addr;
503  } else {
504    imps_lapic_addr = LAPIC_ADDR_DEFAULT;
505  }
506  printk("    APIC config: \"%s mode\"    Local APIC address: 0x%x\n",
507          str_ptr, imps_lapic_addr);
508  if (imps_lapic_addr != (READ_MSR_LO(0x1b) & 0xFFFFF000)) {
509    printk("Inconsistent Local APIC address, Disabling SMP support\n");
510    return;
511  }
512  imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
513
514  /*
515   *  Setup primary CPU.
516   */
517  apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
518  IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
519  apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
520  imps_cpu_apic_map[0] = apicid;
521  imps_apic_cpu_map[apicid] = 0;
522
523  if (fps_ptr->cth_ptr) {
524    char str1[16], str2[16];
525    memcpy(str1, local_cth_ptr->oem_id, 8);
526    str1[8] = 0;
527    memcpy(str2, local_cth_ptr->prod_id, 12);
528    str2[12] = 0;
529    printk("  OEM id: %s  Product id: %s\n", str1, str2);
530    cth_start = ((unsigned) local_cth_ptr) + sizeof(imps_cth);
531    cth_count = local_cth_ptr->entry_count;
532  } else {
533    *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) =  IOAPIC_ID;
534    defconfig.ioapic.id
535      = APIC_ID(*((volatile unsigned *)
536            (IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
537    *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) =  IOAPIC_VER;
538    defconfig.ioapic.ver
539      = APIC_VERSION(*((volatile unsigned *)
540           (IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
541    defconfig.proc[apicid].flags
542      = IMPS_FLAG_ENABLED|IMPS_CPUFLAG_BOOT;
543    defconfig.proc[!apicid].flags = IMPS_FLAG_ENABLED;
544    imps_num_cpus = 2;
545    if (fps_ptr->feature_info[0] == 1
546     || fps_ptr->feature_info[0] == 5) {
547      memcpy(defconfig.bus[0].bus_type, "ISA   ", 6);
548    }
549    if (fps_ptr->feature_info[0] == 4
550     || fps_ptr->feature_info[0] == 7) {
551      memcpy(defconfig.bus[0].bus_type, "MCA   ", 6);
552    }
553    if (fps_ptr->feature_info[0] > 4) {
554      defconfig.proc[0].apic_ver = 0x10;
555      defconfig.proc[1].apic_ver = 0x10;
556      defconfig.bus[1].type = IMPS_BCT_BUS;
557    }
558    if (fps_ptr->feature_info[0] == 2) {
559      defconfig.intin[2].type = 255;
560      defconfig.intin[13].type = 255;
561    }
562    if (fps_ptr->feature_info[0] == 7) {
563      defconfig.intin[0].type = 255;
564    }
565    cth_start = (unsigned) &defconfig;
566    cth_count = DEF_ENTRIES;
567  }
568  imps_read_config_table(cth_start, cth_count);
569
570  /* %%%%% ESB read extended entries here */
571
572  imps_enabled = 1;
573}
574
575/*
576 *  Given a region to check, this actually looks for the "MP Floating
577 *  Pointer Structure".  The return value indicates if the correct
578 *  signature and checksum for a floating pointer structure of the
579 *  appropriate spec revision was found.  If so, then do not search
580 *  further.
581 *
582 *  NOTE:  The memory scan will always be in the bottom 1 MB.
583 *
584 *  This function presumes that "start" will always be aligned to a 16-bit
585 *  boundary.
586 *
587 *  Function finished.
588 */
589static int
590imps_scan(unsigned start, unsigned length)
591{
592  printk("Scanning from 0x%x for %d bytes\n", start, length);
593
594  while (length > 0) {
595    imps_fps *fps_ptr = (imps_fps *) PHYS_TO_VIRTUAL(start);
596
597    if (fps_ptr->sig == IMPS_FPS_SIGNATURE
598     && fps_ptr->length == 1
599     && (fps_ptr->spec_rev == 1 || fps_ptr->spec_rev == 4)
600     && !get_checksum(start, 16)) {
601      printk("Found MP Floating Structure Pointer at %x\n", start);
602      imps_read_bios(fps_ptr);
603      return 1;
604    }
605
606    length -= 16;
607    start += 16;
608  }
609
610  return 0;
611}
612
613#if !defined(__rtems__)
614/*
615 *  This is the primary function to "force" SMP support, with
616 *  the assumption that you have consecutively numbered APIC ids.
617 */
618int
619imps_force(int ncpus)
620{
621  int apicid, i;
622  imps_processor p;
623
624  printk("Intel MultiProcessor \"Force\" Support\n");
625
626  imps_lapic_addr = (READ_MSR_LO(0x1b) & 0xFFFFF000);
627  imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
628
629  /*
630   *  Setup primary CPU.
631   */
632  apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
633  IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
634  apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
635  imps_cpu_apic_map[0] = apicid;
636  imps_apic_cpu_map[apicid] = 0;
637
638  p.type = 0;
639  p.apic_ver = 0x10;
640  p.signature = p.features = 0;
641
642  for (i = 0; i < ncpus; i++) {
643    if (apicid == i) {
644      p.flags = IMPS_FLAG_ENABLED | IMPS_CPUFLAG_BOOT;
645    } else {
646      p.flags = IMPS_FLAG_ENABLED;
647    }
648    p.apic_id = i;
649    add_processor(&p);
650  }
651
652  return imps_num_cpus;
653}
654#endif
655
656/*
657 *  This is the primary function for probing for MPS compatible hardware
658 *  and BIOS information.  Call this during the early stages of OS startup,
659 *  before memory can be messed up.
660 *
661 *  The probe looks for the "MP Floating Pointer Structure" at locations
662 *  listed at the top of page 4-2 of the spec.
663 *
664 *  Environment requirements from the OS to run:
665 *
666 *   (1) : A non-linear virtual to physical memory mapping is probably OK,
667 *       as (I think) the structures all fall within page boundaries,
668 *       but a linear mapping is recommended.  Currently assumes that
669 *       the mapping will remain identical over time (which should be
670 *       OK since it only accesses memory which shouldn't be munged
671 *       by the OS anyway).
672 *   (2) : The OS only consumes memory which the BIOS says is OK to use,
673 *       and not any of the BIOS standard areas (the areas 0x400 to
674 *       0x600, the EBDA, 0xE0000 to 0xFFFFF, and unreported physical
675 *       RAM).  Sometimes a small amount of physical RAM is not
676 *       reported by the BIOS, to be used to store MPS and other
677 *       information.
678 *   (3) : It must be possible to read the CMOS.
679 *   (4) : There must be between 512K and 640K of lower memory (this is a
680 *       sanity check).
681 *
682 *  Function finished.
683 */
684static int
685imps_probe(void)
686{
687  /*
688   *  Determine possible address of the EBDA
689   */
690  unsigned ebda_addr = *((unsigned short *)
691             PHYS_TO_VIRTUAL(EBDA_SEG_ADDR)) << 4;
692
693  /*
694   *  Determine amount of installed lower memory (not *available*
695   *  lower memory).
696   *
697   *  NOTE:  This should work reliably as long as we verify the
698   *         machine is at least a system that could possibly have
699   *         MPS compatibility to begin with.
700   */
701  unsigned mem_lower = ((CMOS_READ_BYTE(CMOS_BASE_MEMORY+1) << 8)
702            | CMOS_READ_BYTE(CMOS_BASE_MEMORY))       << 10;
703
704#ifdef IMPS_DEBUG
705  imps_enabled = 0;
706  imps_num_cpus = 1;
707#endif
708
709  /*
710   *  Sanity check : if this isn't reasonable, it is almost impossibly
711   *    unlikely to be an MPS compatible machine, so return failure.
712   */
713  if (mem_lower < 512*1024 || mem_lower > 640*1024) {
714    return 0;
715  }
716
717  if (ebda_addr > mem_lower - 1024
718   || ebda_addr + *((unsigned char *) PHYS_TO_VIRTUAL(ebda_addr))
719         * 1024 > mem_lower) {
720    ebda_addr = 0;
721  }
722
723  if (((ebda_addr && imps_scan(ebda_addr, 1024))
724   || (!ebda_addr && imps_scan(mem_lower - 1024, 1024))
725   || imps_scan(0xF0000, 0x10000)) && imps_enabled) {
726    return imps_num_cpus;
727  }
728
729  /*
730   *  If no BIOS info on MPS hardware is found, then return failure.
731   */
732
733  return 0;
734}
735
736/*
737 *  RTEMS SMP BSP Support
738 */
739static void smp_apic_ack(void)
740{
741  (void) IMPS_LAPIC_READ(LAPIC_SPIV);  /* dummy read */
742  IMPS_LAPIC_WRITE(LAPIC_EOI, 0 );     /* ACK the interrupt */
743}
744
745static void bsp_inter_processor_interrupt(void *arg)
746{
747  (void) arg;
748
749  smp_apic_ack();
750
751  _SMP_Inter_processor_interrupt_handler();
752}
753
754static void ipi_install_irq(void)
755{
756  rtems_status_code status;
757
758  status = rtems_interrupt_handler_install(
759    16,
760    "smp-imps",
761    RTEMS_INTERRUPT_UNIQUE,
762    bsp_inter_processor_interrupt,
763    NULL
764  );
765  assert(status == RTEMS_SUCCESSFUL);
766}
767
768#ifdef __SSE__
769extern void enable_sse(void);
770#endif
771
772/* pc386 specific initialization */
773static void secondary_cpu_initialize(void)
774{
775  int apicid;
776
777  asm volatile( "lidt IDT_Descriptor" );
778
779  apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
780  IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
781
782#ifdef __SSE__
783  enable_sse();
784#endif
785
786  _SMP_Start_multitasking_on_secondary_processor();
787}
788
789uint32_t _CPU_SMP_Initialize( void )
790{
791  /* XXX need to deal with finding too many cores */
792
793  return (uint32_t) imps_probe();
794}
795
796bool _CPU_SMP_Start_processor( uint32_t cpu_index )
797{
798  (void) cpu_index;
799
800  return true;
801}
802
803void _CPU_SMP_Finalize_initialization( uint32_t cpu_count )
804{
805  if ( cpu_count > 1 )
806    ipi_install_irq();
807}
808
809void _CPU_SMP_Send_interrupt( uint32_t target_processor_index )
810{
811  send_ipi( target_processor_index, 0x30 );
812}
Note: See TracBrowser for help on using the repository browser.