Merge ~waveform/ubuntu-release-upgrader:remove-uboot into ubuntu-release-upgrader: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)
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

To post a comment you must log in.
Revision history for this message
Brian Murray (brian-murray) wrote :

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.

review: Needs Fixing
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
diff --git a/DistUpgrade/DistUpgradeQuirks.py b/DistUpgrade/DistUpgradeQuirks.py
index 712c98e..9d6400b 100644
--- a/DistUpgrade/DistUpgradeQuirks.py
+++ b/DistUpgrade/DistUpgradeQuirks.py
@@ -145,6 +145,9 @@ class DistUpgradeQuirks(object):
145 if 'ubuntu-desktop-raspi' in cache:145 if 'ubuntu-desktop-raspi' in cache:
146 if cache['ubuntu-desktop-raspi'].is_installed:146 if cache['ubuntu-desktop-raspi'].is_installed:
147 self._replace_fkms_overlay()147 self._replace_fkms_overlay()
148 if 'linux-firmware-raspi2' in cache:
149 if cache['linux-firmware-raspi2'].is_installed:
150 self._remove_uboot_on_rpi()
148151
149 # individual quirks handler when the dpkg run is finished ---------152 # individual quirks handler when the dpkg run is finished ---------
150 def PostCleanup(self):153 def PostCleanup(self):
@@ -1157,7 +1160,7 @@ class DistUpgradeQuirks(object):
11571160
1158 def _replace_fkms_overlay(self, boot_dir='/boot/firmware'):1161 def _replace_fkms_overlay(self, boot_dir='/boot/firmware'):
1159 failure_action = (1162 failure_action = (
1160 "you may need to replace the vc4-fkms-v3d overlay with "1163 "You may need to replace the vc4-fkms-v3d overlay with "
1161 "vc4-kms-v3d in config.txt on your boot partition")1164 "vc4-kms-v3d in config.txt on your boot partition")
11621165
1163 try:1166 try:
@@ -1205,3 +1208,136 @@ class DistUpgradeQuirks(object):
1205 except IOError as exc:1208 except IOError as exc:
1206 logging.error("unable to write new boot config to %s: %s; %s",1209 logging.error("unable to write new boot config to %s: %s; %s",
1207 boot_config_filename, exc, failure_action)1210 boot_config_filename, exc, failure_action)
1211
1212 def _remove_uboot_on_rpi(self, boot_dir='/boot/firmware'):
1213 kernel_line = 'kernel=vmlinuz'
1214 initramfs_line = 'initramfs initrd.img followkernel'
1215 failure_action = (
1216 "You may need to replace u_boot_* with vmlinuz, and add "
1217 "{initramfs_line!r} to config.txt on your boot "
1218 "partition; see LP: #1936401 for further details")
1219 failure_action = failure_action.format(initramfs_line=initramfs_line)
1220 change_prefix = '# commented by do-release-upgrade (LP: #1936401)'
1221 added_prefix = '# added by do-release-upgrade (LP: #1936401)'
1222 merge_prefix = '# merged from {} by do-release-upgrade (LP: #1936401)'
1223
1224 try:
1225 boot_config_filename = os.path.join(boot_dir, 'config.txt')
1226 with open(boot_config_filename, 'r', encoding='utf-8') as f:
1227 boot_config = f.read()
1228 except FileNotFoundError:
1229 logging.error("failed to open boot configuration in %s; %s",
1230 boot_config_filename, failure_action)
1231 return
1232
1233 def replace_uboot(lines):
1234 result = []
1235 removed_uboot = added_kernel = False
1236 for line in lines:
1237 if line == '[all]':
1238 # Add the vmlinuz kernel and initrd.img to the first
1239 # encountered [all] section, just in case the user has any
1240 # custom kernel overrides later in the sequence
1241 result.append(line)
1242 if not added_kernel:
1243 result.append(added_prefix)
1244 result.append(kernel_line)
1245 result.append(initramfs_line)
1246 added_kernel = True
1247 elif line.startswith('device_tree_address='):
1248 # Disable any device_tree_address= line (leave the boot
1249 # firmware to place this rather than risk trampling over it
1250 # with kernel/initrd)
1251 result.append(change_prefix)
1252 result.append('#' + line)
1253 elif line.startswith('kernel=uboot_rpi_'):
1254 # Disable all kernel=uboot_rpi_* lines found in the
1255 # configuration
1256 removed_uboot = True
1257 result.append(change_prefix)
1258 result.append('#' + line)
1259 else:
1260 result.append(line)
1261 # We don't *want* to touch the config unless we absolutely have to,
1262 # and the user may have already performed this conversion (e.g. to
1263 # take advantage of USB MSD boot), or otherwise customized their
1264 # boot configuration. Hence, if we didn't find (and remove) any
1265 # u-boot entries, assume the kernel/initrd config was already fine
1266 # and return the verbatim config
1267 if removed_uboot:
1268 if not added_kernel:
1269 result.append(added_prefix)
1270 result.append('[all]')
1271 result.append(kernel_line)
1272 result.append(initramfs_line)
1273 return result
1274 else:
1275 return lines
1276
1277 def merge_includes(lines):
1278 result = []
1279 skip_comments = True
1280 found_includes = False
1281 for line in lines:
1282 # Skip the initial comment block warning that config.txt is not
1283 # to be edited (the usercfg.txt and syscfg.txt files were
1284 # merged in the groovy release, but not for upgraders)
1285 if line.startswith('#') and skip_comments:
1286 continue
1287 skip_comments = False
1288 if line in ('include syscfg.txt', 'include usercfg.txt'):
1289 # Merge the usercfg.txt and syscfg.txt configuration files
1290 # into config.txt, skipping the initial comments. Note that
1291 # we don't bother with other includes the user has added
1292 # themselves (as they presumably want to keep those)
1293 found_includes = True
1294 included_filename = line.split(maxsplit=1)[1]
1295 result.append(merge_prefix.format(included_filename))
1296 included_filename = os.path.join(boot_dir,
1297 included_filename)
1298 skip_comments = True
1299 with open(included_filename, 'r', encoding='utf-8') as f:
1300 for line in f:
1301 if line.startswith('#') and skip_comments:
1302 continue
1303 skip_comments = False
1304 result.append(line.rstrip())
1305 target_filename = included_filename + '.distUpgrade'
1306 try:
1307 os.rename(included_filename, target_filename)
1308 except IOError as exc:
1309 logging.error("failed to move included configuration "
1310 "from %s to %s; %s", included_filename,
1311 target_filename, exc)
1312 else:
1313 result.append(line)
1314 # Again, if we found no includes, return the original verbatim
1315 # (i.e. with initial comments intact)
1316 if found_includes:
1317 return result
1318 else:
1319 return lines
1320
1321 lines = [line.rstrip() for line in boot_config.splitlines()]
1322 lines = replace_uboot(merge_includes(lines))
1323 new_config = ''.join(line + '\n' for line in lines)
1324
1325 if new_config == boot_config:
1326 logging.warning("no u-boot removal performed in %s",
1327 boot_config_filename)
1328 return
1329
1330 try:
1331 boot_backup_filename = boot_config_filename + '.distUpgrade'
1332 with open(boot_backup_filename, 'w', encoding='utf-8') as f:
1333 f.write(boot_config)
1334 except IOError as exc:
1335 logging.error("unable to write boot config backup to %s: %s; %s",
1336 boot_backup_filename, exc, failure_action)
1337 return
1338 try:
1339 with open(boot_config_filename, 'w', encoding='utf-8') as f:
1340 f.write(new_config)
1341 except IOError as exc:
1342 logging.error("unable to write new boot config to %s: %s; %s",
1343 boot_config_filename, exc, failure_action)
diff --git a/debian/changelog b/debian/changelog
index 762e5e9..15d3ca0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
1ubuntu-release-upgrader (1:21.10.7) impish; urgency=medium
2
3 * DistUpgrade/DistUpgradeQuirks.py: Add quirk to merge raspi boot config
4 into one file, and remove u-boot references (LP: #1936401)
5
6 -- Dave Jones <dave.jones@canonical.com> Tue, 03 Aug 2021 14:50:36 +0100
7
1ubuntu-release-upgrader (1:21.10.6) impish; urgency=medium8ubuntu-release-upgrader (1:21.10.6) impish; urgency=medium
29
3 * DistUpgrade/ReleaseAnnouncement, DistUpgrade/EOLReleaseAnnouncement:10 * DistUpgrade/ReleaseAnnouncement, DistUpgrade/EOLReleaseAnnouncement:
diff --git a/tests/test_quirks.py b/tests/test_quirks.py
index cc4c8f1..2e96087 100644
--- a/tests/test_quirks.py
+++ b/tests/test_quirks.py
@@ -458,13 +458,14 @@ class TestQuirks(unittest.TestCase):
458458
459 def test_replace_fkms_overlay_no_changes(self):459 def test_replace_fkms_overlay_no_changes(self):
460 with tempfile.TemporaryDirectory() as boot_dir:460 with tempfile.TemporaryDirectory() as boot_dir:
461 demo_config = ("# This is a demo boot config\n"461 demo_config = """\
462 "[pi4\n"462# This is a demo boot config
463 "max_framebuffers=2\n"463[pi4]
464 "[all]\n"464max_framebuffers=2
465 "arm_64bit=1\n"465[all]
466 "kernel=vmlinuz\n"466arm_64bit=1
467 "initramfs initrd.img followkernel\n")467kernel=vmlinuz
468initramfs initrd.img followkernel"""
468 with open(os.path.join(boot_dir, 'config.txt'), 'w') as f:469 with open(os.path.join(boot_dir, 'config.txt'), 'w') as f:
469 f.write(demo_config)470 f.write(demo_config)
470471
@@ -480,32 +481,34 @@ class TestQuirks(unittest.TestCase):
480481
481 def test_replace_fkms_overlay_with_changes(self):482 def test_replace_fkms_overlay_with_changes(self):
482 with tempfile.TemporaryDirectory() as boot_dir:483 with tempfile.TemporaryDirectory() as boot_dir:
483 demo_config = (484 demo_config = """\
484 "# This is a demo boot config\n"485# This is a demo boot config
485 "[pi4\n"486[pi4]
486 "max_framebuffers=2\n"487max_framebuffers=2
487 "[all]\n"488[all]
488 "arm_64bit=1\n"489arm_64bit=1
489 "kernel=vmlinuz\n"490kernel=vmlinuz
490 "initramfs initrd.img followkernel\n"491initramfs initrd.img followkernel
491 "dtoverlay=vc4-fkms-v3d,cma-256\n"492dtoverlay=vc4-fkms-v3d,cma-256
492 "start_x=1\n"493start_x=1
493 "gpu_mem=256\n")494gpu_mem=256
494 expected_config = (495"""
495 "# This is a demo boot config\n"496 expected_config = """\
496 "[pi4\n"497# This is a demo boot config
497 "max_framebuffers=2\n"498[pi4]
498 "[all]\n"499max_framebuffers=2
499 "arm_64bit=1\n"500[all]
500 "kernel=vmlinuz\n"501arm_64bit=1
501 "initramfs initrd.img followkernel\n"502kernel=vmlinuz
502 "# changed by do-release-upgrade (LP: #1923673)\n"503initramfs initrd.img followkernel
503 "#dtoverlay=vc4-fkms-v3d,cma-256\n"504# changed by do-release-upgrade (LP: #1923673)
504 "dtoverlay=vc4-kms-v3d,cma-256\n"505#dtoverlay=vc4-fkms-v3d,cma-256
505 "# disabled by do-release-upgrade (LP: #1923673)\n"506dtoverlay=vc4-kms-v3d,cma-256
506 "#start_x=1\n"507# disabled by do-release-upgrade (LP: #1923673)
507 "# disabled by do-release-upgrade (LP: #1923673)\n"508#start_x=1
508 "#gpu_mem=256\n")509# disabled by do-release-upgrade (LP: #1923673)
510#gpu_mem=256
511"""
509 with open(os.path.join(boot_dir, 'config.txt'), 'w') as f:512 with open(os.path.join(boot_dir, 'config.txt'), 'w') as f:
510 f.write(demo_config)513 f.write(demo_config)
511514
@@ -519,6 +522,205 @@ class TestQuirks(unittest.TestCase):
519 with open(os.path.join(boot_dir, 'config.txt')) as f:522 with open(os.path.join(boot_dir, 'config.txt')) as f:
520 self.assertTrue(f.read() == expected_config)523 self.assertTrue(f.read() == expected_config)
521524
525 def test_remove_uboot_no_config(self):
526 with tempfile.TemporaryDirectory() as boot_dir:
527 mock_controller = mock.Mock()
528 q = DistUpgradeQuirks(mock_controller, mock.Mock())
529 q._remove_uboot_on_rpi(boot_dir)
530
531 self.assertFalse(os.path.exists(os.path.join(
532 boot_dir, 'config.txt.distUpgrade')))
533
534 def test_remove_uboot_no_changes(self):
535 with tempfile.TemporaryDirectory() as boot_dir:
536 native_config = """\
537# This is a demo boot config with a comment at the start that should not
538# be removed
539
540[pi4]
541max_framebuffers=2
542
543[all]
544arm_64bit=1
545kernel=vmlinuz
546initramfs initrd.img followkernel
547
548# This is a user-added include that should not be merged
549include custom.txt
550"""
551 custom_config = """\
552# This is the custom included configuration file
553
554hdmi_group=1
555hdmi_mode=4
556"""
557 with open(os.path.join(boot_dir, 'config.txt'), 'w') as f:
558 f.write(native_config)
559 with open(os.path.join(boot_dir, 'custom.txt'), 'w') as f:
560 f.write(custom_config)
561
562 mock_controller = mock.Mock()
563 q = DistUpgradeQuirks(mock_controller, mock.Mock())
564 q._remove_uboot_on_rpi(boot_dir)
565
566 self.assertFalse(os.path.exists(os.path.join(
567 boot_dir, 'config.txt.distUpgrade')))
568 self.assertFalse(os.path.exists(os.path.join(
569 boot_dir, 'custom.txt.distUpgrade')))
570 with open(os.path.join(boot_dir, 'config.txt')) as f:
571 self.assertTrue(f.read() == native_config)
572 with open(os.path.join(boot_dir, 'custom.txt')) as f:
573 self.assertTrue(f.read() == custom_config)
574
575 def test_remove_uboot_with_changes(self):
576 with tempfile.TemporaryDirectory() as boot_dir:
577 config_txt = """\
578# This is a warning that you should not edit this file. The upgrade should
579# remove this comment
580
581[pi4]
582# This is a comment that should be included
583kernel=uboot_rpi_4.bin
584max_framebuffers=2
585
586[pi2]
587kernel=uboot_rpi_2.bin
588
589[pi3]
590kernel=uboot_rpi_3.bin
591
592[all]
593arm_64bit=1
594device_tree_address=0x3000000
595include syscfg.txt
596include usercfg.txt
597dtoverlay=vc4-fkms-v3d,cma-256
598include custom.txt
599"""
600 usercfg_txt = """\
601# Another chunk of warning text that should be skipped
602"""
603 syscfg_txt = """\
604# Yet more warnings to exclude
605dtparam=audio=on
606dtparam=spi=on
607enable_uart=1
608"""
609 custom_txt = """\
610# This is a user-added file that should be left alone by the upgrade
611[gpio4=1]
612kernel=custom
613"""
614 expected_config_txt = """\
615
616[pi4]
617# This is a comment that should be included
618# commented by do-release-upgrade (LP: #1936401)
619#kernel=uboot_rpi_4.bin
620max_framebuffers=2
621
622[pi2]
623# commented by do-release-upgrade (LP: #1936401)
624#kernel=uboot_rpi_2.bin
625
626[pi3]
627# commented by do-release-upgrade (LP: #1936401)
628#kernel=uboot_rpi_3.bin
629
630[all]
631# added by do-release-upgrade (LP: #1936401)
632kernel=vmlinuz
633initramfs initrd.img followkernel
634arm_64bit=1
635# commented by do-release-upgrade (LP: #1936401)
636#device_tree_address=0x3000000
637# merged from syscfg.txt by do-release-upgrade (LP: #1936401)
638dtparam=audio=on
639dtparam=spi=on
640enable_uart=1
641# merged from usercfg.txt by do-release-upgrade (LP: #1936401)
642dtoverlay=vc4-fkms-v3d,cma-256
643include custom.txt
644"""
645 with open(os.path.join(boot_dir, 'config.txt'), 'w') as f:
646 f.write(config_txt)
647 with open(os.path.join(boot_dir, 'syscfg.txt'), 'w') as f:
648 f.write(syscfg_txt)
649 with open(os.path.join(boot_dir, 'usercfg.txt'), 'w') as f:
650 f.write(usercfg_txt)
651 with open(os.path.join(boot_dir, 'custom.txt'), 'w') as f:
652 f.write(custom_txt)
653
654 mock_controller = mock.Mock()
655 q = DistUpgradeQuirks(mock_controller, mock.Mock())
656 q._remove_uboot_on_rpi(boot_dir)
657
658 self.assertTrue(os.path.exists(os.path.join(
659 boot_dir, 'config.txt.distUpgrade')))
660 self.assertTrue(os.path.exists(os.path.join(
661 boot_dir, 'syscfg.txt.distUpgrade')))
662 self.assertTrue(os.path.exists(os.path.join(
663 boot_dir, 'usercfg.txt.distUpgrade')))
664 self.assertTrue(os.path.exists(os.path.join(
665 boot_dir, 'config.txt')))
666 self.assertTrue(os.path.exists(os.path.join(
667 boot_dir, 'custom.txt')))
668 self.assertFalse(os.path.exists(os.path.join(
669 boot_dir, 'syscfg.txt')))
670 self.assertFalse(os.path.exists(os.path.join(
671 boot_dir, 'usercfg.txt')))
672 self.assertFalse(os.path.exists(os.path.join(
673 boot_dir, 'custom.txt.distUpgrade')))
674 with open(os.path.join(boot_dir, 'config.txt')) as f:
675 self.assertTrue(f.read() == expected_config_txt)
676 with open(os.path.join(boot_dir, 'custom.txt')) as f:
677 self.assertTrue(f.read() == custom_txt)
678
679 def test_remove_uboot_no_all_section(self):
680 with tempfile.TemporaryDirectory() as boot_dir:
681 config_txt = """\
682arm_64bit=1
683device_tree_address=0x3000000
684
685[pi4]
686# This is a comment that should be included
687kernel=uboot_rpi_4.bin
688max_framebuffers=2
689
690[pi3]
691kernel=uboot_rpi_3.bin
692"""
693 expected_config_txt = """\
694arm_64bit=1
695# commented by do-release-upgrade (LP: #1936401)
696#device_tree_address=0x3000000
697
698[pi4]
699# This is a comment that should be included
700# commented by do-release-upgrade (LP: #1936401)
701#kernel=uboot_rpi_4.bin
702max_framebuffers=2
703
704[pi3]
705# commented by do-release-upgrade (LP: #1936401)
706#kernel=uboot_rpi_3.bin
707# added by do-release-upgrade (LP: #1936401)
708[all]
709kernel=vmlinuz
710initramfs initrd.img followkernel
711"""
712 with open(os.path.join(boot_dir, 'config.txt'), 'w') as f:
713 f.write(config_txt)
714
715 mock_controller = mock.Mock()
716 q = DistUpgradeQuirks(mock_controller, mock.Mock())
717 q._remove_uboot_on_rpi(boot_dir)
718
719 self.assertTrue(os.path.exists(os.path.join(
720 boot_dir, 'config.txt.distUpgrade')))
721 with open(os.path.join(boot_dir, 'config.txt')) as f:
722 self.assertTrue(f.read() == expected_config_txt)
723
522724
523class TestSnapQuirks(unittest.TestCase):725class TestSnapQuirks(unittest.TestCase):
524726

Subscribers

People subscribed via source and target branches