Skip to content

Commit ff1af0c

Browse files
committed
asahi_firmware: Add cpio file generation
Signed-off-by: Hector Martin <[email protected]>
1 parent 3eb5e7f commit ff1af0c

6 files changed

Lines changed: 135 additions & 20 deletions

File tree

asahi_firmware/core.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: MIT
2-
import tarfile, io, logging
2+
import tarfile, io, logging, os.path
33
from hashlib import sha256
4+
from . import cpio
45

56
class FWFile(object):
67
def __init__(self, name, data):
@@ -20,14 +21,31 @@ def __hash__(self):
2021
return hash(self.sha)
2122

2223
class FWPackage(object):
23-
def __init__(self, target):
24-
self.path = target
25-
self.tarfile = tarfile.open(target, mode="w")
24+
def __init__(self, tar_path, cpio_path):
25+
self.closed = False
26+
self.tar_path = tar_path
27+
self.cpio_path = cpio_path
28+
self.tarfile = tarfile.open(tar_path, mode="w")
29+
self.cpiofile = cpio.CPIO(cpio_path)
2630
self.hashes = {}
2731
self.manifest = []
2832

2933
def close(self):
34+
if self.closed:
35+
return
36+
37+
ti = tarfile.TarInfo("vendorfw/.vendorfw.manifest")
38+
ti.type = tarfile.REGTYPE
39+
fd = io.BytesIO()
40+
for i in self.manifest:
41+
fd.write(i.encode("ascii") + b"\n")
42+
ti.size = fd.tell()
43+
fd.seek(0)
44+
self.cpiofile.addfile(ti, fd)
45+
3046
self.tarfile.close()
47+
self.cpiofile.close()
48+
self.closed = True
3149

3250
def add_file(self, name, data):
3351
ti = tarfile.TarInfo(name)
@@ -45,6 +63,12 @@ def add_file(self, name, data):
4563

4664
logging.info(f"+ {self.manifest[-1]}")
4765
self.tarfile.addfile(ti, fd)
66+
if fd is not None:
67+
fd.seek(0)
68+
ti.name = os.path.join("vendorfw", ti.name)
69+
if ti.linkname:
70+
ti.linkname = os.path.join("vendorfw", ti.linkname)
71+
self.cpiofile.addfile(ti, fd)
4872

4973
def add_files(self, it):
5074
for name, data in it:
@@ -56,4 +80,4 @@ def save_manifest(self, filename):
5680
fd.write(i + "\n")
5781

5882
def __del__(self):
59-
self.tarfile.close()
83+
self.close()

asahi_firmware/cpio.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# SPDX-License-Identifier: MIT
2+
import os.path, tarfile
3+
4+
class CPIO:
5+
DIR = 0o040755
6+
FILE = 0o100644
7+
8+
def __init__(self, filename):
9+
self.fd = open(filename, "wb")
10+
self.closed = False
11+
self.dirs = set()
12+
self.inode = 1
13+
self.nlink = {}
14+
self.nlinkoff = {}
15+
self.inodemap = {}
16+
17+
def cpio_hdr(self, name, mode, size, target=None):
18+
if target is not None:
19+
inode = self.inodemap[target]
20+
self.nlink[inode] += 1
21+
p = self.fd.tell()
22+
for i in self.nlinkoff[inode]:
23+
self.fd.seek(i)
24+
self.fd.write(b"%08x" % self.nlink[inode])
25+
self.fd.seek(p)
26+
else:
27+
inode = self.inode
28+
self.inode += 1
29+
self.nlink[inode] = 1
30+
self.nlinkoff[inode] = []
31+
32+
self.inodemap[name] = inode
33+
34+
p = self.fd.tell()
35+
if p & 3:
36+
self.fd.write(bytes(4 - (p & 3)))
37+
38+
self.fd.write(b"070701")
39+
self.nlinkoff[inode].append(self.fd.tell() + 8 * 4)
40+
hdr = [
41+
inode,
42+
mode,
43+
0, # uid
44+
0, # gid
45+
self.nlink[inode],
46+
0, # mtime
47+
size,
48+
0, # maj
49+
0, # min
50+
0, # rmaj
51+
0, # rmin
52+
len(name) + 1,
53+
0, # chksum
54+
]
55+
self.fd.write(b"".join(b"%08x" % i for i in hdr))
56+
self.fd.write(name.encode("ascii") + b"\x00")
57+
58+
p = self.fd.tell()
59+
if p & 3:
60+
self.fd.write(bytes(4 - (p & 3)))
61+
62+
def addfile(self, ti, fd):
63+
path = ""
64+
for i in ti.name.split("/")[:-1]:
65+
if not i:
66+
continue
67+
path = os.path.join(path, i)
68+
if path not in self.dirs:
69+
self.cpio_hdr(path, self.DIR, 0)
70+
self.dirs.add(path)
71+
72+
if ti.type == tarfile.LNKTYPE:
73+
self.cpio_hdr(ti.name, self.FILE, 0, ti.linkname)
74+
elif ti.type == tarfile.REGTYPE:
75+
self.cpio_hdr(ti.name, self.FILE, ti.size)
76+
self.fd.write(fd.read())
77+
else:
78+
raise Exception(f"Unsupported file type {ti.type}")
79+
80+
def close(self):
81+
if self.closed:
82+
return
83+
self.closed = True
84+
self.cpio_hdr("TRAILER!!!", self.FILE, 0);
85+
self.fd.close()
86+
87+
def __del__(self):
88+
self.close()

