boot_linux_console.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. # Functional test that boots a Linux kernel and checks the console
  2. #
  3. # Copyright (c) 2018 Red Hat, Inc.
  4. #
  5. # Author:
  6. # Cleber Rosa <crosa@redhat.com>
  7. #
  8. # This work is licensed under the terms of the GNU GPL, version 2 or
  9. # later. See the COPYING file in the top-level directory.
  10. import os
  11. import lzma
  12. import gzip
  13. import shutil
  14. from avocado import skip
  15. from avocado import skipUnless
  16. from avocado import skipUnless
  17. from avocado_qemu import QemuSystemTest
  18. from avocado_qemu import exec_command
  19. from avocado_qemu import exec_command_and_wait_for_pattern
  20. from avocado_qemu import interrupt_interactive_console_until_pattern
  21. from avocado_qemu import wait_for_console_pattern
  22. from avocado.utils import process
  23. from avocado.utils import archive
  24. """
  25. Round up to next power of 2
  26. """
  27. def pow2ceil(x):
  28. return 1 if x == 0 else 2**(x - 1).bit_length()
  29. def file_truncate(path, size):
  30. if size != os.path.getsize(path):
  31. with open(path, 'ab+') as fd:
  32. fd.truncate(size)
  33. """
  34. Expand file size to next power of 2
  35. """
  36. def image_pow2ceil_expand(path):
  37. size = os.path.getsize(path)
  38. size_aligned = pow2ceil(size)
  39. if size != size_aligned:
  40. with open(path, 'ab+') as fd:
  41. fd.truncate(size_aligned)
  42. class LinuxKernelTest(QemuSystemTest):
  43. KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
  44. def wait_for_console_pattern(self, success_message, vm=None):
  45. wait_for_console_pattern(self, success_message,
  46. failure_message='Kernel panic - not syncing',
  47. vm=vm)
  48. def extract_from_deb(self, deb, path):
  49. """
  50. Extracts a file from a deb package into the test workdir
  51. :param deb: path to the deb archive
  52. :param path: path within the deb archive of the file to be extracted
  53. :returns: path of the extracted file
  54. """
  55. cwd = os.getcwd()
  56. os.chdir(self.workdir)
  57. file_path = process.run("ar t %s" % deb).stdout_text.split()[2]
  58. process.run("ar x %s %s" % (deb, file_path))
  59. archive.extract(file_path, self.workdir)
  60. os.chdir(cwd)
  61. # Return complete path to extracted file. Because callers to
  62. # extract_from_deb() specify 'path' with a leading slash, it is
  63. # necessary to use os.path.relpath() as otherwise os.path.join()
  64. # interprets it as an absolute path and drops the self.workdir part.
  65. return os.path.normpath(os.path.join(self.workdir,
  66. os.path.relpath(path, '/')))
  67. def extract_from_rpm(self, rpm, path):
  68. """
  69. Extracts a file from an RPM package into the test workdir.
  70. :param rpm: path to the rpm archive
  71. :param path: path within the rpm archive of the file to be extracted
  72. needs to be a relative path (starting with './') because
  73. cpio(1), which is used to extract the file, expects that.
  74. :returns: path of the extracted file
  75. """
  76. cwd = os.getcwd()
  77. os.chdir(self.workdir)
  78. process.run("rpm2cpio %s | cpio -id %s" % (rpm, path), shell=True)
  79. os.chdir(cwd)
  80. return os.path.normpath(os.path.join(self.workdir, path))
  81. class BootLinuxConsole(LinuxKernelTest):
  82. """
  83. Boots a Linux kernel and checks that the console is operational and the
  84. kernel command line is properly passed from QEMU to the kernel
  85. """
  86. timeout = 90
  87. def test_x86_64_pc(self):
  88. """
  89. :avocado: tags=arch:x86_64
  90. :avocado: tags=machine:pc
  91. """
  92. kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
  93. '/linux/releases/29/Everything/x86_64/os/images/pxeboot'
  94. '/vmlinuz')
  95. kernel_hash = '23bebd2680757891cf7adedb033532163a792495'
  96. kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
  97. self.vm.set_console()
  98. kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
  99. self.vm.add_args('-kernel', kernel_path,
  100. '-append', kernel_command_line)
  101. self.vm.launch()
  102. console_pattern = 'Kernel command line: %s' % kernel_command_line
  103. self.wait_for_console_pattern(console_pattern)
  104. def test_arm_virt(self):
  105. """
  106. :avocado: tags=arch:arm
  107. :avocado: tags=machine:virt
  108. :avocado: tags=accel:tcg
  109. """
  110. kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
  111. '/linux/releases/29/Everything/armhfp/os/images/pxeboot'
  112. '/vmlinuz')
  113. kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4'
  114. kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
  115. self.vm.set_console()
  116. kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
  117. 'console=ttyAMA0')
  118. self.vm.add_args('-kernel', kernel_path,
  119. '-append', kernel_command_line)
  120. self.vm.launch()
  121. console_pattern = 'Kernel command line: %s' % kernel_command_line
  122. self.wait_for_console_pattern(console_pattern)
  123. def test_arm_emcraft_sf2(self):
  124. """
  125. :avocado: tags=arch:arm
  126. :avocado: tags=machine:emcraft-sf2
  127. :avocado: tags=endian:little
  128. :avocado: tags=u-boot
  129. :avocado: tags=accel:tcg
  130. """
  131. self.require_netdev('user')
  132. uboot_url = ('https://raw.githubusercontent.com/'
  133. 'Subbaraya-Sundeep/qemu-test-binaries/'
  134. 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot')
  135. uboot_hash = 'cbb8cbab970f594bf6523b9855be209c08374ae2'
  136. uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash)
  137. spi_url = ('https://raw.githubusercontent.com/'
  138. 'Subbaraya-Sundeep/qemu-test-binaries/'
  139. 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin')
  140. spi_hash = '65523a1835949b6f4553be96dec1b6a38fb05501'
  141. spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash)
  142. spi_path_rw = os.path.join(self.workdir, os.path.basename(spi_path))
  143. shutil.copy(spi_path, spi_path_rw)
  144. file_truncate(spi_path_rw, 16 << 20) # Spansion S25FL128SDPBHICO is 16 MiB
  145. self.vm.set_console()
  146. kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE
  147. self.vm.add_args('-kernel', uboot_path,
  148. '-append', kernel_command_line,
  149. '-drive', 'file=' + spi_path_rw + ',if=mtd,format=raw',
  150. '-no-reboot')
  151. self.vm.launch()
  152. self.wait_for_console_pattern('Enter \'help\' for a list')
  153. exec_command_and_wait_for_pattern(self, 'ifconfig eth0 10.0.2.15',
  154. 'eth0: link becomes ready')
  155. exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2',
  156. '3 packets transmitted, 3 packets received, 0% packet loss')
  157. def test_arm_exynos4210_initrd(self):
  158. """
  159. :avocado: tags=arch:arm
  160. :avocado: tags=machine:smdkc210
  161. :avocado: tags=accel:tcg
  162. """
  163. deb_url = ('https://snapshot.debian.org/archive/debian/'
  164. '20190928T224601Z/pool/main/l/linux/'
  165. 'linux-image-4.19.0-6-armmp_4.19.67-2+deb10u1_armhf.deb')
  166. deb_hash = 'fa9df4a0d38936cb50084838f2cb933f570d7d82'
  167. deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
  168. kernel_path = self.extract_from_deb(deb_path,
  169. '/boot/vmlinuz-4.19.0-6-armmp')
  170. dtb_path = '/usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb'
  171. dtb_path = self.extract_from_deb(deb_path, dtb_path)
  172. initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
  173. '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
  174. 'arm/rootfs-armv5.cpio.gz')
  175. initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b'
  176. initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
  177. initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
  178. archive.gzip_uncompress(initrd_path_gz, initrd_path)
  179. self.vm.set_console()
  180. kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
  181. 'earlycon=exynos4210,0x13800000 earlyprintk ' +
  182. 'console=ttySAC0,115200n8 ' +
  183. 'random.trust_cpu=off cryptomgr.notests ' +
  184. 'cpuidle.off=1 panic=-1 noreboot')
  185. self.vm.add_args('-kernel', kernel_path,
  186. '-dtb', dtb_path,
  187. '-initrd', initrd_path,
  188. '-append', kernel_command_line,
  189. '-no-reboot')
  190. self.vm.launch()
  191. self.wait_for_console_pattern('Boot successful.')
  192. # TODO user command, for now the uart is stuck
  193. def test_arm_cubieboard_initrd(self):
  194. """
  195. :avocado: tags=arch:arm
  196. :avocado: tags=machine:cubieboard
  197. :avocado: tags=accel:tcg
  198. """
  199. deb_url = ('https://apt.armbian.com/pool/main/l/'
  200. 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb')
  201. deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b'
  202. deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
  203. kernel_path = self.extract_from_deb(deb_path,
  204. '/boot/vmlinuz-6.6.16-current-sunxi')
  205. dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb'
  206. dtb_path = self.extract_from_deb(deb_path, dtb_path)
  207. initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
  208. '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
  209. 'arm/rootfs-armv5.cpio.gz')
  210. initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b'
  211. initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
  212. initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
  213. archive.gzip_uncompress(initrd_path_gz, initrd_path)
  214. self.vm.set_console()
  215. kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
  216. 'console=ttyS0,115200 '
  217. 'usbcore.nousb '
  218. 'panic=-1 noreboot')
  219. self.vm.add_args('-kernel', kernel_path,
  220. '-dtb', dtb_path,
  221. '-initrd', initrd_path,
  222. '-append', kernel_command_line,
  223. '-no-reboot')
  224. self.vm.launch()
  225. self.wait_for_console_pattern('Boot successful.')
  226. exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
  227. 'Allwinner sun4i/sun5i')
  228. exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
  229. 'system-control@1c00000')
  230. exec_command_and_wait_for_pattern(self, 'reboot',
  231. 'reboot: Restarting system')
  232. # Wait for VM to shut down gracefully
  233. self.vm.wait()
  234. def test_arm_cubieboard_sata(self):
  235. """
  236. :avocado: tags=arch:arm
  237. :avocado: tags=machine:cubieboard
  238. :avocado: tags=accel:tcg
  239. """
  240. deb_url = ('https://apt.armbian.com/pool/main/l/'
  241. 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb')
  242. deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b'
  243. deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
  244. kernel_path = self.extract_from_deb(deb_path,
  245. '/boot/vmlinuz-6.6.16-current-sunxi')
  246. dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb'
  247. dtb_path = self.extract_from_deb(deb_path, dtb_path)
  248. rootfs_url = ('https://github.com/groeck/linux-build-test/raw/'
  249. '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
  250. 'arm/rootfs-armv5.ext2.gz')
  251. rootfs_hash = '093e89d2b4d982234bf528bc9fb2f2f17a9d1f93'
  252. rootfs_path_gz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash)
  253. rootfs_path = os.path.join(self.workdir, 'rootfs.cpio')
  254. archive.gzip_uncompress(rootfs_path_gz, rootfs_path)
  255. self.vm.set_console()
  256. kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
  257. 'console=ttyS0,115200 '
  258. 'usbcore.nousb '
  259. 'root=/dev/sda ro '
  260. 'panic=-1 noreboot')
  261. self.vm.add_args('-kernel', kernel_path,
  262. '-dtb', dtb_path,
  263. '-drive', 'if=none,format=raw,id=disk0,file='
  264. + rootfs_path,
  265. '-device', 'ide-hd,bus=ide.0,drive=disk0',
  266. '-append', kernel_command_line,
  267. '-no-reboot')
  268. self.vm.launch()
  269. self.wait_for_console_pattern('Boot successful.')
  270. exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
  271. 'Allwinner sun4i/sun5i')
  272. exec_command_and_wait_for_pattern(self, 'cat /proc/partitions',
  273. 'sda')
  274. exec_command_and_wait_for_pattern(self, 'reboot',
  275. 'reboot: Restarting system')
  276. # Wait for VM to shut down gracefully
  277. self.vm.wait()
  278. @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited')
  279. def test_arm_cubieboard_openwrt_22_03_2(self):
  280. """
  281. :avocado: tags=arch:arm
  282. :avocado: tags=machine:cubieboard
  283. :avocado: tags=device:sd
  284. """
  285. # This test download a 7.5 MiB compressed image and expand it
  286. # to 126 MiB.
  287. image_url = ('https://downloads.openwrt.org/releases/22.03.2/targets/'
  288. 'sunxi/cortexa8/openwrt-22.03.2-sunxi-cortexa8-'
  289. 'cubietech_a10-cubieboard-ext4-sdcard.img.gz')
  290. image_hash = ('94b5ecbfbc0b3b56276e5146b899eafa'
  291. '2ac5dc2d08733d6705af9f144f39f554')
  292. image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash,
  293. algorithm='sha256')
  294. image_path = archive.extract(image_path_gz, self.workdir)
  295. image_pow2ceil_expand(image_path)
  296. self.vm.set_console()
  297. self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw',
  298. '-nic', 'user',
  299. '-no-reboot')
  300. self.vm.launch()
  301. kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
  302. 'usbcore.nousb '
  303. 'noreboot')
  304. self.wait_for_console_pattern('U-Boot SPL')
  305. interrupt_interactive_console_until_pattern(
  306. self, 'Hit any key to stop autoboot:', '=>')
  307. exec_command_and_wait_for_pattern(self, "setenv extraargs '" +
  308. kernel_command_line + "'", '=>')
  309. exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...');
  310. self.wait_for_console_pattern(
  311. 'Please press Enter to activate this console.')
  312. exec_command_and_wait_for_pattern(self, ' ', 'root@')
  313. exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
  314. 'Allwinner sun4i/sun5i')
  315. exec_command_and_wait_for_pattern(self, 'reboot',
  316. 'reboot: Restarting system')
  317. # Wait for VM to shut down gracefully
  318. self.vm.wait()
  319. @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
  320. def test_arm_quanta_gsj(self):
  321. """
  322. :avocado: tags=arch:arm
  323. :avocado: tags=machine:quanta-gsj
  324. :avocado: tags=accel:tcg
  325. """
  326. # 25 MiB compressed, 32 MiB uncompressed.
  327. image_url = (
  328. 'https://github.com/hskinnemoen/openbmc/releases/download/'
  329. '20200711-gsj-qemu-0/obmc-phosphor-image-gsj.static.mtd.gz')
  330. image_hash = '14895e634923345cb5c8776037ff7876df96f6b1'
  331. image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash)
  332. image_name = 'obmc.mtd'
  333. image_path = os.path.join(self.workdir, image_name)
  334. archive.gzip_uncompress(image_path_gz, image_path)
  335. self.vm.set_console()
  336. drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0'
  337. self.vm.add_args('-drive', drive_args)
  338. self.vm.launch()
  339. # Disable drivers and services that stall for a long time during boot,
  340. # to avoid running past the 90-second timeout. These may be removed
  341. # as the corresponding device support is added.
  342. kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + (
  343. 'console=${console} '
  344. 'mem=${mem} '
  345. 'initcall_blacklist=npcm_i2c_bus_driver_init '
  346. 'systemd.mask=systemd-random-seed.service '
  347. 'systemd.mask=dropbearkey.service '
  348. )
  349. self.wait_for_console_pattern('> BootBlock by Nuvoton')
  350. self.wait_for_console_pattern('>Device: Poleg BMC NPCM730')
  351. self.wait_for_console_pattern('>Skip DDR init.')
  352. self.wait_for_console_pattern('U-Boot ')
  353. interrupt_interactive_console_until_pattern(
  354. self, 'Hit any key to stop autoboot:', 'U-Boot>')
  355. exec_command_and_wait_for_pattern(
  356. self, "setenv bootargs ${bootargs} " + kernel_command_line,
  357. 'U-Boot>')
  358. exec_command_and_wait_for_pattern(
  359. self, 'run romboot', 'Booting Kernel from flash')
  360. self.wait_for_console_pattern('Booting Linux on physical CPU 0x0')
  361. self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0')
  362. self.wait_for_console_pattern('OpenBMC Project Reference Distro')
  363. self.wait_for_console_pattern('gsj login:')
  364. def test_arm_quanta_gsj_initrd(self):
  365. """
  366. :avocado: tags=arch:arm
  367. :avocado: tags=machine:quanta-gsj
  368. :avocado: tags=accel:tcg
  369. """
  370. initrd_url = (
  371. 'https://github.com/hskinnemoen/openbmc/releases/download/'
  372. '20200711-gsj-qemu-0/obmc-phosphor-initramfs-gsj.cpio.xz')
  373. initrd_hash = '98fefe5d7e56727b1eb17d5c00311b1b5c945300'
  374. initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
  375. kernel_url = (
  376. 'https://github.com/hskinnemoen/openbmc/releases/download/'
  377. '20200711-gsj-qemu-0/uImage-gsj.bin')
  378. kernel_hash = 'fa67b2f141d56d39b3c54305c0e8a899c99eb2c7'
  379. kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
  380. dtb_url = (
  381. 'https://github.com/hskinnemoen/openbmc/releases/download/'
  382. '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb')
  383. dtb_hash = '18315f7006d7b688d8312d5c727eecd819aa36a4'
  384. dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash)
  385. self.vm.set_console()
  386. kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
  387. 'console=ttyS0,115200n8 '
  388. 'earlycon=uart8250,mmio32,0xf0001000')
  389. self.vm.add_args('-kernel', kernel_path,
  390. '-initrd', initrd_path,
  391. '-dtb', dtb_path,
  392. '-append', kernel_command_line)
  393. self.vm.launch()
  394. self.wait_for_console_pattern('Booting Linux on physical CPU 0x0')
  395. self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0')
  396. self.wait_for_console_pattern(
  397. 'Give root password for system maintenance')
  398. def test_arm_ast2600_debian(self):
  399. """
  400. :avocado: tags=arch:arm
  401. :avocado: tags=machine:rainier-bmc
  402. """
  403. deb_url = ('http://snapshot.debian.org/archive/debian/'
  404. '20220606T211338Z/'
  405. 'pool/main/l/linux/'
  406. 'linux-image-5.17.0-2-armmp_5.17.6-1%2Bb1_armhf.deb')
  407. deb_hash = '8acb2b4439faedc2f3ed4bdb2847ad4f6e0491f73debaeb7f660c8abe4dcdc0e'
  408. deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash,
  409. algorithm='sha256')
  410. kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinuz-5.17.0-2-armmp')
  411. dtb_path = self.extract_from_deb(deb_path,
  412. '/usr/lib/linux-image-5.17.0-2-armmp/aspeed-bmc-ibm-rainier.dtb')
  413. self.vm.set_console()
  414. self.vm.add_args('-kernel', kernel_path,
  415. '-dtb', dtb_path,
  416. '-net', 'nic')
  417. self.vm.launch()
  418. self.wait_for_console_pattern("Booting Linux on physical CPU 0xf00")
  419. self.wait_for_console_pattern("SMP: Total of 2 processors activated")
  420. self.wait_for_console_pattern("No filesystem could mount root")