def test_rd(kdb): '''Test the `rd` command. Grab the contents of the register set and (optionally) perform a per-arch sanity test. The actual call to the rd command happens inside the library code in ktest.py [get_regs_kdb()]. However it is worth pulling out into a seperate test because other tests won't fail in such an easily understood way if there were a regression in the output of `rd`. ''' c = kdb.console.enter_kdb() try: regs = c.get_regs() if kbuild.get_arch() == 'arm64': assert 'x0' in regs assert regs['sp'].startswith('ffff') assert regs['pc'].startswith('ffff') elif kbuild.get_arch == 'riscv': assert regs['sp'].startswith('ffff') assert regs['pc'].startswith('ffff') elif kbuild.get_arch() == 'x86': assert 'ax' in regs assert regs['sp'].startswith('ffff') assert regs['ip'].startswith('ffff') finally: c.exit_kdb()
def test_earlycon(): kbuild.config(kgdb=True) kbuild.build() # Handle platforms that cannot auto-configure the earlycon arch = kbuild.get_arch() if 'x86' == arch: earlycon = "earlycon=uart8250,io,0x3f8" elif 'arm' == arch: earlycon = "earlycon=pl011,0x1c090000" else: earlycon = "earlycon" qemu = ktest.qemu(append=f'{earlycon} kgdboc_earlycon kgdbwait') console = qemu.console def expect_and_page(c, needle): choices = [needle, 'more>'] choice = c.expect(choices) while choice == 1: c.send(' ') choice = c.expect(choices) print(choice) print(choices) print("Done") # This test is too low level to use the regular console helpers console.expect_kdb() console.sendline_kdb('bt') if 'x86' in kbuild.get_arch(): # x86 uses earlycon with arguments and supports very early # consoles, expect to break whilst parsing early parameters expect_and_page(console, 'parse_early_param') elif 'arm64' in kbuild.get_arch(): # Currently arm64 doesn't implement ARCH_HAS_EARLY_DEBUG # expect_and_page(console, 'console_init') pass expect_and_page(console, 'start_kernel') console.expect_kdb() console.sendline_kdb('go') # At this point kgdb has already fired up (on the earlycon). That # means the early boot messages have already passed by, hence we # need to set skip_early in the boot expectations. qemu.console.expect_boot(skip_early=True) qemu.console.expect_busybox() qemu.close()
def expect_boot(self, bootloader=(), skip_early=False, skip_late=False): for msg in bootloader: self.expect(msg) if not skip_early: self.expect('Linux version.*$') self.expect('Calibrating delay loop') # Initramfs decompression can take a long time (especially on a # TCG based VM on a busy machine). Extend the timeout until # the next console interaction... self.expect('[Uu]npack.*initramfs') self.timeout *= 2 if not skip_late: # Memory is not *always* freed after unpacking the initramfs so # we must also look for other common messages that indicate we # moved on from unpacking the initramfs. self.expect(['Freeing initrd memory', 'io scheduler.*registered', 'Registered I/O driver kgdboc']) # We need a wildcard here because some newer kernels now say: # "Free unused kernel image memory". self.expect('Freeing unused kernel.*memory') # Restore the normal timeout self.timeout = self.default_timeout # Reset the terminal when running in platforms whose bootloader # output screws up the handling of line breaks... a bit gross # but makes reading the diagnostic output less painful! arch = kbuild.get_arch() if arch == 'x86': os.system('reset')
def test_kgdb(): '''High-level kgdb smoke test. Tour a number of kgdb features to prove that basic functionality if working OK. ''' kbuild.config(kgdb=True) kbuild.build() qemu = ktest.qemu(second_uart=True, gdb=True, append='kgdbwait') console = qemu.console gdb = qemu.debug console.expect_boot(skip_late=True) console.expect('Waiting for connection from remote gdb...') gdb.connect_to_target() gdb.send('where\r') gdb.expect('kgdb_initial_breakpoint') gdb.expect(['init_kgdboc', 'configure_kgdboc']) gdb.expect_prompt() gdb.send('break do_sys_open\r') gdb.expect_prompt() gdb.send('continue\r') gdb.expect('hit Breakpoint') gdb.expect_prompt() gdb.send('info registers\r') # If the PC is not automatically shown symbolically in a # register dump then we must look it up explicitly. if kbuild.get_arch() in ('mips', ): gdb.expect_prompt() gdb.send('info symbol $pc\r') # On x86_64 the PC lookup for this is __x64_sys_open (and on mips # it can be sys_open so we don't expect the 'do_'! gdb.expect('sys_open') gdb.expect_prompt() gdb.send('info thread\r') gdb.expect('oom_reaper') gdb.expect_prompt() gdb.send('thread 10\r') gdb.expect_prompt() gdb.send('where\r') gdb.expect('kthread') gdb.expect(['ret_from_fork', 'ret_from_kernel_thread', 'riscv.*entry[.]S']) gdb.expect_prompt() gdb.send('delete 1\r') gdb.expect_prompt() gdb.send('continue\r') console.expect_busybox() qemu.close()
def test_info_registers(kgdb): (console, gdb) = kgdb.enter_gdb() try: gdb.sendline('info registers') # Without doing architecture specific checks we can # only really look for the PC/IP being inside the # kgdb_breakpoint function (and even that doesn't work # for architectures where the PC doesn't get shown # symbolically). if kbuild.get_arch() not in ('mips',): gdb.expect('kgdb_breakpoint') finally: gdb.expect_prompt() kgdb.exit_gdb()
def test_WARNING(kdb): ''' Test that kdb does *not* enter during a WARN_ON() ''' kdb.console.sendline( 'echo WARNING > /sys/kernel/debug/provoke-crash/DIRECT') kdb.console.expect('WARNING') #kdb.console.expect('lkdtm_WARNING') # riscv doesn't issue stack trace on warnings if kbuild.get_arch() != 'riscv': kdb.console.expect('vfs_write') kdb.console.expect_prompt()
def kdb(): kbuild.config(kgdb=True) kbuild.build() qemu = ktest.qemu() console = qemu.console console.expect_boot() console.expect_busybox() yield qemu qemu.close() @pytest.mark.xfail(condition=(kbuild.get_arch() == 'mips'), reason="Triggers breakpoint twice", run=False) @pytest.mark.xfail(condition=(kbuild.get_arch() == 'x86'), reason="Triggers breakpoint twice", run=False) def test_BUG(kdb): ''' Test how kdb reacts to a BUG() ''' # Must use /bin/echo... if we use a shell built-in then the # kernel will kill off the shell when we resume kdb.console.sendline( '/bin/echo BUG > /sys/kernel/debug/provoke-crash/DIRECT')
gdb.expect_prompt() kgdb.exit_gdb() def test_info_target(kgdb): (console, gdb) = kgdb.enter_gdb() try: gdb.sendline('info target') gdb.expect('Debugging a target over a serial line') finally: gdb.expect_prompt() kgdb.exit_gdb() # The failure on x86 has a reproduction rate of approximately 5%. # It is rare but is frequent enough to affect suite reliability # (and it causes a cascading failure so we will avoid runninng it) @pytest.mark.xfail(condition = kbuild.get_arch() == 'x86', run = False, reason = 'GDB reports packet errors') def test_info_thread(kgdb): (console, gdb) = kgdb.enter_gdb() try: gdb.sendline('info thread') # One of the CPUs should be stopped in kgdb_breakpoint() gdb.expect('[(].*CPU.*[)][^\r\n]*kgdb_breakpoint') # Look for a few common thread names gdb.expect('[(]init[)]') gdb.expect('[(]kthreadd[)]') gdb.expect('[(]kworker/0:0[)]') # Check the shell process is in kgdb_breakpoint() gdb.expect('[(]sh[)][^\r\n]*kgdb_breakpoint')
import kbuild import ktest import pytest @pytest.mark.xfail(condition=(kbuild.get_arch() == 'arm64' and kbuild.get_version() < (4, 17)), reason='Thread 10 has a PC of 0x0 (so cannot unwind)') @pytest.mark.xfail(condition=(kbuild.get_arch() == 'x86'), reason='inconsistent lock state') def test_kgdb(): '''High-level kgdb smoke test. Tour a number of kgdb features to prove that basic functionality if working OK. ''' kbuild.config(kgdb=True) kbuild.build() qemu = ktest.qemu(second_uart=True, gdb=True, append='kgdbwait') console = qemu.console gdb = qemu.debug console.expect_boot(skip_late=True) console.expect('Waiting for connection from remote gdb...') gdb.connect_to_target() gdb.send('where\r') gdb.expect('kgdb_initial_breakpoint') gdb.expect(['init_kgdboc', 'configure_kgdboc'])
def qemu(kdb=True, append=None, gdb=False, gfx=False, interactive=False, second_uart=False): '''Create a qemu instance and provide pexpect channels to control it''' arch = kbuild.get_arch() host_arch = kbuild.get_host_arch() if arch == 'arm' or arch == 'arm64': tty = 'ttyAMA' else: tty = 'ttyS' if arch == 'arm64' or arch == 'riscv': second_uart = False cmdline = '' if gfx: cmdline += ' console=tty0' cmdline += ' console={}0,115200'.format(tty) if kdb: cmdline += ' kgdboc=' if gfx: cmdline += 'kms,kbd,' if not second_uart: cmdline += '{}0'.format(tty) else: cmdline += '{}1'.format(tty) if gdb: cmdline += ' nokaslr' if arch == 'arm': # The versatile boot process no longer contains sneaky # ordering tricks to allow the console to come up without an # -EPROBE_DEFER. Putting it another way... we'll see timeouts # in several tests unless we have an earlycon. cmdline += ' earlycon=pl011,0x1c090000' if append: cmdline += ' ' + append # Heavily broken out so we can easily slot in support for other # architectures. if arch == 'arm': cmd = 'qemu-system-arm' cmd += ' -accel tcg,thread=multi ' cmd += ' -M vexpress-a15 -cpu cortex-a15' cmd += ' -m 1G -smp 2' cmd += ' -kernel arch/arm/boot/zImage' cmd += ' -dtb arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dtb' elif arch == 'arm64': cmd = 'qemu-system-aarch64' if host_arch == 'arm64': cmd += ' -cpu host -M virt,gic_version=3,accel=kvm' else: cmd += ' -accel tcg,thread=multi ' cmd += ' -M virt,gic_version=3 -cpu cortex-a57' cmd += ' -m 1G -smp 2' cmd += ' -kernel arch/arm64/boot/Image' elif arch == 'mips': cmd = 'qemu-system-mips64el' cmd += ' -accel tcg,thread=multi ' cmd += ' -cpu I6400 -M malta' cmd += ' -m 1G -smp 2' cmd += ' -kernel vmlinux' elif arch == 'riscv': cmd = 'qemu-system-riscv64' cmd += ' -accel tcg,thread=multi' cmd += ' -machine virt' cmd += ' -m 1G -smp 2' cmd += ' -kernel arch/riscv/boot/Image' elif arch == 'x86': cmd = 'qemu-system-x86_64' if host_arch == 'x86': cmd += ' -enable-kvm' cmd += ' -m 1G -smp 2' cmd += ' -kernel arch/x86/boot/bzImage' else: assert False if not gfx: cmd += ' -nographic' if second_uart: cmd += ' -monitor none' cmd += ' -chardev stdio,id=mon,mux=on,signal=off -serial chardev:mon' cmd += ' -chardev socket,id=ttyS1,path=ttyS1.sock,server,nowait' cmd += ' -serial chardev:ttyS1' elif gdb: cmd += ' -S -chardev pty,id=ttyS0 -serial chardev:ttyS0' else: cmd += ' -monitor none' cmd += ' -chardev stdio,id=mon,mux=on,signal=off -serial chardev:mon' cmd += ' -initrd rootfs.cpio.gz' cmd += ' -append "{}"'.format(cmdline) if gdb: gdbcmd = kbuild.get_cross_compile('gdb') gdbcmd += ' vmlinux' gdbcmd += ' -ex "set pagination 0"' if interactive: if gdb: gdbcmd += ' -ex "target extended-remote |' + \ 'socat - UNIX:ttyS1.sock"' print ('\n>>> (cd {}; {})\n'.format( kbuild.get_kdir(), gdbcmd)) print('+| ' + cmd) time.sleep(5) os.system(cmd) return None print('+| ' + cmd) qemu = pexpect.spawn(cmd, encoding='utf-8', logfile=sys.stdout) if gdb: print('+| ' + gdbcmd) gdb = pexpect.spawn(gdbcmd, encoding='utf-8', logfile=sys.stdout) else: gdb = None if gdb and not second_uart: monitor = qemu monitor.expect('char device redirected to (/dev/pts/[0-9]*) .label') uart_pty = monitor.match.group(1) dmx = pexpect.spawn(f'kdmx -p {uart_pty}', encoding='utf-8', logfile=sys.stdout) dmx.expect('(/dev/pts/[0-9]*) is slave pty for terminal emulator') console_pty = dmx.match.group(1) dmx.expect('(/dev/pts/[0-9]*) is slave pty for gdb') gdb_pty = dmx.match.group(1) gdb.connection = gdb_pty; print(f'Demuxing from {uart_pty} to {console_pty}^{gdb_pty}') # Start picocom and wait for it to be ready. It *must* be ready before # we (cont)inue in the qemu monitor or we will miss the initial boot # messages. Sadly it appears that, even though picocom has opened all # the file handles when it says "Terminal ready" the connection isn't # fully established until picocom calls select() in its event loop. # Enable/disable local echo in order to guarantee we get that far. console = pexpect.spawn(f'picocom {console_pty}', encoding='utf-8', logfile=sys.stdout) console.expect('picocom') console.expect('Terminal ready') console.send('\x01\x03\x01\03') console.expect('local echo.*no') time.sleep(1) # Attach the kdmx process to the monitor (otherwise it will be # closed when the function exits. monitor.dmx = dmx # Set everything running monitor.expect('[(]qemu[)]') monitor.sendline('cont') monitor.expect('[(]qemu[)]') return ConsoleWrapper(console, gdb, monitor) else: if gdb: gdb.connection = '|socat - UNIX:ttyS1.sock' return ConsoleWrapper(qemu, gdb)
import kbuild import ktest import pytest @pytest.mark.xfail(condition=kbuild.get_version() < (5, 8), run=False, reason='Not implemented until 5.8-rc1') @pytest.mark.skipif(kbuild.get_arch() not in ('arm', 'arm64', 'x86'), reason='earlycon not configured for this arch') def test_earlycon(): kbuild.config(kgdb=True) kbuild.build() # Handle platforms that cannot auto-configure the earlycon arch = kbuild.get_arch() if 'x86' == arch: earlycon = "earlycon=uart8250,io,0x3f8" elif 'arm' == arch: earlycon = "earlycon=pl011,0x1c090000" else: earlycon = "earlycon" qemu = ktest.qemu(append=f'{earlycon} kgdboc_earlycon kgdbwait') console = qemu.console def expect_and_page(c, needle): choices = [needle, 'more>'] choice = c.expect(choices) while choice == 1: