1155928Ssam/*- 2155928Ssam * Copyright (c) 2005 Sandvine Incorporated. All righs reserved. 3155928Ssam * 4155928Ssam * Redistribution and use in source and binary forms, with or without 5155928Ssam * modification, are permitted provided that the following conditions 6155928Ssam * are met: 7155928Ssam * 1. Redistributions of source code must retain the above copyright 8155928Ssam * notice, this list of conditions and the following disclaimer. 9155928Ssam * 2. Redistributions in binary form must reproduce the above copyright 10155928Ssam * notice, this list of conditions and the following disclaimer in the 11155928Ssam * documentation and/or other materials provided with the distribution. 12155928Ssam * 13155928Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14155928Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15155928Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16155928Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17155928Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18155928Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19155928Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20155928Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21155928Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22155928Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23155928Ssam * SUCH DAMAGE. 24155928Ssam * 25209745Semaste * Author: Ed Maste <emaste@FreeBSD.org> 26155928Ssam */ 27155928Ssam 28155928Ssam/* 29155928Ssam * This module detects Intel Multiprocessor spec info (mptable) and returns 30155928Ssam * the number of cpu's identified. 31155928Ssam */ 32155928Ssam 33155928Ssam#include <sys/cdefs.h> 34155928Ssam__FBSDID("$FreeBSD$"); 35155928Ssam 36156362Ssam#include <sys/types.h> 37219519Sbrucec#include <x86/mptable.h> 38156362Ssam 39156362Ssam#include <err.h> 40155928Ssam#include <fcntl.h> 41156362Ssam#include <inttypes.h> 42155928Ssam#include <paths.h> 43156362Ssam#include <stdlib.h> 44156362Ssam#include <string.h> 45156362Ssam#include <unistd.h> 46155928Ssam 47155928Ssam#define MPFPS_SIG "_MP_" 48155928Ssam#define MPCTH_SIG "PCMP" 49155928Ssam 50155928Ssam#define PTOV(pa) ((off_t)(pa)) 51155928Ssam 52155928Ssamstatic mpfps_t biosmptable_find_mpfps(void); 53155928Ssamstatic mpfps_t biosmptable_search_mpfps(off_t base, int length); 54155928Ssamstatic mpcth_t biosmptable_check_mpcth(off_t addr); 55155928Ssam 56155928Ssamstatic int memopen(void); 57155928Ssamstatic void memclose(void); 58155928Ssam 59156362Ssamint biosmptable_detect(void); 60156362Ssam 61155928Ssamint 62155928Ssambiosmptable_detect(void) 63155928Ssam{ 64155928Ssam mpfps_t mpfps; 65155928Ssam mpcth_t mpcth; 66155928Ssam char *entry_type_p; 67155928Ssam proc_entry_ptr proc; 68155928Ssam int ncpu, i; 69155928Ssam 70155928Ssam if (!memopen()) 71155928Ssam return -1; /* XXX 0? */ 72155928Ssam /* locate and validate the mpfps */ 73155928Ssam mpfps = biosmptable_find_mpfps(); 74155928Ssam mpcth = NULL; 75155928Ssam if (mpfps == NULL) { 76155928Ssam ncpu = 0; 77155928Ssam } else if (mpfps->config_type != 0) { 78155928Ssam /* 79155928Ssam * If thie config_type is nonzero then this is a default configuration 80155928Ssam * from Chapter 5 in the MP spec. Report 2 cpus and 1 I/O APIC. 81155928Ssam */ 82155928Ssam ncpu = 2; 83155928Ssam } else { 84155928Ssam ncpu = 0; 85155928Ssam mpcth = biosmptable_check_mpcth(PTOV(mpfps->pap)); 86155928Ssam if (mpcth != NULL) { 87155928Ssam entry_type_p = (char *)(mpcth + 1); 88155928Ssam for (i = 0; i < mpcth->entry_count; i++) { 89155928Ssam switch (*entry_type_p) { 90155928Ssam case 0: 91155928Ssam entry_type_p += sizeof(struct PROCENTRY); 92155928Ssam proc = (proc_entry_ptr) entry_type_p; 93155928Ssam warnx("MPTable: Found CPU APIC ID %d %s", 94155928Ssam proc->apic_id, 95155928Ssam proc->cpu_flags & PROCENTRY_FLAG_EN ? 96155928Ssam "enabled" : "disabled"); 97155928Ssam if (proc->cpu_flags & PROCENTRY_FLAG_EN) 98155928Ssam ncpu++; 99155928Ssam break; 100155928Ssam case 1: 101155928Ssam entry_type_p += sizeof(struct BUSENTRY); 102155928Ssam break; 103155928Ssam case 2: 104155928Ssam entry_type_p += sizeof(struct IOAPICENTRY); 105155928Ssam break; 106155928Ssam case 3: 107155928Ssam case 4: 108155928Ssam entry_type_p += sizeof(struct INTENTRY); 109155928Ssam break; 110155928Ssam default: 111155928Ssam warnx("unknown mptable entry type (%d)", *entry_type_p); 112155928Ssam goto done; /* XXX error return? */ 113155928Ssam } 114155928Ssam } 115155928Ssam done: 116155928Ssam ; 117155928Ssam } 118155928Ssam } 119155928Ssam memclose(); 120155928Ssam if (mpcth != NULL) 121155928Ssam free(mpcth); 122155928Ssam if (mpfps != NULL) 123155928Ssam free(mpfps); 124155928Ssam 125155928Ssam return ncpu; 126155928Ssam} 127155928Ssam 128155928Ssamstatic int pfd = -1; 129155928Ssam 130155928Ssamstatic int 131155928Ssammemopen(void) 132155928Ssam{ 133155928Ssam if (pfd < 0) { 134155928Ssam pfd = open(_PATH_MEM, O_RDONLY); 135155928Ssam if (pfd < 0) 136155928Ssam warn("%s: cannot open", _PATH_MEM); 137155928Ssam } 138155928Ssam return pfd >= 0; 139155928Ssam} 140155928Ssam 141155928Ssamstatic void 142155928Ssammemclose(void) 143155928Ssam{ 144155928Ssam if (pfd >= 0) { 145155928Ssam close(pfd); 146155928Ssam pfd = -1; 147155928Ssam } 148155928Ssam} 149155928Ssam 150155928Ssamstatic int 151155928Ssammemread(off_t addr, void* entry, size_t size) 152155928Ssam{ 153156362Ssam if ((size_t)pread(pfd, entry, size, addr) != size) { 154156362Ssam warn("pread (%zu @ 0x%jx)", size, (intmax_t)addr); 155155928Ssam return 0; 156155928Ssam } 157155928Ssam return 1; 158155928Ssam} 159155928Ssam 160155928Ssam 161155928Ssam/* 162155928Ssam * Find the MP Floating Pointer Structure. See the MP spec section 4.1. 163155928Ssam */ 164155928Ssamstatic mpfps_t 165155928Ssambiosmptable_find_mpfps(void) 166155928Ssam{ 167155928Ssam mpfps_t mpfps; 168155928Ssam uint16_t addr; 169155928Ssam 170155928Ssam /* EBDA is the 1 KB addressed by the 16 bit pointer at 0x40E. */ 171155928Ssam if (!memread(PTOV(0x40E), &addr, sizeof(addr))) 172155928Ssam return (NULL); 173155928Ssam mpfps = biosmptable_search_mpfps(PTOV(addr << 4), 0x400); 174155928Ssam if (mpfps != NULL) 175155928Ssam return (mpfps); 176155928Ssam 177155928Ssam /* Check the BIOS. */ 178155928Ssam mpfps = biosmptable_search_mpfps(PTOV(0xf0000), 0x10000); 179155928Ssam if (mpfps != NULL) 180155928Ssam return (mpfps); 181155928Ssam 182155928Ssam return (NULL); 183155928Ssam} 184155928Ssam 185155928Ssamstatic mpfps_t 186155928Ssambiosmptable_search_mpfps(off_t base, int length) 187155928Ssam{ 188155928Ssam mpfps_t mpfps; 189155928Ssam u_int8_t *cp, sum; 190155928Ssam int ofs, idx; 191155928Ssam 192155928Ssam mpfps = malloc(sizeof(*mpfps)); 193155928Ssam if (mpfps == NULL) { 194155928Ssam warnx("unable to malloc space for MP Floating Pointer Structure"); 195155928Ssam return (NULL); 196155928Ssam } 197155928Ssam /* search on 16-byte boundaries */ 198155928Ssam for (ofs = 0; ofs < length; ofs += 16) { 199155928Ssam if (!memread(base + ofs, mpfps, sizeof(*mpfps))) 200155928Ssam break; 201155928Ssam 202155928Ssam /* compare signature, validate checksum */ 203155928Ssam if (!strncmp(mpfps->signature, MPFPS_SIG, strlen(MPFPS_SIG))) { 204155928Ssam cp = (u_int8_t *)mpfps; 205155928Ssam sum = 0; 206155928Ssam /* mpfps is 16 bytes, or one "paragraph" */ 207155928Ssam if (mpfps->length != 1) { 208155928Ssam warnx("bad mpfps length (%d)", mpfps->length); 209155928Ssam continue; 210155928Ssam } 211155928Ssam for (idx = 0; idx < mpfps->length * 16; idx++) 212155928Ssam sum += *(cp + idx); 213155928Ssam if (sum != 0) { 214155928Ssam warnx("bad mpfps checksum (%d)\n", sum); 215155928Ssam continue; 216155928Ssam } 217155928Ssam return (mpfps); 218155928Ssam } 219155928Ssam } 220155928Ssam free(mpfps); 221155928Ssam return (NULL); 222155928Ssam} 223155928Ssam 224155928Ssamstatic mpcth_t 225155928Ssambiosmptable_check_mpcth(off_t addr) 226155928Ssam{ 227155928Ssam mpcth_t mpcth; 228155928Ssam u_int8_t *cp, sum; 229155928Ssam int idx, table_length; 230155928Ssam 231155928Ssam /* mpcth must be in the first 1MB */ 232155928Ssam if ((u_int32_t)addr >= 1024 * 1024) { 233156362Ssam warnx("bad mpcth address (0x%jx)\n", (intmax_t)addr); 234155928Ssam return (NULL); 235155928Ssam } 236155928Ssam 237155928Ssam mpcth = malloc(sizeof(*mpcth)); 238155928Ssam if (mpcth == NULL) { 239155928Ssam warnx("unable to malloc space for MP Configuration Table Header"); 240155928Ssam return (NULL); 241155928Ssam } 242155928Ssam if (!memread(addr, mpcth, sizeof(*mpcth))) 243155928Ssam goto bad; 244155928Ssam /* Compare signature and validate checksum. */ 245155928Ssam if (strncmp(mpcth->signature, MPCTH_SIG, strlen(MPCTH_SIG)) != 0) { 246155928Ssam warnx("bad mpcth signature"); 247155928Ssam goto bad; 248155928Ssam } 249155928Ssam table_length = mpcth->base_table_length; 250156362Ssam mpcth = realloc(mpcth, table_length); 251155928Ssam if (mpcth == NULL) { 252155928Ssam warnx("unable to realloc space for mpcth (len %u)", table_length); 253155928Ssam return (NULL); 254155928Ssam } 255155928Ssam if (!memread(addr, mpcth, table_length)) 256155928Ssam goto bad; 257155928Ssam cp = (u_int8_t *)mpcth; 258155928Ssam sum = 0; 259155928Ssam for (idx = 0; idx < mpcth->base_table_length; idx++) 260155928Ssam sum += *(cp + idx); 261155928Ssam if (sum != 0) { 262155928Ssam warnx("bad mpcth checksum (%d)", sum); 263155928Ssam goto bad; 264155928Ssam } 265155928Ssam 266155928Ssam return mpcth; 267155928Ssambad: 268155928Ssam free(mpcth); 269155928Ssam return (NULL); 270155928Ssam} 271