1 //==========================================================================
5 // HAL SMP implementation
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
44 // Contributors: nickg
46 // Purpose: HAL SMP implementation
47 // Description: This file contains SMP support functions.
49 //####DESCRIPTIONEND####
51 //========================================================================*/
53 #include <pkgconf/hal.h>
54 #include <pkgconf/hal_i386.h>
55 #include <pkgconf/hal_i386_pcmb.h>
57 #ifdef CYGPKG_HAL_SMP_SUPPORT
60 #include <pkgconf/kernel.h>
63 #include <cyg/infra/cyg_type.h> // Base types
64 #include <cyg/infra/cyg_trac.h> // tracing macros
65 #include <cyg/infra/cyg_ass.h> // assertion macros
66 #include <cyg/infra/diag.h>
68 #include <cyg/hal/hal_intr.h>
69 #include <cyg/hal/hal_io.h>
70 #include <cyg/hal/hal_smp.h>
72 // ------------------------------------------------------------------------
73 // Debugging/diagnostic controls
75 // Setting this to 1 causes the parsing of the MP structures and the
76 // initialization of the APIC and IOAPIC to be reported on the
77 // diagnostic channel.
78 #define SHOW_DIAGNOSTICS 0
80 // Setting this to 1 causes various things to be displayed on the PC
81 // screen during normal operation. These are mostly for my own use,
82 // and may be somewhat obscure to anyone else.
83 #define SCREEN_DIAGNOSTICS 0
85 #if SCREEN_DIAGNOSTICS==0
86 #undef PC_WRITE_SCREEN
87 #undef PC_WRITE_SCREEN_8
88 #undef PC_WRITE_SCREEN_16
89 #undef PC_WRITE_SCREEN_32
90 #define PC_WRITE_SCREEN( pos, ch )
91 #define PC_WRITE_SCREEN_8( pos, val )
92 #define PC_WRITE_SCREEN_16( pos, val )
93 #define PC_WRITE_SCREEN_32( pos, val )
96 // ------------------------------------------------------------------------
98 static int streq( const char *s1, const char *s2 )
100 while( *s1 == *s2 && *s1 && *s2 ) s1++,s2++;
105 /*------------------------------------------------------------------------*/
108 #define MP_FPS_SIGNATURE 0
109 #define MP_FPS_MPCT 4
110 #define MP_FPS_LENGTH 8
112 #define MP_FPS_CSUM 10
113 #define MP_FPS_FEATURE1 11
114 #define MP_FPS_FEATURE2 12
115 #define MP_FPS_FEATURE3 13
116 #define MP_FPS_FEATURE4 14
117 #define MP_FPS_FEATURE5 15
118 #define MP_FPS_SIZE 16
120 #define MP_FPS_SIGNATURE_VALUE 0x5f504d5f
122 /*------------------------------------------------------------------------*/
123 // MP config table structure
127 #define MPCT_HDR_SIGNATURE 0
128 #define MPCT_HDR_LENGTH 4
129 #define MPCT_HDR_REV 6
130 #define MPCT_HDR_CHECKSUM 7
131 #define MPCT_HDR_OEM_ID 8
132 #define MPCT_HDR_PROD_ID 16
133 #define MPCT_HDR_OEM_TAB 28
134 #define MPCT_HDR_OEM_TAB_SIZE 32
135 #define MPCT_HDR_ENTRY_COUNT 34
136 #define MPCT_HDR_LOCAL_APIC 36
137 #define MPCT_HDR_XTAB_LENGTH 40
138 #define MPCT_HDR_XTAB_CHECKSUM 42
139 #define MPCT_HDR_SIZE 44
141 #define MPCT_HDR_SIGNATURE_VAL 0x504d4350
143 #define MPCT_ENTRY_TYPE_PROCESSOR 0
144 #define MPCT_ENTRY_TYPE_BUS 1
145 #define MPCT_ENTRY_TYPE_IOAPIC 2
146 #define MPCT_ENTRY_TYPE_INTERRUPT_IO 3
147 #define MPCT_ENTRY_TYPE_INTERRUPT_LOCAL 4
149 #define MPCT_ENTRY_PROC_TYPE 0
150 #define MPCT_ENTRY_PROC_APIC_ID 1
151 #define MPCT_ENTRY_PROC_APIC_VER 2
152 #define MPCT_ENTRY_PROC_CPU_FLAGS 3
153 #define MPCT_ENTRY_PROC_CPU_SIGNATURE 4
154 #define MPCT_ENTRY_PROC_FEATURE_FLAGS 8
155 #define MPCT_ENTRY_PROC_RESERVED0 12
156 #define MPCT_ENTRY_PROC_RESERVED1 16
157 #define MPCT_ENTRY_PROC_SIZE 20
159 #define MPCT_ENTRY_BUS_TYPE 0
160 #define MPCT_ENTRY_BUS_ID 1
161 #define MPCT_ENTRY_BUS_TYPE_STRING 2
162 #define MPCT_ENTRY_BUS_SIZE 8
164 #define MPCT_ENTRY_IOAPIC_TYPE 0
165 #define MPCT_ENTRY_IOAPIC_ID 1
166 #define MPCT_ENTRY_IOAPIC_VER 2
167 #define MPCT_ENTRY_IOAPIC_FLAGS 3
168 #define MPCT_ENTRY_IOAPIC_ADDRESS 4
169 #define MPCT_ENTRY_IOAPIC_SIZE 8
171 #define MPCT_ENTRY_IOINT_TYPE 0
172 #define MPCT_ENTRY_IOINT_INT_TYPE 1
173 #define MPCT_ENTRY_IOINT_FLAGS 2
174 #define MPCT_ENTRY_IOINT_SOURCE_BUS 4
175 #define MPCT_ENTRY_IOINT_SOURCE_IRQ 5
176 #define MPCT_ENTRY_IOINT_DEST_APIC 6
177 #define MPCT_ENTRY_IOINT_DEST_INT 7
178 #define MPCT_ENTRY_IOINT_SIZE 8
180 #define MPCT_ENTRY_LOCINT_TYPE 0
181 #define MPCT_ENTRY_LOCINT_INT_TYPE 1
182 #define MPCT_ENTRY_LOCINT_FLAGS 2
183 #define MPCT_ENTRY_LOCINT_SOURCE_BUS 4
184 #define MPCT_ENTRY_LOCINT_SOURCE_IRQ 5
185 #define MPCT_ENTRY_LOCINT_DEST_APIC 6
186 #define MPCT_ENTRY_LOCINT_DEST_INT 7
187 #define MPCT_ENTRY_LOCINT_SIZE 8
189 /*------------------------------------------------------------------------*/
190 // Exported SMP configuration
192 CYG_ADDRESS cyg_hal_smp_local_apic;
194 CYG_ADDRESS cyg_hal_smp_io_apic;
196 CYG_WORD32 cyg_hal_smp_cpu_count = 0;
198 CYG_BYTE cyg_hal_smp_cpu_flags[HAL_SMP_CPU_MAX];
200 CYG_BYTE cyg_hal_isa_bus_id = 0xff;
201 CYG_BYTE cyg_hal_isa_bus_irq[16];
203 CYG_BYTE cyg_hal_pci_bus_id = 0xff;
204 CYG_BYTE cyg_hal_pci_bus_irq[4];
206 HAL_SPINLOCK_TYPE cyg_hal_ioapic_lock;
208 /*------------------------------------------------------------------------*/
212 static CYG_ADDRESS mp_fps;
213 static CYG_ADDRESS mpct;
216 /*------------------------------------------------------------------------*/
218 static CYG_WORD32 mp_checksum(CYG_BYTE *p, CYG_WORD32 len)
222 while (len--) sum += *p++;
227 /*------------------------------------------------------------------------*/
229 static cyg_bool cyg_hal_scan_smp_config( CYG_ADDRESS base, CYG_ADDRWORD size )
233 diag_printf("Scan for MP struct: %08x..%08x\n",base,base+size);
240 HAL_READMEM_UINT32( base, sig );
242 if( sig == MP_FPS_SIGNATURE_VALUE )
245 // We have a candidate for the floating structure
248 diag_printf("MP_FPS candidate found at %08x\n",base);
251 HAL_READMEM_UINT8( base+MP_FPS_LENGTH, val8 );
256 HAL_READMEM_UINT8( base+MP_FPS_REV, val8 );
258 if( val8 != 1 && val8 != 4 )
261 if( mp_checksum( (CYG_BYTE *)base, MP_FPS_SIZE ) != 0 )
266 HAL_READMEM_UINT32( base+MP_FPS_MPCT, mpct );
269 diag_printf("MPCT at %08x\n",mpct);
281 /*------------------------------------------------------------------------*/
283 static cyg_bool cyg_hal_find_smp_config( void )
286 if( cyg_hal_scan_smp_config(0x00000, 0x00400 ) )
289 // check top 1k of RAM
290 if( cyg_hal_scan_smp_config(0x9fc00, 0x00400 ) )
294 if( cyg_hal_scan_smp_config(0xf0000, 0x10000 ) )
298 /*------------------------------------------------------------------------*/
300 static cyg_bool cyg_hal_parse_smp_config( void )
311 diag_printf("FPS address: %08x\n",mp_fps);
312 HAL_READMEM_UINT32( mp_fps+MP_FPS_SIGNATURE, val32);
313 diag_printf("FPS signature: %08x\n",val32);
314 HAL_READMEM_UINT32( mp_fps+MP_FPS_MPCT, val32);
315 diag_printf("FPS MPCT address: %08x\n",val32);
316 HAL_READMEM_UINT8( mp_fps+MP_FPS_LENGTH, val8);
317 diag_printf("FPS length: %02x\n",val8);
318 HAL_READMEM_UINT8( mp_fps+MP_FPS_REV, val8);
319 diag_printf("FPS spec rev: %02x\n",val8);
320 HAL_READMEM_UINT8( mp_fps+MP_FPS_CSUM, val8);
321 diag_printf("FPS checksum: %02x\n",val8);
323 for( i = 0; i < 5; i++ )
325 HAL_READMEM_UINT8( mp_fps+MP_FPS_FEATURE1, val8);
326 diag_printf("FPS feature byte %d: %02x\n",i,val8);
335 HAL_READMEM_UINT32( mpct+MPCT_HDR_SIGNATURE, val32);
337 if( val32 != MPCT_HDR_SIGNATURE_VAL )
340 HAL_READMEM_UINT16( mpct+MPCT_HDR_LENGTH, val16);
341 if( mp_checksum( (CYG_BYTE *)mpct, val16 ) != 0 )
348 diag_printf("MPCT address: %08x\n",mpct);
349 HAL_READMEM_UINT32( mpct+MPCT_HDR_SIGNATURE, val32);
350 diag_printf("MPCT signature: %08x\n",val32);
352 HAL_READMEM_UINT16( mpct+MPCT_HDR_LENGTH, val16);
353 diag_printf("MPCT length: %04x\n",val16);
354 HAL_READMEM_UINT8( mpct+MPCT_HDR_REV, val8);
355 diag_printf("MPCT spec rev: %02x\n",val8);
356 HAL_READMEM_UINT8( mpct+MPCT_HDR_CHECKSUM, val8);
357 diag_printf("MPCT checksum: %02x\n",val8);
359 for( i = 0; i < 8; i++ )
361 HAL_READMEM_UINT8( mpct+MPCT_HDR_OEM_ID+i, val8);
365 diag_printf("MPCT OEM Id: %s\n",id);
367 for( i = 0; i < 12; i++ )
369 HAL_READMEM_UINT8( mpct+MPCT_HDR_PROD_ID+i, val8);
373 diag_printf("MPCT Product Id: %s\n",id);
375 HAL_READMEM_UINT32( mpct+MPCT_HDR_OEM_TAB, val32);
376 diag_printf("MPCT OEM table: %08x\n",val32);
377 HAL_READMEM_UINT16( mpct+MPCT_HDR_OEM_TAB_SIZE, val16);
378 diag_printf("MPCT OEM table size: %04x\n",val16);
380 HAL_READMEM_UINT16( mpct+MPCT_HDR_ENTRY_COUNT, val16);
381 diag_printf("MPCT entry count: %04x\n",val16);
383 HAL_READMEM_UINT32( mpct+MPCT_HDR_LOCAL_APIC, val32);
384 diag_printf("MPCT local APIC: %08x\n",val32);
386 HAL_READMEM_UINT16( mpct+MPCT_HDR_XTAB_LENGTH, val16);
387 diag_printf("MPCT extended table length: %04x\n",val16);
388 HAL_READMEM_UINT8( mpct+MPCT_HDR_XTAB_CHECKSUM, val8);
389 diag_printf("MPCT extended table checksum: %02x\n",val8);
393 // Extract the data we need from the structure
395 HAL_READMEM_UINT32( mpct+MPCT_HDR_LOCAL_APIC, cyg_hal_smp_local_apic);
397 // now parse the base table, looking for processor and IOAPIC entries.
401 CYG_ADDRESS entry = mpct + MPCT_HDR_SIZE;
403 HAL_READMEM_UINT16( mpct+MPCT_HDR_ENTRY_COUNT, entries);
406 diag_printf("\nBase table:\n");
413 HAL_READMEM_UINT8( entry, type );
417 case MPCT_ENTRY_TYPE_PROCESSOR:
419 diag_printf(" Processor\n");
420 HAL_READMEM_UINT8( entry+MPCT_ENTRY_PROC_APIC_ID, val8 );
421 diag_printf(" APIC ID: %02x\n",val8);
422 HAL_READMEM_UINT8( entry+MPCT_ENTRY_PROC_APIC_VER, val8 );
423 diag_printf(" APIC Version: %02x\n",val8);
424 HAL_READMEM_UINT8( entry+MPCT_ENTRY_PROC_CPU_FLAGS, val8 );
425 diag_printf(" CPU Flags: %02x\n",val8);
426 HAL_READMEM_UINT8( entry+MPCT_ENTRY_PROC_CPU_SIGNATURE, val32 );
427 diag_printf(" CPU Signature: %08x\n",val32);
428 HAL_READMEM_UINT8( entry+MPCT_ENTRY_PROC_FEATURE_FLAGS, val32 );
429 diag_printf(" Feature flags: %08x\n",val32);
434 // Index CPUs by their APIC IDs
435 HAL_READMEM_UINT8( entry+MPCT_ENTRY_PROC_APIC_ID, cpuid );
437 // Get flags for this CPU.
438 HAL_READMEM_UINT8(entry+MPCT_ENTRY_PROC_CPU_FLAGS, cyg_hal_smp_cpu_flags[cpuid]);
440 cyg_hal_smp_cpu_count++; // count another CPU
443 entry += MPCT_ENTRY_PROC_SIZE;
446 case MPCT_ENTRY_TYPE_BUS:
450 HAL_READMEM_UINT8( entry+MPCT_ENTRY_BUS_ID, id );
452 diag_printf(" Bus\n");
453 diag_printf(" ID: %02x\n",id);
457 for( i = 0; i < 6; i++ )
459 HAL_READMEM_UINT8( entry+MPCT_ENTRY_BUS_TYPE_STRING+i, val8 );
464 diag_printf(" Type: %s\n",&bustype[0]);
468 if( streq( bustype, "ISA " ) )
470 cyg_hal_isa_bus_id = id;
471 for( i = 0; i < 16; i++ )
472 cyg_hal_isa_bus_irq[i] = 100;
474 if( streq( bustype, "PCI " ) )
476 cyg_hal_pci_bus_id = id;
480 entry += MPCT_ENTRY_BUS_SIZE;
483 case MPCT_ENTRY_TYPE_IOAPIC:
485 diag_printf(" I/O APIC\n");
486 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOAPIC_ID, val8 );
487 diag_printf(" ID: %02x\n",val8);
488 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOAPIC_VER, val8 );
489 diag_printf(" Version: %02x\n",val8);
490 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOAPIC_FLAGS, val8 );
491 diag_printf(" Flags: %02x\n",val8);
492 HAL_READMEM_UINT32( entry+MPCT_ENTRY_IOAPIC_ADDRESS, val32 );
493 diag_printf(" Address: %08x\n",val32);
496 HAL_READMEM_UINT32( entry+MPCT_ENTRY_IOAPIC_ADDRESS, cyg_hal_smp_io_apic );
497 entry += MPCT_ENTRY_IOAPIC_SIZE;
500 case MPCT_ENTRY_TYPE_INTERRUPT_IO:
502 CYG_BYTE bus, irq, dst;
503 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOINT_SOURCE_BUS, bus );
504 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOINT_SOURCE_IRQ, irq );
505 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOINT_DEST_INT, dst );
507 diag_printf(" I/O interrupt assignment\n");
508 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOINT_TYPE, val8 );
509 diag_printf(" Type: %02x\n",val8);
510 HAL_READMEM_UINT16( entry+MPCT_ENTRY_IOINT_TYPE, val16 );
511 diag_printf(" Flags: %04x\n",val16);
512 diag_printf(" Source bus: %02x\n",bus);
513 diag_printf(" Source IRQ: %02x\n",irq);
514 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOINT_DEST_APIC, val8 );
515 diag_printf(" Dest APIC: %02x\n",val8);
516 diag_printf(" Dest Interrupt: %02x\n",dst);
519 if( bus == cyg_hal_isa_bus_id )
520 cyg_hal_isa_bus_irq[irq] = dst;
521 // if( bus == cyg_hal_pci_bus_id )
522 // cyg_hal_pci_bus_irq[irq] = dst;
525 entry += MPCT_ENTRY_IOINT_SIZE;
528 case MPCT_ENTRY_TYPE_INTERRUPT_LOCAL:
530 diag_printf(" Local interrupt assignment\n");
531 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOINT_TYPE, val8 );
532 diag_printf(" Type: %02x\n",val8);
533 HAL_READMEM_UINT16( entry+MPCT_ENTRY_IOINT_TYPE, val16 );
534 diag_printf(" Flags: %04x\n",val16);
535 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOINT_SOURCE_BUS, val8 );
536 diag_printf(" Source bus: %02x\n",val8);
537 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOINT_SOURCE_IRQ, val8 );
538 diag_printf(" Source IRQ: %02x\n",val8);
539 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOINT_DEST_APIC, val8 );
540 diag_printf(" Dest APIC: %02x\n",val8);
541 HAL_READMEM_UINT8( entry+MPCT_ENTRY_IOINT_DEST_INT, val8 );
542 diag_printf(" Dest Interrupt: %02x\n",val8);
544 entry += MPCT_ENTRY_LOCINT_SIZE;
549 diag_printf(" MPCT Entry: unknown type %02x\n",type);
562 diag_printf("Exported configuration:\n");
563 diag_printf(" Local APIC: %08x\n", cyg_hal_smp_local_apic );
564 diag_printf(" I/O APIC: %08x\n", cyg_hal_smp_io_apic );
565 diag_printf(" CPU count: %d\n", cyg_hal_smp_cpu_count );
567 for( i = 0; i < cyg_hal_smp_cpu_count; i++ )
569 diag_printf(" CPU %d %sactive %s\n",i,
570 ((cyg_hal_smp_cpu_flags[i]&1)?"":"in"),
571 ((cyg_hal_smp_cpu_flags[i]&2)?"master":"slave")
575 diag_printf(" ISA IRQ map:\n");
577 for( i = 0; i < 16; i++ )
579 diag_printf(" IRQ %2d -> IOAPIC INT %2d\n",i,cyg_hal_isa_bus_irq[i]);
587 /*------------------------------------------------------------------------*/
590 static cyg_bool cyg_hal_smp_init_apic(void)
595 HAL_SMP_CPU_TYPE cpu;
597 HAL_APIC_READ(HAL_APIC_ID, val );
601 diag_printf("Local APIC: %08x\n",cyg_hal_smp_local_apic);
602 diag_printf(" ID: %08x\n",val);
604 // get max local vector table entry offset
605 HAL_APIC_READ(HAL_APIC_VER, maxlvt );
607 diag_printf(" VERSION: %08x\n",maxlvt);
614 diag_printf("maxlvt = %d\n",maxlvt);
617 // Start by ensuring that all interrupt sources are disabled. The
618 // following code ensures that this happens cleanly.
620 // Local timer vector
621 HAL_APIC_READ( HAL_APIC_LVT_TIMER, val );
622 val |= HAL_APIC_LVT_MASK ;
623 HAL_APIC_WRITE( HAL_APIC_LVT_TIMER, val );
626 diag_printf(" APIC_LVT_TIMER: %08x\n",val);
629 // Local interrupt vectors
630 HAL_APIC_READ( HAL_APIC_LVT_INT0, val );
631 val |= HAL_APIC_LVT_MASK ;
632 HAL_APIC_WRITE( HAL_APIC_LVT_INT0, val );
635 diag_printf(" APIC_LVT_INT0: %08x\n",val);
638 HAL_APIC_READ( HAL_APIC_LVT_INT1, val );
639 val |= HAL_APIC_LVT_MASK ;
640 HAL_APIC_WRITE( HAL_APIC_LVT_INT1, val );
643 diag_printf(" APIC_LVT_INT1: %08x\n",val);
648 HAL_APIC_READ( HAL_APIC_LVT_ERROR, val );
649 val |= HAL_APIC_LVT_MASK ;
650 HAL_APIC_WRITE( HAL_APIC_LVT_ERROR, val );
652 diag_printf(" APIC_LVT_ERROR: %08x\n",val);
657 HAL_APIC_READ( HAL_APIC_LVT_PC, val );
658 val |= HAL_APIC_LVT_MASK ;
659 HAL_APIC_WRITE( HAL_APIC_LVT_PC, val );
661 diag_printf(" APIC_LVT_PC: %08x\n",val);
665 // Now initialize the local vector table.
667 HAL_APIC_WRITE( HAL_APIC_LVT_TIMER, HAL_APIC_LVT_MASK );
668 HAL_APIC_WRITE( HAL_APIC_LVT_INT0, HAL_APIC_LVT_MASK );
669 HAL_APIC_WRITE( HAL_APIC_LVT_INT1, HAL_APIC_LVT_MASK );
671 HAL_APIC_WRITE( HAL_APIC_LVT_ERROR, HAL_APIC_LVT_MASK );
673 HAL_APIC_WRITE( HAL_APIC_LVT_PC, HAL_APIC_LVT_MASK );
676 // Set up DFR to flat delivery mode.
677 HAL_APIC_WRITE( HAL_APIC_DFR, 0xffffffff );
679 // Set up logical destination id. We set bit 1<<cpuid in the LDR
682 HAL_APIC_READ( HAL_APIC_LDR, val );
684 HAL_APIC_WRITE( HAL_APIC_LDR, val );
686 // Set TPR register to accept all.
687 HAL_APIC_WRITE( HAL_APIC_TPR, 0 );
689 // Enable APIC in SPIV
690 HAL_APIC_WRITE( HAL_APIC_SPIV, 0x00000100 );
693 if( cyg_hal_smp_cpu_flags[HAL_SMP_CPU_THIS()] & 2 )
695 // This is the boot CPU, switch its PIC into APIC mode
696 // Non-boot CPUs are already in APIC mode.
698 HAL_WRITE_UINT8( 0x22, 0x70 );
699 HAL_WRITE_UINT8( 0x23, 0x01 );
705 /*------------------------------------------------------------------------*/
706 // Initialize I/O APIC
708 static cyg_bool cyg_hal_smp_init_ioapic(void)
711 cyg_uint32 tabsize = 0;
713 HAL_SMP_CPU_TYPE cpu_this = HAL_SMP_CPU_THIS();
715 HAL_SPINLOCK_CLEAR( cyg_hal_ioapic_lock );
716 HAL_SPINLOCK_SPIN( cyg_hal_ioapic_lock );
718 HAL_IOAPIC_READ( HAL_IOAPIC_REG_APICVER, val );
719 tabsize = (val>>16)&0xFF;
721 // Set up ISA interrupts
722 for( i = 0; i < 16; i++ )
724 if( cyg_hal_isa_bus_irq[i] != 100 )
726 CYG_WORD32 tehi = 0, telo = 0x00010000;
728 tehi |= cpu_this<<24;
730 telo |= CYGNUM_HAL_ISR_MIN+i;
732 HAL_IOAPIC_WRITE( HAL_IOAPIC_REG_REDIR_LO(cyg_hal_isa_bus_irq[i]), telo );
733 HAL_IOAPIC_WRITE( HAL_IOAPIC_REG_REDIR_HI(cyg_hal_isa_bus_irq[i]), tehi );
739 diag_printf("I/O APIC: %08x\n",cyg_hal_smp_io_apic);
741 HAL_IOAPIC_READ( HAL_IOAPIC_REG_APICID, val );
742 diag_printf(" ID: %08x\n",val);
744 HAL_IOAPIC_READ( HAL_IOAPIC_REG_APICVER, val );
745 diag_printf(" VER: %08x\n",val);
747 HAL_IOAPIC_READ( HAL_IOAPIC_REG_APICARB, val );
748 diag_printf(" ARB: %08x\n",val);
750 diag_printf(" Redirection Table:\n");
751 for( i = 0; i < tabsize; i++ )
753 CYG_WORD32 tehi, telo;
755 HAL_IOAPIC_READ( HAL_IOAPIC_REG_REDIR_LO(i), telo );
756 HAL_IOAPIC_READ( HAL_IOAPIC_REG_REDIR_HI(i), tehi );
757 diag_printf(" %02d: %08x %08x\n",i,tehi,telo);
761 HAL_SPINLOCK_CLEAR( cyg_hal_ioapic_lock );
766 /*------------------------------------------------------------------------*/
768 static volatile CYG_WORD32 init_deasserted;
770 __externC volatile CYG_WORD32 cyg_hal_smp_cpu_sync_flag[HAL_SMP_CPU_MAX];
771 __externC volatile CYG_WORD32 cyg_hal_smp_cpu_sync[HAL_SMP_CPU_MAX];
772 __externC volatile void (*cyg_hal_smp_cpu_entry[HAL_SMP_CPU_MAX])(void);
773 __externC volatile CYG_WORD32 cyg_hal_smp_vsr_sync_flag;
774 __externC volatile CYG_WORD32 cyg_hal_smp_cpu_running[HAL_SMP_CPU_MAX];
776 /*------------------------------------------------------------------------*/
778 __externC void cyg_hal_smp_start(void);
780 __externC CYG_BYTE cyg_hal_slave_trampoline[];
781 __externC CYG_BYTE cyg_hal_slave_trampoline_end[];
783 #define HAL_SLAVE_START_ADDRESS 0x00002000
785 __externC void cyg_hal_cpu_start( HAL_SMP_CPU_TYPE cpu )
788 CYG_WORD32 icrlo, icrhi;
789 CYG_BYTE *p = &cyg_hal_slave_trampoline[0];
790 CYG_BYTE *q = (CYG_BYTE *)HAL_SLAVE_START_ADDRESS;
794 // Copy the trampoline over...
798 } while( p != &cyg_hal_slave_trampoline_end[0]);
800 // Init synchronization spinlock to locked to halt slave CPU in
801 // cyg_hal_smp_startup().
805 // We now have to execute the grungy and unpleasant AP startup
806 // sequence to get the cpu running. I'm sure that not all of this
807 // is strictly necessary, but it works and it is too much effort
808 // to work out the minimal subset.
810 // Write warm-reset code into CMOS RAM and write the address of
811 // the trampoline entry point into location 40:67.
812 HAL_READ_CMOS( 0x0f, old_cmos );
813 HAL_WRITE_CMOS( 0x0f, 0x0a );
814 HAL_WRITEMEM_UINT16( 0x467, HAL_SLAVE_START_ADDRESS & 0xf );
815 HAL_WRITEMEM_UINT16( 0x469, HAL_SLAVE_START_ADDRESS>>4 );
818 // Send an INIT interrupt to the dest CPU
822 HAL_APIC_WRITE( HAL_APIC_ICR_HI, icrhi );
823 HAL_APIC_WRITE( HAL_APIC_ICR_LO, icrlo );
825 hal_delay_us( 10 * 1000 ); // Wait 10ms
827 // Wait for the ICR to become inactive
829 HAL_APIC_READ( HAL_APIC_ICR_LO, icrlo );
830 } while( (icrlo & 0x00001000) != 0 );
832 // Now de-assert INIT
836 HAL_APIC_WRITE( HAL_APIC_ICR_HI, icrhi );
837 HAL_APIC_WRITE( HAL_APIC_ICR_LO, icrlo );
841 // Now we send two STARTUP IPIs
843 for( i = 0; i < 2; i++ )
845 icrlo = 0x00000600 | (HAL_SLAVE_START_ADDRESS>>12);
847 // Send the STARTUP IPI
848 HAL_APIC_WRITE( HAL_APIC_ICR_HI, icrhi );
849 HAL_APIC_WRITE( HAL_APIC_ICR_LO, icrlo );
853 // Wait for the ICR to become inactive
855 HAL_APIC_READ( HAL_APIC_ICR_LO, icrlo );
856 } while( (icrlo & 0x00001000) != 0 );
861 HAL_WRITE_CMOS( 0x0f, old_cmos );
863 // PC_WRITE_SCREEN( PC_SCREEN_LINE(5)+0, '!' );
867 // PC_WRITE_SCREEN( PC_SCREEN_LINE(5)+1, '!' );
872 /*------------------------------------------------------------------------*/
874 __externC void cyg_hal_smp_start(void);
875 __externC void cyg_hal_smp_startup(void);
877 __externC void cyg_hal_cpu_release( HAL_SMP_CPU_TYPE cpu )
879 // PC_WRITE_SCREEN( PC_SCREEN_LINE(13), '!' );
880 // PC_WRITE_SCREEN_8( PC_SCREEN_LINE(13), cpu );
882 cyg_hal_smp_cpu_entry[cpu] = cyg_hal_smp_start;
884 while( cyg_hal_smp_cpu_entry[cpu] != 0 )
886 // PC_WRITE_SCREEN_32( PC_SCREEN_LINE(13)+4, cyg_hal_smp_cpu_entry[cpu] );
892 /*------------------------------------------------------------------------*/
894 __externC void cyg_hal_smp_startup(void)
896 HAL_SMP_CPU_TYPE cpu;
898 // PC_WRITE_SCREEN( PC_SCREEN_LINE(2)+0, '!' );
900 #ifndef CYG_HAL_STARTUP_RAM
901 // Wait for INIT interrupt to be deasserted
902 while( !init_deasserted )
906 // PC_WRITE_SCREEN( PC_SCREEN_LINE(2)+1, '!' );
908 cpu = HAL_SMP_CPU_THIS();
910 // PC_WRITE_SCREEN_8( PC_SCREEN_LINE(2)+6, cpu );
912 #ifndef CYG_HAL_STARTUP_RAM
913 // Wait 1s for the world to settle
914 hal_delay_us( 1000000 );
916 // PC_WRITE_SCREEN( PC_SCREEN_LINE(2)+2, '!' );
919 cyg_hal_smp_init_apic();
922 // PC_WRITE_SCREEN( PC_SCREEN_LINE(2)+3, '!' );
924 #ifdef CYGPKG_KERNEL_SMP_SUPPORT
925 cyg_hal_smp_cpu_running[cpu] = 1;
926 cyg_kernel_smp_startup();
932 while( (entry = cyg_hal_smp_cpu_entry[cpu]) == 0 )
934 #if 0 //SCREEN_DIAGNOSTICS
936 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(2)+10, n );
937 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(2)+15, cyg_hal_smp_cpu_sync[cpu] );
938 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(2)+30, cyg_hal_smp_cpu_sync_flag[0] );
939 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(2)+35, cyg_hal_smp_cpu_sync_flag[1] );
940 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(2)+40, cyg_hal_smp_vsr_sync_flag );
946 // PC_WRITE_SCREEN( PC_SCREEN_LINE(2)+4, '!' );
948 cyg_hal_smp_cpu_entry[cpu] = 0;
950 // PC_WRITE_SCREEN_32( PC_SCREEN_LINE(2)+20, entry );
954 cyg_hal_smp_cpu_running[cpu] = 1;
961 /*------------------------------------------------------------------------*/
963 __externC void cyg_hal_smp_init(void)
965 if( !cyg_hal_find_smp_config() )
968 if( !cyg_hal_parse_smp_config() )
971 if( !cyg_hal_smp_init_apic() )
974 if( !cyg_hal_smp_init_ioapic() )
978 /*------------------------------------------------------------------------*/
980 __externC void cyg_hal_smp_cpu_start_all(void)
982 HAL_SMP_CPU_TYPE cpu;
984 for( cpu = 0; cpu < HAL_SMP_CPU_COUNT(); cpu++ )
986 cyg_hal_smp_cpu_sync[cpu] = 0;
987 cyg_hal_smp_cpu_sync_flag[cpu] = 0;
988 cyg_hal_smp_cpu_running[cpu] = 0;
989 cyg_hal_smp_cpu_entry[cpu] = 0;
991 if( cpu != HAL_SMP_CPU_THIS() )
992 cyg_hal_cpu_start( cpu );
993 else cyg_hal_smp_cpu_running[cpu] = 1;
997 /*------------------------------------------------------------------------*/
998 // SMP message buffers.
999 // SMP CPUs pass messages to eachother via a small circular buffer
1000 // protected by a spinlock. Each message is a single 32 bit word with
1001 // a type code in the top 4 bits and any argument in the remaining
1004 #define SMP_MSGBUF_SIZE 4
1006 static struct smp_msg_t
1008 HAL_SPINLOCK_TYPE lock; // protecting spinlock
1009 volatile CYG_WORD32 msgs[SMP_MSGBUF_SIZE]; // message buffer
1010 volatile CYG_WORD32 head; // head of list
1011 volatile CYG_WORD32 tail; // tail of list
1012 volatile CYG_WORD32 reschedule; // reschedule request
1013 volatile CYG_WORD32 timeslice; // timeslice request
1014 } smp_msg[HAL_SMP_CPU_MAX];
1016 /*------------------------------------------------------------------------*/
1017 // Pass a message to another CPU.
1019 #if SCREEN_DIAGNOSTICS
1020 static int res_msgs[2], tms_msgs[2];
1023 __externC void cyg_hal_cpu_message( HAL_SMP_CPU_TYPE cpu,
1029 CYG_INTERRUPT_STATE istate;
1030 struct smp_msg_t *m = &smp_msg[cpu];
1032 HAL_SMP_CPU_TYPE me = HAL_SMP_CPU_THIS();
1034 HAL_DISABLE_INTERRUPTS( istate );
1036 // Get access to the message buffer for the selected CPU
1037 HAL_SPINLOCK_SPIN( m->lock );
1039 #if 0 //SCREEN_DIAGNOSTICS
1040 if( msg == HAL_SMP_MESSAGE_RESCHEDULE )
1042 else if( msg == HAL_SMP_MESSAGE_TIMESLICE )
1044 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(18+me), me);
1045 PC_WRITE_SCREEN_16( PC_SCREEN_LINE(18+me)+40, res_msgs[me]);
1046 PC_WRITE_SCREEN_16( PC_SCREEN_LINE(18+me)+45, tms_msgs[me]);
1049 if( msg == HAL_SMP_MESSAGE_RESCHEDULE )
1050 m->reschedule = true;
1051 else if( msg == HAL_SMP_MESSAGE_TIMESLICE )
1052 m->timeslice = true;
1055 CYG_WORD32 next = (m->tail + 1) & (SMP_MSGBUF_SIZE-1);
1057 // If the buffer is full, wait for space to appear in it.
1058 // This should only need to be done very rarely.
1060 while( next == m->head )
1062 HAL_SPINLOCK_CLEAR( m->lock );
1063 for( i = 0; i < 1000; i++ );
1064 HAL_SPINLOCK_SPIN( m->lock );
1067 m->msgs[m->tail] = msg | arg;
1072 // Now send an interrupt to the CPU.
1074 // PC_WRITE_SCREEN_16( PC_SCREEN_LINE(18+me)+50, cyg_hal_smp_cpu_running[cpu] );
1076 if( cyg_hal_smp_cpu_running[cpu] )
1078 CYG_WORD32 icrlo, icrhi;
1080 // Set the ICR fields we want to write. Most fields are zero
1081 // except the destination in the high word and the vector
1082 // number in the low.
1084 icrlo = CYGNUM_HAL_SMP_CPU_INTERRUPT_VECTOR( cpu );
1086 // Write the ICR register. The interrupt will be raised when
1087 // the low word is written.
1088 HAL_APIC_WRITE( HAL_APIC_ICR_HI, icrhi );
1089 HAL_APIC_WRITE( HAL_APIC_ICR_LO, icrlo );
1091 // Wait for the ICR to become inactive
1093 #if 0 //SCREEN_DIAGNOSTICS
1095 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(18+me)+55, n );
1098 HAL_APIC_READ( HAL_APIC_ICR_LO, icrlo );
1099 } while( (icrlo & 0x00001000) != 0 );
1102 HAL_SPINLOCK_CLEAR( m->lock );
1104 // If we are expected to wait for the command to complete, then
1105 // spin here until it does. We actually wait for the destination
1106 // CPU to empty its input buffer. So we might wait for messages
1107 // from other CPUs as well. But this is benign.
1111 for( i = 0; i < 1000; i++ );
1113 HAL_SPINLOCK_SPIN( m->lock );
1115 if( m->head == m->tail )
1118 HAL_SPINLOCK_CLEAR( m->lock );
1122 HAL_RESTORE_INTERRUPTS( istate );
1126 /*------------------------------------------------------------------------*/
1128 #if SCREEN_DIAGNOSTICS
1133 __externC CYG_WORD32 cyg_hal_cpu_message_isr( CYG_WORD32 vector, CYG_ADDRWORD data )
1135 HAL_SMP_CPU_TYPE me = HAL_SMP_CPU_THIS();
1136 struct smp_msg_t *m = &smp_msg[me];
1138 CYG_INTERRUPT_STATE istate;
1140 HAL_DISABLE_INTERRUPTS( istate );
1142 HAL_SPINLOCK_SPIN( m->lock );
1144 // First, acknowledge the interrupt.
1146 HAL_INTERRUPT_ACKNOWLEDGE( vector );
1148 #if SCREEN_DIAGNOSTICS
1150 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(18+me), me);
1151 PC_WRITE_SCREEN_16( PC_SCREEN_LINE(18+me)+5, isrs[me]);
1154 if( m->reschedule || m->timeslice )
1155 ret |= 2; // Ask for the DSR to be called.
1157 // Now pick messages out of the buffer and handle them
1159 while( m->head != m->tail )
1161 CYG_WORD32 msg = m->msgs[m->head];
1163 switch( msg & HAL_SMP_MESSAGE_TYPE )
1165 case HAL_SMP_MESSAGE_RESCHEDULE:
1166 ret |= 2; // Ask for the DSR to be called.
1168 case HAL_SMP_MESSAGE_MASK:
1169 // Mask the supplied vector
1170 // cyg_hal_interrupt_set_mask( msg&HAL_SMP_MESSAGE_ARG, false );
1172 case HAL_SMP_MESSAGE_UNMASK:
1173 // Unmask the supplied vector
1174 // cyg_hal_interrupt_set_mask( msg&HAL_SMP_MESSAGE_ARG, true );
1176 case HAL_SMP_MESSAGE_REVECTOR:
1177 // Deal with a change of CPU assignment for a vector. We
1178 // only actually worry about what happens when the vector
1179 // is changed to some other CPU. We just mask the
1180 // interrupt locally.
1181 // if( hal_interrupt_cpu[msg&HAL_SMP_MESSAGE_ARG] != me )
1182 // cyg_hal_interrupt_set_mask( msg&HAL_SMP_MESSAGE_ARG, false );
1186 // Update the head pointer after handling the message, so that
1187 // the wait in cyg_hal_cpu_message() completes after the action
1190 m->head = (m->head + 1) & (SMP_MSGBUF_SIZE-1);
1193 HAL_SPINLOCK_CLEAR( m->lock );
1195 HAL_RESTORE_INTERRUPTS( istate );
1200 /*------------------------------------------------------------------------*/
1202 // This is only executed if the message was
1203 // HAL_SMP_MESSAGE_RESCHEDULE. It calls up into the kernel to effect a
1206 __externC void cyg_scheduler_set_need_reschedule(void);
1207 __externC void cyg_scheduler_timeslice_cpu(void);
1209 #if SCREEN_DIAGNOSTICS
1210 __externC int cyg_scheduler_sched_lock;
1211 static int rescheds[2];
1212 static int timeslices[2];
1215 __externC CYG_WORD32 cyg_hal_cpu_message_dsr( CYG_WORD32 vector, CYG_ADDRWORD data )
1217 HAL_SMP_CPU_TYPE me = HAL_SMP_CPU_THIS();
1218 struct smp_msg_t *m = &smp_msg[me];
1219 CYG_INTERRUPT_STATE istate;
1220 CYG_WORD32 reschedule, timeslice;
1222 HAL_DISABLE_INTERRUPTS( istate );
1223 HAL_SPINLOCK_SPIN( m->lock );
1225 #if SCREEN_DIAGNOSTICS
1227 PC_WRITE_SCREEN_16( PC_SCREEN_LINE(18+me)+10, dsrs[me]);
1228 PC_WRITE_SCREEN_16( PC_SCREEN_LINE(18+me)+15, cyg_scheduler_sched_lock);
1231 reschedule = m->reschedule;
1232 timeslice = m->timeslice;
1233 m->reschedule = m->timeslice = false;
1235 HAL_SPINLOCK_CLEAR( m->lock );
1236 HAL_RESTORE_INTERRUPTS( istate );
1240 #if SCREEN_DIAGNOSTICS
1242 PC_WRITE_SCREEN_16( PC_SCREEN_LINE(18+me)+20, rescheds[me]);
1244 cyg_scheduler_set_need_reschedule();
1248 #if SCREEN_DIAGNOSTICS
1250 PC_WRITE_SCREEN_16( PC_SCREEN_LINE(18+me)+25, timeslices[me]);
1252 cyg_scheduler_timeslice_cpu();
1259 /*------------------------------------------------------------------------*/
1261 #if SCREEN_DIAGNOSTICS
1265 __externC void cyg_hal_smp_halt_other_cpus(void)
1268 HAL_SMP_CPU_TYPE me = HAL_SMP_CPU_THIS();
1270 // PC_WRITE_SCREEN_8( PC_SCREEN_LINE(6+me), me );
1272 for( i = 0 ; i < HAL_SMP_CPU_COUNT(); i++ )
1274 if( i != me && cyg_hal_smp_cpu_running[i] )
1276 CYG_WORD32 icrhi, icrlo;
1279 // PC_WRITE_SCREEN_8( PC_SCREEN_LINE(6+me)+40, i );
1281 oldsync = cyg_hal_smp_cpu_sync_flag[i];
1282 cyg_hal_smp_cpu_sync[i] = 0;
1286 icrlo = CYGNUM_HAL_VECTOR_NMI; // not really used
1287 icrlo |= 0x00000400; // Delivery = NMI
1288 //icrlo |= 0x000C0000; // Dest = all excluding self
1290 // Write the ICR register. The interrupt will be raised when
1291 // the low word is written.
1292 HAL_APIC_WRITE( HAL_APIC_ICR_HI, icrhi );
1293 HAL_APIC_WRITE( HAL_APIC_ICR_LO, icrlo );
1295 // Wait for the ICR to become inactive
1297 #if 0 //SCREEN_DIAGNOSTICS
1299 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(6+me)+45, n );
1302 HAL_APIC_READ( HAL_APIC_ICR_LO, icrlo );
1303 } while( (icrlo & 0x00001000) != 0 );
1305 // Wait for CPU to halt
1306 while( cyg_hal_smp_cpu_sync_flag[i] == oldsync )
1308 #if 0 //SCREEN_DIAGNOSTICS
1309 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(6+me)+4, x ); x++;
1310 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(6+me)+10+(i*8), cyg_hal_smp_cpu_sync_flag[i] );
1311 PC_WRITE_SCREEN_8( PC_SCREEN_LINE(6+me)+10+(i*8)+4, oldsync );
1313 hal_delay_us( 100 );
1321 __externC void cyg_hal_smp_release_other_cpus(void)
1324 for( i = 0 ; i < HAL_SMP_CPU_COUNT(); i++ )
1326 if( i != HAL_SMP_CPU_THIS() && cyg_hal_smp_cpu_running[i] )
1328 CYG_WORD32 oldsync = cyg_hal_smp_cpu_sync_flag[i];
1329 cyg_hal_smp_cpu_sync[i] = 1;
1330 while( cyg_hal_smp_cpu_sync_flag[i] == oldsync )
1332 cyg_hal_smp_cpu_sync[i] = 0;
1337 #endif // CYGPKG_HAL_SMP_SUPPORT
1339 /*------------------------------------------------------------------------*/
1340 /* End of pcmb_smp.c */