Skip to content

Commit 3865eba

Browse files
WhatAmISupposedToPutHerejannau
authored andcommitted
Add an option to rebuild vendorfw bundle
Signed-off-by: Sasha Finkelstein <[email protected]>
1 parent 763ee10 commit 3865eba

2 files changed

Lines changed: 102 additions & 18 deletions

File tree

src/main.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,74 @@ def action_repair_or_upgrade(self, oses, upgrade):
437437
# Go for step2 again
438438
self.step2()
439439

440+
def action_rebuild_vendorfw(self, oses):
441+
choices = {str(i): f"{p.desc}\n {str(o)}" for i, (p, o) in enumerate(oses)}
442+
443+
if len(choices) > 1:
444+
print()
445+
p_question("Choose an install to rebuild firmware for:")
446+
idx = self.choice("Installed OS", choices)
447+
else:
448+
idx = list(choices.keys())[0]
449+
450+
self.part, osi = oses[int(idx)]
451+
self.ins = stub.StubInstaller(self.sysinfo, self.dutil, self.osinfo)
452+
if not self.ins.check_existing_install(osi):
453+
p_error( "The existing installation is missing files.")
454+
p_message(f"This tool can only rebuild firmware on installations that completed the first")
455+
p_message( "stage of the installation process. If it was interrupted, please")
456+
p_message( "delete the partitions manually and reinstall from scratch.")
457+
return False
458+
459+
vars = m1n1.extract_vars(self.ins.boot_obj_path)
460+
if vars is None:
461+
p_error("Could not get variables from the installed m1n1")
462+
p_message(f"Path: {self.ins.boot_obj_path}")
463+
return False
464+
465+
esp_id = None
466+
for var in vars:
467+
k, v = var.split('=')
468+
if k == "chosen.asahi,efi-system-partition":
469+
esp_id = v
470+
break
471+
if esp_id is None:
472+
p_error("Unable to determine the associated EFI system partition for this install.")
473+
p_message("Is first stage m1n1 corrupted?")
474+
return False
475+
476+
target = self.dutil.get_partition_info(esp_id)
477+
mountpoint = self.dutil.mount(target.name)
478+
479+
os.makedirs("vendorfw", exist_ok=True)
480+
fw_pkg = asahi_firmware.core.FWPackage("vendorfw")
481+
ipsw = None
482+
for ver in IPSW_VERSIONS:
483+
if ver.version == osi.version:
484+
ipsw = ver
485+
break
486+
if ipsw is None:
487+
p_error(f"This install is using an unsupported macOS version {osi.version}")
488+
p_message("Unable to rebuild firmware")
489+
return False
490+
491+
self.ins.load_ipsw(ipsw)
492+
self.ins.load_identity()
493+
self.ins.collect_firmware(fw_pkg)
494+
fw_pkg.close()
495+
496+
p_plain(f" Copying firmware into {target.name} partition...")
497+
base = os.path.join(mountpoint, "vendorfw")
498+
logging.info(f"Firmware -> {base}")
499+
shutil.copytree(fw_pkg.path, base, dirs_exist_ok=True)
500+
501+
print()
502+
p_success(f"Firmware rebuild complete. Press enter to continue.")
503+
self.input()
504+
print()
505+
506+
return True
507+
440508
def do_install(self, total_size=None):
441509
p_progress(f"Installing stub macOS into {self.part.name} ({self.part.label})")
442510

@@ -907,6 +975,7 @@ def main_loop(self):
907975
parts_resizable = []
908976
oses_incomplete = []
909977
oses_upgradable = []
978+
oses_vendorfw = []
910979

911980
for i, p in enumerate(self.parts):
912981
p.index = i
@@ -981,6 +1050,8 @@ def main_loop(self):
9811050
p_plain(f" OS: [{state}] {os}")
9821051
if os.attached_partitions:
9831052
p_plain(f" Extra partitions: {' '.join('#' + str(i.index) for i in os.attached_partitions)}")
1053+
if os.stub and os.m1n1_ver:
1054+
oses_vendorfw.append((p, os))
9841055
if os.stub and os.m1n1_ver and os.m1n1_ver != self.m1n1_ver:
9851056
oses_upgradable.append((p, os))
9861057
elif os.stub and not (os.bp and os.bp.get("coih", None)):
@@ -1018,6 +1089,9 @@ def main_loop(self):
10181089
if oses_upgradable:
10191090
actions["m"] = "Upgrade m1n1 on an existing OS"
10201091
default = default or "m"
1092+
if oses_vendorfw:
1093+
actions["v"] = "Rebuild vendor firmware package"
1094+
default = default or "v"
10211095

10221096
if not actions:
10231097
p_error("No actions available on this system.")
@@ -1046,6 +1120,8 @@ def main_loop(self):
10461120
return self.action_select_disk()
10471121
elif act == "w":
10481122
return self.action_wipe()
1123+
elif act == "v":
1124+
return self.action_rebuild_vendorfw(oses_vendorfw)
10491125
elif act == "q":
10501126
return False
10511127

src/stub.py

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -209,26 +209,20 @@ def repair(self, cur_os):
209209
else:
210210
logging.info("'asahi' dir not found in ESP")
211211

212-
def install_files(self, cur_os):
213-
logging.info("StubInstaller.install_files()")
214-
logging.info(f"VGID: {self.osi.vgid}")
215-
logging.info(f"OS info: {self.osi}")
216-
217-
p_progress("Beginning stub OS install...")
212+
def load_identity(self):
218213
self.get_paths()
219214

220215
logging.info("Parsing metadata...")
221216

