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

4.115
Last change on this file since 894a2c91 was 894a2c91, checked in by Joel Sherrill <joel.sherrill@…>, on 07/18/11 at 16:33:30

2011-07-18 Joel Sherrill <joel.sherrill@…>

  • shared/irq/apic.h, shared/smp/smp-imps.c, shared/smp/smp-imps.h: Reformat to be more compliant with RTEMS style.
  • Property mode set to 100644
File size: 19.0 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 *  XXXXX  The following absolutely must be defined!!!
51 *
52 *  The "KERNEL_PRINT" could be made a null macro with no danger, of
53 *  course, but pretty much nothing would work without the other
54 *  ones defined.
55 */
56
57#if 0
58#define KERNEL_PRINT(x)       /* some kind of print function */
59#define CMOS_WRITE_BYTE(x,y)  /* write unsigned char "y" at CMOS loc "x" */
60#define CMOS_READ_BYTE(x)     /* read unsigned char at CMOS loc "x" */
61#define PHYS_TO_VIRTUAL(x)    /* convert physical address "x" to virtual */
62#define VIRTUAL_TO_PHYS(x)    /* convert virtual address "x" to physical */
63#define UDELAY(x)             /* delay roughly at least "x" microsecs */
64#define TEST_BOOTED(x)        /* test bootaddr x to see if CPU started */
65#define READ_MSR_LO(x)        /* Read MSR low function */
66#endif
67
68/*
69 *  Includes here
70 */
71
72#define IMPS_DEBUG
73
74#include "apic.h"
75#include "smp-imps.h"
76
77
78/*
79 *  Defines that are here so as not to be in the global header file.
80 */
81#define EBDA_SEG_ADDR       0x40E
82#define BIOS_RESET_VECTOR   0x467
83#define LAPIC_ADDR_DEFAULT  0xFEE00000uL
84#define IOAPIC_ADDR_DEFAULT 0xFEC00000uL
85#define CMOS_RESET_CODE     0xF
86#define CMOS_RESET_JUMP     0xa
87#define CMOS_BASE_MEMORY    0x15
88
89/*
90 *  Static defines here for SMP use.
91 */
92
93#define DEF_ENTRIES  23
94
95static int lapic_dummy = 0;
96static struct {
97  imps_processor proc[2];
98  imps_bus bus[2];
99  imps_ioapic ioapic;
100  imps_interrupt intin[16];
101  imps_interrupt lintin[2];
102} defconfig = {
103  { { IMPS_BCT_PROCESSOR, 0, 0, 0, 0, 0},
104    { IMPS_BCT_PROCESSOR, 1, 0, 0, 0, 0} },
105  { { IMPS_BCT_BUS, 0, {'E', 'I', 'S', 'A', ' ', ' '}},
106    { 255, 1, {'P', 'C', 'I', ' ', ' ', ' '}} },
107  { IMPS_BCT_IOAPIC, 0, 0, IMPS_FLAG_ENABLED, IOAPIC_ADDR_DEFAULT },
108  { { IMPS_BCT_IO_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 0, 0xFF, 0},
109    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 1, 0xFF, 1},
110    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 0, 0xFF, 2},
111    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 3, 0xFF, 3},
112    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 4, 0xFF, 4},
113    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 5, 0xFF, 5},
114    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 6, 0xFF, 6},
115    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 7, 0xFF, 7},
116    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 8, 0xFF, 8},
117    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 9, 0xFF, 9},
118    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 10, 0xFF, 10},
119    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 11, 0xFF, 11},
120    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 12, 0xFF, 12},
121    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 13, 0xFF, 13},
122    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 14, 0xFF, 14},
123    { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 15, 0xFF, 15} },
124  { { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 15, 0xFF, 0},
125    { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_NMI, 0, 0, 15, 0xFF, 1} }
126};
127
128/*
129 *  Exported globals here.
130 */
131
132volatile int imps_release_cpus = 0;
133int imps_enabled = 0;
134int imps_num_cpus = 1;
135unsigned imps_lapic_addr = ((unsigned)(&lapic_dummy)) - LAPIC_ID;
136unsigned char imps_cpu_apic_map[IMPS_MAX_CPUS];
137unsigned char imps_apic_cpu_map[IMPS_MAX_CPUS];
138
139/*
140 *  MPS checksum function
141 *
142 *  Function finished.
143 */
144static int
145get_checksum(unsigned start, int length)
146{
147  unsigned sum = 0;
148
149  while (length-- > 0) {
150    sum += *((unsigned char *) (start++));
151  }
152
153  return (sum&0xFF);
154}
155
156/*
157 *  APIC ICR write and status check function.
158 */
159static int
160send_ipi(unsigned int dst, unsigned int v)
161{
162  int to, send_status;
163
164  IMPS_LAPIC_WRITE(LAPIC_ICR+0x10, (dst << 24));
165  IMPS_LAPIC_WRITE(LAPIC_ICR, v);
166
167  /* Wait for send to finish */
168  to = 0;
169  do {
170    UDELAY(100);
171    send_status = IMPS_LAPIC_READ(LAPIC_ICR) & LAPIC_ICR_STATUS_PEND;
172  } while (send_status && (to++ < 1000));
173
174  return (to < 1000);
175}
176
177/*
178 *  Primary function for booting individual CPUs.
179 *
180 *  This must be modified to perform whatever OS-specific initialization
181 *  that is required.
182 */
183static int
184boot_cpu(imps_processor *proc)
185{
186  int apicid = proc->apic_id, success = 1, to;
187  unsigned bootaddr, accept_status;
188  unsigned bios_reset_vector = PHYS_TO_VIRTUAL(BIOS_RESET_VECTOR);
189
190  /*
191   * Copy boot code for secondary CPUs here.  Find it in between
192   * "patch_code_start" and "patch_code_end" symbols.  The other CPUs
193   * will start there in 16-bit real mode under the 1MB boundary.
194   * "patch_code_start" should be placed at a 4K-aligned address
195   * under the 1MB boundary.
196   */
197
198  extern char patch_code_start[];
199  extern char patch_code_end[];
200  bootaddr = (512-64)*1024;
201  memcpy((char *)bootaddr, patch_code_start, patch_code_end - patch_code_start);
202
203  /*
204   *  Generic CPU startup sequence starts here.
205   */
206
207  /* set BIOS reset vector */
208  CMOS_WRITE_BYTE(CMOS_RESET_CODE, CMOS_RESET_JUMP);
209  *((volatile unsigned *) bios_reset_vector) = ((bootaddr & 0xFF000) << 12);
210
211  /* clear the APIC error register */
212  IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
213  accept_status = IMPS_LAPIC_READ(LAPIC_ESR);
214
215  /* assert INIT IPI */
216  send_ipi(
217    apicid, LAPIC_ICR_TM_LEVEL | LAPIC_ICR_LEVELASSERT | LAPIC_ICR_DM_INIT);
218
219  UDELAY(10000);
220
221  /* de-assert INIT IPI */
222  send_ipi(apicid, LAPIC_ICR_TM_LEVEL | LAPIC_ICR_DM_INIT);
223
224  UDELAY(10000);
225
226  /*
227   *  Send Startup IPIs if not an old pre-integrated APIC.
228   */
229
230  if (proc->apic_ver >= APIC_VER_NEW) {
231    int i;
232    for (i = 1; i <= 2; i++) {
233      send_ipi(apicid, LAPIC_ICR_DM_SIPI | ((bootaddr >> 12) & 0xFF));
234      UDELAY(1000);
235    }
236  }
237
238  /*
239   *  Check to see if other processor has started.
240   */
241  to = 0;
242  while (!TEST_BOOTED(bootaddr) && to++ < 100)
243    UDELAY(10000);
244  if (to >= 100) {
245    KERNEL_PRINT(("CPU Not Responding, DISABLED"));
246    success = 0;
247  } else {
248    KERNEL_PRINT(("#%d  Application Processor (AP)", imps_num_cpus));
249  }
250
251  /*
252   *  Generic CPU startup sequence ends here, the rest is cleanup.
253   */
254
255  /* clear the APIC error register */
256  IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
257  accept_status = IMPS_LAPIC_READ(LAPIC_ESR);
258
259  /* clean up BIOS reset vector */
260  CMOS_WRITE_BYTE(CMOS_RESET_CODE, 0);
261  *((volatile unsigned *) bios_reset_vector) = 0;
262
263  KERNEL_PRINT(("\n"));
264
265  return success;
266}
267
268/*
269 *  read bios stuff and fill tables
270 */
271static void
272add_processor(imps_processor *proc)
273{
274  int apicid = proc->apic_id;
275
276  KERNEL_PRINT(("  Processor [APIC id %d ver %d]:  ",
277          apicid, proc->apic_ver));
278  if (!(proc->flags & IMPS_FLAG_ENABLED)) {
279    KERNEL_PRINT(("DISABLED\n"));
280    return;
281  }
282  if (proc->flags & (IMPS_CPUFLAG_BOOT)) {
283    KERNEL_PRINT(("#0  BootStrap Processor (BSP)\n"));
284    return;
285  }
286  if (boot_cpu(proc)) {
287
288    /*  XXXXX  add OS-specific setup for secondary CPUs here */
289
290    imps_cpu_apic_map[imps_num_cpus] = apicid;
291    imps_apic_cpu_map[apicid] = imps_num_cpus;
292    imps_num_cpus++;
293  }
294}
295
296
297static void
298add_bus(imps_bus *bus)
299{
300  char str[8];
301
302  memcpy(str, bus->bus_type, 6);
303  str[6] = 0;
304  KERNEL_PRINT(("  Bus id %d is %s\n", bus->id, str));
305
306  /*  XXXXX  add OS-specific code here */
307}
308
309static void
310add_ioapic(imps_ioapic *ioapic)
311{
312  KERNEL_PRINT(("  I/O APIC id %d ver %d, address: 0x%x  ",
313          ioapic->id, ioapic->ver, ioapic->addr));
314  if (!(ioapic->flags & IMPS_FLAG_ENABLED)) {
315    KERNEL_PRINT(("DISABLED\n"));
316    return;
317  }
318  KERNEL_PRINT(("\n"));
319
320  /*  XXXXX  add OS-specific code here */
321}
322
323static void
324imps_read_config_table(unsigned start, int count)
325{
326  while (count-- > 0) {
327    switch (*((unsigned char *)start)) {
328    case IMPS_BCT_PROCESSOR:
329      add_processor((imps_processor *)start);
330      start += 12;  /* 20 total */
331      break;
332    case IMPS_BCT_BUS:
333      add_bus((imps_bus *)start);
334      break;
335    case IMPS_BCT_IOAPIC:
336      add_ioapic((imps_ioapic *)start);
337      break;
338#if 0  /*  XXXXX  uncomment this if "add_io_interrupt" is implemented */
339    case IMPS_BCT_IO_INTERRUPT:
340      add_io_interrupt((imps_interrupt *)start);
341      break;
342#endif
343#if 0  /*  XXXXX  uncomment this if "add_local_interrupt" is implemented */
344    case IMPS_BCT_LOCAL_INTERRUPT:
345      add_local_interupt((imps_interrupt *)start);
346      break;
347#endif
348    default:
349      break;
350    }
351    start += 8;
352  }
353}
354
355static int
356imps_bad_bios(imps_fps *fps_ptr)
357{
358  int sum;
359  imps_cth *local_cth_ptr
360    = (imps_cth *) PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
361
362  if (fps_ptr->feature_info[0] > IMPS_FPS_DEFAULT_MAX) {
363    KERNEL_PRINT(("    Invalid MP System Configuration type %d\n",
364            fps_ptr->feature_info[0]));
365    return 1;
366  }
367
368  if (fps_ptr->cth_ptr) {
369    sum = get_checksum((unsigned)local_cth_ptr,
370                                   local_cth_ptr->base_length);
371    if (local_cth_ptr->sig != IMPS_CTH_SIGNATURE || sum) {
372      KERNEL_PRINT(
373        ("    Bad MP Config Table sig 0x%x and/or checksum 0x%x\n",
374        (unsigned)(fps_ptr->cth_ptr),
375        sum)
376      );
377      return 1;
378    }
379    if (local_cth_ptr->spec_rev != fps_ptr->spec_rev) {
380      KERNEL_PRINT(
381        ("    Bad MP Config Table sub-revision # %d\n",
382        local_cth_ptr->spec_rev)
383      );
384      return 1;
385    }
386    if (local_cth_ptr->extended_length) {
387      sum = (get_checksum(((unsigned)local_cth_ptr)
388              + local_cth_ptr->base_length,
389              local_cth_ptr->extended_length)
390             + local_cth_ptr->extended_checksum) & 0xFF;
391      if (sum) {
392        KERNEL_PRINT(("    Bad Extended MP Config Table checksum 0x%x\n", sum));
393        return 1;
394      }
395    }
396  } else if (!fps_ptr->feature_info[0]) {
397    KERNEL_PRINT(("    Missing configuration information\n"));
398    return 1;
399  }
400
401  return 0;
402}
403
404static void
405imps_read_bios(imps_fps *fps_ptr)
406{
407  int apicid;
408  unsigned cth_start, cth_count;
409  imps_cth *local_cth_ptr
410    = (imps_cth *)PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
411  char *str_ptr;
412
413  KERNEL_PRINT(("Intel MultiProcessor Spec 1.%d BIOS support detected\n",
414          fps_ptr->spec_rev));
415
416  /*
417   *  Do all checking of errors which would definitely
418   *  lead to failure of the SMP boot here.
419   */
420  if (imps_bad_bios(fps_ptr)) {
421    KERNEL_PRINT(("    Disabling MPS support\n"));
422    return;
423  }
424
425  if (fps_ptr->feature_info[1] & IMPS_FPS_IMCRP_BIT) {
426    str_ptr = "IMCR and PIC";
427  } else {
428    str_ptr = "Virtual Wire";
429  }
430  if (fps_ptr->cth_ptr) {
431    imps_lapic_addr = local_cth_ptr->lapic_addr;
432  } else {
433    imps_lapic_addr = LAPIC_ADDR_DEFAULT;
434  }
435  KERNEL_PRINT(("    APIC config: \"%s mode\"    Local APIC address: 0x%x\n",
436          str_ptr, imps_lapic_addr));
437  if (imps_lapic_addr != (READ_MSR_LO(0x1b) & 0xFFFFF000)) {
438    KERNEL_PRINT(("Inconsistent Local APIC address, Disabling SMP support\n"));
439    return;
440  }
441  imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
442
443  /*
444   *  Setup primary CPU.
445   */
446  apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
447  IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
448  apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
449  imps_cpu_apic_map[0] = apicid;
450  imps_apic_cpu_map[apicid] = 0;
451
452  if (fps_ptr->cth_ptr) {
453    char str1[16], str2[16];
454    memcpy(str1, local_cth_ptr->oem_id, 8);
455    str1[8] = 0;
456    memcpy(str2, local_cth_ptr->prod_id, 12);
457    str2[12] = 0;
458    KERNEL_PRINT(("  OEM id: %s  Product id: %s\n", str1, str2));
459    cth_start = ((unsigned) local_cth_ptr) + sizeof(imps_cth);
460    cth_count = local_cth_ptr->entry_count;
461  } else {
462    *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) =  IOAPIC_ID;
463    defconfig.ioapic.id
464      = APIC_ID(*((volatile unsigned *)
465            (IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
466    *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) =  IOAPIC_VER;
467    defconfig.ioapic.ver
468      = APIC_VERSION(*((volatile unsigned *)
469           (IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
470    defconfig.proc[apicid].flags
471      = IMPS_FLAG_ENABLED|IMPS_CPUFLAG_BOOT;
472    defconfig.proc[!apicid].flags = IMPS_FLAG_ENABLED;
473    imps_num_cpus = 2;
474    if (fps_ptr->feature_info[0] == 1
475     || fps_ptr->feature_info[0] == 5) {
476      memcpy(defconfig.bus[0].bus_type, "ISA   ", 6);
477    }
478    if (fps_ptr->feature_info[0] == 4
479     || fps_ptr->feature_info[0] == 7) {
480      memcpy(defconfig.bus[0].bus_type, "MCA   ", 6);
481    }
482    if (fps_ptr->feature_info[0] > 4) {
483      defconfig.proc[0].apic_ver = 0x10;
484      defconfig.proc[1].apic_ver = 0x10;
485      defconfig.bus[1].type = IMPS_BCT_BUS;
486    }
487    if (fps_ptr->feature_info[0] == 2) {
488      defconfig.intin[2].type = 255;
489      defconfig.intin[13].type = 255;
490    }
491    if (fps_ptr->feature_info[0] == 7) {
492      defconfig.intin[0].type = 255;
493    }
494    cth_start = (unsigned) &defconfig;
495    cth_count = DEF_ENTRIES;
496  }
497  imps_read_config_table(cth_start, cth_count);
498
499  /* %%%%% ESB read extended entries here */
500
501  imps_enabled = 1;
502}
503
504/*
505 *  Given a region to check, this actually looks for the "MP Floating
506 *  Pointer Structure".  The return value indicates if the correct
507 *  signature and checksum for a floating pointer structure of the
508 *  appropriate spec revision was found.  If so, then do not search
509 *  further.
510 *
511 *  NOTE:  The memory scan will always be in the bottom 1 MB.
512 *
513 *  This function presumes that "start" will always be aligned to a 16-bit
514 *  boundary.
515 *
516 *  Function finished.
517 */
518static int
519imps_scan(unsigned start, unsigned length)
520{
521  IMPS_DEBUG_PRINT(("Scanning from 0x%x for %d bytes\n",
522        start, length));
523
524  while (length > 0) {
525    imps_fps *fps_ptr = (imps_fps *) PHYS_TO_VIRTUAL(start);
526
527    if (fps_ptr->sig == IMPS_FPS_SIGNATURE
528     && fps_ptr->length == 1
529     && (fps_ptr->spec_rev == 1 || fps_ptr->spec_rev == 4)
530     && !get_checksum(start, 16)) {
531      IMPS_DEBUG_PRINT(("Found MP Floating Structure Pointer at %x\n", start));
532      imps_read_bios(fps_ptr);
533      return 1;
534    }
535
536    length -= 16;
537    start += 16;
538  }
539
540  return 0;
541}
542
543/*
544 *  This is the primary function to "force" SMP support, with
545 *  the assumption that you have consecutively numbered APIC ids.
546 */
547int
548imps_force(int ncpus)
549{
550  int apicid, i;
551  imps_processor p;
552
553  KERNEL_PRINT(("Intel MultiProcessor \"Force\" Support\n"));
554
555  imps_lapic_addr = (READ_MSR_LO(0x1b) & 0xFFFFF000);
556  imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
557
558  /*
559   *  Setup primary CPU.
560   */
561  apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
562  IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
563  apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
564  imps_cpu_apic_map[0] = apicid;
565  imps_apic_cpu_map[apicid] = 0;
566
567  p.type = 0;
568  p.apic_ver = 0x10;
569  p.signature = p.features = 0;
570
571  for (i = 0; i < ncpus; i++) {
572    if (apicid == i) {
573      p.flags = IMPS_FLAG_ENABLED | IMPS_CPUFLAG_BOOT;
574    } else {
575      p.flags = IMPS_FLAG_ENABLED;
576    }
577    p.apic_id = i;
578    add_processor(&p);
579  }
580
581  return imps_num_cpus;
582}
583
584/*
585 *  This is the primary function for probing for MPS compatible hardware
586 *  and BIOS information.  Call this during the early stages of OS startup,
587 *  before memory can be messed up.
588 *
589 *  The probe looks for the "MP Floating Pointer Structure" at locations
590 *  listed at the top of page 4-2 of the spec.
591 *
592 *  Environment requirements from the OS to run:
593 *
594 *   (1) : A non-linear virtual to physical memory mapping is probably OK,
595 *       as (I think) the structures all fall within page boundaries,
596 *       but a linear mapping is recommended.  Currently assumes that
597 *       the mapping will remain identical over time (which should be
598 *       OK since it only accesses memory which shouldn't be munged
599 *       by the OS anyway).
600 *   (2) : The OS only consumes memory which the BIOS says is OK to use,
601 *       and not any of the BIOS standard areas (the areas 0x400 to
602 *       0x600, the EBDA, 0xE0000 to 0xFFFFF, and unreported physical
603 *       RAM).  Sometimes a small amount of physical RAM is not
604 *       reported by the BIOS, to be used to store MPS and other
605 *       information.
606 *   (3) : It must be possible to read the CMOS.
607 *   (4) : There must be between 512K and 640K of lower memory (this is a
608 *       sanity check).
609 *
610 *  Function finished.
611 */
612int
613imps_probe(void)
614{
615  /*
616   *  Determine possible address of the EBDA
617   */
618  unsigned ebda_addr = *((unsigned short *)
619             PHYS_TO_VIRTUAL(EBDA_SEG_ADDR)) << 4;
620
621  /*
622   *  Determine amount of installed lower memory (not *available*
623   *  lower memory).
624   *
625   *  NOTE:  This should work reliably as long as we verify the
626   *         machine is at least a system that could possibly have
627   *         MPS compatibility to begin with.
628   */
629  unsigned mem_lower = ((CMOS_READ_BYTE(CMOS_BASE_MEMORY+1) << 8)
630            | CMOS_READ_BYTE(CMOS_BASE_MEMORY))       << 10;
631
632#ifdef IMPS_DEBUG
633  imps_enabled = 0;
634  imps_num_cpus = 1;
635#endif
636
637  /*
638   *  Sanity check : if this isn't reasonable, it is almost impossibly
639   *    unlikely to be an MPS compatible machine, so return failure.
640   */
641  if (mem_lower < 512*1024 || mem_lower > 640*1024) {
642    return 0;
643  }
644
645  if (ebda_addr > mem_lower - 1024
646   || ebda_addr + *((unsigned char *) PHYS_TO_VIRTUAL(ebda_addr))
647         * 1024 > mem_lower) {
648    ebda_addr = 0;
649  }
650
651  if (((ebda_addr && imps_scan(ebda_addr, 1024))
652   || (!ebda_addr && imps_scan(mem_lower - 1024, 1024))
653   || imps_scan(0xF0000, 0x10000)) && imps_enabled) {
654    return imps_num_cpus;
655  }
656
657  /*
658   *  If no BIOS info on MPS hardware is found, then return failure.
659   */
660
661  return 0;
662}
663
Note: See TracBrowser for help on using the repository browser.