]>
Commit | Line | Data |
---|---|---|
a65627a8 TL |
1 | #!/usr/bin/env python3 |
2 | # | |
3 | # Copyright 2019-2021 Canonical Ltd. | |
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 | ||
20 | import enum | |
21 | import pexpect | |
22 | import subprocess | |
23 | import sys | |
24 | import unittest | |
25 | ||
26 | from UEFI.Filesystems import GrubShellBootableIsoImage | |
27 | from UEFI.Qemu import QemuEfiMachine, QemuEfiVariant, QemuEfiFlashSize | |
28 | from UEFI import Qemu | |
29 | ||
30 | DPKG_ARCH = subprocess.check_output( | |
31 | ['dpkg', '--print-architecture'] | |
32 | ).decode().rstrip() | |
33 | ||
34 | ||
35 | class BootToShellTest(unittest.TestCase): | |
36 | debug = True | |
37 | ||
38 | def run_cmd_check_shell(self, cmd): | |
39 | child = pexpect.spawn(' '.join(cmd)) | |
40 | ||
41 | if self.debug: | |
42 | child.logfile = sys.stdout.buffer | |
43 | try: | |
44 | while True: | |
45 | i = child.expect( | |
46 | [ | |
47 | 'Press .* or any other key to continue', | |
48 | 'Shell> ' | |
49 | ], | |
50 | timeout=60, | |
51 | ) | |
52 | if i == 0: | |
53 | child.sendline('\x1b') | |
54 | continue | |
55 | if i == 1: | |
56 | child.sendline('reset -s\r') | |
57 | continue | |
58 | except pexpect.EOF: | |
59 | return | |
60 | except pexpect.TIMEOUT as err: | |
61 | self.fail("%s\n" % (err)) | |
62 | ||
63 | def run_cmd_check_secure_boot(self, cmd, efiarch, should_verify): | |
64 | class State(enum.Enum): | |
65 | PRE_EXEC = 1 | |
66 | POST_EXEC = 2 | |
67 | ||
68 | child = pexpect.spawn(' '.join(cmd)) | |
69 | ||
70 | if self.debug: | |
71 | child.logfile = sys.stdout.buffer | |
72 | try: | |
73 | state = State.PRE_EXEC | |
74 | while True: | |
75 | i = child.expect( | |
76 | [ | |
77 | 'Press .* or any other key to continue', | |
78 | 'Shell> ', | |
79 | "FS0:\\\\> ", | |
80 | 'grub> ', | |
81 | 'Command Error Status: Access Denied', | |
82 | ], | |
83 | timeout=60, | |
84 | ) | |
85 | if i == 0: | |
86 | child.sendline('\x1b') | |
87 | continue | |
88 | if i == 1: | |
89 | child.sendline('fs0:\r') | |
90 | continue | |
91 | if i == 2: | |
92 | if state == State.PRE_EXEC: | |
93 | child.sendline(f'\\efi\\boot\\boot{efiarch}.efi\r') | |
94 | state = State.POST_EXEC | |
95 | elif state == State.POST_EXEC: | |
96 | child.sendline('reset -s\r') | |
97 | continue | |
98 | if i == 3: | |
99 | child.sendline('halt\r') | |
100 | verified = True | |
101 | continue | |
102 | if i == 4: | |
103 | verified = False | |
104 | continue | |
105 | except pexpect.TIMEOUT as err: | |
106 | self.fail("%s\n" % (err)) | |
107 | except pexpect.EOF: | |
108 | pass | |
109 | self.assertEqual(should_verify, verified) | |
110 | ||
111 | def test_aavmf(self): | |
112 | q = Qemu.QemuCommand(QemuEfiMachine.AAVMF) | |
113 | self.run_cmd_check_shell(q.command) | |
114 | ||
115 | @unittest.skipUnless(DPKG_ARCH == 'arm64', "Requires grub-efi-arm64") | |
116 | def test_aavmf_ms_secure_boot_signed(self): | |
117 | q = Qemu.QemuCommand( | |
118 | QemuEfiMachine.AAVMF, | |
119 | variant=QemuEfiVariant.MS, | |
120 | ) | |
121 | iso = GrubShellBootableIsoImage('AA64', use_signed=True) | |
122 | q.add_disk(iso.path) | |
123 | self.run_cmd_check_secure_boot(q.command, 'aa64', True) | |
124 | ||
125 | @unittest.skipUnless(DPKG_ARCH == 'arm64', "Requires grub-efi-arm64") | |
126 | def test_aavmf_ms_secure_boot_unsigned(self): | |
127 | q = Qemu.QemuCommand( | |
128 | QemuEfiMachine.AAVMF, | |
129 | variant=QemuEfiVariant.MS, | |
130 | ) | |
131 | iso = GrubShellBootableIsoImage('AA64', use_signed=False) | |
132 | q.add_disk(iso.path) | |
133 | self.run_cmd_check_secure_boot(q.command, 'aa64', False) | |
134 | ||
135 | def test_aavmf_snakeoil(self): | |
136 | q = Qemu.QemuCommand( | |
137 | QemuEfiMachine.AAVMF, | |
138 | variant=QemuEfiVariant.SNAKEOIL, | |
139 | ) | |
140 | self.run_cmd_check_shell(q.command) | |
141 | ||
142 | def test_aavmf32(self): | |
143 | q = Qemu.QemuCommand(QemuEfiMachine.AAVMF32) | |
144 | self.run_cmd_check_shell(q.command) | |
145 | ||
146 | def test_ovmf_pc(self): | |
147 | q = Qemu.QemuCommand( | |
148 | QemuEfiMachine.OVMF_PC, flash_size=QemuEfiFlashSize.SIZE_2MB, | |
149 | ) | |
150 | self.run_cmd_check_shell(q.command) | |
151 | ||
152 | def test_ovmf_q35(self): | |
153 | q = Qemu.QemuCommand( | |
154 | QemuEfiMachine.OVMF_Q35, flash_size=QemuEfiFlashSize.SIZE_2MB, | |
155 | ) | |
156 | self.run_cmd_check_shell(q.command) | |
157 | ||
158 | def test_ovmf_secboot(self): | |
159 | q = Qemu.QemuCommand( | |
160 | QemuEfiMachine.OVMF_Q35, | |
161 | variant=QemuEfiVariant.SECBOOT, | |
162 | flash_size=QemuEfiFlashSize.SIZE_2MB, | |
163 | ) | |
164 | self.run_cmd_check_shell(q.command) | |
165 | ||
166 | def test_ovmf_ms(self): | |
167 | q = Qemu.QemuCommand( | |
168 | QemuEfiMachine.OVMF_Q35, | |
169 | variant=QemuEfiVariant.MS, | |
170 | flash_size=QemuEfiFlashSize.SIZE_2MB, | |
171 | ) | |
172 | self.run_cmd_check_shell(q.command) | |
173 | ||
174 | @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only") | |
175 | def test_ovmf_ms_secure_boot_signed(self): | |
176 | q = Qemu.QemuCommand( | |
177 | QemuEfiMachine.OVMF_Q35, | |
178 | variant=QemuEfiVariant.MS, | |
179 | flash_size=QemuEfiFlashSize.SIZE_2MB, | |
180 | ) | |
181 | iso = GrubShellBootableIsoImage('X64', use_signed=True) | |
182 | q.add_disk(iso.path) | |
183 | self.run_cmd_check_secure_boot(q.command, 'x64', True) | |
184 | ||
185 | @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only") | |
186 | def test_ovmf_ms_secure_boot_unsigned(self): | |
187 | q = Qemu.QemuCommand( | |
188 | QemuEfiMachine.OVMF_Q35, | |
189 | variant=QemuEfiVariant.MS, | |
190 | flash_size=QemuEfiFlashSize.SIZE_2MB, | |
191 | ) | |
192 | iso = GrubShellBootableIsoImage('X64', use_signed=False) | |
193 | q.add_disk(iso.path) | |
194 | self.run_cmd_check_secure_boot(q.command, 'x64', False) | |
195 | ||
196 | def test_ovmf_4m(self): | |
197 | q = Qemu.QemuCommand( | |
198 | QemuEfiMachine.OVMF_Q35, | |
199 | flash_size=QemuEfiFlashSize.SIZE_4MB, | |
200 | ) | |
201 | self.run_cmd_check_shell(q.command) | |
202 | ||
203 | def test_ovmf_4m_secboot(self): | |
204 | q = Qemu.QemuCommand( | |
205 | QemuEfiMachine.OVMF_Q35, | |
206 | variant=QemuEfiVariant.SECBOOT, | |
207 | flash_size=QemuEfiFlashSize.SIZE_4MB, | |
208 | ) | |
209 | self.run_cmd_check_shell(q.command) | |
210 | ||
211 | def test_ovmf_4m_ms(self): | |
212 | q = Qemu.QemuCommand( | |
213 | QemuEfiMachine.OVMF_Q35, | |
214 | variant=QemuEfiVariant.MS, | |
215 | flash_size=QemuEfiFlashSize.SIZE_4MB, | |
216 | ) | |
217 | self.run_cmd_check_shell(q.command) | |
218 | ||
219 | def test_ovmf_snakeoil(self): | |
220 | q = Qemu.QemuCommand( | |
221 | QemuEfiMachine.OVMF_Q35, | |
222 | variant=QemuEfiVariant.SNAKEOIL, | |
223 | ) | |
224 | self.run_cmd_check_shell(q.command) | |
225 | ||
226 | @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only") | |
227 | def test_ovmf_4m_ms_secure_boot_signed(self): | |
228 | q = Qemu.QemuCommand( | |
229 | QemuEfiMachine.OVMF_Q35, | |
230 | variant=QemuEfiVariant.MS, | |
231 | flash_size=QemuEfiFlashSize.SIZE_4MB, | |
232 | ) | |
233 | iso = GrubShellBootableIsoImage('X64', use_signed=True) | |
234 | q.add_disk(iso.path) | |
235 | self.run_cmd_check_secure_boot(q.command, 'x64', True) | |
236 | ||
237 | @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only") | |
238 | def test_ovmf_4m_ms_secure_boot_unsigned(self): | |
239 | q = Qemu.QemuCommand( | |
240 | QemuEfiMachine.OVMF_Q35, | |
241 | variant=QemuEfiVariant.MS, | |
242 | flash_size=QemuEfiFlashSize.SIZE_4MB, | |
243 | ) | |
244 | iso = GrubShellBootableIsoImage('X64', use_signed=False) | |
245 | q.add_disk(iso.path) | |
246 | self.run_cmd_check_secure_boot(q.command, 'x64', False) | |
247 | ||
248 | def test_ovmf32_4m_secboot(self): | |
249 | q = Qemu.QemuCommand( | |
250 | QemuEfiMachine.OVMF32, | |
251 | variant=QemuEfiVariant.SECBOOT, | |
252 | flash_size=QemuEfiFlashSize.SIZE_4MB, | |
253 | ) | |
254 | self.run_cmd_check_shell(q.command) | |
255 | ||
256 | ||
257 | if __name__ == '__main__': | |
258 | unittest.main(verbosity=2) |