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
biosmptable_detect(void)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
memopen(void)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
memclose(void)139 memclose(void)
140 {
141 if (pfd >= 0) {
142 close(pfd);
143 pfd = -1;
144 }
145 }
146
147 static int
memread(off_t addr,void * entry,size_t size)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
biosmptable_find_mpfps(void)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
biosmptable_search_mpfps(off_t base,int length)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
biosmptable_check_mpcth(off_t addr)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