]>
Commit | Line | Data |
---|---|---|
a65627a8 TL |
1 | # |
2 | # Copyright 2019-2021 Canonical Ltd. | |
3 | # Authors: | |
4 | # - dann frazier <dann.frazier@canonical.com> | |
5 | # | |
6 | # This program is free software: you can redistribute it and/or modify it | |
7 | # under the terms of the GNU General Public License version 3, as published | |
8 | # by the Free Software Foundation. | |
9 | # | |
10 | # This program is distributed in the hope that it will be useful, but WITHOUT | |
11 | # ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, | |
12 | # SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | # General Public License for more details. | |
14 | # | |
15 | # You should have received a copy of the GNU General Public License along with | |
16 | # this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | # | |
18 | ||
19 | import enum | |
20 | import os | |
21 | import shutil | |
22 | import tempfile | |
23 | ||
24 | ||
25 | class QemuEfiMachine(enum.Enum): | |
26 | OVMF_PC = enum.auto() | |
27 | OVMF_Q35 = enum.auto() | |
28 | OVMF32 = enum.auto() | |
29 | AAVMF = enum.auto() | |
30 | AAVMF32 = enum.auto() | |
d7298091 | 31 | RISCV64 = enum.auto() |
a65627a8 TL |
32 | |
33 | ||
34 | class QemuEfiVariant(enum.Enum): | |
35 | MS = enum.auto() | |
36 | SECBOOT = enum.auto() | |
37 | SNAKEOIL = enum.auto() | |
38 | ||
39 | ||
40 | class QemuEfiFlashSize(enum.Enum): | |
41 | DEFAULT = enum.auto | |
42 | SIZE_2MB = enum.auto() | |
43 | SIZE_4MB = enum.auto() | |
44 | ||
45 | ||
46 | class QemuCommand: | |
a65627a8 TL |
47 | Qemu_Common_Params = [ |
48 | '-no-user-config', '-nodefaults', | |
49 | '-m', '256', | |
d7274593 | 50 | '-smp', '1,sockets=1,cores=1,threads=1', |
a65627a8 TL |
51 | '-display', 'none', |
52 | '-serial', 'stdio', | |
53 | ] | |
54 | Ovmf_Common_Params = Qemu_Common_Params + [ | |
55 | '-chardev', 'pty,id=charserial1', | |
56 | '-device', 'isa-serial,chardev=charserial1,id=serial1', | |
57 | ] | |
58 | Aavmf_Common_Params = Qemu_Common_Params + [ | |
59 | '-machine', 'virt', '-device', 'virtio-serial-device', | |
60 | ] | |
d7298091 TL |
61 | RiscV_Common_Params = Qemu_Common_Params + [ |
62 | '-machine', 'virt', '-device', 'virtio-serial-device', | |
63 | ] | |
a65627a8 TL |
64 | Machine_Base_Command = { |
65 | QemuEfiMachine.AAVMF: [ | |
66 | 'qemu-system-aarch64', '-cpu', 'cortex-a57', | |
67 | ] + Aavmf_Common_Params, | |
68 | QemuEfiMachine.AAVMF32: [ | |
69 | 'qemu-system-aarch64', '-cpu', 'cortex-a15', | |
70 | ] + Aavmf_Common_Params, | |
71 | QemuEfiMachine.OVMF_PC: [ | |
72 | 'qemu-system-x86_64', '-machine', 'pc,accel=tcg', | |
73 | ] + Ovmf_Common_Params, | |
74 | QemuEfiMachine.OVMF_Q35: [ | |
75 | 'qemu-system-x86_64', '-machine', 'q35,accel=tcg', | |
76 | ] + Ovmf_Common_Params, | |
77 | QemuEfiMachine.OVMF32: [ | |
78 | 'qemu-system-i386', '-machine', 'q35,accel=tcg', | |
79 | ] + Ovmf_Common_Params, | |
d7298091 TL |
80 | QemuEfiMachine.RISCV64: [ |
81 | 'qemu-system-riscv64', | |
82 | ] + RiscV_Common_Params, | |
a65627a8 TL |
83 | } |
84 | ||
85 | def _get_default_flash_paths(self, machine, variant, flash_size): | |
86 | assert(machine in QemuEfiMachine) | |
87 | assert(variant is None or variant in QemuEfiVariant) | |
88 | assert(flash_size in QemuEfiFlashSize) | |
89 | ||
90 | code_ext = vars_ext = '' | |
91 | if variant == QemuEfiVariant.MS: | |
92 | code_ext = vars_ext = '.ms' | |
93 | elif variant == QemuEfiVariant.SECBOOT: | |
94 | code_ext = '.secboot' | |
95 | elif variant == QemuEfiVariant.SNAKEOIL: | |
96 | vars_ext = '.snakeoil' | |
97 | ||
98 | if machine == QemuEfiMachine.AAVMF: | |
99 | assert(flash_size == QemuEfiFlashSize.DEFAULT) | |
100 | return ( | |
101 | f'/usr/share/AAVMF/AAVMF_CODE{code_ext}.fd', | |
102 | f'/usr/share/AAVMF/AAVMF_VARS{code_ext}.fd', | |
103 | ) | |
104 | if machine == QemuEfiMachine.AAVMF32: | |
105 | assert(variant is None) | |
106 | assert(flash_size == QemuEfiFlashSize.DEFAULT) | |
107 | return ( | |
108 | '/usr/share/AAVMF/AAVMF32_CODE.fd', | |
109 | '/usr/share/AAVMF/AAVMF32_VARS.fd' | |
110 | ) | |
111 | if machine == QemuEfiMachine.OVMF32: | |
112 | assert(variant is None or variant in [QemuEfiVariant.SECBOOT]) | |
113 | assert( | |
114 | flash_size in [ | |
115 | QemuEfiFlashSize.DEFAULT, QemuEfiFlashSize.SIZE_4MB | |
116 | ] | |
117 | ) | |
118 | return ( | |
119 | '/usr/share/OVMF/OVMF32_CODE_4M.secboot.fd', | |
120 | '/usr/share/OVMF/OVMF32_VARS_4M.fd', | |
121 | ) | |
122 | # Remaining possibilities are OVMF variants | |
123 | if machine == QemuEfiMachine.OVMF_PC: | |
124 | assert(variant is None) | |
d7298091 TL |
125 | if machine == QemuEfiMachine.RISCV64: |
126 | assert(variant is None) | |
127 | assert(flash_size == QemuEfiFlashSize.DEFAULT) | |
128 | return ( | |
129 | '/usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd', | |
130 | '/usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd', | |
131 | ) | |
a65627a8 TL |
132 | if variant == QemuEfiVariant.SNAKEOIL: |
133 | # We provide one size - you don't get to pick. | |
134 | assert(flash_size == QemuEfiFlashSize.DEFAULT) | |
135 | size_ext = '' if flash_size == QemuEfiFlashSize.SIZE_2MB else '_4M' | |
136 | return ( | |
137 | f'/usr/share/OVMF/OVMF_CODE{size_ext}{code_ext}.fd', | |
138 | f'/usr/share/OVMF/OVMF_VARS{size_ext}{vars_ext}.fd' | |
139 | ) | |
140 | ||
141 | def __init__( | |
142 | self, machine, variant=None, | |
143 | code_path=None, vars_template_path=None, | |
144 | flash_size=QemuEfiFlashSize.DEFAULT, | |
145 | ): | |
146 | assert( | |
147 | (code_path and vars_template_path) or | |
148 | (not code_path and not vars_template_path) | |
149 | ) | |
150 | ||
151 | if not code_path: | |
152 | (code_path, vars_template_path) = self._get_default_flash_paths( | |
153 | machine, variant, flash_size) | |
154 | ||
155 | self.pflash = self.PflashParams(code_path, vars_template_path) | |
156 | self.command = self.Machine_Base_Command[machine] + self.pflash.params | |
157 | if variant in [QemuEfiVariant.MS, QemuEfiVariant.SECBOOT] and \ | |
158 | flash_size == QemuEfiFlashSize.SIZE_2MB: | |
159 | # 2MB images have 64-bit PEI that does not support S3 w/ SMM | |
160 | self.command.extend(['-global', 'ICH9-LPC.disable_s3=1']) | |
161 | ||
162 | def add_disk(self, path): | |
163 | self.command = self.command + [ | |
164 | '-drive', 'file=%s,format=raw' % (path) | |
165 | ] | |
166 | ||
167 | def add_oem_string(self, type, string): | |
168 | string = string.replace(",", ",,") | |
169 | self.command = self.command + [ | |
170 | '-smbios', f'type={type},value={string}' | |
171 | ] | |
172 | ||
173 | class PflashParams: | |
174 | ''' | |
175 | Used to generate the appropriate -pflash arguments for QEMU. Mostly | |
176 | used as a fancy way to generate a per-instance vars file and have it | |
177 | be automatically cleaned up when the object is destroyed. | |
178 | ''' | |
179 | def __init__(self, code_path, vars_template_path): | |
d7298091 TL |
180 | self.params = [ |
181 | '-drive', | |
182 | 'file=%s,if=pflash,format=raw,unit=0,readonly=on' % | |
183 | (code_path), | |
184 | ] | |
185 | if vars_template_path is None: | |
186 | self.varfile_path = None | |
187 | return | |
a65627a8 TL |
188 | with tempfile.NamedTemporaryFile(delete=False) as varfile: |
189 | self.varfile_path = varfile.name | |
190 | with open(vars_template_path, 'rb') as template: | |
191 | shutil.copyfileobj(template, varfile) | |
d7298091 TL |
192 | self.params = self.params + [ |
193 | '-drive', | |
194 | 'file=%s,if=pflash,format=raw,unit=1,readonly=off' % | |
195 | (varfile.name) | |
196 | ] | |
a65627a8 TL |
197 | |
198 | def __del__(self): | |
d7298091 TL |
199 | if self.varfile_path is None: |
200 | return | |
a65627a8 | 201 | os.unlink(self.varfile_path) |