]> git.proxmox.com Git - pve-edk2-firmware.git/blame - debian/tests/shell.py
split out AARCH64 files into separate package
[pve-edk2-firmware.git] / debian / tests / shell.py
CommitLineData
a65627a8
TL
1#!/usr/bin/env python3
2#
d7274593 3# Copyright 2019-2022 Canonical Ltd.
a65627a8
TL
4# Authors:
5# - dann frazier <dann.frazier@canonical.com>
6#
7# This program is free software: you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 3, as published
9# by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful, but WITHOUT
12# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
13# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14# General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along with
17# this program. If not, see <http://www.gnu.org/licenses/>.
18#
19
20import enum
d7274593 21import os
a65627a8
TL
22import pexpect
23import subprocess
24import sys
d7274593 25import time
a65627a8
TL
26import unittest
27
28from UEFI.Filesystems import GrubShellBootableIsoImage
d7274593 29from UEFI.SignedBinary import SignedBinary
a65627a8
TL
30from UEFI.Qemu import QemuEfiMachine, QemuEfiVariant, QemuEfiFlashSize
31from UEFI import Qemu
32
33DPKG_ARCH = subprocess.check_output(
34 ['dpkg', '--print-architecture']
35).decode().rstrip()
36
d7274593
TL
37EfiArchToGrubArch = {
38 'X64': "x86_64",
39 'AA64': "arm64",
40}
41
42TEST_TIMEOUT = 120
43
44
45def get_local_grub_path(efi_arch, signed=False):
46 grub_subdir = "%s-efi" % EfiArchToGrubArch[efi_arch.upper()]
47 ext = "efi"
48 if signed:
49 grub_subdir = f"{grub_subdir}-signed"
50 ext = f"{ext}.signed"
51
52 grub_path = os.path.join(
53 os.path.sep, 'usr', 'lib', 'grub',
54 '%s' % (grub_subdir),
55 "" if signed else "monolithic",
56 'grub%s.%s' % (efi_arch.lower(), ext)
57 )
58 return grub_path
59
60
61def get_local_shim_path(efi_arch, signed=False):
62 ext = 'efi'
63 if signed:
64 ext = f"{ext}.signed"
65 shim_path = os.path.join(
66 os.path.sep, 'usr', 'lib', 'shim',
67 'shim%s.%s' % (efi_arch.lower(), ext)
68 )
69 return shim_path
70
a65627a8
TL
71
72class BootToShellTest(unittest.TestCase):
73 debug = True
74
d7274593
TL
75 def setUp(self):
76 self.startTime = time.time()
77
78 def tearDown(self):
79 t = time.time() - self.startTime
80 sys.stdout.write("%s runtime: %.3fs\n" % (self.id(), t))
81
a65627a8 82 def run_cmd_check_shell(self, cmd):
d7274593 83 child = pexpect.spawn(' '.join(cmd), encoding='UTF-8')
a65627a8
TL
84
85 if self.debug:
d7274593 86 child.logfile = sys.stdout
a65627a8
TL
87 try:
88 while True:
89 i = child.expect(
90 [
91 'Press .* or any other key to continue',
92 'Shell> '
93 ],
d7274593 94 timeout=TEST_TIMEOUT,
a65627a8
TL
95 )
96 if i == 0:
97 child.sendline('\x1b')
98 continue
99 if i == 1:
100 child.sendline('reset -s\r')
101 continue
102 except pexpect.EOF:
d7274593
TL
103 child.close()
104 if child.exitstatus != 0:
105 self.fail("ERROR: exit code %d\n" % (child.exitstatus))
a65627a8
TL
106 except pexpect.TIMEOUT as err:
107 self.fail("%s\n" % (err))
108
109 def run_cmd_check_secure_boot(self, cmd, efiarch, should_verify):
110 class State(enum.Enum):
111 PRE_EXEC = 1
112 POST_EXEC = 2
113
d7274593 114 child = pexpect.spawn(' '.join(cmd), encoding='UTF-8')
a65627a8
TL
115
116 if self.debug:
d7274593 117 child.logfile = sys.stdout
a65627a8
TL
118 try:
119 state = State.PRE_EXEC
120 while True:
121 i = child.expect(
122 [
123 'Press .* or any other key to continue',
124 'Shell> ',
125 "FS0:\\\\> ",
126 'grub> ',
127 'Command Error Status: Access Denied',
128 ],
d7274593 129 timeout=TEST_TIMEOUT,
a65627a8
TL
130 )
131 if i == 0:
132 child.sendline('\x1b')
133 continue
134 if i == 1:
135 child.sendline('fs0:\r')
136 continue
137 if i == 2:
138 if state == State.PRE_EXEC:
139 child.sendline(f'\\efi\\boot\\boot{efiarch}.efi\r')
140 state = State.POST_EXEC
141 elif state == State.POST_EXEC:
142 child.sendline('reset -s\r')
143 continue
144 if i == 3:
145 child.sendline('halt\r')
146 verified = True
147 continue
148 if i == 4:
149 verified = False
150 continue
d7274593
TL
151 except pexpect.EOF:
152 child.close()
153 if child.exitstatus != 0:
154 self.fail("ERROR: exit code %d\n" % (child.exitstatus))
a65627a8
TL
155 except pexpect.TIMEOUT as err:
156 self.fail("%s\n" % (err))
a65627a8
TL
157 self.assertEqual(should_verify, verified)
158
159 def test_aavmf(self):
160 q = Qemu.QemuCommand(QemuEfiMachine.AAVMF)
161 self.run_cmd_check_shell(q.command)
162
163 @unittest.skipUnless(DPKG_ARCH == 'arm64', "Requires grub-efi-arm64")
d7274593
TL
164 @unittest.skipUnless(
165 subprocess.run(
166 ['dpkg-vendor', '--derives-from', 'Ubuntu']
167 ).returncode == 0,
168 "Debian does not provide a signed shim for arm64, see #992073"
169 )
a65627a8
TL
170 def test_aavmf_ms_secure_boot_signed(self):
171 q = Qemu.QemuCommand(
172 QemuEfiMachine.AAVMF,
173 variant=QemuEfiVariant.MS,
174 )
d7274593
TL
175 grub = get_local_grub_path('AA64', signed=True)
176 shim = get_local_shim_path('AA64', signed=True)
177 iso = GrubShellBootableIsoImage('AA64', shim, grub)
a65627a8
TL
178 q.add_disk(iso.path)
179 self.run_cmd_check_secure_boot(q.command, 'aa64', True)
180
181 @unittest.skipUnless(DPKG_ARCH == 'arm64', "Requires grub-efi-arm64")
182 def test_aavmf_ms_secure_boot_unsigned(self):
183 q = Qemu.QemuCommand(
184 QemuEfiMachine.AAVMF,
185 variant=QemuEfiVariant.MS,
186 )
d7274593
TL
187 grub = get_local_grub_path('AA64', signed=False)
188 shim = get_local_shim_path('AA64', signed=False)
189 iso = GrubShellBootableIsoImage('AA64', shim, grub)
a65627a8
TL
190 q.add_disk(iso.path)
191 self.run_cmd_check_secure_boot(q.command, 'aa64', False)
192
193 def test_aavmf_snakeoil(self):
194 q = Qemu.QemuCommand(
195 QemuEfiMachine.AAVMF,
196 variant=QemuEfiVariant.SNAKEOIL,
197 )
198 self.run_cmd_check_shell(q.command)
199
200 def test_aavmf32(self):
201 q = Qemu.QemuCommand(QemuEfiMachine.AAVMF32)
202 self.run_cmd_check_shell(q.command)
203
204 def test_ovmf_pc(self):
205 q = Qemu.QemuCommand(
206 QemuEfiMachine.OVMF_PC, flash_size=QemuEfiFlashSize.SIZE_2MB,
207 )
208 self.run_cmd_check_shell(q.command)
209
210 def test_ovmf_q35(self):
211 q = Qemu.QemuCommand(
212 QemuEfiMachine.OVMF_Q35, flash_size=QemuEfiFlashSize.SIZE_2MB,
213 )
214 self.run_cmd_check_shell(q.command)
215
216 def test_ovmf_secboot(self):
217 q = Qemu.QemuCommand(
218 QemuEfiMachine.OVMF_Q35,
219 variant=QemuEfiVariant.SECBOOT,
220 flash_size=QemuEfiFlashSize.SIZE_2MB,
221 )
222 self.run_cmd_check_shell(q.command)
223
224 def test_ovmf_ms(self):
225 q = Qemu.QemuCommand(
226 QemuEfiMachine.OVMF_Q35,
227 variant=QemuEfiVariant.MS,
228 flash_size=QemuEfiFlashSize.SIZE_2MB,
229 )
230 self.run_cmd_check_shell(q.command)
231
232 @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
233 def test_ovmf_ms_secure_boot_signed(self):
234 q = Qemu.QemuCommand(
235 QemuEfiMachine.OVMF_Q35,
236 variant=QemuEfiVariant.MS,
237 flash_size=QemuEfiFlashSize.SIZE_2MB,
238 )
d7274593
TL
239 grub = get_local_grub_path('X64', signed=True)
240 shim = get_local_shim_path('X64', signed=True)
241 iso = GrubShellBootableIsoImage('X64', shim, grub)
a65627a8
TL
242 q.add_disk(iso.path)
243 self.run_cmd_check_secure_boot(q.command, 'x64', True)
244
245 @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
246 def test_ovmf_ms_secure_boot_unsigned(self):
247 q = Qemu.QemuCommand(
248 QemuEfiMachine.OVMF_Q35,
249 variant=QemuEfiVariant.MS,
250 flash_size=QemuEfiFlashSize.SIZE_2MB,
251 )
d7274593
TL
252 grub = get_local_grub_path('X64', signed=False)
253 shim = get_local_shim_path('X64', signed=False)
254 iso = GrubShellBootableIsoImage('X64', shim, grub)
a65627a8
TL
255 q.add_disk(iso.path)
256 self.run_cmd_check_secure_boot(q.command, 'x64', False)
257
258 def test_ovmf_4m(self):
259 q = Qemu.QemuCommand(
260 QemuEfiMachine.OVMF_Q35,
261 flash_size=QemuEfiFlashSize.SIZE_4MB,
262 )
263 self.run_cmd_check_shell(q.command)
264
265 def test_ovmf_4m_secboot(self):
266 q = Qemu.QemuCommand(
267 QemuEfiMachine.OVMF_Q35,
268 variant=QemuEfiVariant.SECBOOT,
269 flash_size=QemuEfiFlashSize.SIZE_4MB,
270 )
271 self.run_cmd_check_shell(q.command)
272
273 def test_ovmf_4m_ms(self):
274 q = Qemu.QemuCommand(
275 QemuEfiMachine.OVMF_Q35,
276 variant=QemuEfiVariant.MS,
277 flash_size=QemuEfiFlashSize.SIZE_4MB,
278 )
279 self.run_cmd_check_shell(q.command)
280
281 def test_ovmf_snakeoil(self):
282 q = Qemu.QemuCommand(
283 QemuEfiMachine.OVMF_Q35,
284 variant=QemuEfiVariant.SNAKEOIL,
285 )
286 self.run_cmd_check_shell(q.command)
287
288 @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
289 def test_ovmf_4m_ms_secure_boot_signed(self):
290 q = Qemu.QemuCommand(
291 QemuEfiMachine.OVMF_Q35,
292 variant=QemuEfiVariant.MS,
293 flash_size=QemuEfiFlashSize.SIZE_4MB,
294 )
d7274593
TL
295 grub = get_local_grub_path('X64', signed=True)
296 shim = get_local_shim_path('X64', signed=True)
297 iso = GrubShellBootableIsoImage('X64', shim, grub)
a65627a8
TL
298 q.add_disk(iso.path)
299 self.run_cmd_check_secure_boot(q.command, 'x64', True)
300
301 @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
302 def test_ovmf_4m_ms_secure_boot_unsigned(self):
303 q = Qemu.QemuCommand(
304 QemuEfiMachine.OVMF_Q35,
305 variant=QemuEfiVariant.MS,
306 flash_size=QemuEfiFlashSize.SIZE_4MB,
307 )
d7274593
TL
308 grub = get_local_grub_path('X64', signed=False)
309 shim = get_local_shim_path('X64', signed=False)
310 iso = GrubShellBootableIsoImage('X64', shim, grub)
311 q.add_disk(iso.path)
312 self.run_cmd_check_secure_boot(q.command, 'x64', False)
313
314 @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
315 def test_ovmf_snakeoil_secure_boot_signed(self):
316 q = Qemu.QemuCommand(
317 QemuEfiMachine.OVMF_Q35,
318 variant=QemuEfiVariant.SNAKEOIL,
319 )
320 shim = SignedBinary(
321 get_local_shim_path('X64', signed=False),
322 "/usr/share/ovmf/PkKek-1-snakeoil.key",
323 "/usr/share/ovmf/PkKek-1-snakeoil.pem",
324 "snakeoil",
325 )
326 grub = SignedBinary(
327 get_local_grub_path('X64', signed=False),
328 "/usr/share/ovmf/PkKek-1-snakeoil.key",
329 "/usr/share/ovmf/PkKek-1-snakeoil.pem",
330 "snakeoil",
331 )
332 iso = GrubShellBootableIsoImage('X64', shim.path, grub.path)
333 q.add_disk(iso.path)
334 self.run_cmd_check_secure_boot(q.command, 'x64', True)
335
336 @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
337 def test_ovmf_snakeoil_secure_boot_unsigned(self):
338 q = Qemu.QemuCommand(
339 QemuEfiMachine.OVMF_Q35,
340 variant=QemuEfiVariant.SNAKEOIL,
341 flash_size=QemuEfiFlashSize.DEFAULT,
342 )
343 grub = get_local_grub_path('X64', signed=False)
344 shim = get_local_shim_path('X64', signed=False)
345 iso = GrubShellBootableIsoImage('X64', shim, grub)
a65627a8
TL
346 q.add_disk(iso.path)
347 self.run_cmd_check_secure_boot(q.command, 'x64', False)
348
349 def test_ovmf32_4m_secboot(self):
350 q = Qemu.QemuCommand(
351 QemuEfiMachine.OVMF32,
352 variant=QemuEfiVariant.SECBOOT,
353 flash_size=QemuEfiFlashSize.SIZE_4MB,
354 )
355 self.run_cmd_check_shell(q.command)
356
357
358if __name__ == '__main__':
359 unittest.main(verbosity=2)