1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2003 Matthew N. Dodd <winter@jurai.net>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32/*
33 * VPD decoder for IBM systems (Thinkpads)
34 * http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-45120
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/socket.h>
41#include <sys/sysctl.h>
42
43#include <sys/module.h>
44#include <sys/bus.h>
45
46#include <machine/bus.h>
47#include <machine/resource.h>
48#include <sys/rman.h>
49
50#include <vm/vm.h>
51#include <vm/vm_param.h>
52#include <vm/pmap.h>
53#include <machine/md_var.h>
54#include <machine/pc/bios.h>
55
56/*
57 * Vital Product Data
58 */
59struct vpd {
60	u_int16_t	Header;			/* 0x55AA */
61	u_int8_t	Signature[3];		/* Always 'VPD' */
62	u_int8_t	Length;			/* Sructure Length */
63
64	u_int8_t	Reserved[7];		/* Reserved */
65
66	u_int8_t	BuildID[9];		/* BIOS Build ID */
67	u_int8_t	BoxSerial[7];		/* Box Serial Number */
68	u_int8_t	PlanarSerial[11];	/* Motherboard Serial Number */
69	u_int8_t	MachType[7];		/* Machine Type/Model */
70	u_int8_t	Checksum;		/* Checksum */
71} __packed;
72
73struct vpd_softc {
74	device_t		dev;
75	struct resource *	res;
76	int			rid;
77
78	struct vpd *		vpd;
79
80	struct sysctl_ctx_list  ctx;
81
82	char		BuildID[10];
83	char		BoxSerial[8];
84	char		PlanarSerial[12];
85	char		MachineType[5];
86	char		MachineModel[4];
87};
88
89#define	VPD_START	0xf0000
90#define	VPD_STEP	0x10
91#define	VPD_OFF		2
92#define	VPD_LEN		3
93#define	VPD_SIG		"VPD"
94
95#define	RES2VPD(res)	((struct vpd *)rman_get_virtual(res))
96#define	ADDR2VPD(addr)	((struct vpd *)BIOS_PADDRTOVADDR(addr))
97
98static devclass_t vpd_devclass;
99
100static void	vpd_identify	(driver_t *, device_t);
101static int	vpd_probe	(device_t);
102static int	vpd_attach	(device_t);
103static int	vpd_detach	(device_t);
104static int	vpd_modevent	(module_t, int, void *);
105
106static int	vpd_cksum	(struct vpd *);
107
108static SYSCTL_NODE(_hw, OID_AUTO, vpd, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
109    NULL);
110static SYSCTL_NODE(_hw_vpd, OID_AUTO, machine,
111    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
112    NULL);
113static SYSCTL_NODE(_hw_vpd_machine, OID_AUTO, type,
114    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
115    NULL);
116static SYSCTL_NODE(_hw_vpd_machine, OID_AUTO, model,
117    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
118    NULL);
119static SYSCTL_NODE(_hw_vpd, OID_AUTO, build_id,
120    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
121    NULL);
122static SYSCTL_NODE(_hw_vpd, OID_AUTO, serial,
123    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
124    NULL);
125static SYSCTL_NODE(_hw_vpd_serial, OID_AUTO, box,
126    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
127    NULL);
128static SYSCTL_NODE(_hw_vpd_serial, OID_AUTO, planar,
129    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
130    NULL);
131
132static void
133vpd_identify (driver_t *driver, device_t parent)
134{
135	device_t child;
136	u_int32_t addr;
137	int length;
138	int rid;
139
140	if (!device_is_alive(parent))
141		return;
142
143	addr = bios_sigsearch(VPD_START, VPD_SIG, VPD_LEN, VPD_STEP, VPD_OFF);
144	if (addr != 0) {
145		rid = 0;
146		length = ADDR2VPD(addr)->Length;
147
148		child = BUS_ADD_CHILD(parent, 5, "vpd", -1);
149		device_set_driver(child, driver);
150		bus_set_resource(child, SYS_RES_MEMORY, rid, addr, length);
151		device_set_desc(child, "Vital Product Data Area");
152	}
153
154	return;
155}
156
157static int
158vpd_probe (device_t dev)
159{
160	struct resource *res;
161	int rid;
162	int error;
163
164	error = 0;
165	rid = 0;
166	res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
167	if (res == NULL) {
168		device_printf(dev, "Unable to allocate memory resource.\n");
169		error = ENOMEM;
170		goto bad;
171	}
172
173	if (vpd_cksum(RES2VPD(res)))
174		device_printf(dev, "VPD checksum failed.  BIOS update may be required.\n");
175
176bad:
177	if (res)
178		bus_release_resource(dev, SYS_RES_MEMORY, rid, res);
179	return (error);
180}
181
182static int
183vpd_attach (device_t dev)
184{
185	struct vpd_softc *sc;
186	char unit[4];
187	int error;
188
189	sc = device_get_softc(dev);
190	error = 0;
191
192	sc->dev = dev;
193	sc->rid = 0;
194	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
195		RF_ACTIVE);
196	if (sc->res == NULL) {
197		device_printf(dev, "Unable to allocate memory resource.\n");
198		error = ENOMEM;
199		goto bad;
200	}
201	sc->vpd = RES2VPD(sc->res);
202
203	snprintf(unit, sizeof(unit), "%d", device_get_unit(sc->dev));
204	snprintf(sc->MachineType, 5, "%.4s", sc->vpd->MachType);
205	snprintf(sc->MachineModel, 4, "%.3s", sc->vpd->MachType+4);
206	snprintf(sc->BuildID, 10, "%.9s", sc->vpd->BuildID);
207	snprintf(sc->BoxSerial, 8, "%.7s", sc->vpd->BoxSerial);
208	snprintf(sc->PlanarSerial, 12, "%.11s", sc->vpd->PlanarSerial);
209
210	sysctl_ctx_init(&sc->ctx);
211	SYSCTL_ADD_STRING(&sc->ctx,
212		SYSCTL_STATIC_CHILDREN(_hw_vpd_machine_type), OID_AUTO,
213		unit, CTLFLAG_RD, sc->MachineType, 0, NULL);
214	SYSCTL_ADD_STRING(&sc->ctx,
215		SYSCTL_STATIC_CHILDREN(_hw_vpd_machine_model), OID_AUTO,
216		unit, CTLFLAG_RD, sc->MachineModel, 0, NULL);
217	SYSCTL_ADD_STRING(&sc->ctx,
218		SYSCTL_STATIC_CHILDREN(_hw_vpd_build_id), OID_AUTO,
219		unit, CTLFLAG_RD, sc->BuildID, 0, NULL);
220	SYSCTL_ADD_STRING(&sc->ctx,
221		SYSCTL_STATIC_CHILDREN(_hw_vpd_serial_box), OID_AUTO,
222		unit, CTLFLAG_RD, sc->BoxSerial, 0, NULL);
223	SYSCTL_ADD_STRING(&sc->ctx,
224		SYSCTL_STATIC_CHILDREN(_hw_vpd_serial_planar), OID_AUTO,
225		unit, CTLFLAG_RD, sc->PlanarSerial, 0, NULL);
226
227	device_printf(dev, "Machine Type: %.4s, Model: %.3s, Build ID: %.9s\n",
228		sc->MachineType, sc->MachineModel, sc->BuildID);
229	device_printf(dev, "Box Serial: %.7s, Planar Serial: %.11s\n",
230		sc->BoxSerial, sc->PlanarSerial);
231
232	return (0);
233bad:
234	if (sc->res)
235		bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
236	return (error);
237}
238
239static int
240vpd_detach (device_t dev)
241{
242	struct vpd_softc *sc;
243
244	sc = device_get_softc(dev);
245
246	if (sc->res)
247		bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
248
249	sysctl_ctx_free(&sc->ctx);
250
251	return (0);
252}
253
254static int
255vpd_modevent (mod, what, arg)
256        module_t        mod;
257        int             what;
258        void *          arg;
259{
260	device_t *	devs;
261	int		count;
262	int		i;
263
264	switch (what) {
265	case MOD_LOAD:
266		break;
267	case MOD_UNLOAD:
268		devclass_get_devices(vpd_devclass, &devs, &count);
269		for (i = 0; i < count; i++) {
270			device_delete_child(device_get_parent(devs[i]), devs[i]);
271		}
272		break;
273	default:
274		break;
275	}
276
277	return (0);
278}
279
280static device_method_t vpd_methods[] = {
281	/* Device interface */
282	DEVMETHOD(device_identify,      vpd_identify),
283	DEVMETHOD(device_probe,         vpd_probe),
284	DEVMETHOD(device_attach,        vpd_attach),
285	DEVMETHOD(device_detach,        vpd_detach),
286	{ 0, 0 }
287};
288
289static driver_t vpd_driver = {
290	"vpd",
291	vpd_methods,
292	sizeof(struct vpd_softc),
293};
294
295DRIVER_MODULE(vpd, nexus, vpd_driver, vpd_devclass, vpd_modevent, 0);
296MODULE_VERSION(vpd, 1);
297
298/*
299 * Perform a checksum over the VPD structure, starting with
300 * the BuildID.  (Jean Delvare <khali@linux-fr.org>)
301 */
302static int
303vpd_cksum (struct vpd *v)
304{
305	u_int8_t *ptr;
306	u_int8_t cksum;
307	int i;
308
309	ptr = (u_int8_t *)v;
310	cksum = 0;
311	for (i = offsetof(struct vpd, BuildID); i < v->Length ; i++)
312		cksum += ptr[i];
313	return (cksum);
314}
315