Merge ~waveform/ubuntu-release-upgrader:remove-uboot into ubuntu-release-upgrader:ubuntu/master
- Git
- lp:~waveform/ubuntu-release-upgrader
- remove-uboot
- Merge into ubuntu/master
Proposed by
Dave Jones
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 36ed0aa86545ab59e44cb12eea07574f39f36d24 | ||||
Proposed branch: | ~waveform/ubuntu-release-upgrader:remove-uboot | ||||
Merge into: | ubuntu-release-upgrader:ubuntu/master | ||||
Diff against target: |
467 lines (+379/-34) 3 files modified
DistUpgrade/DistUpgradeQuirks.py (+137/-1) debian/changelog (+7/-0) tests/test_quirks.py (+235/-33) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brian Murray | Needs Fixing | ||
Review via email: mp+406663@code.launchpad.net |
Commit message
Adds a quirk to remote u-boot from the boot sequence of Raspberry Pi installations on impish. This doesn't remove the u-boot binaries themselves, and leaves the actual configuration commented out in the config.txt in case user's wish to revert easily.
LP: #1936401
Description of the change
To post a comment you must log in.
Revision history for this message
Dave Jones (waveform) wrote : | # |
Added responses to each review comment. Will upload a commit to address the outstanding points.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/DistUpgrade/DistUpgradeQuirks.py b/DistUpgrade/DistUpgradeQuirks.py |
2 | index 712c98e..9d6400b 100644 |
3 | --- a/DistUpgrade/DistUpgradeQuirks.py |
4 | +++ b/DistUpgrade/DistUpgradeQuirks.py |
5 | @@ -145,6 +145,9 @@ class DistUpgradeQuirks(object): |
6 | if 'ubuntu-desktop-raspi' in cache: |
7 | if cache['ubuntu-desktop-raspi'].is_installed: |
8 | self._replace_fkms_overlay() |
9 | + if 'linux-firmware-raspi2' in cache: |
10 | + if cache['linux-firmware-raspi2'].is_installed: |
11 | + self._remove_uboot_on_rpi() |
12 | |
13 | # individual quirks handler when the dpkg run is finished --------- |
14 | def PostCleanup(self): |
15 | @@ -1157,7 +1160,7 @@ class DistUpgradeQuirks(object): |
16 | |
17 | def _replace_fkms_overlay(self, boot_dir='/boot/firmware'): |
18 | failure_action = ( |
19 | - "you may need to replace the vc4-fkms-v3d overlay with " |
20 | + "You may need to replace the vc4-fkms-v3d overlay with " |
21 | "vc4-kms-v3d in config.txt on your boot partition") |
22 | |
23 | try: |
24 | @@ -1205,3 +1208,136 @@ class DistUpgradeQuirks(object): |
25 | except IOError as exc: |
26 | logging.error("unable to write new boot config to %s: %s; %s", |
27 | boot_config_filename, exc, failure_action) |
28 | + |
29 | + def _remove_uboot_on_rpi(self, boot_dir='/boot/firmware'): |
30 | + kernel_line = 'kernel=vmlinuz' |
31 | + initramfs_line = 'initramfs initrd.img followkernel' |
32 | + failure_action = ( |
33 | + "You may need to replace u_boot_* with vmlinuz, and add " |
34 | + "{initramfs_line!r} to config.txt on your boot " |
35 | + "partition; see LP: #1936401 for further details") |
36 | + failure_action = failure_action.format(initramfs_line=initramfs_line) |
37 | + change_prefix = '# commented by do-release-upgrade (LP: #1936401)' |
38 | + added_prefix = '# added by do-release-upgrade (LP: #1936401)' |
39 | + merge_prefix = '# merged from {} by do-release-upgrade (LP: #1936401)' |
40 | + |
41 | + try: |
42 | + boot_config_filename = os.path.join(boot_dir, 'config.txt') |
43 | + with open(boot_config_filename, 'r', encoding='utf-8') as f: |
44 | + boot_config = f.read() |
45 | + except FileNotFoundError: |
46 | + logging.error("failed to open boot configuration in %s; %s", |
47 | + boot_config_filename, failure_action) |
48 | + return |
49 | + |
50 | + def replace_uboot(lines): |
51 | + result = [] |
52 | + removed_uboot = added_kernel = False |
53 | + for line in lines: |
54 | + if line == '[all]': |
55 | + # Add the vmlinuz kernel and initrd.img to the first |
56 | + # encountered [all] section, just in case the user has any |
57 | + # custom kernel overrides later in the sequence |
58 | + result.append(line) |
59 | + if not added_kernel: |
60 | + result.append(added_prefix) |
61 | + result.append(kernel_line) |
62 | + result.append(initramfs_line) |
63 | + added_kernel = True |
64 | + elif line.startswith('device_tree_address='): |
65 | + # Disable any device_tree_address= line (leave the boot |
66 | + # firmware to place this rather than risk trampling over it |
67 | + # with kernel/initrd) |
68 | + result.append(change_prefix) |
69 | + result.append('#' + line) |
70 | + elif line.startswith('kernel=uboot_rpi_'): |
71 | + # Disable all kernel=uboot_rpi_* lines found in the |
72 | + # configuration |
73 | + removed_uboot = True |
74 | + result.append(change_prefix) |
75 | + result.append('#' + line) |
76 | + else: |
77 | + result.append(line) |
78 | + # We don't *want* to touch the config unless we absolutely have to, |
79 | + # and the user may have already performed this conversion (e.g. to |
80 | + # take advantage of USB MSD boot), or otherwise customized their |
81 | + # boot configuration. Hence, if we didn't find (and remove) any |
82 | + # u-boot entries, assume the kernel/initrd config was already fine |
83 | + # and return the verbatim config |
84 | + if removed_uboot: |
85 | + if not added_kernel: |
86 | + result.append(added_prefix) |
87 | + result.append('[all]') |
88 | + result.append(kernel_line) |
89 | + result.append(initramfs_line) |
90 | + return result |
91 | + else: |
92 | + return lines |
93 | + |
94 | + def merge_includes(lines): |
95 | + result = [] |
96 | + skip_comments = True |
97 | + found_includes = False |
98 | + for line in lines: |
99 | + # Skip the initial comment block warning that config.txt is not |
100 | + # to be edited (the usercfg.txt and syscfg.txt files were |
101 | + # merged in the groovy release, but not for upgraders) |
102 | + if line.startswith('#') and skip_comments: |
103 | + continue |
104 | + skip_comments = False |
105 | + if line in ('include syscfg.txt', 'include usercfg.txt'): |
106 | + # Merge the usercfg.txt and syscfg.txt configuration files |
107 | + # into config.txt, skipping the initial comments. Note that |
108 | + # we don't bother with other includes the user has added |
109 | + # themselves (as they presumably want to keep those) |
110 | + found_includes = True |
111 | + included_filename = line.split(maxsplit=1)[1] |
112 | + result.append(merge_prefix.format(included_filename)) |
113 | + included_filename = os.path.join(boot_dir, |
114 | + included_filename) |
115 | + skip_comments = True |
116 | + with open(included_filename, 'r', encoding='utf-8') as f: |
117 | + for line in f: |
118 | + if line.startswith('#') and skip_comments: |
119 | + continue |
120 | + skip_comments = False |
121 | + result.append(line.rstrip()) |
122 | + target_filename = included_filename + '.distUpgrade' |
123 | + try: |
124 | + os.rename(included_filename, target_filename) |
125 | + except IOError as exc: |
126 | + logging.error("failed to move included configuration " |
127 | + "from %s to %s; %s", included_filename, |
128 | + target_filename, exc) |
129 | + else: |
130 | + result.append(line) |
131 | + # Again, if we found no includes, return the original verbatim |
132 | + # (i.e. with initial comments intact) |
133 | + if found_includes: |
134 | + return result |
135 | + else: |
136 | + return lines |
137 | + |
138 | + lines = [line.rstrip() for line in boot_config.splitlines()] |
139 | + lines = replace_uboot(merge_includes(lines)) |
140 | + new_config = ''.join(line + '\n' for line in lines) |
141 | + |
142 | + if new_config == boot_config: |
143 | + logging.warning("no u-boot removal performed in %s", |
144 | + boot_config_filename) |
145 | + return |
146 | + |
147 | + try: |
148 | + boot_backup_filename = boot_config_filename + '.distUpgrade' |
149 | + with open(boot_backup_filename, 'w', encoding='utf-8') as f: |
150 | + f.write(boot_config) |
151 | + except IOError as exc: |
152 | + logging.error("unable to write boot config backup to %s: %s; %s", |
153 | + boot_backup_filename, exc, failure_action) |
154 | + return |
155 | + try: |
156 | + with open(boot_config_filename, 'w', encoding='utf-8') as f: |
157 | + f.write(new_config) |
158 | + except IOError as exc: |
159 | + logging.error("unable to write new boot config to %s: %s; %s", |
160 | + boot_config_filename, exc, failure_action) |
161 | diff --git a/debian/changelog b/debian/changelog |
162 | index 762e5e9..15d3ca0 100644 |
163 | --- a/debian/changelog |
164 | +++ b/debian/changelog |
165 | @@ -1,3 +1,10 @@ |
166 | +ubuntu-release-upgrader (1:21.10.7) impish; urgency=medium |
167 | + |
168 | + * DistUpgrade/DistUpgradeQuirks.py: Add quirk to merge raspi boot config |
169 | + into one file, and remove u-boot references (LP: #1936401) |
170 | + |
171 | + -- Dave Jones <dave.jones@canonical.com> Tue, 03 Aug 2021 14:50:36 +0100 |
172 | + |
173 | ubuntu-release-upgrader (1:21.10.6) impish; urgency=medium |
174 | |
175 | * DistUpgrade/ReleaseAnnouncement, DistUpgrade/EOLReleaseAnnouncement: |
176 | diff --git a/tests/test_quirks.py b/tests/test_quirks.py |
177 | index cc4c8f1..2e96087 100644 |
178 | --- a/tests/test_quirks.py |
179 | +++ b/tests/test_quirks.py |
180 | @@ -458,13 +458,14 @@ class TestQuirks(unittest.TestCase): |
181 | |
182 | def test_replace_fkms_overlay_no_changes(self): |
183 | with tempfile.TemporaryDirectory() as boot_dir: |
184 | - demo_config = ("# This is a demo boot config\n" |
185 | - "[pi4\n" |
186 | - "max_framebuffers=2\n" |
187 | - "[all]\n" |
188 | - "arm_64bit=1\n" |
189 | - "kernel=vmlinuz\n" |
190 | - "initramfs initrd.img followkernel\n") |
191 | + demo_config = """\ |
192 | +# This is a demo boot config |
193 | +[pi4] |
194 | +max_framebuffers=2 |
195 | +[all] |
196 | +arm_64bit=1 |
197 | +kernel=vmlinuz |
198 | +initramfs initrd.img followkernel""" |
199 | with open(os.path.join(boot_dir, 'config.txt'), 'w') as f: |
200 | f.write(demo_config) |
201 | |
202 | @@ -480,32 +481,34 @@ class TestQuirks(unittest.TestCase): |
203 | |
204 | def test_replace_fkms_overlay_with_changes(self): |
205 | with tempfile.TemporaryDirectory() as boot_dir: |
206 | - demo_config = ( |
207 | - "# This is a demo boot config\n" |
208 | - "[pi4\n" |
209 | - "max_framebuffers=2\n" |
210 | - "[all]\n" |
211 | - "arm_64bit=1\n" |
212 | - "kernel=vmlinuz\n" |
213 | - "initramfs initrd.img followkernel\n" |
214 | - "dtoverlay=vc4-fkms-v3d,cma-256\n" |
215 | - "start_x=1\n" |
216 | - "gpu_mem=256\n") |
217 | - expected_config = ( |
218 | - "# This is a demo boot config\n" |
219 | - "[pi4\n" |
220 | - "max_framebuffers=2\n" |
221 | - "[all]\n" |
222 | - "arm_64bit=1\n" |
223 | - "kernel=vmlinuz\n" |
224 | - "initramfs initrd.img followkernel\n" |
225 | - "# changed by do-release-upgrade (LP: #1923673)\n" |
226 | - "#dtoverlay=vc4-fkms-v3d,cma-256\n" |
227 | - "dtoverlay=vc4-kms-v3d,cma-256\n" |
228 | - "# disabled by do-release-upgrade (LP: #1923673)\n" |
229 | - "#start_x=1\n" |
230 | - "# disabled by do-release-upgrade (LP: #1923673)\n" |
231 | - "#gpu_mem=256\n") |
232 | + demo_config = """\ |
233 | +# This is a demo boot config |
234 | +[pi4] |
235 | +max_framebuffers=2 |
236 | +[all] |
237 | +arm_64bit=1 |
238 | +kernel=vmlinuz |
239 | +initramfs initrd.img followkernel |
240 | +dtoverlay=vc4-fkms-v3d,cma-256 |
241 | +start_x=1 |
242 | +gpu_mem=256 |
243 | +""" |
244 | + expected_config = """\ |
245 | +# This is a demo boot config |
246 | +[pi4] |
247 | +max_framebuffers=2 |
248 | +[all] |
249 | +arm_64bit=1 |
250 | +kernel=vmlinuz |
251 | +initramfs initrd.img followkernel |
252 | +# changed by do-release-upgrade (LP: #1923673) |
253 | +#dtoverlay=vc4-fkms-v3d,cma-256 |
254 | +dtoverlay=vc4-kms-v3d,cma-256 |
255 | +# disabled by do-release-upgrade (LP: #1923673) |
256 | +#start_x=1 |
257 | +# disabled by do-release-upgrade (LP: #1923673) |
258 | +#gpu_mem=256 |
259 | +""" |
260 | with open(os.path.join(boot_dir, 'config.txt'), 'w') as f: |
261 | f.write(demo_config) |
262 | |
263 | @@ -519,6 +522,205 @@ class TestQuirks(unittest.TestCase): |
264 | with open(os.path.join(boot_dir, 'config.txt')) as f: |
265 | self.assertTrue(f.read() == expected_config) |
266 | |
267 | + def test_remove_uboot_no_config(self): |
268 | + with tempfile.TemporaryDirectory() as boot_dir: |
269 | + mock_controller = mock.Mock() |
270 | + q = DistUpgradeQuirks(mock_controller, mock.Mock()) |
271 | + q._remove_uboot_on_rpi(boot_dir) |
272 | + |
273 | + self.assertFalse(os.path.exists(os.path.join( |
274 | + boot_dir, 'config.txt.distUpgrade'))) |
275 | + |
276 | + def test_remove_uboot_no_changes(self): |
277 | + with tempfile.TemporaryDirectory() as boot_dir: |
278 | + native_config = """\ |
279 | +# This is a demo boot config with a comment at the start that should not |
280 | +# be removed |
281 | + |
282 | +[pi4] |
283 | +max_framebuffers=2 |
284 | + |
285 | +[all] |
286 | +arm_64bit=1 |
287 | +kernel=vmlinuz |
288 | +initramfs initrd.img followkernel |
289 | + |
290 | +# This is a user-added include that should not be merged |
291 | +include custom.txt |
292 | +""" |
293 | + custom_config = """\ |
294 | +# This is the custom included configuration file |
295 | + |
296 | +hdmi_group=1 |
297 | +hdmi_mode=4 |
298 | +""" |
299 | + with open(os.path.join(boot_dir, 'config.txt'), 'w') as f: |
300 | + f.write(native_config) |
301 | + with open(os.path.join(boot_dir, 'custom.txt'), 'w') as f: |
302 | + f.write(custom_config) |
303 | + |
304 | + mock_controller = mock.Mock() |
305 | + q = DistUpgradeQuirks(mock_controller, mock.Mock()) |
306 | + q._remove_uboot_on_rpi(boot_dir) |
307 | + |
308 | + self.assertFalse(os.path.exists(os.path.join( |
309 | + boot_dir, 'config.txt.distUpgrade'))) |
310 | + self.assertFalse(os.path.exists(os.path.join( |
311 | + boot_dir, 'custom.txt.distUpgrade'))) |
312 | + with open(os.path.join(boot_dir, 'config.txt')) as f: |
313 | + self.assertTrue(f.read() == native_config) |
314 | + with open(os.path.join(boot_dir, 'custom.txt')) as f: |
315 | + self.assertTrue(f.read() == custom_config) |
316 | + |
317 | + def test_remove_uboot_with_changes(self): |
318 | + with tempfile.TemporaryDirectory() as boot_dir: |
319 | + config_txt = """\ |
320 | +# This is a warning that you should not edit this file. The upgrade should |
321 | +# remove this comment |
322 | + |
323 | +[pi4] |
324 | +# This is a comment that should be included |
325 | +kernel=uboot_rpi_4.bin |
326 | +max_framebuffers=2 |
327 | + |
328 | +[pi2] |
329 | +kernel=uboot_rpi_2.bin |
330 | + |
331 | +[pi3] |
332 | +kernel=uboot_rpi_3.bin |
333 | + |
334 | +[all] |
335 | +arm_64bit=1 |
336 | +device_tree_address=0x3000000 |
337 | +include syscfg.txt |
338 | +include usercfg.txt |
339 | +dtoverlay=vc4-fkms-v3d,cma-256 |
340 | +include custom.txt |
341 | +""" |
342 | + usercfg_txt = """\ |
343 | +# Another chunk of warning text that should be skipped |
344 | +""" |
345 | + syscfg_txt = """\ |
346 | +# Yet more warnings to exclude |
347 | +dtparam=audio=on |
348 | +dtparam=spi=on |
349 | +enable_uart=1 |
350 | +""" |
351 | + custom_txt = """\ |
352 | +# This is a user-added file that should be left alone by the upgrade |
353 | +[gpio4=1] |
354 | +kernel=custom |
355 | +""" |
356 | + expected_config_txt = """\ |
357 | + |
358 | +[pi4] |
359 | +# This is a comment that should be included |
360 | +# commented by do-release-upgrade (LP: #1936401) |
361 | +#kernel=uboot_rpi_4.bin |
362 | +max_framebuffers=2 |
363 | + |
364 | +[pi2] |
365 | +# commented by do-release-upgrade (LP: #1936401) |
366 | +#kernel=uboot_rpi_2.bin |
367 | + |
368 | +[pi3] |
369 | +# commented by do-release-upgrade (LP: #1936401) |
370 | +#kernel=uboot_rpi_3.bin |
371 | + |
372 | +[all] |
373 | +# added by do-release-upgrade (LP: #1936401) |
374 | +kernel=vmlinuz |
375 | +initramfs initrd.img followkernel |
376 | +arm_64bit=1 |
377 | +# commented by do-release-upgrade (LP: #1936401) |
378 | +#device_tree_address=0x3000000 |
379 | +# merged from syscfg.txt by do-release-upgrade (LP: #1936401) |
380 | +dtparam=audio=on |
381 | +dtparam=spi=on |
382 | +enable_uart=1 |
383 | +# merged from usercfg.txt by do-release-upgrade (LP: #1936401) |
384 | +dtoverlay=vc4-fkms-v3d,cma-256 |
385 | +include custom.txt |
386 | +""" |
387 | + with open(os.path.join(boot_dir, 'config.txt'), 'w') as f: |
388 | + f.write(config_txt) |
389 | + with open(os.path.join(boot_dir, 'syscfg.txt'), 'w') as f: |
390 | + f.write(syscfg_txt) |
391 | + with open(os.path.join(boot_dir, 'usercfg.txt'), 'w') as f: |
392 | + f.write(usercfg_txt) |
393 | + with open(os.path.join(boot_dir, 'custom.txt'), 'w') as f: |
394 | + f.write(custom_txt) |
395 | + |
396 | + mock_controller = mock.Mock() |
397 | + q = DistUpgradeQuirks(mock_controller, mock.Mock()) |
398 | + q._remove_uboot_on_rpi(boot_dir) |
399 | + |
400 | + self.assertTrue(os.path.exists(os.path.join( |
401 | + boot_dir, 'config.txt.distUpgrade'))) |
402 | + self.assertTrue(os.path.exists(os.path.join( |
403 | + boot_dir, 'syscfg.txt.distUpgrade'))) |
404 | + self.assertTrue(os.path.exists(os.path.join( |
405 | + boot_dir, 'usercfg.txt.distUpgrade'))) |
406 | + self.assertTrue(os.path.exists(os.path.join( |
407 | + boot_dir, 'config.txt'))) |
408 | + self.assertTrue(os.path.exists(os.path.join( |
409 | + boot_dir, 'custom.txt'))) |
410 | + self.assertFalse(os.path.exists(os.path.join( |
411 | + boot_dir, 'syscfg.txt'))) |
412 | + self.assertFalse(os.path.exists(os.path.join( |
413 | + boot_dir, 'usercfg.txt'))) |
414 | + self.assertFalse(os.path.exists(os.path.join( |
415 | + boot_dir, 'custom.txt.distUpgrade'))) |
416 | + with open(os.path.join(boot_dir, 'config.txt')) as f: |
417 | + self.assertTrue(f.read() == expected_config_txt) |
418 | + with open(os.path.join(boot_dir, 'custom.txt')) as f: |
419 | + self.assertTrue(f.read() == custom_txt) |
420 | + |
421 | + def test_remove_uboot_no_all_section(self): |
422 | + with tempfile.TemporaryDirectory() as boot_dir: |
423 | + config_txt = """\ |
424 | +arm_64bit=1 |
425 | +device_tree_address=0x3000000 |
426 | + |
427 | +[pi4] |
428 | +# This is a comment that should be included |
429 | +kernel=uboot_rpi_4.bin |
430 | +max_framebuffers=2 |
431 | + |
432 | +[pi3] |
433 | +kernel=uboot_rpi_3.bin |
434 | +""" |
435 | + expected_config_txt = """\ |
436 | +arm_64bit=1 |
437 | +# commented by do-release-upgrade (LP: #1936401) |
438 | +#device_tree_address=0x3000000 |
439 | + |
440 | +[pi4] |
441 | +# This is a comment that should be included |
442 | +# commented by do-release-upgrade (LP: #1936401) |
443 | +#kernel=uboot_rpi_4.bin |
444 | +max_framebuffers=2 |
445 | + |
446 | +[pi3] |
447 | +# commented by do-release-upgrade (LP: #1936401) |
448 | +#kernel=uboot_rpi_3.bin |
449 | +# added by do-release-upgrade (LP: #1936401) |
450 | +[all] |
451 | +kernel=vmlinuz |
452 | +initramfs initrd.img followkernel |
453 | +""" |
454 | + with open(os.path.join(boot_dir, 'config.txt'), 'w') as f: |
455 | + f.write(config_txt) |
456 | + |
457 | + mock_controller = mock.Mock() |
458 | + q = DistUpgradeQuirks(mock_controller, mock.Mock()) |
459 | + q._remove_uboot_on_rpi(boot_dir) |
460 | + |
461 | + self.assertTrue(os.path.exists(os.path.join( |
462 | + boot_dir, 'config.txt.distUpgrade'))) |
463 | + with open(os.path.join(boot_dir, 'config.txt')) as f: |
464 | + self.assertTrue(f.read() == expected_config_txt) |
465 | + |
466 | |
467 | class TestSnapQuirks(unittest.TestCase): |
468 |
I found the function _remove_ uboot_on_ rpi a bit hard to read and had to scroll up and down multiple times because the file is read, then some other functions are defined, and then the file is passed to those functions. It might be easier for others to read if that was restructured a bit.
I can't really comment on the correctness of the boot config contents but as a release upgrader quirk this seems good to me.
I've some additional comments in-line.