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 52static mpfps_t biosmptable_find_mpfps(void); 53static mpfps_t biosmptable_search_mpfps(off_t base, int length); 54static mpcth_t biosmptable_check_mpcth(off_t addr); 55 56static int memopen(void); 57static void memclose(void); 58 59int biosmptable_detect(void); 60 61int 62biosmptable_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 128static int pfd = -1; 129 130static int 131memopen(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 141static void 142memclose(void) 143{ 144 if (pfd >= 0) { 145 close(pfd); 146 pfd = -1; 147 } 148} 149 150static int 151memread(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 */ 164static mpfps_t 165biosmptable_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 185static mpfps_t 186biosmptable_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 224static mpcth_t 225biosmptable_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; 267bad: 268 free(mpcth); 269 return (NULL); 270} 271