3 # Copyright 2019-2021 Canonical Ltd.
5 # - dann frazier <dann.frazier@canonical.com>
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.
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.
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/>.
26 from UEFI
.Filesystems
import GrubShellBootableIsoImage
27 from UEFI
.Qemu
import QemuEfiMachine
, QemuEfiVariant
, QemuEfiFlashSize
30 DPKG_ARCH
= subprocess
.check_output(
31 ['dpkg', '--print-architecture']
35 class BootToShellTest(unittest
.TestCase
):
38 def run_cmd_check_shell(self
, cmd
):
39 child
= pexpect
.spawn(' '.join(cmd
))
42 child
.logfile
= sys
.stdout
.buffer
47 'Press .* or any other key to continue',
53 child
.sendline('\x1b')
56 child
.sendline('reset -s\r')
60 except pexpect
.TIMEOUT
as err
:
61 self
.fail("%s\n" % (err
))
63 def run_cmd_check_secure_boot(self
, cmd
, efiarch
, should_verify
):
64 class State(enum
.Enum
):
68 child
= pexpect
.spawn(' '.join(cmd
))
71 child
.logfile
= sys
.stdout
.buffer
73 state
= State
.PRE_EXEC
77 'Press .* or any other key to continue',
81 'Command Error Status: Access Denied',
86 child
.sendline('\x1b')
89 child
.sendline('fs0:\r')
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')
99 child
.sendline('halt\r')
105 except pexpect
.TIMEOUT
as err
:
106 self
.fail("%s\n" % (err
))
109 self
.assertEqual(should_verify
, verified
)
111 def test_aavmf(self
):
112 q
= Qemu
.QemuCommand(QemuEfiMachine
.AAVMF
)
113 self
.run_cmd_check_shell(q
.command
)
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
,
121 iso
= GrubShellBootableIsoImage('AA64', use_signed
=True)
123 self
.run_cmd_check_secure_boot(q
.command
, 'aa64', True)
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
,
131 iso
= GrubShellBootableIsoImage('AA64', use_signed
=False)
133 self
.run_cmd_check_secure_boot(q
.command
, 'aa64', False)
135 def test_aavmf_snakeoil(self
):
136 q
= Qemu
.QemuCommand(
137 QemuEfiMachine
.AAVMF
,
138 variant
=QemuEfiVariant
.SNAKEOIL
,
140 self
.run_cmd_check_shell(q
.command
)
142 def test_aavmf32(self
):
143 q
= Qemu
.QemuCommand(QemuEfiMachine
.AAVMF32
)
144 self
.run_cmd_check_shell(q
.command
)
146 def test_ovmf_pc(self
):
147 q
= Qemu
.QemuCommand(
148 QemuEfiMachine
.OVMF_PC
, flash_size
=QemuEfiFlashSize
.SIZE_2MB
,
150 self
.run_cmd_check_shell(q
.command
)
152 def test_ovmf_q35(self
):
153 q
= Qemu
.QemuCommand(
154 QemuEfiMachine
.OVMF_Q35
, flash_size
=QemuEfiFlashSize
.SIZE_2MB
,
156 self
.run_cmd_check_shell(q
.command
)
158 def test_ovmf_secboot(self
):
159 q
= Qemu
.QemuCommand(
160 QemuEfiMachine
.OVMF_Q35
,
161 variant
=QemuEfiVariant
.SECBOOT
,
162 flash_size
=QemuEfiFlashSize
.SIZE_2MB
,
164 self
.run_cmd_check_shell(q
.command
)
166 def test_ovmf_ms(self
):
167 q
= Qemu
.QemuCommand(
168 QemuEfiMachine
.OVMF_Q35
,
169 variant
=QemuEfiVariant
.MS
,
170 flash_size
=QemuEfiFlashSize
.SIZE_2MB
,
172 self
.run_cmd_check_shell(q
.command
)
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
,
181 iso
= GrubShellBootableIsoImage('X64', use_signed
=True)
183 self
.run_cmd_check_secure_boot(q
.command
, 'x64', True)
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
,
192 iso
= GrubShellBootableIsoImage('X64', use_signed
=False)
194 self
.run_cmd_check_secure_boot(q
.command
, 'x64', False)
196 def test_ovmf_4m(self
):
197 q
= Qemu
.QemuCommand(
198 QemuEfiMachine
.OVMF_Q35
,
199 flash_size
=QemuEfiFlashSize
.SIZE_4MB
,
201 self
.run_cmd_check_shell(q
.command
)
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
,
209 self
.run_cmd_check_shell(q
.command
)
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
,
217 self
.run_cmd_check_shell(q
.command
)
219 def test_ovmf_snakeoil(self
):
220 q
= Qemu
.QemuCommand(
221 QemuEfiMachine
.OVMF_Q35
,
222 variant
=QemuEfiVariant
.SNAKEOIL
,
224 self
.run_cmd_check_shell(q
.command
)
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
,
233 iso
= GrubShellBootableIsoImage('X64', use_signed
=True)
235 self
.run_cmd_check_secure_boot(q
.command
, 'x64', True)
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
,
244 iso
= GrubShellBootableIsoImage('X64', use_signed
=False)
246 self
.run_cmd_check_secure_boot(q
.command
, 'x64', False)
248 def test_ovmf32_4m_secboot(self
):
249 q
= Qemu
.QemuCommand(
250 QemuEfiMachine
.OVMF32
,
251 variant
=QemuEfiVariant
.SECBOOT
,
252 flash_size
=QemuEfiFlashSize
.SIZE_4MB
,
254 self
.run_cmd_check_shell(q
.command
)
257 if __name__
== '__main__':
258 unittest
.main(verbosity
=2)