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/types.h> 34 #include <x86/mptable.h> 35 36 #include <err.h> 37 #include <fcntl.h> 38 #include <inttypes.h> 39 #include <paths.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 44 #define MPFPS_SIG "_MP_" 45 #define MPCTH_SIG "PCMP" 46 47 #define PTOV(pa) ((off_t)(pa)) 48 49 static mpfps_t biosmptable_find_mpfps(void); 50 static mpfps_t biosmptable_search_mpfps(off_t base, int length); 51 static mpcth_t biosmptable_check_mpcth(off_t addr); 52 53 static int memopen(void); 54 static void memclose(void); 55 56 int biosmptable_detect(void); 57 58 int 59 biosmptable_detect(void) 60 { 61 mpfps_t mpfps; 62 mpcth_t mpcth; 63 char *entry_type_p; 64 proc_entry_ptr proc; 65 int ncpu, i; 66 67 if (!memopen()) 68 return -1; /* XXX 0? */ 69 /* locate and validate the mpfps */ 70 mpfps = biosmptable_find_mpfps(); 71 mpcth = NULL; 72 if (mpfps == NULL) { 73 ncpu = 0; 74 } else if (mpfps->config_type != 0) { 75 /* 76 * If thie config_type is nonzero then this is a default configuration 77 * from Chapter 5 in the MP spec. Report 2 cpus and 1 I/O APIC. 78 */ 79 ncpu = 2; 80 } else { 81 ncpu = 0; 82 mpcth = biosmptable_check_mpcth(PTOV(mpfps->pap)); 83 if (mpcth != NULL) { 84 entry_type_p = (char *)(mpcth + 1); 85 for (i = 0; i < mpcth->entry_count; i++) { 86 switch (*entry_type_p) { 87 case 0: 88 entry_type_p += sizeof(struct PROCENTRY); 89 proc = (proc_entry_ptr) entry_type_p; 90 warnx("MPTable: Found CPU APIC ID %d %s", 91 proc->apic_id, 92 proc->cpu_flags & PROCENTRY_FLAG_EN ? 93 "enabled" : "disabled"); 94 if (proc->cpu_flags & PROCENTRY_FLAG_EN) 95 ncpu++; 96 break; 97 case 1: 98 entry_type_p += sizeof(struct BUSENTRY); 99 break; 100 case 2: 101 entry_type_p += sizeof(struct IOAPICENTRY); 102 break; 103 case 3: 104 case 4: 105 entry_type_p += sizeof(struct INTENTRY); 106 break; 107 default: 108 warnx("unknown mptable entry type (%d)", *entry_type_p); 109 goto done; /* XXX error return? */ 110 } 111 } 112 done: 113 ; 114 } 115 } 116 memclose(); 117 if (mpcth != NULL) 118 free(mpcth); 119 if (mpfps != NULL) 120 free(mpfps); 121 122 return ncpu; 123 } 124 125 static int pfd = -1; 126 127 static int 128 memopen(void) 129 { 130 if (pfd < 0) { 131 pfd = open(_PATH_MEM, O_RDONLY); 132 if (pfd < 0) 133 warn("%s: cannot open", _PATH_MEM); 134 } 135 return pfd >= 0; 136 } 137 138 static void 139 memclose(void) 140 { 141 if (pfd >= 0) { 142 close(pfd); 143 pfd = -1; 144 } 145 } 146 147 static int 148 memread(off_t addr, void* entry, size_t size) 149 { 150 if ((size_t)pread(pfd, entry, size, addr) != size) { 151 warn("pread (%zu @ 0x%jx)", size, (intmax_t)addr); 152 return 0; 153 } 154 return 1; 155 } 156 157 158 /* 159 * Find the MP Floating Pointer Structure. See the MP spec section 4.1. 160 */ 161 static mpfps_t 162 biosmptable_find_mpfps(void) 163 { 164 mpfps_t mpfps; 165 uint16_t addr; 166 167 /* EBDA is the 1 KB addressed by the 16 bit pointer at 0x40E. */ 168 if (!memread(PTOV(0x40E), &addr, sizeof(addr))) 169 return (NULL); 170 mpfps = biosmptable_search_mpfps(PTOV(addr << 4), 0x400); 171 if (mpfps != NULL) 172 return (mpfps); 173 174 /* Check the BIOS. */ 175 mpfps = biosmptable_search_mpfps(PTOV(0xf0000), 0x10000); 176 if (mpfps != NULL) 177 return (mpfps); 178 179 return (NULL); 180 } 181 182 static mpfps_t 183 biosmptable_search_mpfps(off_t base, int length) 184 { 185 mpfps_t mpfps; 186 u_int8_t *cp, sum; 187 int ofs, idx; 188 189 mpfps = malloc(sizeof(*mpfps)); 190 if (mpfps == NULL) { 191 warnx("unable to malloc space for MP Floating Pointer Structure"); 192 return (NULL); 193 } 194 /* search on 16-byte boundaries */ 195 for (ofs = 0; ofs < length; ofs += 16) { 196 if (!memread(base + ofs, mpfps, sizeof(*mpfps))) 197 break; 198 199 /* compare signature, validate checksum */ 200 if (!strncmp(mpfps->signature, MPFPS_SIG, strlen(MPFPS_SIG))) { 201 cp = (u_int8_t *)mpfps; 202 sum = 0; 203 /* mpfps is 16 bytes, or one "paragraph" */ 204 if (mpfps->length != 1) { 205 warnx("bad mpfps length (%d)", mpfps->length); 206 continue; 207 } 208 for (idx = 0; idx < mpfps->length * 16; idx++) 209 sum += *(cp + idx); 210 if (sum != 0) { 211 warnx("bad mpfps checksum (%d)\n", sum); 212 continue; 213 } 214 return (mpfps); 215 } 216 } 217 free(mpfps); 218 return (NULL); 219 } 220 221 static mpcth_t 222 biosmptable_check_mpcth(off_t addr) 223 { 224 mpcth_t mpcth; 225 u_int8_t *cp, sum; 226 int idx, table_length; 227 228 /* mpcth must be in the first 1MB */ 229 if ((u_int32_t)addr >= 1024 * 1024) { 230 warnx("bad mpcth address (0x%jx)\n", (intmax_t)addr); 231 return (NULL); 232 } 233 234 mpcth = malloc(sizeof(*mpcth)); 235 if (mpcth == NULL) { 236 warnx("unable to malloc space for MP Configuration Table Header"); 237 return (NULL); 238 } 239 if (!memread(addr, mpcth, sizeof(*mpcth))) 240 goto bad; 241 /* Compare signature and validate checksum. */ 242 if (strncmp(mpcth->signature, MPCTH_SIG, strlen(MPCTH_SIG)) != 0) { 243 warnx("bad mpcth signature"); 244 goto bad; 245 } 246 table_length = mpcth->base_table_length; 247 mpcth = realloc(mpcth, table_length); 248 if (mpcth == NULL) { 249 warnx("unable to realloc space for mpcth (len %u)", table_length); 250 return (NULL); 251 } 252 if (!memread(addr, mpcth, table_length)) 253 goto bad; 254 cp = (u_int8_t *)mpcth; 255 sum = 0; 256 for (idx = 0; idx < mpcth->base_table_length; idx++) 257 sum += *(cp + idx); 258 if (sum != 0) { 259 warnx("bad mpcth checksum (%d)", sum); 260 goto bad; 261 } 262 263 return mpcth; 264 bad: 265 free(mpcth); 266 return (NULL); 267 } 268