1 /*
2 * Copyright © 2013 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Jani Nikula <jani.nikula@intel.com>
25 *
26 */
27
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <getopt.h>
31 #include <inttypes.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <sys/mman.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40
41 #include "intel_io.h"
42 #include "drmtest.h"
43
44 #define OPREGION_HEADER_OFFSET 0
45 #define OPREGION_ACPI_OFFSET 0x100
46 #define OPREGION_SWSCI_OFFSET 0x200
47 #define OPREGION_ASLE_OFFSET 0x300
48 #define OPREGION_VBT_OFFSET 0x400
49 #define OPREGION_ASLE_EXT_OFFSET 0x1C00
50
51 #define MBOX_ACPI (1 << 0)
52 #define MBOX_SWSCI (1 << 1)
53 #define MBOX_ASLE (1 << 2)
54 #define MBOX_VBT (1 << 3)
55 #define MBOX_ASLE_EXT (1 << 4)
56
57 struct opregion_header {
58 char sign[16];
59 uint32_t size;
60 uint32_t over;
61 char sver[32];
62 char vver[16];
63 char gver[16];
64 uint32_t mbox;
65 uint32_t dmod;
66 uint32_t pcon;
67 char dver[32];
68 uint8_t rsv1[124];
69 } __attribute__((packed));
70
71 /* OpRegion mailbox #1: public ACPI methods */
72 struct opregion_acpi {
73 uint32_t drdy; /* driver readiness */
74 uint32_t csts; /* notification status */
75 uint32_t cevt; /* current event */
76 uint8_t rsvd1[20];
77 uint32_t didl[8]; /* supported display devices ID list */
78 uint32_t cpdl[8]; /* currently presented display list */
79 uint32_t cadl[8]; /* currently active display list */
80 uint32_t nadl[8]; /* next active devices list */
81 uint32_t aslp; /* ASL sleep time-out */
82 uint32_t tidx; /* toggle table index */
83 uint32_t chpd; /* current hotplug enable indicator */
84 uint32_t clid; /* current lid state*/
85 uint32_t cdck; /* current docking state */
86 uint32_t sxsw; /* Sx state resume */
87 uint32_t evts; /* ASL supported events */
88 uint32_t cnot; /* current OS notification */
89 uint32_t nrdy; /* driver status */
90 uint32_t did2[7];
91 uint32_t cpd2[7];
92 uint8_t rsvd2[4];
93 } __attribute__((packed));
94
95 /* OpRegion mailbox #2: SWSCI */
96 struct opregion_swsci {
97 uint32_t scic; /* SWSCI command|status|data */
98 uint32_t parm; /* command parameters */
99 uint32_t dslp; /* driver sleep time-out */
100 uint8_t rsvd[244];
101 } __attribute__((packed));
102
103 /* OpRegion mailbox #3: ASLE */
104 struct opregion_asle {
105 uint32_t ardy; /* driver readiness */
106 uint32_t aslc; /* ASLE interrupt command */
107 uint32_t tche; /* technology enabled indicator */
108 uint32_t alsi; /* current ALS illuminance reading */
109 uint32_t bclp; /* backlight brightness to set */
110 uint32_t pfit; /* panel fitting state */
111 uint32_t cblv; /* current brightness level */
112 uint16_t bclm[20]; /* backlight level duty cycle mapping table */
113 uint32_t cpfm; /* current panel fitting mode */
114 uint32_t epfm; /* enabled panel fitting modes */
115 uint8_t plut_header; /* panel LUT and identifier */
116 uint8_t plut_identifier[10]; /* panel LUT and identifier */
117 uint8_t plut[63]; /* panel LUT and identifier */
118 uint32_t pfmb; /* PWM freq and min brightness */
119 uint32_t ccdv;
120 uint32_t pcft;
121 uint32_t srot;
122 uint32_t iuer;
123 uint8_t fdss[8];
124 uint32_t fdsp;
125 uint32_t stat;
126 uint64_t rvda; /* Physical address of raw vbt data */
127 uint32_t rvds; /* Size of raw vbt data */
128 uint8_t rsvd[58];
129 } __attribute__((packed));
130
131 /* OpRegion mailbox #4: VBT */
132 struct opregion_vbt {
133 char product_string[20];
134 /* rest ignored */
135 } __attribute__((packed));
136
137 /* OpRegion mailbox #5: ASLE extension */
138 struct opregion_asle_ext {
139 uint32_t phed;
140 uint8_t bddc[256];
141 } __attribute__((packed));
142
decode_header(const void * buffer)143 static uint32_t decode_header(const void *buffer)
144 {
145 const struct opregion_header *header = buffer;
146 char *s;
147
148 if (strncmp("IntelGraphicsMem", header->sign, sizeof(header->sign))) {
149 fprintf(stderr, "invalid opregion signature\n");
150 return 0;
151 }
152
153 printf("OpRegion Header:\n");
154
155 s = strndup(header->sign, sizeof(header->sign));
156 printf("\tsign:\t%s\n", s);
157 free(s);
158
159 printf("\tsize:\t0x%08x\n", header->size);
160 printf("\tover:\t0x%08x\n", header->over);
161
162 s = strndup(header->sver, sizeof(header->sver));
163 printf("\tsver:\t%s\n", s);
164 free(s);
165
166 s = strndup(header->vver, sizeof(header->vver));
167 printf("\tvver:\t%s\n", s);
168 free(s);
169
170 s = strndup(header->gver, sizeof(header->gver));
171 printf("\tgver:\t%s\n", s);
172 free(s);
173
174 printf("\tmbox:\t0x%08x\n", header->mbox);
175
176 printf("\tdmod:\t0x%08x\n", header->dmod);
177 printf("\tpcon:\t0x%08x\n", header->pcon);
178
179 s = strndup(header->dver, sizeof(header->dver));
180 printf("\tdver:\t%s\n", s);
181 free(s);
182
183 printf("\n");
184
185 return header->mbox;
186 }
187
decode_acpi(const void * buffer)188 static void decode_acpi(const void *buffer)
189 {
190 const struct opregion_acpi *acpi = buffer;
191 int i;
192
193 printf("OpRegion Mailbox 1: Public ACPI Methods:\n");
194
195 printf("\tdrdy:\t0x%08x\n", acpi->drdy);
196 printf("\tcsts:\t0x%08x\n", acpi->csts);
197 printf("\tcevt:\t0x%08x\n", acpi->cevt);
198
199 printf("\tdidl:\n");
200 for (i = 0; i < ARRAY_SIZE(acpi->didl); i++)
201 printf("\t\tdidl[%d]:\t0x%08x\n", i, acpi->didl[i]);
202
203 printf("\tcpdl:\n");
204 for (i = 0; i < ARRAY_SIZE(acpi->cpdl); i++)
205 printf("\t\tcpdl[%d]:\t0x%08x\n", i, acpi->cpdl[i]);
206
207 printf("\tcadl:\n");
208 for (i = 0; i < ARRAY_SIZE(acpi->cadl); i++)
209 printf("\t\tcadl[%d]:\t0x%08x\n", i, acpi->cadl[i]);
210
211 printf("\tnadl:\n");
212 for (i = 0; i < ARRAY_SIZE(acpi->nadl); i++)
213 printf("\t\tnadl[%d]:\t0x%08x\n", i, acpi->nadl[i]);
214
215 printf("\taslp:\t0x%08x\n", acpi->aslp);
216 printf("\ttidx:\t0x%08x\n", acpi->tidx);
217 printf("\tchpd:\t0x%08x\n", acpi->chpd);
218 printf("\tclid:\t0x%08x\n", acpi->clid);
219 printf("\tcdck:\t0x%08x\n", acpi->cdck);
220 printf("\tsxsw:\t0x%08x\n", acpi->sxsw);
221 printf("\tevts:\t0x%08x\n", acpi->evts);
222 printf("\tcnot:\t0x%08x\n", acpi->cnot);
223 printf("\tnrdy:\t0x%08x\n", acpi->nrdy);
224
225 printf("\tdid2:\n");
226 for (i = 0; i < ARRAY_SIZE(acpi->did2); i++)
227 printf("\t\tdid2[%d]:\t0x%08x\n", i, acpi->did2[i]);
228
229 printf("\tcpd2:\n");
230 for (i = 0; i < ARRAY_SIZE(acpi->cpd2); i++)
231 printf("\t\tcpd2[%d]:\t0x%08x\n", i, acpi->cpd2[i]);
232
233 printf("\n");
234 }
235
decode_swsci(const void * buffer)236 static void decode_swsci(const void *buffer)
237 {
238 const struct opregion_swsci *swsci = buffer;
239
240 printf("OpRegion Mailbox 2: Software SCI Interface (SWSCI):\n");
241
242 printf("\tscic:\t0x%08x\n", swsci->scic);
243 printf("\tparm:\t0x%08x\n", swsci->parm);
244 printf("\tdslp:\t0x%08x\n", swsci->dslp);
245
246 printf("\n");
247 }
248
decode_asle(const void * buffer)249 static void decode_asle(const void *buffer)
250 {
251 const struct opregion_asle *asle = buffer;
252 int i;
253
254 printf("OpRegion Mailbox 3: BIOS to Driver Notification (ASLE):\n");
255
256 printf("\tardy:\t0x%08x\n", asle->ardy);
257 printf("\taslc:\t0x%08x\n", asle->aslc);
258 printf("\ttche:\t0x%08x\n", asle->tche);
259 printf("\talsi:\t0x%08x\n", asle->alsi);
260 printf("\tbclp:\t0x%08x\n", asle->bclp);
261 printf("\tpfit:\t0x%08x\n", asle->pfit);
262 printf("\tcblv:\t0x%08x\n", asle->cblv);
263
264 printf("\tbclm:\n");
265 for (i = 0; i < ARRAY_SIZE(asle->bclm); i++) {
266 int valid = asle->bclm[i] & (1 << 15);
267 int percentage = (asle->bclm[i] & 0x7f00) >> 8;
268 int duty_cycle = asle->bclm[i] & 0xff;
269
270 printf("\t\tbclm[%d]:\t0x%04x", i, asle->bclm[i]);
271 if (valid)
272 printf(" (%3d%% -> 0x%02x)\n", percentage, duty_cycle);
273 else
274 printf("\n");
275
276 }
277
278 printf("\tcpfm:\t0x%08x\n", asle->cpfm);
279 printf("\tepfm:\t0x%08x\n", asle->epfm);
280
281 printf("\tplut header:\t0x%02x\n", asle->plut_header);
282
283 printf("\tplut identifier:");
284 for (i = 0; i < ARRAY_SIZE(asle->plut_identifier); i++)
285 printf(" %02x", asle->plut_identifier[i]);
286 printf("\n");
287
288 printf("\tplut:\n");
289 for (i = 0; i < ARRAY_SIZE(asle->plut); i++) {
290 const int COLUMNS = 7;
291
292 if (i % COLUMNS == 0)
293 printf("\t\tplut[%d]:\t", i / COLUMNS);
294
295 printf("%02x ", asle->plut[i]);
296
297 if (i % COLUMNS == COLUMNS - 1)
298 printf("\n");
299 }
300
301 printf("\tpfmb:\t0x%08x\n", asle->pfmb);
302 printf("\tccdv:\t0x%08x\n", asle->ccdv);
303 printf("\tpcft:\t0x%08x\n", asle->pcft);
304 printf("\tsrot:\t0x%08x\n", asle->srot);
305 printf("\tiuer:\t0x%08x\n", asle->iuer);
306
307 printf("\tfdss:\t");
308 for (i = 0; i < ARRAY_SIZE(asle->fdss); i++)
309 printf("%02x ", asle->fdss[i]);
310 printf("\n");
311
312 printf("\tfdsp:\t0x%08x\n", asle->fdsp);
313 printf("\tstat:\t0x%08x\n", asle->stat);
314 printf("\trvda:\t0x%016"PRIx64"\n", asle->rvda);
315 printf("\trvds:\t0x%08x\n", asle->rvds);
316
317 printf("\n");
318 }
319
decode_vbt(const void * buffer)320 static void decode_vbt(const void *buffer)
321 {
322 const struct opregion_vbt *vbt = buffer;
323 char *s;
324
325 printf("OpRegion Mailbox 4: Video BIOS Table (VBT):\n");
326
327 s = strndup(vbt->product_string, sizeof(vbt->product_string));
328 printf("\tproduct string:\t%s\n", s);
329 free(s);
330
331 printf("\t(use intel_vbt_decode to decode the VBT)\n");
332
333 printf("\n");
334 }
335
decode_asle_ext(const void * buffer)336 static void decode_asle_ext(const void *buffer)
337 {
338 const struct opregion_asle_ext *asle_ext = buffer;
339 int i;
340
341 printf("OpRegion Mailbox 5: BIOS to Driver Notification Extension:\n");
342
343 printf("\tphed:\t0x%08x\n", asle_ext->phed);
344
345 printf("\tbddc:\n");
346 for (i = 0; i < ARRAY_SIZE(asle_ext->bddc); i++) {
347 const int COLUMNS = 16;
348
349 if (i % COLUMNS == 0)
350 printf("\t\tbddc[0x%02x]:\t", i);
351
352 printf("%02x ", asle_ext->bddc[i]);
353
354 if (i % COLUMNS == COLUMNS - 1)
355 printf("\n");
356 }
357
358 printf("\n");
359 }
360
decode_opregion(const uint8_t * opregion,int size)361 static void decode_opregion(const uint8_t *opregion, int size)
362 {
363 uint32_t mbox;
364
365 /* XXX: allow decoding up to size */
366 if (OPREGION_ASLE_EXT_OFFSET + sizeof(struct opregion_asle_ext) > size) {
367 fprintf(stderr, "buffer too small\n");
368 return;
369 }
370
371 mbox = decode_header(opregion + OPREGION_HEADER_OFFSET);
372 if (mbox & MBOX_ACPI)
373 decode_acpi(opregion + OPREGION_ACPI_OFFSET);
374 if (mbox & MBOX_SWSCI)
375 decode_swsci(opregion + OPREGION_SWSCI_OFFSET);
376 if (mbox & MBOX_ASLE)
377 decode_asle(opregion + OPREGION_ASLE_OFFSET);
378 if (mbox & MBOX_VBT)
379 decode_vbt(opregion + OPREGION_VBT_OFFSET);
380 if (mbox & MBOX_ASLE_EXT)
381 decode_asle_ext(opregion + OPREGION_ASLE_EXT_OFFSET);
382 }
383
main(int argc,char * argv[])384 int main(int argc, char *argv[])
385 {
386 const char *filename = "/sys/kernel/debug/dri/0/i915_opregion";
387 int fd;
388 struct stat finfo;
389 uint8_t *opregion;
390 int c, option_index = 0;
391
392 static struct option long_options[] = {
393 { "file", required_argument, 0, 'f' },
394 { "help", no_argument, 0, 'h' },
395 { 0 },
396 };
397
398 while ((c = getopt_long(argc, argv, "hf:",
399 long_options, &option_index)) != -1) {
400 switch (c) {
401 case 'h':
402 printf("usage: intel_opregion_decode [-f|--file=<input>]\n");
403 return 0;
404 case 'f':
405 filename = optarg;
406 break;
407 default:
408 fprintf(stderr, "unkown command options\n");
409 return 1;
410 }
411 }
412
413 fd = open(filename, O_RDONLY);
414 if (fd == -1) {
415 printf("Couldn't open \"%s\": %s\n", filename, strerror(errno));
416 return 1;
417 }
418
419 if (stat(filename, &finfo)) {
420 printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
421 return 1;
422 }
423
424 if (finfo.st_size == 0) {
425 int len = 0, ret;
426 finfo.st_size = 8192;
427 opregion = malloc(finfo.st_size);
428 while ((ret = read(fd, opregion + len, finfo.st_size - len))) {
429 if (ret < 0) {
430 printf("failed to read \"%s\": %s\n", filename,
431 strerror(errno));
432 return 1;
433 }
434
435 len += ret;
436 if (len == finfo.st_size) {
437 finfo.st_size *= 2;
438 opregion = realloc(opregion, finfo.st_size);
439 }
440 }
441 } else {
442 opregion = mmap(NULL, finfo.st_size, PROT_READ, MAP_SHARED,
443 fd, 0);
444 if (opregion == MAP_FAILED) {
445 printf("failed to map \"%s\": %s\n", filename,
446 strerror(errno));
447 return 1;
448 }
449 }
450
451 decode_opregion(opregion, finfo.st_size);
452
453 return 0;
454 }
455