Skip to content

Commit ee88ea0

Browse files
committed
Add support for installing OSes, new step2 mechanism
Signed-off-by: Hector Martin <[email protected]>
1 parent 445e05e commit ee88ea0

12 files changed

Lines changed: 428 additions & 149 deletions

File tree

build.sh

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ PYTHON_PKG=python-$PYTHON_VER-macos11.pkg
1010
PYTHON_URI="https://www.python.org/ftp/python/$PYTHON_VER/$PYTHON_PKG"
1111

1212
M1N1="$PWD/m1n1"
13+
UBOOT="$PWD/u-boot"
1314
ARTWORK="$PWD/artwork"
1415
SRC="$PWD/src"
1516
DL="$PWD/dl"
@@ -32,13 +33,22 @@ wget -Nc "$PYTHON_URI"
3233

3334
echo "Building m1n1..."
3435

35-
make -C "$M1N1"
36+
make -C "$M1N1" -j4
37+
38+
echo "Building u-boot..."
39+
40+
make -C "$UBOOT" CROSS_COMPILE=aarch64-linux-gnu- -j4 apple_m1_defconfig all
3641

3742
echo "Copying files..."
3843

3944
cp -r "$SRC"/* "$PACKAGE/"
4045
cp "$ARTWORK/logos/icns/AsahiLinux_logomark.icns" "$PACKAGE/logo.icns"
41-
cp "$M1N1/build/m1n1.macho" "$M1N1/build/m1n1.bin" "$PACKAGE"
46+
mkdir -p "$PACKAGE/boot"
47+
cp "$M1N1/build/m1n1.bin" "$PACKAGE/boot"
48+
cat "$M1N1/build/m1n1.bin" \
49+
"$UBOOT/arch/arm/dts/"t[86]*.dtb \
50+
"$UBOOT/u-boot-nodtb.bin" \
51+
> "$PACKAGE/boot/m1n1-uboot.bin"
4252

4353
echo "Extracting Python framework..."
4454

data/installer_data.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"os_list": [
3+
{
4+
"name": "Asahi Linux reference distro (Arch Linux ARM)",
5+
"default_os_name": "Asahi Linux",
6+
"boot_object": "m1n1-uboot.bin",
7+
"package": "asahi-base.zip",
8+
"supported_fw": ["12.1"],
9+
"partitions": [
10+
{
11+
"name": "EFI",
12+
"type": "EFI",
13+
"size": "512MB",
14+
"format": "fat",
15+
"volume_id": "0x2abf9f91",
16+
"copy_firmware": true,
17+
"copy_installer_data": true,
18+
"source": "esp"
19+
},
20+
{
21+
"name": "Root",
22+
"type": "Linux",
23+
"size": "5GB",
24+
"expand": true,
25+
"image": "root.img"
26+
}
27+
]
28+
},
29+
{
30+
"name": "UEFI environment only (m1n1 + U-Boot + ESP)",
31+
"default_os_name": "UEFI boot",
32+
"boot_object": "m1n1-uboot.bin",
33+
"partitions": [
34+
{
35+
"name": "EFI",
36+
"type": "EFI",
37+
"size": "512MB",
38+
"format": "fat",
39+
"copy_firmware": true,
40+
"copy_installer_data": true
41+
}
42+
]
43+
},
44+
{
45+
"name": "Tethered boot (m1n1, for development)",
46+
"default_os_name": "m1n1 proxy",
47+
"expert": true,
48+
"boot_object": "m1n1.bin",
49+
"partitions": []
50+
}
51+
]
52+
}

src/bootstrap.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ set -e
66
export LANG=C
77
export LC_ALL=C
88

9-
BASE=http://localhost:5000
9+
export INSTALLER_BASE=http://localhost:5000
10+
export INSTALLER_DATA=http://localhost:5000/data/installer_data.json
11+
export REPO_BASE=https://de.mirror.asahilinux.org
1012
PKG=installer.tar.gz
1113

1214
#TMP="$(mktemp -d)"
@@ -20,7 +22,8 @@ cd "$TMP"
2022

2123
echo " Downloading..."
2224

23-
curl -s -L -O "$BASE/$PKG"
25+
curl -s -L -O "$INSTALLER_BASE/$PKG"
26+
curl -s -L -O "$INSTALLER_DATA"
2427

2528
echo " Extracting..."
2629

src/main.py

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#!/usr/bin/python3
22
# SPDX-License-Identifier: MIT
3-
import os, os.path, shlex, subprocess, sys, time, termios
3+
import os, os.path, shlex, subprocess, sys, time, termios, json
44
from dataclasses import dataclass
55

6-
import system, osenum, stub, diskutil
6+
import system, osenum, stub, diskutil, osinstall, firmware
77
from util import *
88

9-
STUB_SIZE = align_up(2500 * 1000 * 1000)
9+
STUB_SIZE = align_up(2500 * 1000 * 1000, 1024 * 1024)
1010

1111
@dataclass
1212
class IPSW:
@@ -48,6 +48,9 @@ class IPSW:
4848
]
4949

5050
class InstallerMain:
51+
def __init__(self):
52+
self.data = json.load(open("installer_data.json"))
53+
5154
def choice(self, prompt, options, default=None):
5255
is_array = False
5356
if isinstance(options, list):
@@ -93,48 +96,67 @@ def check_cur_os(self):
9396
def action_install_into_container(self, avail_parts):
9497
self.check_cur_os()
9598

99+
template = self.choose_os()
100+
96101
containers = {str(i): p.desc for i,p in enumerate(self.parts) if p in avail_parts}
97102

98103
print()
99104
print("Choose a container to install into:")
100105
idx = self.choice("Target container", containers)
101106
self.part = self.parts[int(idx)]
102107

103-
print(f"Installing stub macOS into {self.part.name} ({self.part.label})")
104-
105108
ipsw = self.choose_ipsw()
106-
self.ins = stub.Installer(self.sysinfo, self.dutil, self.osinfo, ipsw)
109+
self.ins = stub.StubInstaller(self.sysinfo, self.dutil, self.osinfo, ipsw)
110+
self.osins = osinstall.OSInstaller(self.dutil, self.data, template)
111+
self.osins.load_package()
107112

108-
self.ins.prepare_volume(self.part)
109-
self.ins.check_volume()
110-
self.ins.install_files(self.cur_os)
111-
self.step2()
113+
self.do_install()
112114

113115
def action_install_into_free(self, avail_free):
114116
self.check_cur_os()
115117

118+
template = self.choose_os()
119+
120+
self.osins = osinstall.OSInstaller(self.dutil, self.data, template)
121+
self.osins.load_package()
122+
116123
frees = {str(i): p.desc for i,p in enumerate(self.parts) if p in avail_free}
117124

118125
print()
119126
print("Choose a free area to install into:")
120127
idx = self.choice("Target area", frees)
121128
free_part = self.parts[int(idx)]
122129

123-
label = input("Enter a name for your OS (Linux): ") or "Linux"
130+
label = input(f"Enter a name for your OS ({self.osins.name}): ") or self.osins.name
131+
self.osins.name = label
124132
print()
125133

126134
ipsw = self.choose_ipsw()
127-
self.ins = stub.Installer(self.sysinfo, self.dutil, self.osinfo, ipsw)
135+
self.ins = stub.StubInstaller(self.sysinfo, self.dutil, self.osinfo, ipsw)
128136

129137
print(f"Creating new stub macOS named {label}")
130-
self.part = self.dutil.addPartition(free_part.name, "apfs", label, "2.5G")
138+
self.part = self.dutil.addPartition(free_part.name, "apfs", label, STUB_SIZE)
131139

132-
print()
140+
self.do_install()
141+
142+
def do_install(self):
133143
print(f"Installing stub macOS into {self.part.name} ({self.part.label})")
134144

135145
self.ins.prepare_volume(self.part)
136146
self.ins.check_volume()
137147
self.ins.install_files(self.cur_os)
148+
149+
self.osins.partition_disk(self.part.name)
150+
151+
pkg = None
152+
if self.osins.needs_firmware:
153+
pkg = firmware.FWPackage("firmware.tar")
154+
self.ins.collect_firmware(pkg)
155+
pkg.close()
156+
self.osins.firmware_package = pkg
157+
158+
self.osins.install(self.ins.boot_obj_path)
159+
138160
self.step2()
139161

140162
def choose_ipsw(self):
@@ -162,6 +184,11 @@ def choose_ipsw(self):
162184

163185
return ipsw
164186

187+
def choose_os(self):
188+
print("Choose an OS to install:")
189+
idx = self.choice("OS", [i["name"] for i in self.data["os_list"]])
190+
return self.data["os_list"][idx]
191+
165192
def set_reduced_security(self):
166193
print( "We are about to prepare your new stub OS for booting in")
167194
print( "Reduced Security mode. Please enter your macOS credentials")
@@ -197,7 +224,6 @@ def step2(self):
197224
self.step2_indirect()
198225
else:
199226
assert False # should never happen, we don't give users the option
200-
self.step2_old_macos()
201227

202228
def step2_1tr_direct(self):
203229
self.startup_disk_recovery()
@@ -213,6 +239,11 @@ def flush_input(self):
213239
pass
214240

215241
def step2_indirect(self):
242+
# Hide the new volume until step2 is done
243+
os.rename(self.ins.systemversion_path,
244+
self.ins.systemversion_path.replace("SystemVersion.plist",
245+
"SystemVersion-disabled.plist"))
246+
216247
print( "The system will now shut down.")
217248
print( "To complete the installation, perform the following steps:")
218249
print()
@@ -222,46 +253,18 @@ def step2_indirect(self):
222253
print( " and that you press and hold down the button once, not multiple times.")
223254
print( " This is required to put the machine into the right mode.")
224255
print( "3. Release it once 'Entering startup options' is displayed.")
225-
print( "4. Choose Options.")
256+
print(f"4. Choose {self.part.label}.")
226257
print( "5. You will briefly see a 'macOS Recovery' dialog.")
227258
print( " * If you are asked to 'Select a volume to recover',")
228259
print( " then choose your normal macOS volume and click Next.")
229-
print( "6. Click on the Utilities menu and select Terminal.")
230-
print( "7. Type the following command and follow the prompts:")
231-
print()
232-
print(f"/Volumes/{shlex.quote(self.part.label)}/step2.sh")
260+
print( "6. Once the 'Asahi Linux installer' screen appears, follow the prompts.")
233261
print()
234262
time.sleep(2)
235263
self.flush_input()
236264
print( "Press enter to shut down the system.")
237265
input()
238266
os.system("shutdown -h now")
239267

240-
def step2_old_macos(self):
241-
print( "To complete the installation, perform the following steps:")
242-
print()
243-
print( "1. Go to System Settings -> Startup Disk.")
244-
print(f"2. Choose '{self.part.label}' and authenticate yourself.")
245-
print( " * The system will reboot into the Boot Recovery Assistant.")
246-
print( "3. Authenticate yourself again.")
247-
print( " * The system will go into a reboot loop.")
248-
print( "4. Press and hold down the power button to shut the system down.")
249-
print( " * If you end up in the Startup Options screen, choose Shut Down.")
250-
print( " Do not skip ahead to step 5. It won't work.")
251-
print( " * If you end up in Recovery mode, select Shut Down from the Apple menu.")
252-
print( " Do not skip ahead to step 6. It won't work.")
253-
print( "5. Press and hold down the power button to power on the system.")
254-
print( " * It is important that the system be fully powered off before this step,")
255-
print( " and that you press and hold down the button once, not multiple times.")
256-
print( " This is required to put the machine into the right mode.")
257-
print( "6. Release it once 'Entering startup options' is displayed.")
258-
print( "7. Choose Options.")
259-
print( "8. Click on the Utilities menu and select Terminal.")
260-
print( "9. Type the following command and follow the prompts:")
261-
print()
262-
print(f"/Volumes/{shlex.quote(self.part.label)}/step2.sh")
263-
print()
264-
265268
def startup_disk(self, recovery=False, volume_blessed=False, reboot=False):
266269
print(f"When the Startup Disk screen appears, choose '{self.part.label}', then click Restart.")
267270
if not volume_blessed:
@@ -415,13 +418,13 @@ def main(self):
415418
actions = {}
416419

417420
if parts_free:
418-
actions["f"] = "Install Asahi Linux into free space"
421+
actions["f"] = "Install an OS into free space"
419422
if parts_empty_apfs:
420-
actions["a"] = "Install a macOS stub and m1n1 into an existing APFS container"
423+
actions["a"] = "Install an OS into an existing APFS container"
421424
if parts_system and False:
422-
actions["r"] = "Resize an existing OS and install Asahi Linux"
425+
actions["r"] = "Resize an existing OS and install a new OS"
423426
if self.sysinfo.boot_mode == "one true recoveryOS":
424-
actions["m"] = "Install m1n1 into an existing OS container"
427+
actions["m"] = "Upgrade bootloader of an existing OS"
425428

426429
if not actions:
427430
print("No actions available on this system.")

0 commit comments

Comments
 (0)