2 * Copyright (C) 2010-2011 Marcelina KoĆcielnicka <mwk@0x04.net>
3 * Copyright (C) 2011 Martin Peres <martin.peres@ensi-bourges.fr>
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
31 #include "nvhw/chipset.h"
39 int sleep_disabled
= 0;
51 struct rnndeccontext
*ctx
;
52 struct chipset_info chipset
;
62 int pagesnum
, pagesmax
;
63 uint64_t bar0
, bar0l
, bar1
, bar1l
, bar2
, bar2l
;
64 struct i2c_ctx i2cb
[10];
68 uint32_t script
[2048];
77 struct cctx
*cctx
= 0;
78 int cctxnum
= 0, cctxmax
= 0;
82 uint32_t contents
[0x1000/4];
85 uint32_t *findmem (struct cctx
*ctx
, uint64_t addr
) {
87 uint64_t tag
= addr
& ~0xfffull
;
88 for (i
= 0; i
< ctx
->pagesnum
; i
++) {
89 if (tag
== ctx
->pages
[i
]->tag
)
90 return &ctx
->pages
[i
]->contents
[(addr
&0xfff)/4];
92 struct mpage
*pg
= calloc (sizeof *pg
, 1);
94 ADDARRAY(ctx
->pages
, pg
);
95 return &pg
->contents
[(addr
&0xfff)/4];
98 int i2c_bus_num (uint64_t addr
) {
125 void doi2cr (struct cctx
*cc
, struct i2c_ctx
*ctx
, int byte
) {
127 if (ctx
->bits
== 8) {
134 ctx
->wr
= !(ctx
->b
&1);
140 ctx
->b
|= (byte
& 2) >> 1;
142 if (ctx
->bits
== 8) {
143 printf ("<%02x", ctx
->b
);
150 void doi2cw (struct cctx
*cc
, struct i2c_ctx
*ctx
, int byte
) {
153 if (!(byte
& 1) && (ctx
->last
& 1)) {
156 if ((byte
& 1) && !(ctx
->last
& 1)) {
158 printf ("\nI2C LOST!\n");
162 /* clock went high */
163 if (ctx
->bits
== 8) {
164 if (ctx
->wr
|| !ctx
->aok
) {
175 if (ctx
->wr
|| !ctx
->aok
) {
177 ctx
->b
|= (byte
& 2) >> 1;
179 if (ctx
->bits
== 8) {
180 printf (">%02x", ctx
->b
);
187 if ((byte
& 1) && !(byte
& 2) && (ctx
->last
& 2)) {
188 /* data went low with high clock - start bit */
197 if ((byte
& 1) && (byte
& 2) && !(ctx
->last
& 2)) {
198 /* data went high with high clock - stop bit */
211 static void print_help() {
213 "Usage: demmio [-a <NVXXX>|-c|-f <file>|-h]\n"
215 "Decodes MMIO traces using rnndb\n"
218 "\t-a <gen> Specify the chipset variant to use (autodetected by default)\n"
219 "\t-c Disable colors\n"
220 "\t-f <file> Specify the file to read from (defaults to stdin)\n"
221 "\t-h Show this help message\n");
224 int main(int argc
, char **argv
) {
226 char *variant
= NULL
;
227 unsigned long chip
= 0;
229 while ((c
= getopt (argc
, argv
, "f:ca:h")) != -1) {
232 chip
= strtoull(optarg
, NULL
, 16);
234 variant
= strdup(optarg
);
241 file
= strdup(optarg
);
259 struct rnndb
*db
= rnn_newdb();
260 rnn_parsefile (db
, "nv_mmio.xml");
262 struct rnndomain
*mmiodom
= rnn_finddomain(db
, "NV_MMIO");
263 struct rnndomain
*crdom
= rnn_finddomain(db
, "NV_CR");
264 FILE *fin
= (file
==NULL
) ? stdin
: open_input(file
);
266 fprintf (stderr
, "Failed to open input file!\n");
272 const struct disisa
*ctx_isa
= ed_getisa("ctx");
273 struct varinfo
*ctx_var_nv40
= varinfo_new(ctx_isa
->vardata
);
274 struct varinfo
*ctx_var_g80
= varinfo_new(ctx_isa
->vardata
);
275 varinfo_set_variant(ctx_var_nv40
, "nv40");
276 varinfo_set_variant(ctx_var_g80
, "g80");
277 const struct disisa
*hwsq_isa
= ed_getisa("hwsq");
278 struct varinfo
*hwsq_var_nv17
= varinfo_new(hwsq_isa
->vardata
);
279 struct varinfo
*hwsq_var_nv41
= varinfo_new(hwsq_isa
->vardata
);
280 struct varinfo
*hwsq_var_g80
= varinfo_new(hwsq_isa
->vardata
);
281 varinfo_set_variant(hwsq_var_nv17
, "nv17");
282 varinfo_set_variant(hwsq_var_nv41
, "nv41");
283 varinfo_set_variant(hwsq_var_g80
, "g80");
284 const struct envy_colors
*colors
= use_colors
? &envy_def_colors
: &envy_null_colors
;
286 /* yes, static buffer. but mmiotrace lines are bound to have sane length anyway. */
287 if (!fgets(line
, sizeof(line
), fin
))
289 if (!strncmp(line
, "PCIDEV ", 7)) {
290 uint64_t bar
[4], len
[4], pciid
;
291 sscanf (line
, "%*s %*s %"SCNx64
" %*s %"SCNx64
" %"SCNx64
" %"SCNx64
" %"SCNx64
" %*s %*s %*s %"SCNx64
" %"SCNx64
" %"SCNx64
" %"SCNx64
"", &pciid
, &bar
[0], &bar
[1], &bar
[2], &bar
[3], &len
[0], &len
[1], &len
[2], &len
[3]);
292 if ((pciid
>> 16) == 0x10de && bar
[0] && (bar
[0] & 0xf) == 0 && bar
[1] && (bar
[1] & 0x1) == 0x0) {
293 struct cctx nc
= { 0 };
294 nc
.bar0
= bar
[0], nc
.bar0l
= len
[0];
295 nc
.bar1
= bar
[1], nc
.bar1l
= len
[1];
297 nc
.bar2
= bar
[2], nc
.bar2l
= len
[2];
299 nc
.bar2
= bar
[3], nc
.bar2l
= len
[3];
304 nc
.ctx
= rnndec_newcontext(db
);
305 nc
.ctx
->colors
= colors
;
306 for (i
= 0; i
< 10; i
++)
311 } else if (!strncmp(line
, "W ", 2) || !strncmp(line
, "R ", 2)) {
313 static double timestamp
, timestamp_old
= 0;
314 uint64_t addr
, value
;
317 sscanf (line
, "%*s %d %lf %*d %"SCNx64
" %"SCNx64
, &width
, ×tamp
, &addr
, &value
);
320 /* Add a SLEEP line when two mmio accesses are more distant than 100”s */
321 if (!sleep_disabled
&& timestamp_old
> 0 && (timestamp
- timestamp_old
) > 0.0001)
322 printf("SLEEP %lfms\n", (timestamp
- timestamp_old
)*1000.0);
323 timestamp_old
= timestamp
;
325 for (cci
= 0; cci
< cctxnum
; cci
++) {
326 struct cctx
*cc
= &cctx
[cci
];
327 if (cc
->bar0
&& addr
>= cc
->bar0
&& addr
< cc
->bar0
+cc
->bar0l
) {
329 if (cc
->hwsqip
&& addr
!= cc
->hwsqnext
) {
330 struct varinfo
*var
= hwsq_var_nv17
;
331 if (cc
->chipset
.chipset
>= 0x41)
333 if (cc
->chipset
.card_type
== 0x50)
335 envydis(hwsq_isa
, stdout
, cc
->hwsq
, 0, cc
->hwsqnext
& 0x3fc, var
, 0, 0, 0, colors
);
339 if (cc
->seq
.action
== SEQ_SKIP
&& addr
!= 0x10a1c4) {
340 cc
->seq
.action
= SEQ_NONE
;
341 } else if (cc
->seq
.action
== SEQ_PRINT
&& addr
!= 0x10a1c4) {
342 seq_print(cc
->seq
.script
, cc
->seq
.len
, cc
->ctx
, mmiodom
);
344 cc
->seq
.action
= SEQ_NONE
;
347 if (!cc
->chipset
.chipset
) {
348 /* The user may have manually specified this */
350 rnndec_varadd(cc
->ctx
, "chipset", variant
);
352 rnndec_varaddvalue(cc
->ctx
, "chipset", chip
);
353 } else if (addr
== 0) {
354 parse_pmc_id(value
, &cc
->chipset
);
355 if (cc
->chipset
.chipset
)
356 rnndec_varaddvalue(cc
->ctx
, "chipset",
357 cc
->chipset
.chipset
);
359 } else if (cc
->chipset
.card_type
>= 0x50 && addr
== 0x1700) {
360 cc
->praminbase
= value
<< 16;
361 } else if (cc
->chipset
.card_type
== 0x50 && addr
== 0x1704) {
362 cc
->fakechan
= (value
& 0xfffffff) << 12;
363 } else if (cc
->chipset
.card_type
== 0x50 && addr
== 0x170c) {
364 cc
->ramins
= (value
& 0xffff) << 4;
365 } else if (cc
->chipset
.card_type
>= 0xc0 && addr
== 0x1714) {
366 cc
->ramins
= (value
& 0xfffffff) << 12;
367 } else if (addr
== 0x6013d4) {
368 cc
->crx0
= value
& 0xff;
369 } else if (addr
== 0x6033d4) {
370 cc
->crx1
= value
& 0xff;
371 } else if (addr
== 0x6013d5) {
372 struct rnndecaddrinfo
*ai
= rnndec_decodeaddr(cc
->ctx
, crdom
, cc
->crx0
, line
[0] == 'W');
373 char *decoded_val
= rnndec_decodeval(cc
->ctx
, ai
->typeinfo
, value
, ai
->width
);
374 printf ("[%d] %lf HEAD0 %c 0x%02x 0x%02"PRIx64
" %s %s %s\n", cci
, timestamp
, line
[0], cc
->crx0
, value
, ai
->name
, line
[0]=='W'?"<=":"=>", decoded_val
);
375 rnndec_free_decaddrinfo(ai
);
378 } else if (addr
== 0x6033d5) {
379 struct rnndecaddrinfo
*ai
= rnndec_decodeaddr(cc
->ctx
, crdom
, cc
->crx1
, line
[0] == 'W');
380 char *decoded_val
= rnndec_decodeval(cc
->ctx
, ai
->typeinfo
, value
, ai
->width
);
381 printf ("[%d] %lf HEAD1 %c 0x%02x 0x%02"PRIx64
" %s %s %s\n", cci
, timestamp
, line
[0], cc
->crx1
, value
, ai
->name
, line
[0]=='W'?"<=":"=>", decoded_val
);
382 rnndec_free_decaddrinfo(ai
);
385 } else if (cc
->chipset
.card_type
>= 0x50 && (addr
& 0xfff000) == 0xe000) {
386 int bus
= i2c_bus_num(addr
);
388 if (cc
->i2cip
!= bus
) {
391 struct rnndecaddrinfo
*ai
= rnndec_decodeaddr(cc
->ctx
, mmiodom
, addr
, line
[0] == 'W');
392 printf ("[%d] I2C 0x%06"PRIx64
" %s ", cci
, addr
, ai
->name
);
393 rnndec_free_decaddrinfo(ai
);
396 if (line
[0] == 'R') {
397 doi2cr(cc
, &cc
->i2cb
[bus
], value
);
399 doi2cw(cc
, &cc
->i2cb
[bus
], value
);
403 } else if ((addr
& 0xfff000) == 0x9000 && (cc
->i2cip
!= -1)) {
404 /* ignore PTIMER meddling during I2C */
406 } else if (addr
== 0x1400 || addr
== 0x80000 || (addr
== cc
->hwsqnext
&& cc
->hwsqip
)) {
408 struct rnndecaddrinfo
*ai
= rnndec_decodeaddr(cc
->ctx
, mmiodom
, addr
, line
[0] == 'W');
409 printf ("[%d] HWSQ 0x%06"PRIx64
" %s\n", cci
, addr
, ai
->name
);
410 rnndec_free_decaddrinfo(ai
);
412 cc
->hwsq
[(addr
& 0x1fc) + 0] = value
;
413 cc
->hwsq
[(addr
& 0x1fc) + 1] = value
>> 8;
414 cc
->hwsq
[(addr
& 0x1fc) + 2] = value
>> 16;
415 cc
->hwsq
[(addr
& 0x1fc) + 3] = value
>> 24;
417 cc
->hwsqnext
= addr
+ 4;
419 } else if (addr
== 0x10a1c4) {
420 if (cc
->seq
.action
== SEQ_NONE
) {
421 /* Crude test whether this vaguely looks like an opcode..
422 * print will do a more thorough check */
423 if ((value
& 0xfc00ffc0) == 0 && value
!= 0) {
424 cc
->seq
.action
= SEQ_PRINT
;
426 cc
->seq
.action
= SEQ_SKIP
;
430 if(cc
->seq
.action
== SEQ_PRINT
) {
431 if (cc
->seq
.len
< 2048) {
432 cc
->seq
.script
[cc
->seq
.len
] = value
;
435 printf("[%d] PDAEMON %06"PRIx64
" Script too long, skipping\n", cci
, addr
);
437 cc
->seq
.action
= SEQ_SKIP
;
440 } else if (addr
== 0x400324 && cc
->chipset
.card_type
>= 0x40 && cc
->chipset
.card_type
<= 0x50) {
442 } else if (addr
== 0x400328 && cc
->chipset
.card_type
>= 0x40 && cc
->chipset
.card_type
<= 0x50) {
445 param
[1] = value
>> 8;
446 param
[2] = value
>> 16;
447 param
[3] = value
>> 24;
448 struct rnndecaddrinfo
*ai
= rnndec_decodeaddr(cc
->ctx
, mmiodom
, addr
, line
[0] == 'W');
449 printf ("[%d] MMIO%d %c 0x%06"PRIx64
" 0x%08"PRIx64
" %s %s ", cci
, width
, line
[0], addr
, value
, ai
->name
, line
[0]=='W'?"<=":"=>");
450 envydis(ctx_isa
, stdout
, param
, cc
->ctxpos
, 1, (cc
->chipset
.card_type
== 0x50 ? ctx_var_g80
: ctx_var_nv40
), 0, 0, 0, colors
);
452 rnndec_free_decaddrinfo(ai
);
455 if (!skip
&& (cc
->i2cip
!= -1)) {
459 if (cc
->chipset
.card_type
>= 0x50 && addr
>= 0x700000 && addr
< 0x800000) {
461 addr
+= cc
->praminbase
;
462 printf ("[%d] %lf, MEM%d %"PRIx64
" %s %"PRIx64
"\n", cci
, timestamp
, width
, addr
, line
[0]=='W'?"<=":"=>", value
);
463 *findmem(cc
, addr
) = value
;
465 struct rnndecaddrinfo
*ai
= rnndec_decodeaddr(cc
->ctx
, mmiodom
, addr
, line
[0] == 'W');
466 if (width
== 32 && ai
->width
== 8) {
467 /* 32-bit write to 8-bit location - split it up */
469 rnndec_free_decaddrinfo(ai
);
471 for (b
= 0; b
< 4; b
++) {
472 struct rnndecaddrinfo
*ai
= rnndec_decodeaddr(cc
->ctx
, mmiodom
, addr
+b
, line
[0] == 'W');
473 char *decoded_val
= rnndec_decodeval(cc
->ctx
, ai
->typeinfo
, value
>> b
* 8 & 0xff, ai
->width
);
475 printf ("[%d] %lf MMIO%d %c 0x%06"PRIx64
" 0x%08"PRIx64
" %n%s %s %s\n", cci
, timestamp
, width
, line
[0], addr
, value
, &cnt
, ai
->name
, line
[0]=='W'?"<=":"=>", decoded_val
);
478 for (c
= 0; c
< cnt
; c
++)
480 printf ("%s %s %s\n", ai
->name
, line
[0]=='W'?"<=":"=>", decoded_val
);
482 rnndec_free_decaddrinfo(ai
);
486 char *decoded_val
= rnndec_decodeval(cc
->ctx
, ai
->typeinfo
, value
, ai
->width
);
487 printf ("[%d] %lf MMIO%d %c 0x%06"PRIx64
" 0x%08"PRIx64
" %s %s %s\n", cci
, timestamp
, width
, line
[0], addr
, value
, ai
->name
, line
[0]=='W'?"<=":"=>", decoded_val
);
493 } else if (cc
->bar1
&& addr
>= cc
->bar1
&& addr
< cc
->bar1
+cc
->bar1l
) {
495 printf ("[%d] %lf, FB%d %"PRIx64
" %s %"PRIx64
"\n", cci
, timestamp
, width
, addr
, line
[0]=='W'?"<=":"=>", value
);
496 } else if (cc
->bar2
&& addr
>= cc
->bar2
&& addr
< cc
->bar2
+cc
->bar2l
) {
498 if (cc
->chipset
.card_type
>= 0xc0) {
499 uint64_t pd
= *findmem(cc
, cc
->ramins
+ 0x200);
500 uint64_t pt
= *findmem(cc
, pd
+ 4);
503 uint64_t pg
= *findmem(cc
, pt
+ (addr
/0x1000) * 8);
507 *findmem(cc
, pg
) = value
;
508 // printf ("%"PRIx64" %"PRIx64" %"PRIx64" %"PRIx64"\n", ramins, pd, pt, pg);
509 printf ("[%d] %lf RAMIN%d %"PRIx64
" %"PRIx64
" %s %"PRIx64
"\n", cci
, timestamp
, width
, addr
, pg
, line
[0]=='W'?"<=":"=>", value
);
510 } else if (cc
->chipset
.card_type
== 0x50) {
511 uint64_t paddr
= addr
;
512 paddr
+= *findmem(cc
, cc
->fakechan
+ cc
->ramins
+ 8);
513 paddr
+= (uint64_t)(*findmem(cc
, cc
->fakechan
+ cc
->ramins
+ 12) >> 24) << 32;
514 uint64_t pt
= *findmem(cc
, cc
->fakechan
+ (cc
->chipset
.chipset
== 0x50 ? 0x1400 : 0x200) + ((paddr
>> 29) << 3));
515 // printf ("%#"PRIx64" PT: %#"PRIx64" %#"PRIx64" ", paddr, fakechan + 0x200 + ((paddr >> 29) << 3), pt);
516 uint32_t div
= (pt
& 2 ? 0x1000 : 0x10000);
518 uint64_t pg
= *findmem(cc
, pt
+ ((paddr
&0x1ffff000)/div
) * 8);
519 uint64_t pgh
= *findmem(cc
, pt
+ ((paddr
&0x1ffff000)/div
) * 8 + 4);
520 // printf ("PG: %#"PRIx64" %#"PRIx64"\n", pt + ((paddr&0x1ffff000)/div) * 8, pgh << 32 | pg);
522 pg
|= (pgh
& 0xff) << 32;
523 pg
+= (paddr
& (div
-1));
524 *findmem(cc
, pg
) = value
;
525 // printf ("%"PRIx64" %"PRIx64" %"PRIx64" %"PRIx64"\n", ramins, pd, pt, pg);
526 printf ("[%d] %lf RAMIN%d %"PRIx64
" %"PRIx64
" %s %"PRIx64
"\n", cci
, timestamp
, width
, addr
, pg
, line
[0]=='W'?"<=":"=>", value
);
528 printf ("[%d] %lf RAMIN%d %"PRIx64
" %s %"PRIx64
"\n", cci
, timestamp
, width
, addr
, line
[0]=='W'?"<=":"=>", value
);