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