33import os , os .path , shlex , subprocess , sys , time , termios , json , getpass
44from dataclasses import dataclass
55
6- import system , osenum , stub , diskutil , osinstall , asahi_firmware
6+ import system , osenum , stub , diskutil , osinstall , asahi_firmware , m1n1
77from util import *
88
99PART_ALIGN = psize ("1MiB" )
@@ -89,6 +89,8 @@ def __init__(self):
8989 self .ipsw = None
9090 self .osins = None
9191 self .osi = None
92+ self .m1n1 = "boot/m1n1.bin"
93+ self .m1n1_ver = m1n1 .get_version (self .m1n1 )
9294
9395 def input (self ):
9496 self .flush_input ()
@@ -307,30 +309,58 @@ def action_install_into_free(self, avail_free):
307309
308310 self .do_install (os_size )
309311
310- def action_resume (self , oses ):
312+ def action_resume_or_upgrade (self , oses , upgrade ):
311313 choices = {str (i ): f"{ p .desc } \n { str (o )} " for i , (p , o ) in enumerate (oses )}
312314
313315 if len (choices ) > 1 :
314316 print ()
315- p_question ("Choose an incomplete install to resume:" )
317+ if upgrade :
318+ p_question ("Choose an existing install to upgrade:" )
319+ else :
320+ p_question ("Choose an incomplete install to resume:" )
316321 idx = self .choice ("Installed OS" , choices )
317322 else :
318323 idx = list (choices .keys ())[0 ]
319324
320325 self .part , osi = oses [int (idx )]
321326
322- p_progress (f"Resuming installation into { self .part .name } ({ self .part .label } )" )
327+ if upgrade :
328+ p_progress (f"Upgrading installation { self .part .name } ({ self .part .label } )" )
329+ p_info (f" Old m1n1 stage 1 version: { osi .m1n1_ver } " )
330+ p_info (f" New m1n1 stage 1 version: { self .m1n1_ver } " )
331+ print ()
332+ else :
333+ p_progress (f"Resuming installation into { self .part .name } ({ self .part .label } )" )
323334
324335 self .ins = stub .StubInstaller (self .sysinfo , self .dutil , self .osinfo )
325336 if not self .ins .check_existing_install (osi ):
326- p_error ("The existing installation is missing files." )
327- p_message ("This tool can only resume installations that completed the first" )
328- p_message ("stage of the installation process. If it was interrupted, please" )
329- p_message ("delete the partitions manually and reinstall from scratch." )
330- return True
337+ op = "upgrade" if upgrade else "resume"
338+ p_error ( "The existing installation is missing files." )
339+ p_message (f"This tool can only { op } installations that completed the first" )
340+ p_message ( "stage of the installation process. If it was interrupted, please" )
341+ p_message ( "delete the partitions manually and reinstall from scratch." )
342+ return False
331343
332344 self .dutil .remount_rw (self .ins .osi .system )
333345
346+ if upgrade :
347+ # Note: we get the vars out of the boot.bin in the system volume instead of the
348+ # actual installed fuOS. This is arguably the better option, since it allows
349+ # users to fix their install using this functionality if they messed up the boot
350+ # object.
351+ vars = m1n1 .extract_vars (self .ins .boot_obj_path )
352+ if vars is None :
353+ p_error ("Could not get variables from the installed m1n1" )
354+ p_message (f"Path: { self .ins .boot_obj_path } " )
355+ return False
356+
357+ p_progress (f"Transferring m1n1 variables:" )
358+ for v in vars :
359+ p_info (f" { v } " )
360+
361+ print ()
362+ m1n1 .build (self .m1n1 , self .ins .boot_obj_path , vars )
363+
334364 # Unhide the SystemVersion, if hidden
335365 self .ins .prepare_for_bless ()
336366
@@ -792,6 +822,7 @@ def main_loop(self):
792822 parts_empty_apfs = []
793823 parts_resizable = []
794824 oses_incomplete = []
825+ oses_upgradable = []
795826
796827 for i , p in enumerate (self .parts ):
797828 if p .type in ("Apple_APFS_ISC" ,):
@@ -853,7 +884,9 @@ def main_loop(self):
853884 else :
854885 state += " "
855886 p_plain (f" OS: [{ state } ] { os } " )
856- if os .stub and not (os .bp and os .bp .get ("coih" , None )):
887+ if os .stub and os .m1n1_ver and os .m1n1_ver != self .m1n1_ver :
888+ oses_upgradable .append ((p , os ))
889+ elif os .stub and not (os .bp and os .bp .get ("coih" , None )):
857890 oses_incomplete .append ((p , os ))
858891
859892 print ()
@@ -879,8 +912,9 @@ def main_loop(self):
879912 if parts_resizable :
880913 actions ["r" ] = "Resize an existing partition to make space for a new OS"
881914 default = default or "r"
882- if self .sysinfo .boot_mode == "one true recoveryOS" and False :
883- actions ["m" ] = "Upgrade bootloader of an existing OS"
915+ if oses_upgradable :
916+ actions ["m" ] = "Upgrade m1n1 on an existing OS"
917+ default = default or "m"
884918
885919 if not actions :
886920 p_error ("No actions available on this system." )
@@ -900,10 +934,9 @@ def main_loop(self):
900934 elif act == "r" :
901935 return self .action_resize (parts_resizable )
902936 elif act == "m" :
903- p_error ("Unimplemented" )
904- sys .exit (1 )
937+ return self .action_resume_or_upgrade (oses_upgradable , upgrade = True )
905938 elif act == "p" :
906- return self .action_resume (oses_incomplete )
939+ return self .action_resume_or_upgrade (oses_incomplete , upgrade = False )
907940 elif act == "q" :
908941 return False
909942
0 commit comments