xref: /freebsd/tools/tools/ncpus/biosmptable.c (revision 42249ef2)
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