/* [_Anarchy_(alan@lightning.swansea.uk.linux.org)] you should do one check though - if the board seems to be SMP and the CPU in /proc/cpuinfo is non intel dont install an SMP kernel - thats a dual pentium board with a cyrix or similar single cpu in it */ #include #include #include #include #include #include #include #ifdef __alpha__ int alphaDetectSMP(void) { int issmp = 0; FILE *f; f = fopen("/proc/cpuinfo", "r"); if (f) { char buff[1024]; while (fgets (buff, 1024, f) != NULL) { if (!strncmp (buff, "cpus detected\t\t: ", 17)) { if (strtoul (buff + 17, NULL, 0) > 1) issmp = 1; break; } } fclose(f); } else return -1; return issmp; } #endif /* __alpha__ */ #ifdef __sparc__ int sparcDetectSMP(void) { int issmp = 0; FILE *f; f = fopen("/proc/cpuinfo", "r"); if (f) { char buff[1024]; while (fgets (buff, 1024, f) != NULL) { if (!strncmp (buff, "ncpus probed\t: ", 15)) { if (strtoul (buff + 15, NULL, 0) > 1) issmp = 1; break; } } fclose(f); } else return -1; return issmp; } #endif /* __sparc__ */ #ifdef __powerpc__ /* minifind.c -- simple find library * * Copyright (c) 2002 Terra Soft Solutions, Inc. * Written by Dan Burcaw * * This software may be freely redistributed under the terms of the GNU * library public license. * * You should have received a copy of the GNU Library Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include struct pathNode { char *path; struct pathNode *next; }; struct findNode { struct pathNode *result; struct pathNode *exclude; }; // insert a node at head of linked-list static void insert_node(struct pathNode *n, char *path) { struct pathNode *new = (struct pathNode *) malloc(sizeof(struct pathNode)); new->path = path; new->next = n->next; n->next = new; } // return input strip less last character static char *stripLastChar(char *in) { char *out = malloc(sizeof(char)*strlen(in)); snprintf(out, strlen(in) - 1, "%s", in); return out; } // do the work static char *minifind(char *dir, char *search, struct findNode *list) { char *d = NULL; int n; struct dirent **namelist; struct stat buf; if (dir[strlen(dir)-1] == '/') dir = stripLastChar(dir); // check is there is an exact filematch to dir // when search is not specified if (search == NULL) { if (lstat(dir, &buf) == 0) insert_node(list->result, dir); return 0; } n = scandir(dir, &namelist, 0, alphasort); if (n >= 0) { while (n--) { d = malloc(sizeof(char) * (strlen(dir) \ + strlen(namelist[n]->d_name)+1)); sprintf(d, "%s/%s", dir, namelist[n]->d_name); if (strstr(namelist[n]->d_name, search)) insert_node(list->result, d); if ((lstat(d, &buf) == 0) && S_ISDIR(buf.st_mode)) { if (strcmp(namelist[n]->d_name, ".") && strcmp(namelist[n]->d_name, "..")) d = minifind(d, search, list); } free(namelist[n]); } free(namelist); return d; } return 0; } int ppcDetectSMP(void) { int issmp = -1; FILE *f; struct findNode *list = (struct findNode *) malloc(sizeof(struct findNode)); struct pathNode *n; list->result = (struct pathNode *) malloc(sizeof(struct pathNode)); list->result->path = NULL; list->result->next = list->result; minifind("/proc/device-tree/cpus", "device_type", list); for (n = list->result->next; n != list->result; n = n->next) { f = fopen(n->path, "r"); if (f) { char buff[1024]; while (fgets (buff, 1024, f) != NULL) { if (!strncmp (buff, "cpu", 3)) { issmp++; break; } } fclose(f); } } return issmp; } #endif /* __powerpc__ */ #if defined(__i386__) || defined(__x86_64__) /* * Copyright (c) 1996, by Steve Passe * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the developer may NOT be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id$ */ /* * mptable.c */ #define VMAJOR 2 #define VMINOR 0 #define VDELTA 12 /* * this will cause the raw mp table to be dumped to /tmp/mpdump * #define RAW_DUMP */ #define MP_SIG 0x5f504d5f /* _MP_ */ #define EXTENDED_PROCESSING_READY #define OEM_PROCESSING_READY_NOT #include #include #include #include #include #include #define LINUX 1 #if LINUX typedef unsigned int vm_offset_t; #else #include #endif typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef vm_offset_t addr_t; /* EBDA is @ 40:0e in real-mode terms */ #define EBDA_POINTER 0x040e /* location of EBDA pointer */ /* CMOS 'top of mem' is @ 40:13 in real-mode terms */ #define TOPOFMEM_POINTER 0x0413 /* BIOS: base memory size */ #define DEFAULT_TOPOFMEM 0xa0000 #define BIOS_BASE 0xf0000 #define BIOS_BASE2 0xe0000 #define BIOS_SIZE 0x10000 #define ONE_KBYTE 1024 #define GROPE_AREA1 0x80000 #define GROPE_AREA2 0x90000 #define GROPE_SIZE 0x10000 /* MP Floating Pointer Structure */ typedef struct MPFPS { char signature[ 4 ]; addr_t pap; u8 length; u8 spec_rev; u8 checksum; u8 mpfb1; u8 mpfb2; u8 mpfb3; u8 mpfb4; u8 mpfb5; } mpfps_t; /* MP Configuration Table Header */ typedef struct MPCTH { char signature[ 4 ]; u16 base_table_length; u8 spec_rev; u8 checksum; u8 oem_id[ 8 ]; u8 product_id[ 12 ]; addr_t oem_table_pointer; u16 oem_table_size; u16 entry_count; addr_t apic_address; u16 extended_table_length; u8 extended_table_checksum; u8 reserved; } mpcth_t; typedef struct PROCENTRY { u8 type; u8 apicID; u8 apicVersion; u8 cpuFlags; u32 cpuSignature; u32 featureFlags; u32 reserved1; u32 reserved2; } ProcEntry; #define PROCENTRY_FLAG_EN 0x01 static void seekEntry( vm_offset_t addr ); static void apic_probe( vm_offset_t* paddr, int* where ); static void readEntry( void* entry, int size ); /* global data */ static int pfd; /* physical /dev/mem fd */ static int verbose = 0; static int grope = 0; static int readType() { u_char type; if ( read( pfd, &type, sizeof( u_char ) ) != sizeof( u_char ) ) { perror( "type read" ); fprintf( stderr, "\npfd: %d", pfd ); fflush( stderr ); exit( 1 ); } if ( lseek( pfd, -1, SEEK_CUR ) < 0 ) { perror( "type seek" ); exit( 1 ); } return (int)type; } extern int intelDetectSMP(void); // old detection static int intelDetectSMP_mptable(void) { vm_offset_t paddr; int where; mpfps_t mpfps; int rc = 0; int ncpus = 0; /* open physical memory for access to MP structures */ if ( (pfd = open( "/dev/mem", O_RDONLY )) < 0 ) { return 0; } /* probe for MP structures */ apic_probe( &paddr, &where ); if ( where <= 0 ) return 0; seekEntry( paddr ); readEntry( &mpfps, sizeof( mpfps_t ) ); if (mpfps.mpfb1) /* old style */ rc = 1; else { /* go to the config table */ mpcth_t cth; int count, i; paddr = mpfps.pap; seekEntry( paddr ); readEntry( &cth, sizeof( cth ) ); /* if we don't have any entries, the kernel sure won't be able to set up mp. Needs at least one entry for smp kernel */ if (cth.entry_count <= 1) { close (pfd); return 0; } count = cth.entry_count; for (i = 0; i < count; i++) { if ( readType() == 0 ) { ProcEntry entry; readEntry( &entry, sizeof( entry ) ); if (entry.cpuFlags & PROCENTRY_FLAG_EN) ncpus++; } } if (ncpus > 1) rc = 1; } close (pfd); return rc; } /* * set PHYSICAL address of MP floating pointer structure */ #define NEXT(X) ((X) += 4) static void apic_probe( vm_offset_t* paddr, int* where ) { /* * c rewrite of apic_probe() by Jack F. Vogel */ int x; u_short segment; vm_offset_t target; u_int buffer[ BIOS_SIZE / sizeof( int ) ]; if ( verbose ) printf( "\n" ); /* search Extended Bios Data Area, if present */ if ( verbose ) printf( " looking for EBDA pointer @ 0x%04x, ", EBDA_POINTER ); seekEntry( (vm_offset_t)EBDA_POINTER ); readEntry( &segment, 2 ); if ( segment ) { /* search EBDA */ target = (vm_offset_t)segment << 4; if ( verbose ) printf( "found, searching EBDA @ 0x%08x\n", target ); seekEntry( target ); readEntry( buffer, ONE_KBYTE ); for ( x = 0; x < ONE_KBYTE / sizeof ( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 1; *paddr = (x * sizeof( unsigned int )) + target; return; } } } else { if ( verbose ) printf( "NOT found\n" ); } /* read CMOS for real top of mem */ seekEntry( (vm_offset_t)TOPOFMEM_POINTER ); readEntry( &segment, 2 ); --segment; /* less ONE_KBYTE */ target = segment * 1024; if ( verbose ) printf( " searching CMOS 'top of mem' @ 0x%08x (%dK)\n", target, segment ); seekEntry( target ); readEntry( buffer, ONE_KBYTE ); for ( x = 0; x < ONE_KBYTE / sizeof ( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 2; *paddr = (x * sizeof( unsigned int )) + target; return; } } /* we don't necessarily believe CMOS, check base of the last 1K of 640K */ if ( target != (DEFAULT_TOPOFMEM - 1024)) { target = (DEFAULT_TOPOFMEM - 1024); if ( verbose ) printf( " searching default 'top of mem' @ 0x%08x (%dK)\n", target, (target / 1024) ); seekEntry( target ); readEntry( buffer, ONE_KBYTE ); for ( x = 0; x < ONE_KBYTE / sizeof ( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 3; *paddr = (x * sizeof( unsigned int )) + target; return; } } } /* search the BIOS */ if ( verbose ) printf( " searching BIOS @ 0x%08x\n", BIOS_BASE ); seekEntry( BIOS_BASE ); readEntry( buffer, BIOS_SIZE ); for ( x = 0; x < BIOS_SIZE / sizeof( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 4; *paddr = (x * sizeof( unsigned int )) + BIOS_BASE; return; } } /* search the extended BIOS */ if ( verbose ) printf( " searching extended BIOS @ 0x%08x\n", BIOS_BASE2 ); seekEntry( BIOS_BASE2 ); readEntry( buffer, BIOS_SIZE ); for ( x = 0; x < BIOS_SIZE / sizeof( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 5; *paddr = (x * sizeof( unsigned int )) + BIOS_BASE2; return; } } if ( grope ) { /* search additional memory */ target = GROPE_AREA1; if ( verbose ) printf( " groping memory @ 0x%08x\n", target ); seekEntry( target ); readEntry( buffer, GROPE_SIZE ); for ( x = 0; x < GROPE_SIZE / sizeof( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 6; *paddr = (x * sizeof( unsigned int )) + GROPE_AREA1; return; } } target = GROPE_AREA2; if ( verbose ) printf( " groping memory @ 0x%08x\n", target ); seekEntry( target ); readEntry( buffer, GROPE_SIZE ); for ( x = 0; x < GROPE_SIZE / sizeof( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 7; *paddr = (x * sizeof( unsigned int )) + GROPE_AREA2; return; } } } *where = 0; *paddr = (vm_offset_t)0; } /* * */ static void seekEntry( vm_offset_t addr ) { if ( lseek( pfd, (off_t)addr, SEEK_SET ) < 0 ) { return; perror( "/dev/mem seek" ); exit( 1 ); } } /* * */ static void readEntry( void* entry, int size ) { if ( read( pfd, entry, size ) != size ) { return; perror( "readEntry" ); exit( 1 ); } } #endif /* __i386__ */ int detectSMP(void) { static int isSMP = -1; if (isSMP != -1) return isSMP; #ifdef __i386__ return isSMP = intelDetectSMP() || intelDetectSMP_mptable(); #elif __sparc__ return isSMP = sparcDetectSMP(); #elif __alpha__ return isSMP = alphaDetectSMP(); #elif __powerpc__ return isSMP = ppcDetectSMP(); #elif __ia64__ /* TODO: Update to check against /proc/pal/cpuX */ return isSMP = 1; #elif __x86_64__ return isSMP = intelDetectSMP() || intelDetectSMP_mptable(); #else #error unknown architecture #endif } #if TEST int main() { if (detectSMP()) printf("has smp\n"); else printf("no smp\n"); } #endif