Add back code to clean up triggers in entry.S
[riscv-tests.git] / debug / gdbserver.py
1 #!/usr/bin/env python
2
3 import argparse
4 import binascii
5 import random
6 import sys
7 import tempfile
8 import time
9 import os
10
11 import targets
12 import testlib
13 from testlib import assertEqual, assertNotEqual, assertIn, assertNotIn
14 from testlib import assertGreater, assertRegexpMatches, assertLess
15 from testlib import GdbTest
16
17 MSTATUS_UIE = 0x00000001
18 MSTATUS_SIE = 0x00000002
19 MSTATUS_HIE = 0x00000004
20 MSTATUS_MIE = 0x00000008
21 MSTATUS_UPIE = 0x00000010
22 MSTATUS_SPIE = 0x00000020
23 MSTATUS_HPIE = 0x00000040
24 MSTATUS_MPIE = 0x00000080
25 MSTATUS_SPP = 0x00000100
26 MSTATUS_HPP = 0x00000600
27 MSTATUS_MPP = 0x00001800
28 MSTATUS_FS = 0x00006000
29 MSTATUS_XS = 0x00018000
30 MSTATUS_MPRV = 0x00020000
31 MSTATUS_PUM = 0x00040000
32 MSTATUS_MXR = 0x00080000
33 MSTATUS_VM = 0x1F000000
34 MSTATUS32_SD = 0x80000000
35 MSTATUS64_SD = 0x8000000000000000
36
37 # pylint: disable=abstract-method
38
39 def ihex_line(address, record_type, data):
40 assert len(data) < 128
41 line = ":%02X%04X%02X" % (len(data), address, record_type)
42 check = len(data)
43 check += address % 256
44 check += address >> 8
45 check += record_type
46 for char in data:
47 value = ord(char)
48 check += value
49 line += "%02X" % value
50 line += "%02X\n" % ((256-check)%256)
51 return line
52
53 def ihex_parse(line):
54 assert line.startswith(":")
55 line = line[1:]
56 data_len = int(line[:2], 16)
57 address = int(line[2:6], 16)
58 record_type = int(line[6:8], 16)
59 data = ""
60 for i in range(data_len):
61 data += "%c" % int(line[8+2*i:10+2*i], 16)
62 return record_type, address, data
63
64 def readable_binary_string(s):
65 return "".join("%02x" % ord(c) for c in s)
66
67 class SimpleRegisterTest(GdbTest):
68 def check_reg(self, name):
69 a = random.randrange(1<<self.target.xlen)
70 b = random.randrange(1<<self.target.xlen)
71 self.gdb.p("$%s=0x%x" % (name, a))
72 self.gdb.stepi()
73 assertEqual(self.gdb.p("$%s" % name), a)
74 self.gdb.p("$%s=0x%x" % (name, b))
75 self.gdb.stepi()
76 assertEqual(self.gdb.p("$%s" % name), b)
77
78 def setup(self):
79 # 0x13 is nop
80 self.gdb.command("p *((int*) 0x%x)=0x13" % self.target.ram)
81 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 4))
82 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 8))
83 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 12))
84 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 16))
85 self.gdb.p("$pc=0x%x" % self.target.ram)
86
87 class SimpleS0Test(SimpleRegisterTest):
88 def test(self):
89 self.check_reg("s0")
90
91 class SimpleS1Test(SimpleRegisterTest):
92 def test(self):
93 self.check_reg("s1")
94
95 class SimpleT0Test(SimpleRegisterTest):
96 def test(self):
97 self.check_reg("t0")
98
99 class SimpleT1Test(SimpleRegisterTest):
100 def test(self):
101 self.check_reg("t1")
102
103 class SimpleF18Test(SimpleRegisterTest):
104 def check_reg(self, name):
105 self.gdb.p_raw("$mstatus=$mstatus | 0x00006000")
106 self.gdb.stepi()
107 a = random.random()
108 b = random.random()
109 self.gdb.p_raw("$%s=%f" % (name, a))
110 self.gdb.stepi()
111 assertLess(abs(float(self.gdb.p_raw("$%s" % name)) - a), .001)
112 self.gdb.p_raw("$%s=%f" % (name, b))
113 self.gdb.stepi()
114 assertLess(abs(float(self.gdb.p_raw("$%s" % name)) - b), .001)
115
116 def early_applicable(self):
117 return self.target.extensionSupported('F')
118
119 def test(self):
120 self.check_reg("f18")
121
122 class SimpleMemoryTest(GdbTest):
123 def access_test(self, size, data_type):
124 assertEqual(self.gdb.p("sizeof(%s)" % data_type), size)
125 a = 0x86753095555aaaa & ((1<<(size*8))-1)
126 b = 0xdeadbeef12345678 & ((1<<(size*8))-1)
127 self.gdb.p("*((%s*)0x%x) = 0x%x" % (data_type, self.target.ram, a))
128 self.gdb.p("*((%s*)0x%x) = 0x%x" % (data_type, self.target.ram + size,
129 b))
130 assertEqual(self.gdb.p("*((%s*)0x%x)" % (data_type, self.target.ram)),
131 a)
132 assertEqual(self.gdb.p("*((%s*)0x%x)" % (
133 data_type, self.target.ram + size)), b)
134
135 class MemTest8(SimpleMemoryTest):
136 def test(self):
137 self.access_test(1, 'char')
138
139 class MemTest16(SimpleMemoryTest):
140 def test(self):
141 self.access_test(2, 'short')
142
143 class MemTest32(SimpleMemoryTest):
144 def test(self):
145 self.access_test(4, 'int')
146
147 class MemTest64(SimpleMemoryTest):
148 def test(self):
149 self.access_test(8, 'long long')
150
151 # FIXME: I'm not passing back invalid addresses correctly in read/write memory.
152 #class MemTestReadInvalid(SimpleMemoryTest):
153 # def test(self):
154 # # This test relies on 'gdb_report_data_abort enable' being executed in
155 # # the openocd.cfg file.
156 # try:
157 # self.gdb.p("*((int*)0xdeadbeef)")
158 # assert False, "Read should have failed."
159 # except testlib.CannotAccess as e:
160 # assertEqual(e.address, 0xdeadbeef)
161 # self.gdb.p("*((int*)0x%x)" % self.target.ram)
162 #
163 #class MemTestWriteInvalid(SimpleMemoryTest):
164 # def test(self):
165 # # This test relies on 'gdb_report_data_abort enable' being executed in
166 # # the openocd.cfg file.
167 # try:
168 # self.gdb.p("*((int*)0xdeadbeef)=8675309")
169 # assert False, "Write should have failed."
170 # except testlib.CannotAccess as e:
171 # assertEqual(e.address, 0xdeadbeef)
172 # self.gdb.p("*((int*)0x%x)=6874742" % self.target.ram)
173
174 class MemTestBlock(GdbTest):
175 def test(self):
176 length = 1024
177 line_length = 16
178 a = tempfile.NamedTemporaryFile(suffix=".ihex")
179 data = ""
180 for i in range(length / line_length):
181 line_data = "".join(["%c" % random.randrange(256)
182 for _ in range(line_length)])
183 data += line_data
184 a.write(ihex_line(i * line_length, 0, line_data))
185 a.flush()
186
187 self.gdb.command("restore %s 0x%x" % (a.name, self.target.ram))
188 for offset in range(0, length, 19*4) + [length-4]:
189 value = self.gdb.p("*((int*)0x%x)" % (self.target.ram + offset))
190 written = ord(data[offset]) | \
191 (ord(data[offset+1]) << 8) | \
192 (ord(data[offset+2]) << 16) | \
193 (ord(data[offset+3]) << 24)
194 assertEqual(value, written)
195
196 b = tempfile.NamedTemporaryFile(suffix=".ihex")
197 self.gdb.command("dump ihex memory %s 0x%x 0x%x" % (b.name,
198 self.target.ram, self.target.ram + length))
199 for line in b:
200 record_type, address, line_data = ihex_parse(line)
201 if record_type == 0:
202 assertEqual(readable_binary_string(line_data),
203 readable_binary_string(
204 data[address:address+len(line_data)]))
205
206 class InstantHaltTest(GdbTest):
207 def test(self):
208 """Assert that reset is really resetting what it should."""
209 self.gdb.command("monitor reset halt")
210 self.gdb.command("flushregs")
211 threads = self.gdb.threads()
212 pcs = []
213 for t in threads:
214 self.gdb.thread(t)
215 pcs.append(self.gdb.p("$pc"))
216 for pc in pcs:
217 assertEqual(self.target.reset_vector, pc)
218 # mcycle and minstret have no defined reset value.
219 mstatus = self.gdb.p("$mstatus")
220 assertEqual(mstatus & (MSTATUS_MIE | MSTATUS_MPRV |
221 MSTATUS_VM), 0)
222
223 class InstantChangePc(GdbTest):
224 def test(self):
225 """Change the PC right as we come out of reset."""
226 # 0x13 is nop
227 self.gdb.command("monitor reset halt")
228 self.gdb.command("flushregs")
229 self.gdb.command("p *((int*) 0x%x)=0x13" % self.target.ram)
230 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 4))
231 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 8))
232 self.gdb.p("$pc=0x%x" % self.target.ram)
233 self.gdb.stepi()
234 assertEqual((self.target.ram + 4), self.gdb.p("$pc"))
235 self.gdb.stepi()
236 assertEqual((self.target.ram + 8), self.gdb.p("$pc"))
237
238 class DebugTest(GdbTest):
239 # Include malloc so that gdb can make function calls. I suspect this malloc
240 # will silently blow through the memory set aside for it, so be careful.
241 compile_args = ("programs/debug.c", "programs/checksum.c",
242 "programs/tiny-malloc.c", "-DDEFINE_MALLOC", "-DDEFINE_FREE")
243
244 def setup(self):
245 self.gdb.load()
246 self.gdb.b("_exit")
247
248 def exit(self, expected_result=0xc86455d4):
249 output = self.gdb.c()
250 assertIn("Breakpoint", output)
251 assertIn("_exit", output)
252 assertEqual(self.gdb.p("status"), expected_result)
253
254 class DebugCompareSections(DebugTest):
255 def test(self):
256 output = self.gdb.command("compare-sections")
257 matched = 0
258 for line in output.splitlines():
259 if line.startswith("Section"):
260 assert line.endswith("matched.")
261 matched += 1
262 assertGreater(matched, 1)
263
264 class DebugFunctionCall(DebugTest):
265 def test(self):
266 self.gdb.b("main:start")
267 self.gdb.c()
268 assertEqual(self.gdb.p('fib(6)'), 8)
269 assertEqual(self.gdb.p('fib(7)'), 13)
270 self.exit()
271
272 class DebugChangeString(DebugTest):
273 def test(self):
274 text = "This little piggy went to the market."
275 self.gdb.b("main:start")
276 self.gdb.c()
277 self.gdb.p('fox = "%s"' % text)
278 self.exit(0x43b497b8)
279
280 class DebugTurbostep(DebugTest):
281 def test(self):
282 """Single step a bunch of times."""
283 self.gdb.b("main:start")
284 self.gdb.c()
285 self.gdb.command("p i=0")
286 last_pc = None
287 advances = 0
288 jumps = 0
289 for _ in range(10):
290 self.gdb.stepi()
291 pc = self.gdb.p("$pc")
292 assertNotEqual(last_pc, pc)
293 if last_pc and pc > last_pc and pc - last_pc <= 4:
294 advances += 1
295 else:
296 jumps += 1
297 last_pc = pc
298 # Some basic sanity that we're not running between breakpoints or
299 # something.
300 assertGreater(jumps, 1)
301 assertGreater(advances, 5)
302
303 class DebugExit(DebugTest):
304 def test(self):
305 self.exit()
306
307 class DebugSymbols(DebugTest):
308 def test(self):
309 self.gdb.b("main")
310 self.gdb.b("rot13")
311 output = self.gdb.c()
312 assertIn(", main ", output)
313 output = self.gdb.c()
314 assertIn(", rot13 ", output)
315
316 class DebugBreakpoint(DebugTest):
317 def test(self):
318 self.gdb.b("rot13")
319 # The breakpoint should be hit exactly 2 times.
320 for _ in range(2):
321 output = self.gdb.c()
322 self.gdb.p("$pc")
323 assertIn("Breakpoint ", output)
324 assertIn("rot13 ", output)
325 self.exit()
326
327 class Hwbp1(DebugTest):
328 def test(self):
329 if self.target.instruction_hardware_breakpoint_count < 1:
330 return 'not_applicable'
331
332 if not self.target.honors_tdata1_hmode:
333 # Run to main before setting the breakpoint, because startup code
334 # will otherwise clear the trigger that we set.
335 self.gdb.b("main")
336 self.gdb.c()
337
338 self.gdb.hbreak("rot13")
339 # The breakpoint should be hit exactly 2 times.
340 for _ in range(2):
341 output = self.gdb.c()
342 self.gdb.p("$pc")
343 assertRegexpMatches(output, r"[bB]reakpoint")
344 assertIn("rot13 ", output)
345 self.exit()
346
347 class Hwbp2(DebugTest):
348 def test(self):
349 if self.target.instruction_hardware_breakpoint_count < 2:
350 return 'not_applicable'
351
352 self.gdb.hbreak("main")
353 self.gdb.hbreak("rot13")
354 # We should hit 3 breakpoints.
355 for expected in ("main", "rot13", "rot13"):
356 output = self.gdb.c()
357 self.gdb.p("$pc")
358 assertRegexpMatches(output, r"[bB]reakpoint")
359 assertIn("%s " % expected, output)
360 self.exit()
361
362 class TooManyHwbp(DebugTest):
363 def run(self):
364 for i in range(30):
365 self.gdb.hbreak("*rot13 + %d" % (i * 4))
366
367 output = self.gdb.c()
368 assertIn("Cannot insert hardware breakpoint", output)
369 # Clean up, otherwise the hardware breakpoints stay set and future
370 # tests may fail.
371 self.gdb.command("D")
372
373 class Registers(DebugTest):
374 def test(self):
375 # Get to a point in the code where some registers have actually been
376 # used.
377 self.gdb.b("rot13")
378 self.gdb.c()
379 self.gdb.c()
380 # Try both forms to test gdb.
381 for cmd in ("info all-registers", "info registers all"):
382 output = self.gdb.command(cmd)
383 for reg in ('zero', 'ra', 'sp', 'gp', 'tp'):
384 assertIn(reg, output)
385
386 #TODO
387 # mcpuid is one of the few registers that should have the high bit set
388 # (for rv64).
389 # Leave this commented out until gdb and spike agree on the encoding of
390 # mcpuid (which is going to be renamed to misa in any case).
391 #assertRegexpMatches(output, ".*mcpuid *0x80")
392
393 #TODO:
394 # The instret register should always be changing.
395 #last_instret = None
396 #for _ in range(5):
397 # instret = self.gdb.p("$instret")
398 # assertNotEqual(instret, last_instret)
399 # last_instret = instret
400 # self.gdb.stepi()
401
402 self.exit()
403
404 class UserInterrupt(DebugTest):
405 def test(self):
406 """Sending gdb ^C while the program is running should cause it to
407 halt."""
408 self.gdb.b("main:start")
409 self.gdb.c()
410 self.gdb.p("i=123")
411 self.gdb.c(wait=False)
412 time.sleep(0.5)
413 output = self.gdb.interrupt()
414 assert "main" in output
415 assertGreater(self.gdb.p("j"), 10)
416 self.gdb.p("i=0")
417 self.exit()
418
419 class MulticoreTest(GdbTest):
420 compile_args = ("programs/infinite_loop.S", )
421
422 def setup(self):
423 self.gdb.load()
424 self.gdb.b("main")
425 self.gdb.b("main_end")
426 self.gdb.command("set non-stop on")
427 self.gdb.c()
428
429 def test(self):
430 threads = self.gdb.threads()
431 if len(threads) < 2:
432 return 'not_applicable'
433 # Run through the entire loop.
434 for t in threads:
435 self.gdb.thread(t)
436 self.gdb.p("$pc=_start")
437 # Run to main
438 for t in threads:
439 self.gdb.thread(t)
440 self.gdb.c()
441 for t in self.gdb.threads():
442 assertIn("main", t.frame)
443 # Run to end
444 for t in threads:
445 self.gdb.thread(t)
446 self.gdb.c()
447 hart_ids = []
448 for t in self.gdb.threads():
449 assertIn("main_end", t.frame)
450 # Check register values.
451 self.gdb.thread(t)
452 hart_id = self.gdb.p("$x1")
453 assertNotIn(hart_id, hart_ids)
454 hart_ids.append(hart_id)
455 for n in range(2, 32):
456 value = self.gdb.p("$x%d" % n)
457 assertEqual(value, hart_ids[-1] + n - 1)
458
459 # Confirmed that we read different register values for different harts.
460 # Write a new value to x1, and run through the add sequence again.
461
462 # This part isn't working right, because gdb doesn't resume Thread 2
463 # when asked. I don't know the root cause for that, but up to this
464 # point the test is still useful.
465
466 # for t in threads:
467 # self.gdb.thread(t)
468 # self.gdb.p("$x1=0x%x" % (int(t.id) + 0x800))
469 # self.gdb.p("$pc=main_post_csrr")
470 # for t in threads:
471 # self.gdb.thread(t)
472 # self.gdb.c()
473 # for t in self.gdb.threads():
474 # assertIn("main_end", t.frame)
475 # # Check register values.
476 # self.gdb.thread(t)
477 # for n in range(1, 32):
478 # value = self.gdb.p("$x%d" % n)
479 # assertEqual(value, int(t.id) + 0x800 + n - 1)
480
481 class StepTest(GdbTest):
482 compile_args = ("programs/step.S", )
483
484 def setup(self):
485 self.gdb.load()
486 self.gdb.b("main")
487 self.gdb.c()
488
489 def test(self):
490 main_address = self.gdb.p("$pc")
491 if self.target.extensionSupported("c"):
492 sequence = (4, 8, 0xc, 0xe, 0x14, 0x18, 0x22, 0x1c, 0x24, 0x24)
493 else:
494 sequence = (4, 8, 0xc, 0x10, 0x18, 0x1c, 0x28, 0x20, 0x2c, 0x2c)
495 for expected in sequence:
496 self.gdb.stepi()
497 pc = self.gdb.p("$pc")
498 assertEqual("%x" % (pc - main_address), "%x" % expected)
499
500 class TriggerTest(GdbTest):
501 compile_args = ("programs/trigger.S", )
502 def setup(self):
503 self.gdb.load()
504 self.gdb.b("_exit")
505 self.gdb.b("main")
506 self.gdb.c()
507
508 def exit(self):
509 output = self.gdb.c()
510 assertIn("Breakpoint", output)
511 assertIn("_exit", output)
512
513 class TriggerExecuteInstant(TriggerTest):
514 """Test an execute breakpoint on the first instruction executed out of
515 debug mode."""
516 def test(self):
517 main_address = self.gdb.p("$pc")
518 self.gdb.command("hbreak *0x%x" % (main_address + 4))
519 self.gdb.c()
520 assertEqual(self.gdb.p("$pc"), main_address+4)
521
522 # FIXME: Triggers aren't quite working yet
523 #class TriggerLoadAddress(TriggerTest):
524 # def test(self):
525 # self.gdb.command("rwatch *((&data)+1)")
526 # output = self.gdb.c()
527 # assertIn("read_loop", output)
528 # assertEqual(self.gdb.p("$a0"),
529 # self.gdb.p("(&data)+1"))
530 # self.exit()
531
532 class TriggerLoadAddressInstant(TriggerTest):
533 """Test a load address breakpoint on the first instruction executed out of
534 debug mode."""
535 def test(self):
536 self.gdb.command("b just_before_read_loop")
537 self.gdb.c()
538 read_loop = self.gdb.p("&read_loop")
539 self.gdb.command("rwatch data")
540 self.gdb.c()
541 # Accept hitting the breakpoint before or after the load instruction.
542 assertIn(self.gdb.p("$pc"), [read_loop, read_loop + 4])
543 assertEqual(self.gdb.p("$a0"), self.gdb.p("&data"))
544
545 # FIXME: Triggers aren't quite working yet
546 #class TriggerStoreAddress(TriggerTest):
547 # def test(self):
548 # self.gdb.command("watch *((&data)+3)")
549 # output = self.gdb.c()
550 # assertIn("write_loop", output)
551 # assertEqual(self.gdb.p("$a0"),
552 # self.gdb.p("(&data)+3"))
553 # self.exit()
554
555 class TriggerStoreAddressInstant(TriggerTest):
556 def test(self):
557 """Test a store address breakpoint on the first instruction executed out
558 of debug mode."""
559 self.gdb.command("b just_before_write_loop")
560 self.gdb.c()
561 write_loop = self.gdb.p("&write_loop")
562 self.gdb.command("watch data")
563 self.gdb.c()
564 # Accept hitting the breakpoint before or after the store instruction.
565 assertIn(self.gdb.p("$pc"), [write_loop, write_loop + 4])
566 assertEqual(self.gdb.p("$a0"), self.gdb.p("&data"))
567
568 class TriggerDmode(TriggerTest):
569 def early_applicable(self):
570 return self.target.honors_tdata1_hmode
571
572 def check_triggers(self, tdata1_lsbs, tdata2):
573 dmode = 1 << (self.target.xlen-5)
574
575 triggers = []
576
577 if self.target.xlen == 32:
578 xlen_type = 'int'
579 elif self.target.xlen == 64:
580 xlen_type = 'long long'
581 else:
582 raise NotImplementedError
583
584 dmode_count = 0
585 i = 0
586 for i in range(16):
587 tdata1 = self.gdb.p("((%s *)&data)[%d]" % (xlen_type, 2*i))
588 if tdata1 == 0:
589 break
590 tdata2 = self.gdb.p("((%s *)&data)[%d]" % (xlen_type, 2*i+1))
591
592 if tdata1 & dmode:
593 dmode_count += 1
594 else:
595 assertEqual(tdata1 & 0xffff, tdata1_lsbs)
596 assertEqual(tdata2, tdata2)
597
598 assertGreater(i, 1)
599 assertEqual(dmode_count, 1)
600
601 return triggers
602
603 def test(self):
604 self.gdb.command("hbreak write_load_trigger")
605 self.gdb.b("clear_triggers")
606 self.gdb.p("$pc=write_store_trigger")
607 output = self.gdb.c()
608 assertIn("write_load_trigger", output)
609 self.check_triggers((1<<6) | (1<<1), 0xdeadbee0)
610 output = self.gdb.c()
611 assertIn("clear_triggers", output)
612 self.check_triggers((1<<6) | (1<<0), 0xfeedac00)
613
614 class RegsTest(GdbTest):
615 compile_args = ("programs/regs.S", )
616 def setup(self):
617 self.gdb.load()
618 self.gdb.b("main")
619 self.gdb.b("handle_trap")
620 self.gdb.c()
621
622 class WriteGprs(RegsTest):
623 def test(self):
624 regs = [("x%d" % n) for n in range(2, 32)]
625
626 self.gdb.p("$pc=write_regs")
627 for i, r in enumerate(regs):
628 self.gdb.p("$%s=%d" % (r, (0xdeadbeef<<i)+17))
629 self.gdb.p("$x1=data")
630 self.gdb.command("b all_done")
631 output = self.gdb.c()
632 assertIn("Breakpoint ", output)
633
634 # Just to get this data in the log.
635 self.gdb.command("x/30gx data")
636 self.gdb.command("info registers")
637 for n in range(len(regs)):
638 assertEqual(self.gdb.x("data+%d" % (8*n), 'g'),
639 ((0xdeadbeef<<n)+17) & ((1<<self.target.xlen)-1))
640
641 class WriteCsrs(RegsTest):
642 def test(self):
643 # As much a test of gdb as of the simulator.
644 self.gdb.p("$mscratch=0")
645 self.gdb.stepi()
646 assertEqual(self.gdb.p("$mscratch"), 0)
647 self.gdb.p("$mscratch=123")
648 self.gdb.stepi()
649 assertEqual(self.gdb.p("$mscratch"), 123)
650
651 self.gdb.p("$pc=write_regs")
652 self.gdb.p("$x1=data")
653 self.gdb.command("b all_done")
654 self.gdb.command("c")
655
656 assertEqual(123, self.gdb.p("$mscratch"))
657 assertEqual(123, self.gdb.p("$x1"))
658 assertEqual(123, self.gdb.p("$csr832"))
659
660 class DownloadTest(GdbTest):
661 def setup(self):
662 # pylint: disable=attribute-defined-outside-init
663 length = min(2**10, self.target.ram_size - 2048)
664 self.download_c = tempfile.NamedTemporaryFile(prefix="download_",
665 suffix=".c", delete=False)
666 self.download_c.write("#include <stdint.h>\n")
667 self.download_c.write(
668 "unsigned int crc32a(uint8_t *message, unsigned int size);\n")
669 self.download_c.write("uint32_t length = %d;\n" % length)
670 self.download_c.write("uint8_t d[%d] = {\n" % length)
671 self.crc = 0
672 assert length % 16 == 0
673 for i in range(length / 16):
674 self.download_c.write(" /* 0x%04x */ " % (i * 16))
675 for _ in range(16):
676 value = random.randrange(1<<8)
677 self.download_c.write("0x%02x, " % value)
678 self.crc = binascii.crc32("%c" % value, self.crc)
679 self.download_c.write("\n")
680 self.download_c.write("};\n")
681 self.download_c.write("uint8_t *data = &d[0];\n")
682 self.download_c.write(
683 "uint32_t main() { return crc32a(data, length); }\n")
684 self.download_c.flush()
685
686 if self.crc < 0:
687 self.crc += 2**32
688
689 self.binary = self.target.compile(self.download_c.name,
690 "programs/checksum.c")
691 self.gdb.command("file %s" % self.binary)
692
693 def test(self):
694 self.gdb.load()
695 self.gdb.command("b _exit")
696 self.gdb.c(timeout=60)
697 assertEqual(self.gdb.p("status"), self.crc)
698 os.unlink(self.download_c.name)
699
700 # FIXME: PRIV isn't implemented in the current OpenOCD
701 #class MprvTest(GdbTest):
702 # compile_args = ("programs/mprv.S", )
703 # def setup(self):
704 # self.gdb.load()
705 #
706 # def test(self):
707 # """Test that the debugger can access memory when MPRV is set."""
708 # self.gdb.c(wait=False)
709 # time.sleep(0.5)
710 # self.gdb.interrupt()
711 # output = self.gdb.command("p/x *(int*)(((char*)&data)-0x80000000)")
712 # assertIn("0xbead", output)
713 #
714 #class PrivTest(GdbTest):
715 # compile_args = ("programs/priv.S", )
716 # def setup(self):
717 # # pylint: disable=attribute-defined-outside-init
718 # self.gdb.load()
719 #
720 # misa = self.target.misa
721 # self.supported = set()
722 # if misa & (1<<20):
723 # self.supported.add(0)
724 # if misa & (1<<18):
725 # self.supported.add(1)
726 # if misa & (1<<7):
727 # self.supported.add(2)
728 # self.supported.add(3)
729 #
730 #class PrivRw(PrivTest):
731 # def test(self):
732 # """Test reading/writing priv."""
733 # for privilege in range(4):
734 # self.gdb.p("$priv=%d" % privilege)
735 # self.gdb.stepi()
736 # actual = self.gdb.p("$priv")
737 # assertIn(actual, self.supported)
738 # if privilege in self.supported:
739 # assertEqual(actual, privilege)
740 #
741 #class PrivChange(PrivTest):
742 # def test(self):
743 # """Test that the core's privilege level actually changes."""
744 #
745 # if 0 not in self.supported:
746 # return 'not_applicable'
747 #
748 # self.gdb.b("main")
749 # self.gdb.c()
750 #
751 # # Machine mode
752 # self.gdb.p("$priv=3")
753 # main_address = self.gdb.p("$pc")
754 # self.gdb.stepi()
755 # assertEqual("%x" % self.gdb.p("$pc"), "%x" % (main_address+4))
756 #
757 # # User mode
758 # self.gdb.p("$priv=0")
759 # self.gdb.stepi()
760 # # Should have taken an exception, so be nowhere near main.
761 # pc = self.gdb.p("$pc")
762 # assertTrue(pc < main_address or pc > main_address + 0x100)
763
764 parsed = None
765 def main():
766 parser = argparse.ArgumentParser(
767 description="Test that gdb can talk to a RISC-V target.",
768 epilog="""
769 Example command line from the real world:
770 Run all RegsTest cases against a physical FPGA, with custom openocd command:
771 ./gdbserver.py --freedom-e300 --server_cmd "$HOME/SiFive/openocd/src/openocd -s $HOME/SiFive/openocd/tcl -d" Simple
772 """)
773 targets.add_target_options(parser)
774
775 testlib.add_test_run_options(parser)
776
777 # TODO: remove global
778 global parsed # pylint: disable=global-statement
779 parsed = parser.parse_args()
780 target = targets.target(parsed)
781
782 if parsed.xlen:
783 target.xlen = parsed.xlen
784
785 module = sys.modules[__name__]
786
787 return testlib.run_all_tests(module, target, parsed)
788
789 # TROUBLESHOOTING TIPS
790 # If a particular test fails, run just that one test, eg.:
791 # ./gdbserver.py MprvTest.test_mprv
792 # Then inspect gdb.log and spike.log to see what happened in more detail.
793
794 if __name__ == '__main__':
795 sys.exit(main())