xref: /openbsd-src/usr.sbin/acpidump/acpidump.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: acpidump.c,v 1.7 2010/08/08 14:40:19 jmc Exp $	*/
2 /*
3  * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/types.h>
30 
31 #include <assert.h>
32 #include <err.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include <sys/mman.h>
40 #include <sys/queue.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/syslimits.h>
44 
45 #include <uvm/uvm_extern.h>
46 
47 #define vm_page_size sysconf(_SC_PAGESIZE)
48 #define PRINTFLAG(xx)							\
49 	do {								\
50 		if (facp->flags & ACPI_FACP_FLAG_## xx) {		\
51 			fprintf(fhdr, "%c%s", sep, #xx); sep = ',';	\
52 		}							\
53 	} while (0)
54 
55 
56 typedef unsigned long	vm_offset_t;
57 
58 struct ACPIrsdp {
59 	u_char		signature[8];
60 	u_char		sum;
61 	u_char		oem[6];
62 	u_char		res;
63 	u_int32_t	addr;
64 } __packed;
65 
66 struct ACPIsdt {
67 	u_char		signature[4];
68 	u_int32_t	len;
69 	u_char		rev;
70 	u_char		check;
71 	u_char		oemid[6];
72 	u_char		oemtblid[8];
73 	u_int32_t	oemrev;
74 	u_char		creator[4];
75 	u_int32_t	crerev;
76 #define SIZEOF_SDT_HDR	36	/* struct size except body */
77 	u_int32_t	body[1];/* This member should be casted */
78 } __packed;
79 
80 struct ACPIgas {
81 	u_int8_t	address_space_id;
82 #define ACPI_GAS_MEMORY		0
83 #define ACPI_GAS_IO		1
84 #define ACPI_GAS_PCI		2
85 #define ACPI_GAS_EMBEDDED	3
86 #define ACPI_GAS_SMBUS		4
87 #define ACPI_GAS_FIXED		0x7f
88 	u_int8_t	register_bit_width;
89 	u_int8_t	register_bit_offset;
90 	u_int8_t	res;
91 	u_int64_t	address;
92 } __packed;
93 
94 struct FACPbody {
95 	u_int32_t	facs_ptr;
96 	u_int32_t	dsdt_ptr;
97 	u_int8_t	int_model;
98 #define ACPI_FACP_INTMODEL_PIC	0	/* Standard PC-AT PIC */
99 #define ACPI_FACP_INTMODEL_APIC	1	/* Multiple APIC */
100 	u_char		reserved1;
101 	u_int16_t	sci_int;
102 	u_int32_t	smi_cmd;
103 	u_int8_t	acpi_enable;
104 	u_int8_t	acpi_disable;
105 	u_int8_t	s4biosreq;
106 	u_int8_t	reserved2;
107 	u_int32_t	pm1a_evt_blk;
108 	u_int32_t	pm1b_evt_blk;
109 	u_int32_t	pm1a_cnt_blk;
110 	u_int32_t	pm1b_cnt_blk;
111 	u_int32_t	pm2_cnt_blk;
112 	u_int32_t	pm_tmr_blk;
113 	u_int32_t	gpe0_blk;
114 	u_int32_t	gpe1_blk;
115 	u_int8_t	pm1_evt_len;
116 	u_int8_t	pm1_cnt_len;
117 	u_int8_t	pm2_cnt_len;
118 	u_int8_t	pm_tmr_len;
119 	u_int8_t	gpe0_len;
120 	u_int8_t	gpe1_len;
121 	u_int8_t	gpe1_base;
122 	u_int8_t	reserved3;
123 	u_int16_t	p_lvl2_lat;
124 	u_int16_t	p_lvl3_lat;
125 	u_int16_t	flush_size;
126 	u_int16_t	flush_stride;
127 	u_int8_t	duty_off;
128 	u_int8_t	duty_width;
129 	u_int8_t	day_alrm;
130 	u_int8_t	mon_alrm;
131 	u_int8_t	century;
132 	u_int16_t	iapc_boot_arch;
133 	u_char		reserved4[1];
134 	u_int32_t	flags;
135 #define ACPI_FACP_FLAG_WBINVD	1	/* WBINVD is correctly supported */
136 #define ACPI_FACP_FLAG_WBINVD_FLUSH 2	/* WBINVD flushes caches */
137 #define ACPI_FACP_FLAG_PROC_C1	4	/* C1 power state supported */
138 #define ACPI_FACP_FLAG_P_LVL2_UP 8	/* C2 power state works on SMP */
139 #define ACPI_FACP_FLAG_PWR_BUTTON 16	/* Power button uses control method */
140 #define ACPI_FACP_FLAG_SLP_BUTTON 32	/* Sleep button uses control method */
141 #define ACPI_FACP_FLAG_FIX_RTC	64	/* RTC wakeup not supported */
142 #define ACPI_FACP_FLAG_RTC_S4	128	/* RTC can wakeup from S4 state */
143 #define ACPI_FACP_FLAG_TMR_VAL_EXT 256	/* TMR_VAL is 32bit */
144 #define ACPI_FACP_FLAG_DCK_CAP	512	/* Can support docking */
145 	struct ACPIgas	reset_reg;
146 	u_int8_t	reset_value;
147 	u_int8_t	reserved5[3];
148 	u_int64_t	x_firmware_ctrl;
149 	u_int64_t	x_dsdt;
150 	struct ACPIgas	x_pm1a_evt_blk;
151 	struct ACPIgas	x_pm1b_evt_blk;
152 	struct ACPIgas	x_pm1a_cnt_blk;
153 	struct ACPIgas	x_pm1b_cnt_blk;
154 	struct ACPIgas	x_pm2_cnt_blk;
155 	struct ACPIgas	x_pm_tmr_blk;
156 	struct ACPIgas	x_gpe0_blk;
157 	struct ACPIgas	x_gpe1_blk;
158 } __packed;
159 
160 struct acpi_user_mapping {
161 	LIST_ENTRY(acpi_user_mapping)	link;
162 	vm_offset_t			pa;
163 	caddr_t				va;
164 	size_t				size;
165 };
166 
167 LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist;
168 
169 int		acpi_mem_fd = -1;
170 char		*aml_dumpfile;
171 FILE		*fhdr;
172 
173 struct ACPIsdt	dsdt_header = {
174 	"DSDT", 0, 1, 0, "OEMID", "OEMTBLID", 0x12345678, "CRTR", 0x12345678
175 };
176 
177 int
178 acpi_checksum(void *p, size_t length)
179 {
180 	u_int8_t	*bp;
181 	u_int8_t	sum;
182 
183 	bp = p;
184 	sum = 0;
185 	while (length--)
186 		sum += *bp++;
187 
188 	return (sum);
189 }
190 
191 struct acpi_user_mapping *
192 acpi_user_find_mapping(vm_offset_t pa, size_t size)
193 {
194 	struct acpi_user_mapping	*map;
195 
196 	/* First search for an existing mapping */
197 	for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) {
198 		if (map->pa <= pa && map->size >= pa + size - map->pa)
199 			return (map);
200 	}
201 
202 	/* Then create a new one */
203 	size = round_page(pa + size) - trunc_page(pa);
204 	pa = trunc_page(pa);
205 	map = malloc(sizeof(struct acpi_user_mapping));
206 	if (!map)
207 		errx(1, "out of memory");
208 	map->pa = pa;
209 	map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa);
210 	map->size = size;
211 	if (map->va == MAP_FAILED)
212 		err(1, "can't map address");
213 	LIST_INSERT_HEAD(&maplist, map, link);
214 
215 	return (map);
216 }
217 
218 void *
219 acpi_map_physical(vm_offset_t pa, size_t size)
220 {
221 	struct acpi_user_mapping	*map;
222 
223 	map = acpi_user_find_mapping(pa, size);
224 	return (map->va + (pa - map->pa));
225 }
226 
227 void
228 acpi_user_init(void)
229 {
230 	if (acpi_mem_fd == -1) {
231 		acpi_mem_fd = open("/dev/mem", O_RDONLY);
232 		if (acpi_mem_fd == -1)
233 			err(1, "opening /dev/mem");
234 		LIST_INIT(&maplist);
235 	}
236 }
237 
238 struct ACPIrsdp *
239 acpi_find_rsd_ptr()
240 {
241 	int		i;
242 	u_int8_t	buf[sizeof(struct ACPIrsdp)];
243 
244 	acpi_user_init();
245 	for (i = 0; i < 1024 * 1024; i += 16) {
246 		lseek(acpi_mem_fd, i, SEEK_SET);
247 		read(acpi_mem_fd, buf, 16);
248 		if (!memcmp(buf, "RSD PTR ", 8)) {
249 			/* Read the rest of the structure */
250 			read(acpi_mem_fd, buf + 16, sizeof(struct ACPIrsdp) - 16);
251 
252 			/* Verify checksum before accepting it. */
253 			if (acpi_checksum(buf, sizeof(struct ACPIrsdp)))
254 				continue;
255 
256 			return (acpi_map_physical(i, sizeof(struct ACPIrsdp)));
257 		}
258 	}
259 
260 	return (0);
261 }
262 
263 void
264 acpi_print_string(char *s, size_t length)
265 {
266 	int		c;
267 
268 	/* Trim trailing spaces and NULLs */
269 	while (length > 0 && (s[length - 1] == ' ' || s[length - 1] == '\0'))
270 		length--;
271 
272 	while (length--) {
273 		c = *s++;
274 		fputc(c, fhdr);
275 	}
276 }
277 
278 void
279 acpi_print_rsd_ptr(struct ACPIrsdp *rp)
280 {
281 	fprintf(fhdr, "\n");
282 	fprintf(fhdr, "RSD PTR: Checksum=%d, OEMID=", rp->sum);
283 	acpi_print_string(rp->oem, 6);
284 	fprintf(fhdr, ", RsdtAddress=0x%08x\n", rp->addr);
285 	fprintf(fhdr, "\n");
286 }
287 
288 struct ACPIsdt *
289 acpi_map_sdt(vm_offset_t pa)
290 {
291 	struct ACPIsdt	*sp;
292 
293 	sp = acpi_map_physical(pa, sizeof(struct ACPIsdt));
294 	sp = acpi_map_physical(pa, sp->len);
295 	return (sp);
296 }
297 
298 void
299 aml_dump(struct ACPIsdt *hdr)
300 {
301 	static int	hdr_index;
302 	char		name[PATH_MAX];
303 	int		fd;
304 	mode_t		mode;
305 
306 	snprintf(name, sizeof(name), "%s.%c%c%c%c.%d",
307 	    aml_dumpfile, hdr->signature[0], hdr->signature[1],
308 	    hdr->signature[2], hdr->signature[3],
309 	    hdr_index++);
310 
311 	mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
312 	fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode);
313 	if (fd == -1)
314 		err(1, "aml_dump");
315 
316 	write(fd, hdr, SIZEOF_SDT_HDR);
317 	write(fd, hdr->body, hdr->len - SIZEOF_SDT_HDR);
318 	close(fd);
319 }
320 
321 void
322 acpi_print_sdt(struct ACPIsdt *sdp)
323 {
324 	fprintf(fhdr, "\n");
325 	acpi_print_string(sdp->signature, 4);
326 	fprintf(fhdr, ": Length=%d, Revision=%d, Checksum=%d,\n",
327 	       sdp->len, sdp->rev, sdp->check);
328 	fprintf(fhdr, "\tOEMID=");
329 	acpi_print_string(sdp->oemid, 6);
330 	fprintf(fhdr, ", OEM Table ID=");
331 	acpi_print_string(sdp->oemtblid, 8);
332 	fprintf(fhdr, ", OEM Revision=0x%x,\n", sdp->oemrev);
333 	fprintf(fhdr, "\tCreator ID=");
334 	acpi_print_string(sdp->creator, 4);
335 	fprintf(fhdr, ", Creator Revision=0x%x\n", sdp->crerev);
336 	fprintf(fhdr, "\n");
337 	if (!memcmp(sdp->signature, "DSDT", 4))
338 		memcpy(&dsdt_header, sdp, sizeof(dsdt_header));
339 }
340 
341 void
342 acpi_print_rsdt(struct ACPIsdt *rsdp)
343 {
344 	int		i, entries;
345 
346 	acpi_print_sdt(rsdp);
347 	entries = (rsdp->len - SIZEOF_SDT_HDR) / sizeof(u_int32_t);
348 	fprintf(fhdr, "\n");
349 	fprintf(fhdr, "\tEntries={ ");
350 	for (i = 0; i < entries; i++) {
351 		if (i > 0)
352 			fprintf(fhdr, ", ");
353 		fprintf(fhdr, "0x%08x", rsdp->body[i]);
354 	}
355 	fprintf(fhdr, " }\n");
356 	fprintf(fhdr, "\n");
357 }
358 
359 void
360 acpi_print_facp(struct FACPbody *facp)
361 {
362 	char		sep;
363 
364 	fprintf(fhdr, "\n");
365 	fprintf(fhdr, "\tDSDT=0x%x\n", facp->dsdt_ptr);
366 	fprintf(fhdr, "\tINT_MODEL=%s\n", facp->int_model ? "APIC" : "PIC");
367 	fprintf(fhdr, "\tSCI_INT=%d\n", facp->sci_int);
368 	fprintf(fhdr, "\tSMI_CMD=0x%x, ", facp->smi_cmd);
369 	fprintf(fhdr, "ACPI_ENABLE=0x%x, ", facp->acpi_enable);
370 	fprintf(fhdr, "ACPI_DISABLE=0x%x, ", facp->acpi_disable);
371 	fprintf(fhdr, "S4BIOS_REQ=0x%x\n", facp->s4biosreq);
372 	if (facp->pm1a_evt_blk)
373 		fprintf(fhdr, "\tPM1a_EVT_BLK=0x%x-0x%x\n",
374 		    facp->pm1a_evt_blk,
375 		    facp->pm1a_evt_blk + facp->pm1_evt_len - 1);
376 	if (facp->pm1b_evt_blk)
377 		fprintf(fhdr, "\tPM1b_EVT_BLK=0x%x-0x%x\n",
378 		    facp->pm1b_evt_blk,
379 		    facp->pm1b_evt_blk + facp->pm1_evt_len - 1);
380 	if (facp->pm1a_cnt_blk)
381 		fprintf(fhdr, "\tPM1a_CNT_BLK=0x%x-0x%x\n",
382 		    facp->pm1a_cnt_blk,
383 		    facp->pm1a_cnt_blk + facp->pm1_cnt_len - 1);
384 	if (facp->pm1b_cnt_blk)
385 		fprintf(fhdr, "\tPM1b_CNT_BLK=0x%x-0x%x\n",
386 		    facp->pm1b_cnt_blk,
387 		    facp->pm1b_cnt_blk + facp->pm1_cnt_len - 1);
388 	if (facp->pm2_cnt_blk)
389 		fprintf(fhdr, "\tPM2_CNT_BLK=0x%x-0x%x\n",
390 		    facp->pm2_cnt_blk,
391 		    facp->pm2_cnt_blk + facp->pm2_cnt_len - 1);
392 	if (facp->pm_tmr_blk)
393 		fprintf(fhdr, "\tPM2_TMR_BLK=0x%x-0x%x\n",
394 		    facp->pm_tmr_blk,
395 		    facp->pm_tmr_blk + facp->pm_tmr_len - 1);
396 	if (facp->gpe0_blk)
397 		fprintf(fhdr, "\tPM2_GPE0_BLK=0x%x-0x%x\n",
398 		    facp->gpe0_blk,
399 		    facp->gpe0_blk + facp->gpe0_len - 1);
400 	if (facp->gpe1_blk)
401 		fprintf(fhdr, "\tPM2_GPE1_BLK=0x%x-0x%x, GPE1_BASE=%d\n",
402 		    facp->gpe1_blk,
403 		    facp->gpe1_blk + facp->gpe1_len - 1,
404 		    facp->gpe1_base);
405 	fprintf(fhdr, "\tP_LVL2_LAT=%dms, P_LVL3_LAT=%dms\n",
406 	    facp->p_lvl2_lat, facp->p_lvl3_lat);
407 	fprintf(fhdr, "\tFLUSH_SIZE=%d, FLUSH_STRIDE=%d\n",
408 	    facp->flush_size, facp->flush_stride);
409 	fprintf(fhdr, "\tDUTY_OFFSET=%d, DUTY_WIDTH=%d\n",
410 	    facp->duty_off, facp->duty_width);
411 	fprintf(fhdr, "\tDAY_ALRM=%d, MON_ALRM=%d, CENTURY=%d\n",
412 	    facp->day_alrm, facp->mon_alrm, facp->century);
413 	fprintf(fhdr, "\tFlags=");
414 	sep = '{';
415 
416 	PRINTFLAG(WBINVD);
417 	PRINTFLAG(WBINVD_FLUSH);
418 	PRINTFLAG(PROC_C1);
419 	PRINTFLAG(P_LVL2_UP);
420 	PRINTFLAG(PWR_BUTTON);
421 	PRINTFLAG(SLP_BUTTON);
422 	PRINTFLAG(FIX_RTC);
423 	PRINTFLAG(RTC_S4);
424 	PRINTFLAG(TMR_VAL_EXT);
425 	PRINTFLAG(DCK_CAP);
426 
427 	fprintf(fhdr, "}\n");
428 	fprintf(fhdr, "\n");
429 }
430 
431 void
432 acpi_print_dsdt(struct ACPIsdt *dsdp)
433 {
434 	acpi_print_sdt(dsdp);
435 }
436 
437 void
438 acpi_handle_dsdt(struct ACPIsdt *dsdp)
439 {
440 	u_int8_t	*dp;
441 	u_int8_t	*end;
442 
443 	acpi_print_dsdt(dsdp);
444 
445 	dp = (u_int8_t *)dsdp->body;
446 	end = (u_int8_t *)dsdp + dsdp->len;
447 }
448 
449 void
450 acpi_handle_facp(struct FACPbody *facp)
451 {
452 	struct ACPIsdt	*dsdp;
453 
454 	acpi_print_facp(facp);
455 	dsdp = (struct ACPIsdt *) acpi_map_sdt(facp->dsdt_ptr);
456 	if (acpi_checksum(dsdp, dsdp->len))
457 		errx(1, "DSDT is corrupt");
458 	acpi_handle_dsdt(dsdp);
459 	aml_dump(dsdp);
460 }
461 
462 void
463 acpi_handle_rsdt(struct ACPIsdt *rsdp)
464 {
465 	int		i;
466 	int		entries;
467 	struct ACPIsdt	*sdp;
468 
469 	aml_dump(rsdp);
470 	entries = (rsdp->len - SIZEOF_SDT_HDR) / sizeof(u_int32_t);
471 	acpi_print_rsdt(rsdp);
472 	for (i = 0; i < entries; i++) {
473 		sdp = (struct ACPIsdt *) acpi_map_sdt(rsdp->body[i]);
474 		if (acpi_checksum(sdp, sdp->len))
475 			errx(1, "RSDT entry %d is corrupt", i);
476 		aml_dump(sdp);
477 		if (!memcmp(sdp->signature, "FACP", 4)) {
478 			acpi_handle_facp((struct FACPbody *) sdp->body);
479 		} else {
480 			acpi_print_sdt(sdp);
481 		}
482 	}
483 }
484 
485 void
486 asl_dump_from_devmem(void)
487 {
488 	struct ACPIrsdp	*rp;
489 	struct ACPIsdt	*rsdp;
490 	char		name[PATH_MAX];
491 
492 	snprintf(name, sizeof(name), "%s.headers", aml_dumpfile);
493 
494 	rp = acpi_find_rsd_ptr();
495 	if (!rp)
496 		errx(1, "Can't find ACPI information");
497 
498 	fhdr = fopen(name, "w");
499 	if (fhdr == NULL)
500 		err(1, "asl_dump_from_devmem");
501 
502 	acpi_print_rsd_ptr(rp);
503 	rsdp = (struct ACPIsdt *) acpi_map_sdt(rp->addr);
504 	if (memcmp(rsdp->signature, "RSDT", 4) ||
505 	    acpi_checksum(rsdp, rsdp->len))
506 		errx(1, "RSDT is corrupted");
507 
508 	acpi_handle_rsdt(rsdp);
509 
510 	fclose(fhdr);
511 }
512 
513 void
514 usage(void)
515 {
516 	extern char	*__progname;
517 
518 	fprintf(stderr, "%s -o prefix_for_output\n", __progname);
519 	exit(1);
520 }
521 
522 int
523 main(int argc, char *argv[])
524 {
525 	char		c;
526 
527 	while ((c = getopt(argc, argv, "o:")) != -1) {
528 		if (c == 'o')
529 			aml_dumpfile = optarg;
530 		else
531 			usage();
532 	}
533 
534 	if (aml_dumpfile == NULL)
535 		usage();
536 
537 	asl_dump_from_devmem();
538 
539 	return (0);
540 }
541