]>
Commit | Line | Data |
---|---|---|
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() | |
31 | ||
32 | ||
33 | class QemuEfiVariant(enum.Enum): | |
34 | MS = enum.auto() | |
35 | SECBOOT = enum.auto() | |
36 | SNAKEOIL = enum.auto() | |
37 | ||
38 | ||
39 | class QemuEfiFlashSize(enum.Enum): | |
40 | DEFAULT = enum.auto | |
41 | SIZE_2MB = enum.auto() | |
42 | SIZE_4MB = enum.auto() | |
43 | ||
44 | ||
45 | class QemuCommand: | |
46 | # Based on the args used by ovmf-vars-generator | |
47 | Qemu_Common_Params = [ | |
48 | '-no-user-config', '-nodefaults', | |
49 | '-m', '256', | |
50 | '-smp', '2,sockets=2,cores=1,threads=1', | |
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 | ] | |
61 | Machine_Base_Command = { | |
62 | QemuEfiMachine.AAVMF: [ | |
63 | 'qemu-system-aarch64', '-cpu', 'cortex-a57', | |
64 | ] + Aavmf_Common_Params, | |
65 | QemuEfiMachine.AAVMF32: [ | |
66 | 'qemu-system-aarch64', '-cpu', 'cortex-a15', | |
67 | ] + Aavmf_Common_Params, | |
68 | QemuEfiMachine.OVMF_PC: [ | |
69 | 'qemu-system-x86_64', '-machine', 'pc,accel=tcg', | |
70 | ] + Ovmf_Common_Params, | |
71 | QemuEfiMachine.OVMF_Q35: [ | |
72 | 'qemu-system-x86_64', '-machine', 'q35,accel=tcg', | |
73 | ] + Ovmf_Common_Params, | |
74 | QemuEfiMachine.OVMF32: [ | |
75 | 'qemu-system-i386', '-machine', 'q35,accel=tcg', | |
76 | ] + Ovmf_Common_Params, | |
77 | } | |
78 | ||
79 | def _get_default_flash_paths(self, machine, variant, flash_size): | |
80 | assert(machine in QemuEfiMachine) | |
81 | assert(variant is None or variant in QemuEfiVariant) | |
82 | assert(flash_size in QemuEfiFlashSize) | |
83 | ||
84 | code_ext = vars_ext = '' | |
85 | if variant == QemuEfiVariant.MS: | |
86 | code_ext = vars_ext = '.ms' | |
87 | elif variant == QemuEfiVariant.SECBOOT: | |
88 | code_ext = '.secboot' | |
89 | elif variant == QemuEfiVariant.SNAKEOIL: | |
90 | vars_ext = '.snakeoil' | |
91 | ||
92 | if machine == QemuEfiMachine.AAVMF: | |
93 | assert(flash_size == QemuEfiFlashSize.DEFAULT) | |
94 | return ( | |
95 | f'/usr/share/AAVMF/AAVMF_CODE{code_ext}.fd', | |
96 | f'/usr/share/AAVMF/AAVMF_VARS{code_ext}.fd', | |
97 | ) | |
98 | if machine == QemuEfiMachine.AAVMF32: | |
99 | assert(variant is None) | |
100 | assert(flash_size == QemuEfiFlashSize.DEFAULT) | |
101 | return ( | |
102 | '/usr/share/AAVMF/AAVMF32_CODE.fd', | |
103 | '/usr/share/AAVMF/AAVMF32_VARS.fd' | |
104 | ) | |
105 | if machine == QemuEfiMachine.OVMF32: | |
106 | assert(variant is None or variant in [QemuEfiVariant.SECBOOT]) | |
107 | assert( | |
108 | flash_size in [ | |
109 | QemuEfiFlashSize.DEFAULT, QemuEfiFlashSize.SIZE_4MB | |
110 | ] | |
111 | ) | |
112 | return ( | |
113 | '/usr/share/OVMF/OVMF32_CODE_4M.secboot.fd', | |
114 | '/usr/share/OVMF/OVMF32_VARS_4M.fd', | |
115 | ) | |
116 | # Remaining possibilities are OVMF variants | |
117 | if machine == QemuEfiMachine.OVMF_PC: | |
118 | assert(variant is None) | |
119 | if variant == QemuEfiVariant.SNAKEOIL: | |
120 | # We provide one size - you don't get to pick. | |
121 | assert(flash_size == QemuEfiFlashSize.DEFAULT) | |
122 | size_ext = '' if flash_size == QemuEfiFlashSize.SIZE_2MB else '_4M' | |
123 | return ( | |
124 | f'/usr/share/OVMF/OVMF_CODE{size_ext}{code_ext}.fd', | |
125 | f'/usr/share/OVMF/OVMF_VARS{size_ext}{vars_ext}.fd' | |
126 | ) | |
127 | ||
128 | def __init__( | |
129 | self, machine, variant=None, | |
130 | code_path=None, vars_template_path=None, | |
131 | flash_size=QemuEfiFlashSize.DEFAULT, | |
132 | ): | |
133 | assert( | |
134 | (code_path and vars_template_path) or | |
135 | (not code_path and not vars_template_path) | |
136 | ) | |
137 | ||
138 | if not code_path: | |
139 | (code_path, vars_template_path) = self._get_default_flash_paths( | |
140 | machine, variant, flash_size) | |
141 | ||
142 | self.pflash = self.PflashParams(code_path, vars_template_path) | |
143 | self.command = self.Machine_Base_Command[machine] + self.pflash.params | |
144 | if variant in [QemuEfiVariant.MS, QemuEfiVariant.SECBOOT] and \ | |
145 | flash_size == QemuEfiFlashSize.SIZE_2MB: | |
146 | # 2MB images have 64-bit PEI that does not support S3 w/ SMM | |
147 | self.command.extend(['-global', 'ICH9-LPC.disable_s3=1']) | |
148 | ||
149 | def add_disk(self, path): | |
150 | self.command = self.command + [ | |
151 | '-drive', 'file=%s,format=raw' % (path) | |
152 | ] | |
153 | ||
154 | def add_oem_string(self, type, string): | |
155 | string = string.replace(",", ",,") | |
156 | self.command = self.command + [ | |
157 | '-smbios', f'type={type},value={string}' | |
158 | ] | |
159 | ||
160 | class PflashParams: | |
161 | ''' | |
162 | Used to generate the appropriate -pflash arguments for QEMU. Mostly | |
163 | used as a fancy way to generate a per-instance vars file and have it | |
164 | be automatically cleaned up when the object is destroyed. | |
165 | ''' | |
166 | def __init__(self, code_path, vars_template_path): | |
167 | with tempfile.NamedTemporaryFile(delete=False) as varfile: | |
168 | self.varfile_path = varfile.name | |
169 | with open(vars_template_path, 'rb') as template: | |
170 | shutil.copyfileobj(template, varfile) | |
171 | self.params = [ | |
172 | '-drive', | |
173 | 'file=%s,if=pflash,format=raw,unit=0,readonly=on' % | |
174 | (code_path), | |
175 | '-drive', | |
176 | 'file=%s,if=pflash,format=raw,unit=1,readonly=off' % | |
177 | (varfile.name) | |
178 | ] | |
179 | ||
180 | def __del__(self): | |
181 | os.unlink(self.varfile_path) |