74 lines
4.9 KiB
Python
74 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: MPL-2.0
|
|
"""Build an unsigned private Nokia boot trial. Never flashes or unlocks a phone."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
from beatles_boot import unpack_boot, pack_boot, read_cpio, write_cpio, add_file
|
|
|
|
ROOT=Path(__file__).resolve().parents[2]
|
|
ENGINE=ROOT/'tundra/out/beatles/gecko-156.0.1'
|
|
|
|
|
|
def digest(data):return hashlib.sha256(data).hexdigest()
|
|
|
|
|
|
def main():
|
|
parser=argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--backup',type=Path,default=ROOT/'tundra/out/beatles/backups/20261017')
|
|
parser.add_argument('--output',type=Path,required=True)
|
|
args=parser.parse_args();out=args.output.resolve()
|
|
if out.exists():raise ValueError('Choose a new output directory')
|
|
backup=json.loads((args.backup/'backup.json').read_text())
|
|
verified=json.loads((args.backup/'verification.json').read_text())
|
|
if backup['device']!='beatles' or backup['firmware']!='40.00.17.01':raise ValueError('Unsupported firmware')
|
|
for name in ('boot','recovery','aboot','abootbak'):
|
|
data=(args.backup/(name+'.img')).read_bytes(); record=backup['partitions'][name]
|
|
if len(data)!=record['bytes'] or digest(data)!=record['sha256'] or verified['partitions'][name]['sha256']!=record['sha256']:
|
|
raise ValueError('Backup verification failed: '+name)
|
|
original=(args.backup/'boot.img').read_bytes();parts=unpack_boot(original)
|
|
entries=read_cpio(parts['ramdisk'])
|
|
rc=entries['init.b2g.rc']['data']
|
|
expected=b'service b2g /system/bin/b2g.sh\n class main\n'
|
|
if rc.count(expected)!=1:raise ValueError('Unexpected KaiOS service definition')
|
|
entries['init.b2g.rc']['data']=rc.replace(expected,expected+b' disabled\n')
|
|
entries['init.rc']['data']+=b'\nimport /init.vulpes.rc\n'
|
|
out.mkdir(parents=True,mode=0o700)
|
|
sysroot=ENGINE/'sysroot/rootfs'
|
|
compiler=[str(ENGINE/'mozbuild/clang/bin/clang'),'--target=arm-linux-gnueabihf',
|
|
'--sysroot='+str(sysroot),'--gcc-install-dir='+str(sysroot/'usr/lib/gcc/arm-linux-gnueabihf/12'),
|
|
'-fuse-ld=lld','-march=armv7-a','-mfpu=neon','-mfloat-abi=hard','-O2','-Wall','-Wextra','-Werror']
|
|
subprocess.run(compiler+['-static',str(ROOT/'tundra/probes/beatles-control-client.c'),'-o',str(out/'control-client')],check=True)
|
|
supervisor=out/'vulpes-display'
|
|
subprocess.run(compiler+['-static',str(ROOT/'tundra/probes/beatles-display-session.c'),'-o',str(supervisor)],check=True)
|
|
add_file(entries,'sbin/vulpes-display',supervisor.read_bytes(),0o755)
|
|
for name in ('init.vulpes.rc','vulpes-boot.sh'):
|
|
add_file(entries,name,(ROOT/'tundra/devices/beatles/boot'/name).read_bytes(),0o755 if name.endswith('.sh') else 0o644)
|
|
ramdisk=write_cpio(entries);image=pack_boot(original,ramdisk)
|
|
if len(image)>backup['partitions']['boot']['bytes']:raise ValueError('Boot partition too small')
|
|
check=unpack_boot(image)
|
|
if check['kernel']!=parts['kernel']:raise ValueError('Kernel changed')
|
|
reread=read_cpio(check['ramdisk'])
|
|
for name,value in entries.items():
|
|
if reread[name]['data']!=value['data']:raise ValueError('Ramdisk data mismatch: '+name)
|
|
# Only size/name size may change when serializing; owners and modes must survive.
|
|
if any(reread[name]['fields'][i]!=value['fields'][i] for i in (0,1,2,3,4,5,7,8,9,10,12)):
|
|
raise ValueError('Ramdisk metadata mismatch: '+name)
|
|
name='vulpes-beatles-boot-trial-UNSIGNED.img';(out/name).write_bytes(image)
|
|
record={'device':'beatles','firmware':'40.00.17.01','image':name,'sha256':digest(image),
|
|
'bytes':len(image),'kernelSha256':digest(parts['kernel']),'originalBootSha256':digest(original),
|
|
'controlClientSha256':digest((out/'control-client').read_bytes()),
|
|
'gecko':'156.0.1','runtimePath':'/data/local/vulpes-beatles','deadlineSeconds':1800,
|
|
'unsigned':True,'bootTested':False,'flashReady':False,'standaloneSystem':False,
|
|
'dependencies':['Existing vendor /system and modem partitions','Prepared persistent runtime, not included in boot partition'],
|
|
'recovery':'KaiOS fallback if runtime is missing or fails, or after 30 minutes; boot partition restoration not qualified',
|
|
'sourceFiles':{str(p.relative_to(ROOT)):digest(p.read_bytes()) for p in [Path(__file__),ROOT/'tundra/tools/beatles_boot.py',ROOT/'tundra/probes/beatles-display-session.c',ROOT/'tundra/probes/beatles-control-client.c',ROOT/'tundra/probes/beatles-lid.h',ROOT/'tundra/probes/beatles-hall.h',ROOT/'tundra/devices/beatles/boot/init.vulpes.rc',ROOT/'tundra/devices/beatles/boot/vulpes-boot.sh']}}
|
|
(out/'build.json').write_text(json.dumps(record,indent=2)+'\n')
|
|
(out/'SHA256SUMS').write_text(record['sha256']+' '+name+'\n')
|
|
print('Built private unsigned boot trial:',out/name)
|
|
print('Not boot-tested or flash-qualified; existing vendor system and staged runtime required.')
|
|
|
|
|
|
if __name__=='__main__':main()
|