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