]> git.proxmox.com Git - pve-edk2-firmware.git/blame - debian/tests/shell.py
bump version to 4.2023.08-4
[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
a65627a8
TL
204 def test_ovmf_4m(self):
205 q = Qemu.QemuCommand(
206 QemuEfiMachine.OVMF_Q35,
207 flash_size=QemuEfiFlashSize.SIZE_4MB,
208 )
209 self.run_cmd_check_shell(q.command)
210
211 def test_ovmf_4m_secboot(self):
212 q = Qemu.QemuCommand(
213 QemuEfiMachine.OVMF_Q35,
214 variant=QemuEfiVariant.SECBOOT,
215 flash_size=QemuEfiFlashSize.SIZE_4MB,
216 )
217 self.run_cmd_check_shell(q.command)
218
219 def test_ovmf_4m_ms(self):
220 q = Qemu.QemuCommand(
221 QemuEfiMachine.OVMF_Q35,
222 variant=QemuEfiVariant.MS,
223 flash_size=QemuEfiFlashSize.SIZE_4MB,
224 )
225 self.run_cmd_check_shell(q.command)
226
227 def test_ovmf_snakeoil(self):
228 q = Qemu.QemuCommand(
229 QemuEfiMachine.OVMF_Q35,
230 variant=QemuEfiVariant.SNAKEOIL,
231 )
232 self.run_cmd_check_shell(q.command)
233
234 @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
235 def test_ovmf_4m_ms_secure_boot_signed(self):
236 q = Qemu.QemuCommand(
237 QemuEfiMachine.OVMF_Q35,
238 variant=QemuEfiVariant.MS,
239 flash_size=QemuEfiFlashSize.SIZE_4MB,
240 )
d7274593
TL
241 grub = get_local_grub_path('X64', signed=True)
242 shim = get_local_shim_path('X64', signed=True)
243 iso = GrubShellBootableIsoImage('X64', shim, grub)
a65627a8
TL
244 q.add_disk(iso.path)
245 self.run_cmd_check_secure_boot(q.command, 'x64', True)
246
247 @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
248 def test_ovmf_4m_ms_secure_boot_unsigned(self):
249 q = Qemu.QemuCommand(
250 QemuEfiMachine.OVMF_Q35,
251 variant=QemuEfiVariant.MS,
252 flash_size=QemuEfiFlashSize.SIZE_4MB,
253 )
d7274593
TL
254 grub = get_local_grub_path('X64', signed=False)
255 shim = get_local_shim_path('X64', signed=False)
256 iso = GrubShellBootableIsoImage('X64', shim, grub)
257 q.add_disk(iso.path)
258 self.run_cmd_check_secure_boot(q.command, 'x64', False)
259
260 @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
261 def test_ovmf_snakeoil_secure_boot_signed(self):
262 q = Qemu.QemuCommand(
263 QemuEfiMachine.OVMF_Q35,
264 variant=QemuEfiVariant.SNAKEOIL,
265 )
266 shim = SignedBinary(
267 get_local_shim_path('X64', signed=False),
268 "/usr/share/ovmf/PkKek-1-snakeoil.key",
269 "/usr/share/ovmf/PkKek-1-snakeoil.pem",
270 "snakeoil",
271 )
272 grub = SignedBinary(
273 get_local_grub_path('X64', signed=False),
274 "/usr/share/ovmf/PkKek-1-snakeoil.key",
275 "/usr/share/ovmf/PkKek-1-snakeoil.pem",
276 "snakeoil",
277 )
278 iso = GrubShellBootableIsoImage('X64', shim.path, grub.path)
279 q.add_disk(iso.path)
280 self.run_cmd_check_secure_boot(q.command, 'x64', True)
281
282 @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
283 def test_ovmf_snakeoil_secure_boot_unsigned(self):
284 q = Qemu.QemuCommand(
285 QemuEfiMachine.OVMF_Q35,
286 variant=QemuEfiVariant.SNAKEOIL,
287 flash_size=QemuEfiFlashSize.DEFAULT,
288 )
289 grub = get_local_grub_path('X64', signed=False)
290 shim = get_local_shim_path('X64', signed=False)
291 iso = GrubShellBootableIsoImage('X64', shim, grub)
a65627a8
TL
292 q.add_disk(iso.path)
293 self.run_cmd_check_secure_boot(q.command, 'x64', False)
294
295 def test_ovmf32_4m_secboot(self):
296 q = Qemu.QemuCommand(
297 QemuEfiMachine.OVMF32,
298 variant=QemuEfiVariant.SECBOOT,
299 flash_size=QemuEfiFlashSize.SIZE_4MB,
300 )
301 self.run_cmd_check_shell(q.command)
302
d7298091
TL
303 def test_riscv64(self):
304 q = Qemu.QemuCommand(QemuEfiMachine.RISCV64)
305 self.run_cmd_check_shell(q.command)
a65627a8
TL
306
307if __name__ == '__main__':
308 unittest.main(verbosity=2)