]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/tools/ncpus/biosmptable.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / tools / tools / ncpus / biosmptable.c
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 }