]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/sysinstall/biosmptable.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / sysinstall / 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@phaedrus.sandvine.ca>
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 <machine/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 #include "sysinstall.h"
48
49 #define MPFPS_SIG "_MP_"
50 #define MPCTH_SIG "PCMP"
51
52 #define PTOV(pa)        ((off_t)(pa))
53
54 static mpfps_t biosmptable_find_mpfps(void);
55 static mpfps_t biosmptable_search_mpfps(off_t base, int length);
56 static mpcth_t biosmptable_check_mpcth(off_t addr);
57
58 static int memopen(void);
59 static void memclose(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                     msgDebug("MPTable: Found CPU APIC ID %d %s\n",
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                     msgDebug("%s: unknown mptable entry type (%d)\n",
112                         __func__, *entry_type_p);
113                     goto done;          /* XXX error return? */
114                 }
115             }
116         done:
117             ;
118         }
119     }
120     memclose();
121     if (mpcth != NULL)
122         free(mpcth);
123     if (mpfps != NULL)
124         free(mpfps);
125
126     return ncpu;
127 }
128
129 static int pfd = -1;
130
131 static int
132 memopen(void)
133 {
134     if (pfd < 0) {
135         pfd = open(_PATH_MEM, O_RDONLY);
136         if (pfd < 0)
137                 warn("%s: cannot open", _PATH_MEM);
138     }
139     return pfd >= 0;
140 }
141
142 static void
143 memclose(void)
144 {
145     if (pfd >= 0) {
146         close(pfd);
147         pfd = -1;
148     }
149 }
150
151 static int
152 memread(off_t addr, void* entry, size_t size)
153 {
154     if ((size_t)pread(pfd, entry, size, addr) != size) {
155         warn("pread (%zu @ 0x%llx)", size, addr);
156         return 0;
157     }
158     return 1;
159 }
160
161
162 /*
163  * Find the MP Floating Pointer Structure.  See the MP spec section 4.1.
164  */
165 static mpfps_t
166 biosmptable_find_mpfps(void)
167 {
168     mpfps_t mpfps;
169     uint16_t addr;
170
171     /* EBDA is the 1 KB addressed by the 16 bit pointer at 0x40E. */
172     if (!memread(PTOV(0x40E), &addr, sizeof(addr)))
173         return (NULL);
174     mpfps = biosmptable_search_mpfps(PTOV(addr << 4), 0x400);
175     if (mpfps != NULL)
176         return (mpfps);
177
178     /* Check the BIOS. */
179     mpfps = biosmptable_search_mpfps(PTOV(0xf0000), 0x10000);
180     if (mpfps != NULL)
181         return (mpfps);
182
183     return (NULL);
184 }
185
186 static mpfps_t
187 biosmptable_search_mpfps(off_t base, int length)
188 {
189     mpfps_t mpfps;
190     u_int8_t *cp, sum;
191     int ofs, idx;
192
193     mpfps = malloc(sizeof(*mpfps));
194     if (mpfps == NULL) {
195         msgDebug("%s: unable to malloc space for "
196             "MP Floating Pointer Structure\n", __func__);
197         return (NULL);
198     }
199     /* search on 16-byte boundaries */
200     for (ofs = 0; ofs < length; ofs += 16) {
201         if (!memread(base + ofs, mpfps, sizeof(*mpfps)))
202             break;
203
204         /* compare signature, validate checksum */
205         if (!strncmp(mpfps->signature, MPFPS_SIG, strlen(MPFPS_SIG))) {
206             cp = (u_int8_t *)mpfps;
207             sum = 0;
208             /* mpfps is 16 bytes, or one "paragraph" */
209             if (mpfps->length != 1) {
210                 msgDebug("%s: bad mpfps length (%d)\n",
211                     __func__, mpfps->length);
212                 continue;
213             }
214             for (idx = 0; idx < mpfps->length * 16; idx++)
215                 sum += *(cp + idx);
216             if (sum != 0) {
217                 msgDebug("%s: bad mpfps checksum (%d)\n", __func__, sum);
218                 continue;
219             }
220             return (mpfps);
221         }
222     }
223     free(mpfps);
224     return (NULL);
225 }
226
227 static mpcth_t
228 biosmptable_check_mpcth(off_t addr)
229 {
230     mpcth_t mpcth;
231     u_int8_t *cp, sum;
232     int idx, table_length;
233
234     /* mpcth must be in the first 1MB */
235     if ((u_int32_t)addr >= 1024 * 1024) {
236         msgDebug("%s: bad mpcth address (0x%llx)\n", __func__, addr);
237         return (NULL);
238     }
239
240     mpcth = malloc(sizeof(*mpcth));
241     if (mpcth == NULL) {
242         msgDebug("%s: unable to malloc space for "
243             "MP Configuration Table Header\n", __func__);
244         return (NULL);
245     }
246     if (!memread(addr, mpcth, sizeof(*mpcth)))
247         goto bad;
248     /* Compare signature and validate checksum. */
249     if (strncmp(mpcth->signature, MPCTH_SIG, strlen(MPCTH_SIG)) != 0) {
250         msgDebug("%s: bad mpcth signature\n", __func__);
251         goto bad;
252     }
253     table_length = mpcth->base_table_length;
254     mpcth = realloc(mpcth, table_length);
255     if (mpcth == NULL) {
256         msgDebug("%s: unable to realloc space for mpcth (len %u)\n",
257             __func__, table_length);
258         return  (NULL);
259     }
260     if (!memread(addr, mpcth, table_length))
261         goto bad;
262     cp = (u_int8_t *)mpcth;
263     sum = 0;
264     for (idx = 0; idx < mpcth->base_table_length; idx++)
265         sum += *(cp + idx);
266     if (sum != 0) {
267         msgDebug("%s: bad mpcth checksum (%d)\n", __func__, sum);
268         goto bad;
269     }
270
271     return mpcth;
272 bad:
273     free(mpcth);
274     return (NULL);
275 }