222-
sysver = plistlib.load(self.open("SystemVersion.plist"))
223217
manifest = plistlib.load(self.open("BuildManifest.plist"))
224-
bootcaches = plistlib.load(self.open("usr/standalone/bootcaches.plist"))
218+
self.bootcaches = plistlib.load(self.open("usr/standalone/bootcaches.plist"))
225219
self.flush_progress()
226220

227221
if self.is_ota:
228-
variant = "macOS Customer Software Update"
222+
self.variant = "macOS Customer Software Update"
229223
behavior = "Update"
230224
else:
231-
variant = "macOS Customer"
225+
self.variant = "macOS Customer"
232226
behavior = "Erase"
233227

234228
self.manifest = manifest
@@ -237,7 +231,7 @@ def install_files(self, cur_os):
237231
identity["ApChipID"] != f'0x{self.sysinfo.chip_id:04X}' or
238232
identity["Info"]["DeviceClass"] != self.sysinfo.device_class or
239233
identity["Info"]["RestoreBehavior"] != behavior or
240-
identity["Info"]["Variant"] != variant):
234+
identity["Info"]["Variant"] != self.variant):
241235
continue
242236
break
243237
else:
@@ -248,6 +242,16 @@ def install_files(self, cur_os):
248242
self.all_identities = manifest["BuildIdentities"]
249243
self.identity = identity
250244
manifest["BuildIdentities"] = [identity]
245+
return identity
246+
247+
def install_files(self, cur_os):
248+
logging.info("StubInstaller.install_files()")
249+
logging.info(f"VGID: {self.osi.vgid}")
250+
logging.info(f"OS info: {self.osi}")
251+
252+
p_progress("Beginning stub OS install...")
253+
identity = self.load_identity()
254+
sysver = plistlib.load(self.open("SystemVersion.plist"))
251255

252256
self.stub_info.update({
253257
"vgid": self.osi.vgid,
@@ -292,13 +296,13 @@ def install_files(self, cur_os):
292296

293297
os.makedirs(self.pb_vgid, exist_ok=True)
294298

295-
bless2 = bootcaches["bless2"]
299+
bless2 = self.bootcaches["bless2"]
296300

297301
restore_bundle = os.path.join(self.pb_vgid, bless2["RestoreBundlePath"])
298302
os.makedirs(restore_bundle, exist_ok=True)
299303
restore_manifest = os.path.join(restore_bundle, "BuildManifest.plist")
300304
with open(restore_manifest, "wb") as fd:
301-
plistlib.dump(manifest, fd)
305+
plistlib.dump(self.manifest, fd)
302306
self.copy_idata.append((restore_manifest, "BuildManifest.plist"))
303307
self.extract("SystemVersion.plist", restore_bundle)
304308
self.extract("RestoreVersion.plist", restore_bundle)
@@ -311,10 +315,9 @@ def install_files(self, cur_os):
311315
self.extract_file("BootabilityBundle/Restore/Firmware/Bootability.dmg.trustcache",
312316
os.path.join(restore_bundle, "Bootability/Bootability.trustcache"))
313317

314-
self.extract_tree(f"Firmware/Manifests/restore/{variant}/", restore_bundle)
318+
self.extract_tree(f"Firmware/Manifests/restore/{self.variant}/", restore_bundle)
315319

316320
copied = set()
317-
self.kernel_path = None
318321
for key, val in identity["Manifest"].items():
319322
if key in ("BaseSystem", "OS", "Ap,SystemVolumeCanonicalMetadata",
320323
"RestoreRamDisk", "RestoreTrustCache"):
@@ -328,8 +331,6 @@ def install_files(self, cur_os):
328331
if path.startswith("kernelcache."):
329332
name = os.path.basename(path)
330333
self.copy_idata.append((os.path.join(restore_bundle, name), name))
331-
if self.kernel_path is None:
332-
self.kernel_path = os.path.join(restore_bundle, name)
333334
copied.add(path)
334335

335336
self.flush_progress()
@@ -404,7 +405,10 @@ def collect_firmware(self, pkg):
404405
shutil.rmtree("fud_firmware")
405406

406407
os.makedirs("fud_firmware", exist_ok=True)
408+
bless2 = self.bootcaches["bless2"]
409+
restore_bundle = os.path.join(self.pb_vgid, bless2["RestoreBundlePath"])
407410
copied = set()
411+
kernel_path = None
408412
for identity in [self.identity]:
409413
if (identity["Info"]["RestoreBehavior"] != "Erase" or
410414
identity["Info"]["Variant"] != "macOS Customer"):
@@ -419,6 +423,10 @@ def collect_firmware(self, pkg):
419423
"StaticTrustCache", "SystemVolume"):
420424
continue
421425
path = val["Info"]["Path"]
426+
if path.startswith("kernelcache."):
427+
name = os.path.basename(path)
428+
if kernel_path is None:
429+
kernel_path = os.path.join(restore_bundle, name)
422430
if (not val["Info"].get("IsFUDFirmware", False)
423431
or val["Info"].get("IsLoadedByiBoot", False)
424432
or val["Info"].get("IsLoadedByiBootStage1", False)
@@ -450,7 +458,7 @@ def collect_firmware(self, pkg):
450458
col = ISPFWCollection("recovery/usr/sbin/")
451459
pkg.add_files(sorted(col.files()))
452460
logging.info("Collecting Kernel firmware")
453-
col = KernelFWCollection(self.kernel_path)
461+
col = KernelFWCollection(kernel_path)
454462
pkg.add_files(sorted(col.files()))
455463
logging.info("Making fallback firmware archive")
456464
subprocess.run(["tar", "czf", "all_firmware.tar.gz",

0 commit comments

Comments
 (0)