1 /*- 2 * Copyright (c) 2005 Sandvine Incorporated. All righs reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * Author: Ed Maste <emaste@FreeBSD.org> 26 */ 27 28 /* 29 * This module detects Intel Multiprocessor spec info (mptable) and returns 30 * the number of cpu's identified. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/types.h> 37 #include <x86/mptable.h> 38 39 #include <err.h> 40 #include <fcntl.h> 41 #include <inttypes.h> 42 #include <paths.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #define MPFPS_SIG "_MP_" 48 #define MPCTH_SIG "PCMP" 49 50 #define PTOV(pa) ((off_t)(pa)) 51 52 static mpfps_t biosmptable_find_mpfps(void); 53 static mpfps_t biosmptable_search_mpfps(off_t base, int length); 54 static mpcth_t biosmptable_check_mpcth(off_t addr); 55 56 static int memopen(void); 57 static void memclose(void); 58 59 int biosmptable_detect(void); 60 61 int 62 biosmptable_detect(void) 63 { 64 mpfps_t mpfps; 65 mpcth_t mpcth; 66 char *entry_type_p; 67 proc_entry_ptr proc; 68 int ncpu, i; 69 70 if (!memopen()) 71 return -1; /* XXX 0? */ 72 /* locate and validate the mpfps */ 73 mpfps = biosmptable_find_mpfps(); 74 mpcth = NULL; 75 if (mpfps == NULL) { 76 ncpu = 0; 77 } else if (mpfps->config_type != 0) { 78 /* 79 * If thie config_type is nonzero then this is a default configuration 80 * from Chapter 5 in the MP spec. Report 2 cpus and 1 I/O APIC. 81 */ 82 ncpu = 2; 83 } else { 84 ncpu = 0; 85 mpcth = biosmptable_check_mpcth(PTOV(mpfps->pap)); 86 if (mpcth != NULL) { 87 entry_type_p = (char *)(mpcth + 1); 88 for (i = 0; i < mpcth->entry_count; i++) { 89 switch (*entry_type_p) { 90 case 0: 91 entry_type_p += sizeof(struct PROCENTRY); 92 proc = (proc_entry_ptr) entry_type_p; 93 warnx("MPTable: Found CPU APIC ID %d %s", 94 proc->apic_id, 95 proc->cpu_flags & PROCENTRY_FLAG_EN ? 96 "enabled" : "disabled"); 97 if (proc->cpu_flags & PROCENTRY_FLAG_EN) 98 ncpu++; 99 break; 100 case 1: 101 entry_type_p += sizeof(struct BUSENTRY); 102 break; 103 case 2: 104 entry_type_p += sizeof(struct IOAPICENTRY); 105 break; 106 case 3: 107 case 4: 108 entry_type_p += sizeof(struct INTENTRY); 109 break; 110 default: 111 warnx("unknown mptable entry type (%d)", *entry_type_p); 112 goto done; /* XXX error return? */ 113 } 114 } 115 done: 116 ; 117 } 118 } 119 memclose(); 120 if (mpcth != NULL) 121 free(mpcth); 122 if (mpfps != NULL) 123 free(mpfps); 124 125 return ncpu; 126 } 127 128 static int pfd = -1; 129 130 static int 131 memopen(void) 132 { 133 if (pfd < 0) { 134 pfd = open(_PATH_MEM, O_RDONLY); 135 if (pfd < 0) 136 warn("%s: cannot open", _PATH_MEM); 137 } 138 return pfd >= 0; 139 } 140 141 static void 142 memclose(void) 143 { 144 if (pfd >= 0) { 145 close(pfd); 146 pfd = -1; 147 } 148 } 149 150 static int 151 memread(off_t addr, void* entry, size_t size) 152 { 153 if ((size_t)pread(pfd, entry, size, addr) != size) { 154 warn("pread (%zu @ 0x%jx)", size, (intmax_t)addr); 155 return 0; 156 } 157 return 1; 158 } 159 160 161 /* 162 * Find the MP Floating Pointer Structure. See the MP spec section 4.1. 163 */ 164 static mpfps_t 165 biosmptable_find_mpfps(void) 166 { 167 mpfps_t mpfps; 168 uint16_t addr; 169 170 /* EBDA is the 1 KB addressed by the 16 bit pointer at 0x40E. */ 171 if (!memread(PTOV(0x40E), &addr, sizeof(addr))) 172 return (NULL); 173 mpfps = biosmptable_search_mpfps(PTOV(addr << 4), 0x400); 174 if (mpfps != NULL) 175 return (mpfps); 176 177 /* Check the BIOS. */ 178 mpfps = biosmptable_search_mpfps(PTOV(0xf0000), 0x10000); 179 if (mpfps != NULL) 180 return (mpfps); 181 182 return (NULL); 183 } 184 185 static mpfps_t 186 biosmptable_search_mpfps(off_t base, int length) 187 { 188 mpfps_t mpfps; 189 u_int8_t *cp, sum; 190 int ofs, idx; 191 192 mpfps = malloc(sizeof(*mpfps)); 193 if (mpfps == NULL) { 194 warnx("unable to malloc space for MP Floating Pointer Structure"); 195 return (NULL); 196 } 197 /* search on 16-byte boundaries */ 198 for (ofs = 0; ofs < length; ofs += 16) { 199 if (!memread(base + ofs, mpfps, sizeof(*mpfps))) 200 break; 201 202 /* compare signature, validate checksum */ 203 if (!strncmp(mpfps->signature, MPFPS_SIG, strlen(MPFPS_SIG))) { 204 cp = (u_int8_t *)mpfps; 205 sum = 0; 206 /* mpfps is 16 bytes, or one "paragraph" */ 207 if (mpfps->length != 1) { 208 warnx("bad mpfps length (%d)", mpfps->length); 209 continue; 210 } 211 for (idx = 0; idx < mpfps->length * 16; idx++) 212 sum += *(cp + idx); 213 if (sum != 0) { 214 warnx("bad mpfps checksum (%d)\n", sum); 215 continue; 216 } 217 return (mpfps); 218 } 219 } 220 free(mpfps); 221 return (NULL); 222 } 223 224 static mpcth_t 225 biosmptable_check_mpcth(off_t addr) 226 { 227 mpcth_t mpcth; 228 u_int8_t *cp, sum; 229 int idx, table_length; 230 231 /* mpcth must be in the first 1MB */ 232 if ((u_int32_t)addr >= 1024 * 1024) { 233 warnx("bad mpcth address (0x%jx)\n", (intmax_t)addr); 234 return (NULL); 235 } 236 237 mpcth = malloc(sizeof(*mpcth)); 238 if (mpcth == NULL) { 239 warnx("unable to malloc space for MP Configuration Table Header"); 240 return (NULL); 241 } 242 if (!memread(addr, mpcth, sizeof(*mpcth))) 243 goto bad; 244 /* Compare signature and validate checksum. */ 245 if (strncmp(mpcth->signature, MPCTH_SIG, strlen(MPCTH_SIG)) != 0) { 246 warnx("bad mpcth signature"); 247 goto bad; 248 } 249 table_length = mpcth->base_table_length; 250 mpcth = realloc(mpcth, table_length); 251 if (mpcth == NULL) { 252 warnx("unable to realloc space for mpcth (len %u)", table_length); 253 return (NULL); 254 } 255 if (!memread(addr, mpcth, table_length)) 256 goto bad; 257 cp = (u_int8_t *)mpcth; 258 sum = 0; 259 for (idx = 0; idx < mpcth->base_table_length; idx++) 260 sum += *(cp + idx); 261 if (sum != 0) { 262 warnx("bad mpcth checksum (%d)", sum); 263 goto bad; 264 } 265 266 return mpcth; 267 bad: 268 free(mpcth); 269 return (NULL); 270 } 271