asahi_firmware/kernel.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ def __init__(self, source_path):
1212
self.load(source_path)
1313

1414
def load(self, source_path):
15-
for fname in os.listdir(source_path):
16-
if fname.startswith("kernelcache"):
17-
kern_path = os.path.join(source_path, fname)
18-
break
15+
if os.path.isdir(source_path):
16+
for fname in os.listdir(source_path):
17+
if fname.startswith("kernelcache"):
18+
kern_path = os.path.join(source_path, fname)
19+
break
20+
else:
21+
raise Exception("Could not find kernelcache")
1922
else:
20-
raise Exception("Could not find kernelcache")
23+
kern_path = source_path
2124

2225
log.info(f"Extracting firmware from kernel at {kern_path}")
2326

asahi_firmware/update.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
# SPDX-License-Identifier: MIT
2-
import pathlib, tempfile, subprocess
2+
import pathlib, tempfile, subprocess, os.path
33

44
from .core import FWPackage
55
from .wifi import WiFiFWCollection
66
from .bluetooth import BluetoothFWCollection
77
from .multitouch import MultitouchFWCollection
88
from .kernel import KernelFWCollection
99

10-
def update_firmware(source, dest, manifest):
10+
def update_firmware(source, dest):
1111
raw_fw = source.joinpath("all_firmware.tar.gz")
1212
if not raw_fw.exists():
1313
print(f"Could not find {raw_fw}")
1414

15-
pkg = FWPackage(dest)
15+
pkg = FWPackage(os.path.join(dest, "firmware.tar"),
16+
os.path.join(dest, "firmware.cpio"))
1617

1718
with tempfile.TemporaryDirectory() as tmpdir:
1819
tmpdir = pathlib.Path(tmpdir)
@@ -32,7 +33,7 @@ def update_firmware(source, dest, manifest):
3233

3334
pkg.close()
3435

35-
pkg.save_manifest(manifest)
36+
pkg.save_manifest(os.path.join(dest, "manifest.txt"))
3637

3738
if __name__ == "__main__":
3839
import argparse
@@ -42,11 +43,9 @@ def update_firmware(source, dest, manifest):
4243
parser = argparse.ArgumentParser(description='Update vendor firmware tarball')
4344
parser.add_argument('source', metavar='SOURCE', type=pathlib.Path,
4445
help='path containing raw firmware')
45-
parser.add_argument('dest', metavar='TARBALL', type=pathlib.Path,
46-
help='output vendor firmware tarball')
47-
parser.add_argument('manifest', metavar='MANIFEST', type=pathlib.Path,
48-
help='output vendor firmware manifest')
46+
parser.add_argument('dest', metavar='DEST', type=pathlib.Path,
47+
help='output path for vendor firmware')
4948

5049
args = parser.parse_args()
5150

52-
update_firmware(args.source, args.dest, args.manifest)
51+
update_firmware(args.source, args.dest)

src/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ def do_install(self, total_size=None):
317317

318318
pkg = None
319319
if self.osins.needs_firmware:
320-
pkg = asahi_firmware.core.FWPackage("firmware.tar")
320+
pkg = asahi_firmware.core.FWPackage("firmware.tar", "firmware.cpio")
321321
self.ins.collect_firmware(pkg)
322322
pkg.close()
323323
self.osins.firmware_package = pkg

src/osinstall.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ def install(self, boot_obj_path):
123123
logging.info(f"Firmware -> {base}")
124124
os.makedirs(base, exist_ok=True)
125125
shutil.copy(self.firmware_package.path, os.path.join(base, "firmware.tar"))
126+
shutil.copy(self.firmware_package.cpio_path, os.path.join(base, "firmware.cpio"))
126127
self.firmware_package.save_manifest(os.path.join(base, "manifest.txt"))
127128
if part.get("copy_installer_data", False):
128129
mountpoint = self.dutil.mount(info.name)

0 commit comments

Comments
 (0)