1155928Ssam/*-
2155928Ssam * Copyright (c) 2005 Sandvine Incorporated.  All righs reserved.
3155928Ssam *
4155928Ssam * Redistribution and use in source and binary forms, with or without
5155928Ssam * modification, are permitted provided that the following conditions
6155928Ssam * are met:
7155928Ssam * 1. Redistributions of source code must retain the above copyright
8155928Ssam *    notice, this list of conditions and the following disclaimer.
9155928Ssam * 2. Redistributions in binary form must reproduce the above copyright
10155928Ssam *    notice, this list of conditions and the following disclaimer in the
11155928Ssam *    documentation and/or other materials provided with the distribution.
12155928Ssam *
13155928Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14155928Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15155928Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16155928Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17155928Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18155928Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19155928Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20155928Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21155928Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22155928Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23155928Ssam * SUCH DAMAGE.
24155928Ssam *
25209745Semaste * Author: Ed Maste <emaste@FreeBSD.org>
26155928Ssam */
27155928Ssam
28155928Ssam/*
29155928Ssam * This module detects Intel Multiprocessor spec info (mptable) and returns
30155928Ssam * the number of cpu's identified.
31155928Ssam */
32155928Ssam
33155928Ssam#include <sys/cdefs.h>
34155928Ssam__FBSDID("$FreeBSD$");
35155928Ssam
36156362Ssam#include <sys/types.h>
37219519Sbrucec#include <x86/mptable.h>
38156362Ssam
39156362Ssam#include <err.h>
40155928Ssam#include <fcntl.h>
41156362Ssam#include <inttypes.h>
42155928Ssam#include <paths.h>
43156362Ssam#include <stdlib.h>
44156362Ssam#include <string.h>
45156362Ssam#include <unistd.h>
46155928Ssam
47155928Ssam#define MPFPS_SIG "_MP_"
48155928Ssam#define MPCTH_SIG "PCMP"
49155928Ssam
50155928Ssam#define	PTOV(pa)	((off_t)(pa))
51155928Ssam
52155928Ssamstatic mpfps_t biosmptable_find_mpfps(void);
53155928Ssamstatic mpfps_t biosmptable_search_mpfps(off_t base, int length);
54155928Ssamstatic mpcth_t biosmptable_check_mpcth(off_t addr);
55155928Ssam
56155928Ssamstatic int memopen(void);
57155928Ssamstatic void memclose(void);
58155928Ssam
59156362Ssamint biosmptable_detect(void);
60156362Ssam
61155928Ssamint
62155928Ssambiosmptable_detect(void)
63155928Ssam{
64155928Ssam    mpfps_t mpfps;
65155928Ssam    mpcth_t mpcth;
66155928Ssam    char *entry_type_p;
67155928Ssam    proc_entry_ptr proc;
68155928Ssam    int ncpu, i;
69155928Ssam
70155928Ssam    if (!memopen())
71155928Ssam	return -1;		/* XXX 0? */
72155928Ssam    /* locate and validate the mpfps */
73155928Ssam    mpfps = biosmptable_find_mpfps();
74155928Ssam    mpcth = NULL;
75155928Ssam    if (mpfps == NULL) {
76155928Ssam	ncpu = 0;
77155928Ssam    } else if (mpfps->config_type != 0) {
78155928Ssam	/*
79155928Ssam	 * If thie config_type is nonzero then this is a default configuration
80155928Ssam	 * from Chapter 5 in the MP spec.  Report 2 cpus and 1 I/O APIC.
81155928Ssam	 */
82155928Ssam    	ncpu = 2;
83155928Ssam    } else {
84155928Ssam	ncpu = 0;
85155928Ssam	mpcth = biosmptable_check_mpcth(PTOV(mpfps->pap));
86155928Ssam	if (mpcth != NULL) {
87155928Ssam	    entry_type_p = (char *)(mpcth + 1);
88155928Ssam	    for (i = 0; i < mpcth->entry_count; i++) {
89155928Ssam		switch (*entry_type_p) {
90155928Ssam		case 0:
91155928Ssam		    entry_type_p += sizeof(struct PROCENTRY);
92155928Ssam		    proc = (proc_entry_ptr) entry_type_p;
93155928Ssam		    warnx("MPTable: Found CPU APIC ID %d %s",
94155928Ssam			proc->apic_id,
95155928Ssam			proc->cpu_flags & PROCENTRY_FLAG_EN ?
96155928Ssam				"enabled" : "disabled");
97155928Ssam		    if (proc->cpu_flags & PROCENTRY_FLAG_EN)
98155928Ssam			ncpu++;
99155928Ssam		    break;
100155928Ssam		case 1:
101155928Ssam		    entry_type_p += sizeof(struct BUSENTRY);
102155928Ssam		    break;
103155928Ssam		case 2:
104155928Ssam		    entry_type_p += sizeof(struct IOAPICENTRY);
105155928Ssam		    break;
106155928Ssam		case 3:
107155928Ssam		case 4:
108155928Ssam		    entry_type_p += sizeof(struct INTENTRY);
109155928Ssam		    break;
110155928Ssam		default:
111155928Ssam		    warnx("unknown mptable entry type (%d)", *entry_type_p);
112155928Ssam		    goto done;		/* XXX error return? */
113155928Ssam		}
114155928Ssam	    }
115155928Ssam	done:
116155928Ssam	    ;
117155928Ssam	}
118155928Ssam    }
119155928Ssam    memclose();
120155928Ssam    if (mpcth != NULL)
121155928Ssam	free(mpcth);
122155928Ssam    if (mpfps != NULL)
123155928Ssam	free(mpfps);
124155928Ssam
125155928Ssam    return ncpu;
126155928Ssam}
127155928Ssam
128155928Ssamstatic int pfd = -1;
129155928Ssam
130155928Ssamstatic int
131155928Ssammemopen(void)
132155928Ssam{
133155928Ssam    if (pfd < 0) {
134155928Ssam	pfd = open(_PATH_MEM, O_RDONLY);
135155928Ssam	if (pfd < 0)
136155928Ssam		warn("%s: cannot open", _PATH_MEM);
137155928Ssam    }
138155928Ssam    return pfd >= 0;
139155928Ssam}
140155928Ssam
141155928Ssamstatic void
142155928Ssammemclose(void)
143155928Ssam{
144155928Ssam    if (pfd >= 0) {
145155928Ssam	close(pfd);
146155928Ssam	pfd = -1;
147155928Ssam    }
148155928Ssam}
149155928Ssam
150155928Ssamstatic int
151155928Ssammemread(off_t addr, void* entry, size_t size)
152155928Ssam{
153156362Ssam    if ((size_t)pread(pfd, entry, size, addr) != size) {
154156362Ssam	warn("pread (%zu @ 0x%jx)", size, (intmax_t)addr);
155155928Ssam	return 0;
156155928Ssam    }
157155928Ssam    return 1;
158155928Ssam}
159155928Ssam
160155928Ssam
161155928Ssam/*
162155928Ssam * Find the MP Floating Pointer Structure.  See the MP spec section 4.1.
163155928Ssam */
164155928Ssamstatic mpfps_t
165155928Ssambiosmptable_find_mpfps(void)
166155928Ssam{
167155928Ssam    mpfps_t mpfps;
168155928Ssam    uint16_t addr;
169155928Ssam
170155928Ssam    /* EBDA is the 1 KB addressed by the 16 bit pointer at 0x40E. */
171155928Ssam    if (!memread(PTOV(0x40E), &addr, sizeof(addr)))
172155928Ssam	return (NULL);
173155928Ssam    mpfps = biosmptable_search_mpfps(PTOV(addr << 4), 0x400);
174155928Ssam    if (mpfps != NULL)
175155928Ssam	return (mpfps);
176155928Ssam
177155928Ssam    /* Check the BIOS. */
178155928Ssam    mpfps = biosmptable_search_mpfps(PTOV(0xf0000), 0x10000);
179155928Ssam    if (mpfps != NULL)
180155928Ssam	return (mpfps);
181155928Ssam
182155928Ssam    return (NULL);
183155928Ssam}
184155928Ssam
185155928Ssamstatic mpfps_t
186155928Ssambiosmptable_search_mpfps(off_t base, int length)
187155928Ssam{
188155928Ssam    mpfps_t mpfps;
189155928Ssam    u_int8_t *cp, sum;
190155928Ssam    int ofs, idx;
191155928Ssam
192155928Ssam    mpfps = malloc(sizeof(*mpfps));
193155928Ssam    if (mpfps == NULL) {
194155928Ssam	warnx("unable to malloc space for MP Floating Pointer Structure");
195155928Ssam	return (NULL);
196155928Ssam    }
197155928Ssam    /* search on 16-byte boundaries */
198155928Ssam    for (ofs = 0; ofs < length; ofs += 16) {
199155928Ssam	if (!memread(base + ofs, mpfps, sizeof(*mpfps)))
200155928Ssam	    break;
201155928Ssam
202155928Ssam	/* compare signature, validate checksum */
203155928Ssam	if (!strncmp(mpfps->signature, MPFPS_SIG, strlen(MPFPS_SIG))) {
204155928Ssam	    cp = (u_int8_t *)mpfps;
205155928Ssam	    sum = 0;
206155928Ssam	    /* mpfps is 16 bytes, or one "paragraph" */
207155928Ssam	    if (mpfps->length != 1) {
208155928Ssam	    	warnx("bad mpfps length (%d)", mpfps->length);
209155928Ssam		continue;
210155928Ssam	    }
211155928Ssam	    for (idx = 0; idx < mpfps->length * 16; idx++)
212155928Ssam		sum += *(cp + idx);
213155928Ssam	    if (sum != 0) {
214155928Ssam		warnx("bad mpfps checksum (%d)\n", sum);
215155928Ssam		continue;
216155928Ssam	    }
217155928Ssam	    return (mpfps);
218155928Ssam	}
219155928Ssam    }
220155928Ssam    free(mpfps);
221155928Ssam    return (NULL);
222155928Ssam}
223155928Ssam
224155928Ssamstatic mpcth_t
225155928Ssambiosmptable_check_mpcth(off_t addr)
226155928Ssam{
227155928Ssam    mpcth_t mpcth;
228155928Ssam    u_int8_t *cp, sum;
229155928Ssam    int idx, table_length;
230155928Ssam
231155928Ssam    /* mpcth must be in the first 1MB */
232155928Ssam    if ((u_int32_t)addr >= 1024 * 1024) {
233156362Ssam	warnx("bad mpcth address (0x%jx)\n", (intmax_t)addr);
234155928Ssam	return (NULL);
235155928Ssam    }
236155928Ssam
237155928Ssam    mpcth = malloc(sizeof(*mpcth));
238155928Ssam    if (mpcth == NULL) {
239155928Ssam	warnx("unable to malloc space for MP Configuration Table Header");
240155928Ssam	return (NULL);
241155928Ssam    }
242155928Ssam    if (!memread(addr, mpcth, sizeof(*mpcth)))
243155928Ssam	goto bad;
244155928Ssam    /* Compare signature and validate checksum. */
245155928Ssam    if (strncmp(mpcth->signature, MPCTH_SIG, strlen(MPCTH_SIG)) != 0) {
246155928Ssam        warnx("bad mpcth signature");
247155928Ssam	goto bad;
248155928Ssam    }
249155928Ssam    table_length = mpcth->base_table_length;
250156362Ssam    mpcth = realloc(mpcth, table_length);
251155928Ssam    if (mpcth == NULL) {
252155928Ssam	warnx("unable to realloc space for mpcth (len %u)", table_length);
253155928Ssam	return  (NULL);
254155928Ssam    }
255155928Ssam    if (!memread(addr, mpcth, table_length))
256155928Ssam	goto bad;
257155928Ssam    cp = (u_int8_t *)mpcth;
258155928Ssam    sum = 0;
259155928Ssam    for (idx = 0; idx < mpcth->base_table_length; idx++)
260155928Ssam	sum += *(cp + idx);
261155928Ssam    if (sum != 0) {
262155928Ssam	warnx("bad mpcth checksum (%d)", sum);
263155928Ssam	goto bad;
264155928Ssam    }
265155928Ssam
266155928Ssam    return mpcth;
267155928Ssambad:
268155928Ssam    free(mpcth);
269155928Ssam    return (NULL);
270155928Ssam}
271