├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README ├── README.g12a ├── acs_tool.py ├── amlcblk.c ├── amlcblk.h ├── amlsblk.c ├── amlsblk.h ├── bl2.c ├── bl2.h ├── bl3.c ├── bl3.h ├── fip.c ├── fip.h ├── gxlimg.h ├── main.c ├── meson.build ├── reveng ├── aml_encrypt_gxl.decompiled └── bl2.decompiled └── ssl.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | gxlimg 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | env: 4 | global: 5 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 6 | # via the "travis encrypt" command using the project repo's public key 7 | - secure: "DM+2Mn6zlsJEyy/JE0k3xmBU490mnDv5bvF80WqiuCFEIMVMzLy/7WM9AVFC0YrtSxnRSBWkdRY70Heyo+xmYB9DMi+olem6GM2aLiMxKAW6TScrrbs8pyS+hovHiOEOaoy+/TVjzd6+BDgbFfPmSL+KYW9ribfAJ8QsnJW5sJJ6rGpVkeqNuIGh2AuQ3wlJuDWupdJBsQkpIuflfVdOmfGqJUHoxR5zvovfLrQXqBWNgNy1detIVi0bdc1izO3gwfhFqORq/d0rLNKutpoEmDu/BMWdhT1qWd0hOqWUhPKoat8DKlWBrCADSdk1kIVxzs/ekD3V11iNneHRbogZevJAI2TO5ehfs+Rn0b/X7tYFt70NDS/kZebvGdSp82Yvp7VNjvWRrmdLPhnqfz2QzwMnfc3Mxtj1Xw3R1GjqySEhxA1oLNBDwD2C+53CqKN35Cnf+rCas0+yHwtRPeB94BIsVmFs85Vsdzn3o4R9JYYihE/Jwk7/IEaQbCwKDP0FmxcBvpIhGXv3eADY0Y70j5/E85erTYzb8pRUOriyIiGebE9MLYkseRC/EPRz2WUfXubPLTxx+3dKTX+vZmuPxzNqZ1T8uC8Zu5bqk5Gy0KU7suapG1KsJAdmjrWvllhTp+W5Md65JwMjdlkrxz1HDzu7hvU1s9rHDQN3B0DuUUs=" 8 | 9 | before_install: 10 | - sudo apt-get install libssl-dev 11 | - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- 12 | 13 | addons: 14 | coverity_scan: 15 | project: 16 | name: "repk/gxlimg" 17 | description: "Build submitted via Travis CI" 18 | notification_email: repk@triplefau.lt 19 | build_command_prepend: "make clean" 20 | build_command: "make -j4" 21 | branch_pattern: master 22 | 23 | script: 24 | - make clean 25 | - make -j4 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2019 Repk. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIEDi 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC := $(CROSS_COMPILE)gcc 2 | LD := $(CROSS_COMPILE)gcc 3 | CFLAGS ?= -W -Wall -std=gnu99 -D_GNU_SOURCE 4 | LDFLAGS ?= -lssl -lcrypto 5 | 6 | SRC := main.c bl2.c bl3.c amlcblk.c amlsblk.c fip.c 7 | OBJ := $(SRC:%.c=%.o) 8 | BUILDDIR := build 9 | DEPENDS := $(OBJ:%.o=$(BUILDDIR)/%.d) 10 | PROG := gxlimg 11 | FIPARCHIVE := libretech-cc_fip_20180418.tar.gz 12 | 13 | ifeq ($(DEBUG), 1) 14 | CFLAGS += -DDEBUG=1 -g -O0 15 | endif 16 | 17 | define rm-file 18 | @(rm $(1) 2>/dev/null && \ 19 | echo "rm $(1)") || true 20 | 21 | endef 22 | 23 | define rm-dir 24 | @(rmdir -p $(1) 2>/dev/null && \ 25 | echo "rmdir -p $(1)") || true 26 | 27 | endef 28 | 29 | all: $(PROG) 30 | 31 | $(PROG): $(OBJ:%=$(BUILDDIR)/%) 32 | $(LD) -o $@ $^ $(LDFLAGS) 33 | 34 | $(BUILDDIR)/%.o: %.c | builddir 35 | $(CC) -MMD -c $(CFLAGS) -o $@ $< 36 | 37 | $(BUILDDIR)/$(FIPARCHIVE): 38 | curl -L https://github.com/BayLibre/u-boot/releases/download/v2017.11-libretech-cc/$(FIPARCHIVE) -o $(BUILDDIR)/$(FIPARCHIVE) 39 | 40 | fip-clean: 41 | @(rm -r $(BUILDDIR)/fip 2>/dev/null && \ 42 | echo "rm -rf $(BUILDDIR)/fip") || true 43 | 44 | ifdef FIP 45 | fip: fip-clean 46 | cp -r "$(FIP)/" "$(BUILDDIR)/fip" 47 | else 48 | fip: $(BUILDDIR)/$(FIPARCHIVE) fip-clean 49 | tar -mxvzf $< -C $(BUILDDIR) 50 | endif 51 | 52 | $(BUILDDIR)/fip/gxl/bl2_acs.bin: fip 53 | python acs_tool.py $(BUILDDIR)/fip/gxl/bl2.bin \ 54 | $@ $(BUILDDIR)/fip/gxl/acs.bin 0 55 | 56 | $(BUILDDIR)/fip/gxl/bl2_new.bin: $(BUILDDIR)/fip/gxl/bl2_acs.bin fip 57 | $(BUILDDIR)/fip/blx_fix.sh $< zero_tmp \ 58 | $(BUILDDIR)/fip/gxl/bl2_zero.bin $(BUILDDIR)/fip/gxl/bl21.bin \ 59 | $(BUILDDIR)/fip/gxl/bl21_zero.bin $@ bl2 60 | 61 | $(BUILDDIR)/fip/gxl/bl2.bin.enc: $(BUILDDIR)/fip/gxl/bl2_new.bin $(PROG) 62 | ./$(PROG) -t bl2 -s $< $@ 63 | 64 | $(BUILDDIR)/fip/gxl/bl30_new.bin: fip 65 | $(BUILDDIR)/fip/blx_fix.sh $(BUILDDIR)/fip/gxl/bl30.bin zero_tmp \ 66 | $(BUILDDIR)/fip/gxl/bl30_zero.bin $(BUILDDIR)/fip/gxl/bl301.bin \ 67 | $(BUILDDIR)/fip/gxl/bl301_zero.bin $@ bl30 68 | 69 | $(BUILDDIR)/fip/gxl/bl30.bin.enc: $(BUILDDIR)/fip/gxl/bl30_new.bin $(PROG) 70 | ./$(PROG) -t bl3x -c $< $@ 71 | 72 | $(BUILDDIR)/fip/gxl/bl31.bin.enc: fip $(PROG) $(ATF) 73 | ifdef ATF 74 | ./$(PROG) -t bl3x -c "$(ATF)" $@ 75 | else 76 | ./$(PROG) -t bl3x -c $(BUILDDIR)/fip/gxl/bl31.img $@ 77 | endif 78 | 79 | $(BUILDDIR)/fip/gxl/u-boot.bin.enc: $(PROG) $(UBOOT) 80 | ifdef UBOOT 81 | ./$(PROG) -t bl3x -c "$(UBOOT)" $@ 82 | else 83 | $(error UBOOT variable is missing) 84 | endif 85 | 86 | $(BUILDDIR)/gxl-boot.bin: $(PROG) $(BUILDDIR)/fip/gxl/bl2.bin.enc $(BUILDDIR)/fip/gxl/bl30.bin.enc $(BUILDDIR)/fip/gxl/bl31.bin.enc $(BUILDDIR)/fip/gxl/u-boot.bin.enc 87 | ./$(PROG) -t fip \ 88 | --bl2 $(BUILDDIR)/fip/gxl/bl2.bin.enc \ 89 | --bl30 $(BUILDDIR)/fip/gxl/bl30.bin.enc \ 90 | --bl31 $(BUILDDIR)/fip/gxl/bl31.bin.enc \ 91 | --bl33 $(BUILDDIR)/fip/gxl/u-boot.bin.enc \ 92 | $@ 93 | 94 | image: $(BUILDDIR)/gxl-boot.bin 95 | 96 | image-clean: fip-clean 97 | $(call rm-file, $(BUILDDIR)/$(FIPARCHIVE)) 98 | $(call rm-file, $(BUILDDIR)/gxl-boot.bin) 99 | 100 | builddir: 101 | @mkdir -p $(BUILDDIR) 102 | 103 | .PHONY: clean distclean 104 | 105 | clean: image-clean 106 | $(foreach o, $(OBJ:%=$(BUILDDIR)/%), $(call rm-file, $(o))) 107 | $(foreach d, $(DEPENDS), $(call rm-file, $(d))) 108 | $(call rm-dir, $(BUILDDIR)) 109 | 110 | distclean: clean 111 | $(call rm-file, $(PROG)) 112 | 113 | -include ${DEPENDS} 114 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | gxlimg 2 | ====== 3 | 4 | Gxlimg is an amlogic s905x boot image creation tools. This has been made by 5 | reverse engineering the aml_encrypt_gxl binary used to create boot image as 6 | well as the BL2 arm binary that reads FIP header and loads BL3* and u-boot 7 | images. 8 | 9 | Some reverse engineering notes can be found in reveng directory, please note 10 | that because I decompiled aml_encrypt_gxl by hand (not using a tool like IDA), 11 | some mistakes could have slept in those notes. The purpose was first to 12 | familiarized myself with subject such as ARM assembly and Arm Trust Firmware 13 | (ATF). 14 | 15 | 16 | I - Build instructions 17 | ---------------------- 18 | 19 | In order to build this tool just use the following: 20 | 21 | $ make 22 | 23 | If you want some debug information and print use the following instead: 24 | 25 | $ make DEBUG=1 26 | 27 | II.A - Create a boot image - automated version 28 | ---------------------------------------------- 29 | 30 | 1) Compile u-boot: 31 | 32 | Compile mainline u-boot from http://git.denx.de/ 33 | 34 | $ make CROSS_COMPILE=aarch64-elf- libretech-cc_defconfig 35 | $ make CROSS_COMPILE=aarch64-elf- all 36 | 37 | Replace aarch64-elf- with the prefix of your aarch64 toolchain. 38 | This should produce a u-boot-dtb.bin file. 39 | 40 | 2) Generate image 41 | 42 | Use the image target to generate the build/gxl-boot.bin image. 43 | 44 | $ make image UBOOT= 45 | 46 | II.B - Create a boot image - manual version 47 | ------------------------------------------- 48 | 49 | In order to create a boot image for s905x board such as lepotato board, you 50 | can follow those steps below. 51 | 52 | 53 | 1) Get BL* binaries: 54 | 55 | Follow instructions from here https://github.com/BayLibre/u-boot that build 56 | vendor u-boot in order to get BL* binaries. 57 | 58 | 2) Sign BL2: 59 | 60 | Use gxlimg as below to sign BL2 binary along with its DDR initialization 61 | datas: 62 | 63 | $ ./fip/blx_fix.sh fip/gxl/bl2_acs.bin zero_tmp fip/gxl/bl2_zero.bin \ 64 | fip/gxl/bl21.bin fip/gxl/bl21_zero.bin bl2_new.bin bl2 65 | $ gxlimg -t bl2 -s bl2_new.bin bl2.bin.enc 66 | 67 | 3) Encrypt BL3*: 68 | 69 | Use the following to encrypt BL3* binary 70 | 71 | $ gxlimg -t bl3x -c ./fip/gxl/bl30_new.bin bl30.bin.enc 72 | $ gxlimg -t bl3x -c ./fip/gxl/bl31.img bl31.img.enc 73 | 74 | 4) Compile u-boot: 75 | 76 | Compile mainline u-boot from http://git.denx.de/ 77 | 78 | $ make CROSS_COMPILE=aarch64-elf- libretech-cc_defconfig 79 | $ make CROSS_COMPILE=aarch64-elf- all 80 | 81 | Replace aarch64-elf- with the prefix of your aarch64 toolchain. 82 | This should produce a u-boot-dtb.bin file. 83 | 84 | 5) Encrypt u-boot: 85 | 86 | Encrypt the previously built u-boot image: 87 | 88 | $ gxlimg -t bl3x -c u-boot.bin u-boot.bin.enc 89 | 90 | 6) Create the final boot image: 91 | 92 | To create the final image issue the command below: 93 | 94 | $ gxlimg -t fip --bl2 ./bl2.bin.enc --bl30 ./bl30.bin.enc \ 95 | --bl31 ./bl31.img.enc --bl33 ./u-boot.bin.enc ./gxl-boot.bin 96 | 97 | II.c - Create USB boot files 98 | ---------------------------- 99 | 100 | The ROM USB Boot mode on GXL, GXM & AXG uses .usb.bl2 and .usb.tpl files to boot, 101 | as used by pyamlboot (https://github.com/superna9999/pyamlboot) 102 | 103 | The first binary contains the encrypted BL2 and the second the rest of the gxl-boot.bin 104 | 105 | To generate: 106 | 107 | $ dd if=gxl-boot.bin of=u-boot.bin.usb.bl2 bs=49152 count=1 108 | $ dd if=gxl-boot.bin of=u-boot.bin.usb.tpl skip=49152 bs=1 109 | 110 | Then copy them into files//u-boot.bin.usb.bl2 & files//u-boot.bin.usb.tpl 111 | in the checkout source of pyamlboot, then follow instructions at: 112 | https://github.com/superna9999/pyamlboot#booting-full-system-from-usb 113 | 114 | III - Write the image to the SD card 115 | ------------------------------------ 116 | 117 | 1) Prepare the SD card: 118 | 119 | First compute how many sectors gxl-boot.bin uses by getting its size and 120 | dividing it by 512. 121 | 122 | Let's say your gxl-boot.bin is 653312 Bytes thus you need 1276 sectors you 123 | can give you a little bit margin and choose a gap of 2048 sectors for your 124 | first partion. 125 | 126 | Then create the partition: 127 | 128 | $ sudo fdisk /dev/mmcpblk0 129 | $ n 130 | $ p 131 | $ 1 132 | $ 2048 # <- enter the number of sectors previously computed 133 | $ # <- take the whole remaining space for the partition 134 | $ w 135 | 136 | Then create a fs on it: 137 | 138 | $ mkfs.ext3 /dev/mmcblk0p1 139 | 140 | And copy or install a linux ARM file system. 141 | 142 | 2) Copy boot image to SD: 143 | 144 | The copy image to SD being careful to not erase the first 512 block (holding 145 | the MBR partition table) 146 | 147 | $ dd if=./gxl-boot.bin of=/dev/mmcblk0 bs=512 seek=1 148 | -------------------------------------------------------------------------------- /README.g12a: -------------------------------------------------------------------------------- 1 | WiP support for G12a 2 | ==================== 3 | 4 | Completed: 5 | - BL2 signing 6 | - BL30/BL31/BL32/BL33 signing 7 | - FIP 8 | 9 | WiP: 10 | - lz4 compression for BL33 11 | 12 | TODO (when completed): 13 | - Refactor code to merge common functions 14 | - Change name 15 | - Integrate README 16 | 17 | Commands equivalence with aml_encrypt 18 | ------------------------------------- 19 | 20 | gxlimg -t bl2 -s bl2_new.bin bl2.n.bin.sig 21 | aml_encrypt --bl2sig --input bl2_new.bin --output bl2.n.bin.sig 22 | 23 | gxlimg -t bl30 -s bl30_new.bin bl30_new.bin.g12.enc 24 | aml_encrypt --bl30sig --input bl30_new.bin --output bl30_new.bin.g12.enc --level v3 25 | 26 | # For a reason to be determined, this step is useless, using bl30_new.bin.g12.enc for FIP is fine 27 | gxlimg -t bl3x -s bl30_new.bin.g12.enc bl30_new.bin.enc 28 | aml_encrypt --bl3sig --input bl30_new.bin.g12.enc --output bl30_new.bin.enc --level v3 --type bl30 29 | 30 | gxlimg -t bl3x -s bl31.img bl31.img.sig.enc 31 | aml_encrypt --bl3sig --input bl31.img --output bl31.img.enc --level v3 --type bl3 32 | 33 | gxlimg -t bl3x -s bl32.img bl32.img.enc 34 | aml_encrypt --bl3sig --input bl32.img --output bl32.img.enc --level v3 --type bl32 35 | 36 | gxlimg -t bl3x -s u-boot.bin bl33.bin.enc 37 | aml_encrypt --bl3sig --input u-boot.bin --output bl33.bin.enc --level v3 --type bl33 38 | 39 | gxlimg -t fip --bl2 bl2.n.bin.sig --ddrfw ddr4_1d.fw --ddrfw ddr4_2d.fw --ddrfw ddr3_1d.fw --ddrfw piei.fw --ddrfw lpddr4_1d.fw --ddrfw lpddr4_2d.fw --ddrfw diag_lpddr4.fw --ddrfw aml_ddr.fw --ddrfw lpddr3_1d.fw --bl30 bl30_new.bin.g12.enc --bl31 bl31.img.sig.enc --bl33 bl33.bin.enc --rev v3 gxl-boot.bin 40 | aml_encrypt --bootmk --output u-boot.bin --level v3 --bl2 bl2.n.bin.sig --bl30 bl30_new.bin.enc --bl31 bl31.img.enc --bl33 bl33.bin.enc --ddrfw1 ddr4_1d.fw --ddrfw2 ddr4_2d.fw --ddrfw3 ddr3_1d.fw --ddrfw4 piei.fw --ddrfw5 lpddr4_1d.fw --ddrfw6 lpddr4_2d.fw --ddrfw7 diag_lpddr4.fw --ddrfw8 aml_ddr.fw --ddrfw9 lpddr3_1d.fw 41 | 42 | Known limitations 43 | ----------------- 44 | 45 | To be able to sign BL31 and BL32 we are stripping the IMG header to extract 46 | the BIN payload. The IMG header contains some info that is used in the FIP 47 | header even though it seems that this is not vital. TBD if that's important or 48 | not. 49 | -------------------------------------------------------------------------------- /acs_tool.py: -------------------------------------------------------------------------------- 1 | # uncompyle6 version 3.2.4 2 | # Python bytecode 2.7 (62211) 3 | # Decompiled from: Python 3.6.7 (default, Nov 3 2018, 21:32:46) 4 | # [GCC 8.2.0] 5 | # Embedded file name: acs_tool.py 6 | # Compiled at: 2015-09-17 07:30:19 7 | import sys, os, os.path, json 8 | from struct import * 9 | import codecs, shutil, copy, collections 10 | ENTRY_POINT_OFFSET = 4 11 | BL2_HEADER_OFFSET = 4096 12 | ACS_TOOL_VERSION = 1 13 | acs_v1 = collections.OrderedDict() 14 | acs_v1['acs_magic'] = b'acs__' 15 | acs_v1['chip_type'] = 1 16 | acs_v1['version'] = 2 17 | acs_v1['acs_set_length'] = 8 18 | acs_v1['ddr_magic'] = b'ddrs_' 19 | acs_v1['ddr_set_version'] = 1 20 | acs_v1['ddr_set_length'] = 2 21 | acs_v1['ddr_set_addr'] = 8 22 | acs_v1['ddrt_magic'] = b'ddrt_' 23 | acs_v1['ddrt_set_version'] = 1 24 | acs_v1['ddrt_set_length'] = 2 25 | acs_v1['ddrt_set_addr'] = 8 26 | acs_v1['pll_magic'] = b'pll__' 27 | acs_v1['pll_set_version'] = 1 28 | acs_v1['pll_set_length'] = 2 29 | acs_v1['pll_set_addr'] = 8 30 | check_excepts = [ 31 | 'ddr_set_addr', 'ddrt_set_addr', 'pll_set_addr'] 32 | check_excepts_length = ['ddr_set_length', 'ddrt_set_length', 'pll_set_length'] 33 | key_versions = ['version', 'ddr_set_version', 'ddrt_set_version', 'pll_set_version'] 34 | 35 | class acs_tool(object): 36 | 37 | def __init__(self, file_des, file_des_tmp, file_src, debug): 38 | self.debug = int(debug) 39 | self.file_des = file_des 40 | self.file_src = file_src 41 | self.file_des_tmp = file_des_tmp 42 | self.acs_des = copy.deepcopy(acs_v1) 43 | self.acs_src = copy.deepcopy(acs_v1) 44 | self.acs_base = copy.deepcopy(acs_v1) 45 | 46 | def init_acs(self, acs_struct, file_name, bl2): 47 | seek_position = 0 48 | file_handler = open(file_name, 'rb+') 49 | file_handler.seek(ENTRY_POINT_OFFSET) 50 | acs_entry_point, = unpack('H', file_handler.read(2)) 51 | acs_entry_point -= bl2 * BL2_HEADER_OFFSET 52 | seek_position = acs_entry_point 53 | self.log_print(file_name) 54 | for key in acs_struct.keys(): 55 | file_handler.seek(seek_position) 56 | if isinstance(acs_struct[key], bytes): 57 | seek_position += len(acs_struct[key]) 58 | acs_struct[key] = file_handler.read(len(acs_struct[key])) 59 | elif isinstance(acs_struct[key], int): 60 | seek_position += acs_struct[key] 61 | if 1 == acs_struct[key]: 62 | acs_struct[key], = unpack('B', file_handler.read(1)) 63 | else: 64 | acs_struct[key], = unpack('H', file_handler.read(2)) 65 | if key in check_excepts: 66 | acs_struct[key] -= bl2 * BL2_HEADER_OFFSET 67 | else: 68 | raise RuntimeError("Unexpected type") 69 | self.log_print(key + ' ' + str(acs_struct[key])) 70 | 71 | file_handler.close() 72 | 73 | def check_acs(self): 74 | err_counter = 0 75 | for key in self.acs_des.keys(): 76 | if self.acs_des[key] != self.acs_src[key] and key not in check_excepts: 77 | print("Warning! ACS %s doesn't match!! %s/%s" % (key, self.acs_des[key], self.acs_src[key])) 78 | 79 | for key in key_versions: 80 | if self.acs_des[key] > self.acs_src[key]: 81 | self.acs_des[key] = self.acs_src[key] 82 | print('Warning! ACS src %s too old!' % key) 83 | 84 | for key in self.acs_base.keys(): 85 | if isinstance(self.acs_base[key], bytes): 86 | if self.acs_des[key] != self.acs_base[key]: 87 | err_counter += 1 88 | print('Error! ACS DES %s error!! Value: %s, Expect: %s' % (key, self.acs_des[key], self.acs_base[key])) 89 | if self.acs_src[key] != self.acs_base[key]: 90 | err_counter += 1 91 | print('Error! ACS DES %s error!! Value: %s, Expect: %s' % (key, self.acs_src[key], self.acs_base[key])) 92 | 93 | if self.acs_des['version'] > ACS_TOOL_VERSION: 94 | print('Error! Please update acs tool! v%s>v%s' % (self.acs_des['version'], ACS_TOOL_VERSION)) 95 | err_counter += 1 96 | return err_counter 97 | 98 | def copy_data(self): 99 | file_des = open(self.file_des_tmp, 'r+b') 100 | file_src = open(self.file_src, 'rb') 101 | for key_addr, key_length in zip(check_excepts, check_excepts_length): 102 | file_des.seek(self.acs_des[key_addr]) 103 | file_src.seek(self.acs_src[key_addr]) 104 | file_des.write(file_src.read(self.acs_des[key_length])) 105 | 106 | file_des.close() 107 | file_src.close() 108 | return 0 109 | 110 | def run(self): 111 | file_des_tmp_handler = open(self.file_des_tmp, 'wb+') 112 | file_des_handler = open(self.file_des, 'rb+') 113 | file_des_tmp_handler.write(file_des_handler.read()) 114 | file_des_tmp_handler.close() 115 | file_des_handler.close() 116 | self.init_acs(self.acs_des, self.file_des_tmp, 1) 117 | self.init_acs(self.acs_src, self.file_src, 0) 118 | if self.check_acs(): 119 | print('ACS check failed! Compile Abort!') 120 | return -1 121 | self.copy_data() 122 | print('ACS tool process done.') 123 | 124 | def log_print(self, log): 125 | if self.debug or True: 126 | print(log) 127 | 128 | 129 | if __name__ == '__main__': 130 | if sys.argv[1] == '--help' or sys.argv[1] == '-help': 131 | print('acs_tool.py [bl2.bin] [bl2_tmp.bin] [acs.bin] [debug(1/0)]') 132 | exit(1) 133 | if len(sys.argv) != 5: 134 | print('acs_tool.py [bl2.bin] [bl2_tmp.bin] [acs.bin] [debug(1/0)]') 135 | exit(1) 136 | tool = acs_tool(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]) 137 | if tool.run(): 138 | exit(1) 139 | # okay decompiling acs_tool.pyc 140 | -------------------------------------------------------------------------------- /amlcblk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "gxlimg.h" 14 | #include "amlcblk.h" 15 | #include "ssl.h" 16 | 17 | #define AMLCBLK_ENCRYPT (1 << 0) 18 | #define AMLCBLK_HDR (1 << 1) 19 | #define AMLCBLK_IS_ENCRYPT(h) ((h)->flag & (AMLCBLK_ENCRYPT)) 20 | #define AMLCBLK_SET_ENCRYPT(h) ((h)->flag |= (AMLCBLK_ENCRYPT)) 21 | #define AMLCBLK_HAS_HDR(h) ((h)->flag & (AMLCBLK_HDR)) 22 | #define AMLCBLK_SET_HDR(h) ((h)->flag |= (AMLCBLK_HDR)) 23 | 24 | #define bh_wr(h, sz, off, val) \ 25 | (*(uint ## sz ## _t *)((h) + off) = htole ## sz(val)) 26 | #define bh_rd(h, sz, off) \ 27 | (le ## sz ## toh(*(uint ## sz ## _t *)((h) + off))) 28 | 29 | #define AMLCBLKSZ 256 30 | 31 | #define BL31_MAGIC (0x12348765) 32 | #define AMLCBLK_MAGIC (*(uint32_t *)"AMLC") 33 | 34 | /** 35 | * Read a block of data from a file 36 | * 37 | * @param fd: File descriptor to read a block from 38 | * @param blk: Filled with read data 39 | * @param sz: Size of block to read from file 40 | * @return: Negative number on error, read size otherwise. The only reason that 41 | * return value could be different from sz on success is when EOF has been 42 | * encountered while reading the file. 43 | */ 44 | static ssize_t gi_amlcblk_read_blk(int fd, uint8_t *blk, size_t sz) 45 | { 46 | size_t i; 47 | ssize_t nr = 1; 48 | 49 | for(i = 0; (i < sz) && (nr != 0); i += nr) { 50 | nr = read(fd, blk + i, sz - i); 51 | if(nr < 0) 52 | goto out; 53 | } 54 | nr = i; 55 | out: 56 | return nr; 57 | } 58 | 59 | /** 60 | * Write a block of data into a file 61 | * 62 | * @param fd: File descriptor to write a block into 63 | * @param blk: Actual block data 64 | * @param sz: Size of block to write into file 65 | * @return: Negative number on error, sz otherwise. 66 | */ 67 | static ssize_t gi_amlcblk_write_blk(int fd, uint8_t *blk, size_t sz) 68 | { 69 | size_t i; 70 | ssize_t nr; 71 | 72 | for(i = 0; i < sz; i += nr) { 73 | nr = write(fd, blk + i, sz - i); 74 | if(nr < 0) 75 | goto out; 76 | } 77 | nr = i; 78 | out: 79 | return nr; 80 | } 81 | 82 | /** 83 | * Pad a partial filled block 84 | * 85 | * @param acb: Amlogic Control block descriptor 86 | * @param blk: block to pad 87 | * @param sz: Size of actual data in blk 88 | */ 89 | static void gi_amlcblk_blk_pad(struct amlcblk const *acb, uint8_t *blk, 90 | size_t off) 91 | { 92 | memset(blk + off, 0, acb->blksz - off); 93 | } 94 | 95 | /** 96 | * Initialize Amlogic Control block descriptor from binary file 97 | * 98 | * @param acb: Amlogic Control block descriptor to init 99 | * @param fd: Binary file descriptor 100 | * @return: 0 on success, negative number otherwise 101 | */ 102 | int gi_amlcblk_init(struct amlcblk *acb, int fd) 103 | { 104 | size_t nr; 105 | off_t fsz; 106 | uint8_t hdr[AMLCBLKSZ]; 107 | int ret; 108 | 109 | acb->flag = 0; 110 | fsz = lseek(fd, 0, SEEK_SET); 111 | if(fsz < 0) { 112 | SEEK_ERR(fsz, ret); 113 | goto out; 114 | } 115 | 116 | nr = gi_amlcblk_read_blk(fd, hdr, sizeof(hdr)); 117 | if(nr != sizeof(hdr)) { 118 | PERR("Cannot read input header: "); 119 | ret = -EINVAL; 120 | goto out; 121 | } 122 | 123 | if(bh_rd(hdr, 32, 0) == BL31_MAGIC) 124 | AMLCBLK_SET_HDR(acb); 125 | 126 | acb->blksz = 0x200; 127 | fsz = lseek(fd, 0, SEEK_END); 128 | if(fsz < 0) { 129 | SEEK_ERR(fsz, ret); 130 | goto out; 131 | } 132 | fsz = ROUNDUP(fsz, acb->blksz); 133 | if(AMLCBLK_HAS_HDR(acb)) 134 | fsz -= acb->blksz; 135 | acb->firstblk = fsz; 136 | acb->encsz = 0; 137 | acb->payloadsz = fsz; 138 | ret = gi_random(acb->iv, sizeof(acb->iv)); 139 | if(ret < 0) 140 | goto out; 141 | ret = gi_random(acb->aeskey, sizeof(acb->aeskey)); 142 | out: 143 | return ret; 144 | } 145 | 146 | /** 147 | * Compute payload sha256 hash 148 | * 149 | * @param acb: control block descriptor 150 | * @param fd: output boot image file descriptor 151 | * @param hash: Filled with the sha256 hash 152 | * @return: 0 on success, negative number otherwise 153 | */ 154 | static int gi_amlcblk_sha256(struct amlcblk const *acb, int fd, 155 | uint8_t hash[32]) 156 | { 157 | EVP_MD_CTX *ctx; 158 | uint8_t *tmp = NULL; 159 | size_t i; 160 | ssize_t nr; 161 | off_t off; 162 | int ret; 163 | 164 | ctx = EVP_MD_CTX_new(); 165 | if(ctx == NULL) { 166 | ret = -ERR_get_error(); 167 | SSLERR(ret, "Cannot create digest context: "); 168 | goto out; 169 | } 170 | 171 | ret = -ENOMEM; 172 | tmp = malloc(acb->blksz); 173 | if(tmp == NULL) 174 | goto out; 175 | 176 | ret = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); 177 | if(ret != 1) { 178 | ret = -ERR_get_error(); 179 | SSLERR(ret, "Cannot init digest context: "); 180 | goto out; 181 | } 182 | 183 | off = lseek(fd, acb->firstblk, SEEK_SET); 184 | if(off < 0) { 185 | SEEK_ERR(off, ret); 186 | goto out; 187 | } 188 | 189 | for(i = 0; i < acb->encsz; i += nr) { 190 | nr = gi_amlcblk_read_blk(fd, tmp, acb->blksz); 191 | if((nr < 0) || ((size_t)nr != acb->blksz)) { 192 | PERR("Cannot read fd %d:", fd); 193 | ret = (int)nr; 194 | goto out; 195 | } 196 | ret = EVP_DigestUpdate(ctx, tmp, acb->blksz); 197 | if(ret != 1) { 198 | ret = -ERR_get_error(); 199 | SSLERR(ret, "Cannot hash data block: "); 200 | goto out; 201 | } 202 | if(i != 0) 203 | continue; 204 | off = lseek(fd, acb->blksz, SEEK_SET); 205 | if(off < 0) { 206 | SEEK_ERR(off, ret); 207 | goto out; 208 | } 209 | } 210 | ret = EVP_DigestFinal_ex(ctx, hash, NULL); 211 | if(ret != 1) { 212 | ret = -ERR_get_error(); 213 | SSLERR(ret, "Cannot finalize hash: "); 214 | goto out; 215 | } 216 | ret = 0; 217 | out: 218 | EVP_MD_CTX_free(ctx); 219 | free(tmp); 220 | return ret; 221 | } 222 | 223 | /** 224 | * Dump control block boot image cipher description 225 | * 226 | * @param acb: Control block descriptor 227 | * @param buf: Buffer to dump cipher description into 228 | * @return: 0 on success negative number otherwise 229 | */ 230 | static int gi_amlcblk_set_desc(struct amlcblk const *acb, uint8_t buf[96]) 231 | { 232 | struct tm lt; 233 | time_t t; 234 | 235 | if(time(&t) == ((time_t)-1)) { 236 | PERR("Cannot get current time: "); 237 | return -errno; 238 | } 239 | 240 | if(localtime_r(&t, <) == NULL) { 241 | PERR("Cannot convert current time: "); 242 | return -errno; 243 | } 244 | 245 | if(!AMLCBLK_IS_ENCRYPT(acb)) { 246 | ERR("Boot image should be encrypted\n"); 247 | return -EINVAL; 248 | } 249 | 250 | snprintf((char *)buf, 96, "AES-CBC%04d/%02d/%02d %02d:%02d:%02d", 251 | lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, 252 | lt.tm_hour, lt.tm_min, lt.tm_sec); 253 | return 0; 254 | } 255 | 256 | /** 257 | * Dump amlogic control block into boot image file 258 | * 259 | * @param acb: Control block descriptor 260 | * @param fd: boot image output file descriptor 261 | * @return: 0 on success, negative number otherwise 262 | */ 263 | int gi_amlcblk_dump_hdr(struct amlcblk const *acb, int fd) 264 | { 265 | off_t off; 266 | int ret; 267 | uint8_t hash[32]; 268 | uint8_t data[AMLCBLKSZ] = {}; 269 | 270 | ret = gi_amlcblk_sha256(acb, fd, hash); 271 | if(ret < 0) 272 | goto out; 273 | 274 | bh_wr(data, 16, 2, acb->blksz); 275 | bh_wr(data, 16, 250, acb->blksz); 276 | bh_wr(data, 32, 20, acb->blksz); /* TODO Why 32bits here ? */ 277 | bh_wr(data, 16, 4, (AMLCBLK_IS_ENCRYPT(acb)) ? 1 : 0); 278 | bh_wr(data, 16, 6, 1); /* TODO find meaning of that */ 279 | bh_wr(data, 32, 12, AMLCBLK_MAGIC); 280 | bh_wr(data, 32, 252, AMLCBLK_MAGIC); 281 | bh_wr(data, 32, 16, acb->firstblk); 282 | bh_wr(data, 32, 24, acb->encsz); 283 | bh_wr(data, 32, 28, acb->payloadsz); 284 | memcpy(data + 32, hash, sizeof(hash)); 285 | memcpy(data + 64, acb->aeskey, sizeof(acb->aeskey)); 286 | memcpy(data + 96, acb->iv, sizeof(acb->iv)); 287 | gi_amlcblk_set_desc(acb, data + 136); 288 | 289 | off = lseek(fd, 0, SEEK_SET); 290 | if(off < 0) { 291 | SEEK_ERR(off, ret); 292 | goto out; 293 | } 294 | ret = gi_amlcblk_write_blk(fd, data, AMLCBLKSZ); 295 | out: 296 | return ret; 297 | } 298 | 299 | /** 300 | * Get amlogic control block header from boot image file 301 | * 302 | * @param acb: Amlogic control block to init from boot image 303 | * @param fd: boot image file descriptor 304 | * @return: 0 on success, negative number otherwise 305 | */ 306 | int gi_amlcblk_read_hdr(struct amlcblk *acb, int fd) 307 | { 308 | ssize_t nr; 309 | int ret; 310 | uint8_t data[AMLCBLKSZ * 2]; 311 | 312 | bzero(acb, sizeof(*acb)); 313 | nr = gi_amlcblk_read_blk(fd, data, sizeof(data)); 314 | if(nr < 0) { 315 | ret = (int)nr; 316 | goto out; 317 | } 318 | 319 | ret = -EINVAL; 320 | acb->blksz = bh_rd(data, 16, 2); 321 | if(acb->blksz != bh_rd(data, 16, 250)) 322 | goto out; 323 | if((uint32_t)acb->blksz != bh_rd(data, 32, 20)) /* TODO Why 32bits here ? */ 324 | goto out; 325 | if(bh_rd(data, 16, 4)) 326 | AMLCBLK_SET_ENCRYPT(acb); 327 | if(bh_rd(data, 32, 12) != AMLCBLK_MAGIC) 328 | goto out; 329 | if(bh_rd(data, 32,12) != AMLCBLK_MAGIC) 330 | goto out; 331 | if(bh_rd(data, 32, 252) != AMLCBLK_MAGIC) 332 | goto out; 333 | if(bh_rd(data, 32, 256) == BL31_MAGIC) 334 | AMLCBLK_SET_HDR(acb); 335 | acb->firstblk = bh_rd(data, 32, 16); 336 | acb->encsz = bh_rd(data, 32, 24); 337 | acb->payloadsz = bh_rd(data, 32, 28); 338 | /* TODO Check hash ? */ 339 | memcpy(acb->aeskey, data + 64, sizeof(acb->aeskey)); 340 | memcpy(acb->iv, data + 96, sizeof(acb->iv)); 341 | /* TODO check gi_amlcblk_set_desc(acb, data + 136); */ 342 | 343 | ret = 0; 344 | out: 345 | return ret; 346 | } 347 | 348 | /** 349 | * Encode a binary input file into a boot image output file 350 | * 351 | * @param acb: Amlogic Control Block descriptor 352 | * @param fout: Boot image output file descriptor 353 | * @param fin: Binary input file descriptor 354 | * @return: 0 on success, negative number otherwise 355 | */ 356 | int gi_amlcblk_aes_enc(struct amlcblk *acb, int fout, int fin) 357 | { 358 | EVP_CIPHER_CTX *ctx = NULL; 359 | uint8_t *block = NULL, *enc = NULL; 360 | size_t i; 361 | ssize_t nr, wnr; 362 | off_t off; 363 | int ret; 364 | uint8_t hdr[AMLCBLKSZ] = {}; 365 | 366 | ret = -EINVAL; 367 | if(acb->payloadsz % acb->blksz) 368 | goto out; 369 | 370 | ctx = EVP_CIPHER_CTX_new(); 371 | if(ctx == NULL) { 372 | ret = -ERR_get_error(); 373 | SSLERR(ret, "Cannot create cipher context: "); 374 | goto out; 375 | } 376 | 377 | ret = -ENOMEM; 378 | block = malloc(acb->blksz); 379 | if(block == NULL) 380 | goto out; 381 | 382 | enc = malloc(acb->blksz); 383 | if(enc == NULL) 384 | goto out; 385 | 386 | ret = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, 387 | acb->aeskey, acb->iv); 388 | if(ret != 1) { 389 | ret = -ERR_get_error(); 390 | SSLERR(ret, "Cannot init cipher context: "); 391 | goto out; 392 | } 393 | 394 | ret = EVP_CIPHER_CTX_set_padding(ctx, 0); 395 | if(ret != 1) { 396 | ret = -ERR_get_error(); 397 | SSLERR(ret, "Cannot disable cipher padding: "); 398 | goto out; 399 | } 400 | 401 | off = lseek(fin, 0, SEEK_SET); 402 | if(off < 0) { 403 | SEEK_ERR(off, ret); 404 | goto out; 405 | } 406 | /* 407 | * Some binary has specific header at first block to describe how to run it 408 | * (load address, entry point, etc) 409 | */ 410 | if(AMLCBLK_HAS_HDR(acb)) { 411 | nr = gi_amlcblk_read_blk(fin, hdr, AMLCBLKSZ); 412 | if(nr != AMLCBLKSZ) { 413 | PERR("Cannot read fin header\n"); 414 | ret = (int)nr; 415 | goto out; 416 | } 417 | off = lseek(fin, acb->blksz, SEEK_SET); 418 | if(off < 0) { 419 | SEEK_ERR(off, ret); 420 | goto out; 421 | } 422 | } 423 | 424 | off = lseek(fout, AMLCBLKSZ, SEEK_SET); 425 | if(off < 0) { 426 | SEEK_ERR(off, ret); 427 | goto out; 428 | } 429 | wnr = gi_amlcblk_write_blk(fout, hdr, AMLCBLKSZ); 430 | if(wnr < 0) { 431 | PERR("Cannot write header in fd %d: ", fout); 432 | ret = (int)wnr; 433 | goto out; 434 | } 435 | 436 | off = lseek(fout, acb->firstblk, SEEK_SET); 437 | if(off < 0) { 438 | SEEK_ERR(off, ret); 439 | goto out; 440 | } 441 | 442 | /* Encrypt each binary block and write them in boot image */ 443 | for(i = 0; i < acb->payloadsz; i += nr) { 444 | nr = gi_amlcblk_read_blk(fin, block, acb->blksz); 445 | if(nr <= 0) { 446 | PERR("Cannot read fd %d: ", fin); 447 | ret = (int)nr; 448 | goto out; 449 | } 450 | 451 | if((size_t)nr < acb->blksz) 452 | gi_amlcblk_blk_pad(acb, block, nr); 453 | 454 | nr = acb->blksz; 455 | ret = EVP_EncryptUpdate(ctx, enc, (int *)&nr, block, nr); 456 | if((ret != 1) || ((size_t)nr != acb->blksz)) { 457 | ret = -ERR_get_error(); 458 | SSLERR(ret, "Cannot Encrypt block: "); 459 | goto out; 460 | } 461 | 462 | wnr = gi_amlcblk_write_blk(fout, enc, acb->blksz); 463 | if(wnr < 0) { 464 | PERR("Cannot write into fd %d: ", fout); 465 | ret = (int)wnr; 466 | goto out; 467 | } 468 | 469 | if(i != 0) 470 | continue; 471 | off = lseek(fout, nr, SEEK_SET); 472 | if(off < 0) { 473 | SEEK_ERR(off, ret); 474 | goto out; 475 | } 476 | } 477 | ret = EVP_EncryptFinal_ex(ctx, enc, (int *)&nr); 478 | if(ret != 1) { 479 | ret = -ERR_get_error(); 480 | SSLERR(ret, "Cannot finalise binary payload: "); 481 | goto out; 482 | } 483 | acb->encsz = acb->payloadsz; 484 | AMLCBLK_SET_ENCRYPT(acb); 485 | ret = 0; 486 | 487 | out: 488 | free(enc); 489 | free(block); 490 | EVP_CIPHER_CTX_free(ctx); 491 | return ret; 492 | } 493 | 494 | /** 495 | * Decode a boot image output file 496 | * 497 | * @param acb: Amlogic Control Block descriptor 498 | * @param fout: Binary output file descriptor 499 | * @param fin: Boot image input file descriptor 500 | * @return: 0 on success, negative number otherwise 501 | */ 502 | int gi_amlcblk_aes_dec(struct amlcblk *acb, int fout, int fin) 503 | { 504 | EVP_CIPHER_CTX *ctx = NULL; 505 | uint8_t *block = NULL, *dec = NULL; 506 | size_t i; 507 | ssize_t nr, wnr; 508 | off_t off; 509 | int ret; 510 | uint8_t hdr[AMLCBLKSZ]; 511 | 512 | ret = -EINVAL; 513 | if(acb->payloadsz % acb->blksz) 514 | goto out; 515 | 516 | ctx = EVP_CIPHER_CTX_new(); 517 | if(ctx == NULL) { 518 | ret = -ERR_get_error(); 519 | SSLERR(ret, "Cannot create cipher context: "); 520 | goto out; 521 | } 522 | 523 | ret = -ENOMEM; 524 | block = malloc(acb->blksz); 525 | if(block == NULL) 526 | goto out; 527 | 528 | dec = malloc(acb->blksz); 529 | if(dec == NULL) 530 | goto out; 531 | 532 | ret = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, 533 | acb->aeskey, acb->iv); 534 | if(ret != 1) { 535 | ret = -ERR_get_error(); 536 | SSLERR(ret, "Cannot init cipher context: "); 537 | goto out; 538 | } 539 | 540 | ret = EVP_CIPHER_CTX_set_padding(ctx, 0); 541 | if(ret != 1) { 542 | ret = -ERR_get_error(); 543 | SSLERR(ret, "Cannot disable cipher padding: "); 544 | goto out; 545 | } 546 | 547 | off = lseek(fout, 0, SEEK_SET); 548 | if(off < 0) { 549 | SEEK_ERR(off, ret); 550 | goto out; 551 | } 552 | 553 | /* 554 | * Some binary has specific header at first block to describe how to run it 555 | * (load address, entry point, etc) 556 | */ 557 | if(AMLCBLK_HAS_HDR(acb)) { 558 | off = lseek(fin, AMLCBLKSZ, SEEK_SET); 559 | if(off < 0) { 560 | SEEK_ERR(off, ret); 561 | goto out; 562 | } 563 | nr = gi_amlcblk_read_blk(fin, hdr, AMLCBLKSZ); 564 | if(nr != AMLCBLKSZ) { 565 | PERR("Cannot read fin header\n"); 566 | ret = (int)nr; 567 | goto out; 568 | } 569 | wnr = gi_amlcblk_write_blk(fout, hdr, AMLCBLKSZ); 570 | if(wnr < 0) { 571 | PERR("Cannot write header in fd %d: ", fout); 572 | ret = (int)wnr; 573 | goto out; 574 | } 575 | off = lseek(fout, acb->blksz, SEEK_SET); 576 | if(off < 0) { 577 | SEEK_ERR(off, ret); 578 | goto out; 579 | } 580 | } 581 | 582 | off = lseek(fin, acb->firstblk, SEEK_SET); 583 | if(off < 0) { 584 | SEEK_ERR(off, ret); 585 | goto out; 586 | } 587 | 588 | /* Encrypt each binary block and write them in boot image */ 589 | for(i = 0; i < acb->payloadsz; i += nr) { 590 | nr = gi_amlcblk_read_blk(fin, block, acb->blksz); 591 | if(nr <= 0) { 592 | PERR("Cannot read fd %d: ", fin); 593 | ret = (int)nr; 594 | goto out; 595 | } 596 | 597 | nr = acb->blksz; 598 | ret = EVP_DecryptUpdate(ctx, dec, (int *)&nr, block, nr); 599 | if((ret != 1) || ((size_t)nr != acb->blksz)) { 600 | ret = -ERR_get_error(); 601 | SSLERR(ret, "Cannot Encrypt block: "); 602 | goto out; 603 | } 604 | 605 | wnr = gi_amlcblk_write_blk(fout, dec, acb->blksz); 606 | if(wnr < 0) { 607 | PERR("Cannot write into fd %d: ", fout); 608 | ret = (int)wnr; 609 | goto out; 610 | } 611 | 612 | if(i != 0) 613 | continue; 614 | off = lseek(fin, nr, SEEK_SET); 615 | if(off < 0) { 616 | SEEK_ERR(off, ret); 617 | goto out; 618 | } 619 | } 620 | ret = EVP_DecryptFinal_ex(ctx, dec, (int *)&nr); 621 | if(ret != 1) { 622 | ret = -ERR_get_error(); 623 | SSLERR(ret, "Cannot finalise binary payload: "); 624 | goto out; 625 | } 626 | ret = 0; 627 | 628 | out: 629 | free(dec); 630 | free(block); 631 | EVP_CIPHER_CTX_free(ctx); 632 | return ret; 633 | } 634 | -------------------------------------------------------------------------------- /amlcblk.h: -------------------------------------------------------------------------------- 1 | #ifndef _AMLCBLK_H_ 2 | #define _AMLCBLK_H_ 3 | 4 | /* Amlogic control block description */ 5 | struct amlcblk { 6 | size_t blksz; /* Size of one output block */ 7 | size_t firstblk; /* Offset of first block */ 8 | size_t encsz; /* Size of encrypted payload */ 9 | size_t payloadsz; /* Size of decrypted payload */ 10 | uint8_t iv[16]; /* AES-256-CBC Initialization vector */ 11 | uint8_t aeskey[32]; /* AES-256-CBC key */ 12 | uint8_t flag; /* AMLCBLK flags */ 13 | }; 14 | 15 | int gi_amlcblk_init(struct amlcblk *acb, int fd); 16 | int gi_amlcblk_read_hdr(struct amlcblk *acb, int fd); 17 | int gi_amlcblk_dump_hdr(struct amlcblk const *acb, int fd); 18 | int gi_amlcblk_aes_enc(struct amlcblk *acb, int fout, int fin); 19 | int gi_amlcblk_aes_dec(struct amlcblk *acb, int fout, int fin); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /amlsblk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "gxlimg.h" 14 | #include "amlsblk.h" 15 | #include "ssl.h" 16 | 17 | #define AMLSBLK_HDR (1 << 1) 18 | #define AMLSBLK_HAS_HDR(h) ((h)->flag & (AMLSBLK_HDR)) 19 | #define AMLSBLK_SET_HDR(h) ((h)->flag |= (AMLSBLK_HDR)) 20 | 21 | #define htole8(val) (val) 22 | #define le8toh(val) (val) 23 | #define bh_wr(h, sz, off, val) (*(uint ## sz ## _t *)((h) + off) = htole ## sz(val)) 24 | #define bh_rd(h, sz, off) (le ## sz ## toh(*(uint ## sz ## _t *)((h) + off))) 25 | 26 | #define BL31_MAGIC (0x12348765) 27 | 28 | #define AMLSBLK_MAGIC (*(uint32_t *)"@AML") 29 | #define AMLSBLK_KEY_MAGIC (*(uint32_t *)"@KEY") 30 | 31 | /** 32 | * Read a block of data from a file 33 | * 34 | * @param fd: File descriptor to read a block from 35 | * @param blk: Filled with read data 36 | * @param sz: Size of block to read from file 37 | * @return: Negative number on error, read size otherwise. The only reason that 38 | * return value could be different from sz on success is when EOF has been 39 | * encountered while reading the file. 40 | */ 41 | static ssize_t gi_amlsblk_read_blk(int fd, uint8_t *blk, size_t sz) 42 | { 43 | size_t i; 44 | ssize_t nr = 1; 45 | 46 | for(i = 0; (i < sz) && (nr != 0); i += nr) { 47 | nr = read(fd, blk + i, sz - i); 48 | if(nr < 0) 49 | goto out; 50 | } 51 | nr = i; 52 | out: 53 | return nr; 54 | } 55 | 56 | /** 57 | * Write a block of data into a file 58 | * 59 | * @param fd: File descriptor to write a block into 60 | * @param blk: Actual block data 61 | * @param sz: Size of block to write into file 62 | * @return: Negative number on error, sz otherwise. 63 | */ 64 | static ssize_t gi_amlsblk_write_blk(int fd, uint8_t *blk, size_t sz) 65 | { 66 | size_t i; 67 | ssize_t nr; 68 | 69 | for(i = 0; i < sz; i += nr) { 70 | nr = write(fd, blk + i, sz - i); 71 | if(nr < 0) 72 | goto out; 73 | } 74 | nr = i; 75 | out: 76 | return nr; 77 | } 78 | 79 | /** 80 | * Hash the header 81 | * 82 | * @param asb: control signature descriptor 83 | * @return: 0 on success, negative number otherwise 84 | */ 85 | static int gi_amlsblk_hash_header(struct amlsblk *asb) 86 | { 87 | EVP_MD_CTX *ctx; 88 | int ret; 89 | 90 | ctx = EVP_MD_CTX_new(); 91 | if(ctx == NULL) { 92 | SSLERR(ret, "Cannot create digest context: "); 93 | goto out; 94 | } 95 | 96 | ret = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); 97 | if(ret != 1) { 98 | SSLERR(ret, "Cannot init digest context: "); 99 | goto out; 100 | } 101 | 102 | ret = EVP_DigestUpdate(ctx, asb->hdr, sizeof(asb->hdr)); 103 | if(ret != 1) { 104 | SSLERR(ret, "Cannot hash data block: "); 105 | goto out; 106 | } 107 | 108 | ret = EVP_DigestFinal_ex(ctx, asb->hdr_hash, NULL); 109 | if(ret != 1) { 110 | SSLERR(ret, "Cannot finalize hash: "); 111 | goto out; 112 | } 113 | ret = 0; 114 | out: 115 | EVP_MD_CTX_free(ctx); 116 | return ret; 117 | } 118 | 119 | /** 120 | * Build the two headers and flush the payload 121 | * 122 | * @param asb: control signature descriptor 123 | * @param fin: input image file descriptor 124 | * @param fout: output image file descriptor 125 | * @return: 0 on success, negative number otherwise 126 | */ 127 | int gi_amlsblk_flush_data(struct amlsblk *asb, int fin, int fout) 128 | { 129 | uint8_t key_hdr[BL3xKEYHDR_SZ] = { 0 }; 130 | uint8_t empty_nonce[BL3xIV_SZ] = { 0 }; 131 | uint8_t block[512]; 132 | ssize_t rd, wr; 133 | off_t off; 134 | size_t nr; 135 | int ret; 136 | 137 | bh_wr(key_hdr, 32, 0x0, AMLSBLK_KEY_MAGIC); 138 | bh_wr(key_hdr, 32, 0x4, BL3xKEYHDR_SZ); 139 | bh_wr(key_hdr, 8, 0x8, 0x1); 140 | bh_wr(key_hdr, 8, 0xa, BL3xKEYHDR_SZ); 141 | 142 | off = lseek(fout, 0, SEEK_SET); 143 | if(off < 0) { 144 | SEEK_ERR(off, ret); 145 | goto out; 146 | } 147 | 148 | ret = gi_amlsblk_write_blk(fout, key_hdr, sizeof(key_hdr)); 149 | if(ret < 0) { 150 | PERR("Cannot write header in fd %d: ", fout); 151 | goto out; 152 | } 153 | 154 | off = lseek(fout, 0x490, SEEK_SET); 155 | if(off < 0) { 156 | SEEK_ERR(off, ret); 157 | goto out; 158 | } 159 | 160 | ret = gi_amlsblk_write_blk(fout, empty_nonce, sizeof(empty_nonce)); 161 | if(ret < 0) { 162 | PERR("Cannot write header in fd %d: ", fout); 163 | goto out; 164 | } 165 | 166 | ret = gi_amlsblk_write_blk(fout, asb->hdr, sizeof(asb->hdr)); 167 | if(ret < 0) { 168 | PERR("Cannot write header in fd %d: ", fout); 169 | goto out; 170 | } 171 | 172 | ret = gi_amlsblk_write_blk(fout, asb->hdr_hash, sizeof(asb->hdr_hash)); 173 | if(ret < 0) { 174 | PERR("Cannot write header in fd %d: ", fout); 175 | goto out; 176 | } 177 | 178 | off = lseek(fin, 0, SEEK_SET); 179 | if(off < 0) { 180 | SEEK_ERR(off, ret); 181 | goto out; 182 | } 183 | 184 | if(AMLSBLK_HAS_HDR(asb)) { 185 | off = lseek(fin, IMGHDR_SZ, SEEK_SET); 186 | if(off < 0) { 187 | SEEK_ERR(off, ret); 188 | goto out; 189 | } 190 | } 191 | 192 | for(nr = 0; nr < asb->hashsz; nr += rd) { 193 | rd = gi_amlsblk_read_blk(fin, block, sizeof(block)); 194 | if(rd <= 0) { 195 | ret = (int)rd; 196 | goto out; 197 | } 198 | if((size_t)rd < asb->blksz) { 199 | memset(block + rd, 0, asb->blksz - rd); 200 | rd += asb->topad; 201 | } 202 | wr = gi_amlsblk_write_blk(fout, block, rd); 203 | if(wr != rd) { 204 | ret = (int)wr; 205 | goto out; 206 | } 207 | } 208 | 209 | ret = 0; 210 | out: 211 | return ret; 212 | } 213 | 214 | /** 215 | * Build the first header and hash it 216 | * 217 | * @param asb: control signature descriptor 218 | * @return: 0 on success, negative number otherwise 219 | */ 220 | int gi_amlsblk_build_header(struct amlsblk *asb) 221 | { 222 | memset(asb->hdr, 0, sizeof(asb->hdr)); 223 | 224 | bh_wr(asb->hdr, 32, 0x0, AMLSBLK_MAGIC); 225 | bh_wr(asb->hdr, 32, 0x4, 0x1); 226 | bh_wr(asb->hdr, 32, 0x8, 0x0); 227 | bh_wr(asb->hdr, 32, 0xc, 0x0); 228 | bh_wr(asb->hdr, 64, 0x10, asb->hashsz); 229 | bh_wr(asb->hdr, 64, 0x18, BL3xHDR_SZ); 230 | memcpy(asb->hdr + 32, asb->hash, sizeof(asb->hash)); 231 | 232 | return gi_amlsblk_hash_header(asb); 233 | } 234 | 235 | /** 236 | * Compute payload sha256 hash 237 | * 238 | * @param asb: control signature descriptor 239 | * @param fin: input image file descriptor 240 | * @return: 0 on success, negative number otherwise 241 | */ 242 | int gi_amlsblk_hash_payload(struct amlsblk *asb, int fin) 243 | { 244 | uint8_t *block = NULL; 245 | EVP_MD_CTX *ctx; 246 | ssize_t nr; 247 | off_t off; 248 | size_t i; 249 | int ret; 250 | 251 | ctx = EVP_MD_CTX_new(); 252 | if(ctx == NULL) { 253 | SSLERR(ret, "Cannot create digest context: "); 254 | goto out; 255 | } 256 | 257 | ret = -ENOMEM; 258 | block = malloc(asb->blksz); 259 | if(block == NULL) 260 | goto out; 261 | 262 | ret = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); 263 | if(ret != 1) { 264 | SSLERR(ret, "Cannot init digest context: "); 265 | goto out; 266 | } 267 | 268 | off = lseek(fin, 0, SEEK_SET); 269 | if(off < 0) { 270 | SEEK_ERR(off, ret); 271 | goto out; 272 | } 273 | 274 | if(AMLSBLK_HAS_HDR(asb)) { 275 | off = lseek(fin, IMGHDR_SZ, SEEK_SET); 276 | if(off < 0) { 277 | SEEK_ERR(off, ret); 278 | goto out; 279 | } 280 | } 281 | 282 | for(i = 0; i < asb->hashsz; i += nr) { 283 | nr = gi_amlsblk_read_blk(fin, block, asb->blksz); 284 | if(nr < 0) { 285 | PERR("Cannot read fd %d:", fin); 286 | ret = (int)nr; 287 | goto out; 288 | } 289 | 290 | if((size_t)nr < asb->blksz) { 291 | memset(block + nr, 0, asb->blksz - nr); 292 | nr += asb->topad; 293 | } 294 | 295 | ret = EVP_DigestUpdate(ctx, block, nr); 296 | if(ret != 1) { 297 | SSLERR(ret, "Cannot hash data block: "); 298 | goto out; 299 | } 300 | } 301 | 302 | ret = EVP_DigestFinal_ex(ctx, asb->hash, NULL); 303 | if(ret != 1) { 304 | SSLERR(ret, "Cannot finalize hash: "); 305 | goto out; 306 | } 307 | ret = 0; 308 | out: 309 | EVP_MD_CTX_free(ctx); 310 | free(block); 311 | return ret; 312 | } 313 | 314 | /** 315 | * Initialize Amlogic Signature block descriptor from binary file 316 | * 317 | * @param asb: Amlogic Signature block descriptor to init 318 | * @param fd: Binary file descriptor 319 | * @return: 0 on success, negative number otherwise 320 | */ 321 | int gi_amlsblk_init(struct amlsblk *asb, int fd) 322 | { 323 | uint8_t img_hdr[IMGHDR_SZ]; 324 | size_t nr; 325 | off_t fsz; 326 | int ret; 327 | 328 | asb->flag = 0; 329 | fsz = lseek(fd, 0, SEEK_SET); 330 | if(fsz < 0) { 331 | SEEK_ERR(fsz, ret); 332 | goto out; 333 | } 334 | 335 | nr = gi_amlsblk_read_blk(fd, img_hdr, sizeof(img_hdr)); 336 | if(nr != sizeof(img_hdr)) { 337 | PERR("Cannot read input header: "); 338 | ret = -EINVAL; 339 | goto out; 340 | } 341 | 342 | if(bh_rd(img_hdr, 32, 0) == BL31_MAGIC) 343 | AMLSBLK_SET_HDR(asb); 344 | 345 | asb->blksz = 0x200; 346 | fsz = lseek(fd, 0, SEEK_END); 347 | if(fsz < 0) { 348 | SEEK_ERR(fsz, ret); 349 | goto out; 350 | } 351 | 352 | if(AMLSBLK_HAS_HDR(asb)) 353 | fsz -= IMGHDR_SZ; 354 | asb->payloadsz = fsz; 355 | 356 | asb->totsz = ROUNDUP(fsz + BL3xHDR_SZ, asb->blksz); 357 | asb->hashsz = (asb->totsz - BL3xHDR_SZ); 358 | asb->topad = asb->hashsz - asb->payloadsz; 359 | ret = 0; 360 | 361 | out: 362 | return ret; 363 | } 364 | -------------------------------------------------------------------------------- /amlsblk.h: -------------------------------------------------------------------------------- 1 | #ifndef _AMLSBLK_H_ 2 | #define _AMLSBLK_H_ 3 | 4 | #define IMGHDR_SZ 0x200 5 | #define BL3xIV_SZ 0x10 6 | #define BL3xSB_SZ 0x80 7 | #define BL3xSIG_SZ 0x200 8 | #define BL3xKEYHDR_SZ 0x30 9 | #define BL3xSHA2_SZ 0x20 10 | 11 | #define BL3xHDR_SZ (BL3xIV_SZ + BL3xSB_SZ + BL3xSIG_SZ) 12 | 13 | /* Amlogic signature block description */ 14 | struct amlsblk { 15 | size_t payloadsz; 16 | size_t blksz; 17 | size_t totsz; 18 | size_t hashsz; 19 | size_t topad; 20 | uint8_t flag; 21 | uint8_t hash[BL3xSHA2_SZ]; 22 | uint8_t hdr_hash[BL3xSHA2_SZ]; 23 | uint8_t hdr[BL3xHDR_SZ - BL3xIV_SZ - BL3xSHA2_SZ]; 24 | }; 25 | 26 | int gi_amlsblk_init(struct amlsblk *asb, int fd); 27 | int gi_amlsblk_hash_payload(struct amlsblk *asb, int fin); 28 | int gi_amlsblk_build_header(struct amlsblk *asb); 29 | int gi_amlsblk_flush_data(struct amlsblk *asb, int fin, int fout); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /bl2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "gxlimg.h" 11 | #include "bl2.h" 12 | #include "ssl.h" 13 | 14 | #define FOUT_MODE_DFT (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) 15 | 16 | #define htole8(val) (val) 17 | #define le8toh(val) (val) 18 | #define bh_wr(h, sz, off, val) \ 19 | (*(uint ## sz ## _t *)((h) + off) = htole ## sz(val)) 20 | #define bh_rd(h, sz, off) \ 21 | (le ## sz ## toh(*(uint ## sz ## _t *)((h) + off))) 22 | 23 | #define BL2HDR_SZ 0x1000 24 | #define BL2IV_SZ 0x10 25 | #define BL2BLKHDR_SZ 0x40 26 | #define BL2SIG_SZ 0x200 27 | #define BL2KEY_SZ 0xD80 28 | #define BL2KEYHDR_SZ 0x30 29 | #define BL2SHA2_SZ 0x20 30 | 31 | #define BL2HDR_MAGIC (*(uint32_t *)"@AML") 32 | 33 | /** 34 | * BL2 binary file context 35 | */ 36 | struct bl2 { 37 | size_t payloadsz; 38 | size_t totlen; 39 | size_t hash_start; /* Start of Hashed payload */ 40 | size_t hash_size; /* Size of hashed payload */ 41 | uint8_t flag; 42 | }; 43 | #define BF_RSA (1 << 0) 44 | #define BF_IS_RSA(h) ((h)->flag & BF_RSA) 45 | #define BF_SET_RSA(h) ((h)->flag |= BF_RSA) 46 | 47 | /** 48 | * Initialize bl2 context from BL2 binary file descriptor 49 | * 50 | * @param bl2: BL2 binary descriptor to init 51 | * @param fd: BL2 binary file descriptor 52 | * @return: 0 on success, negative number otherwise 53 | */ 54 | static int gi_bl2_init(struct bl2 *bl2, int fd) 55 | { 56 | off_t fsz; 57 | 58 | fsz = lseek(fd, 0, SEEK_END); 59 | if(fsz < 0) { 60 | PERR("Cannot seek file: "); 61 | return (int)fsz; 62 | } 63 | 64 | bl2->payloadsz = fsz; 65 | if (fsz == 0xc000) /* GXL */ 66 | bl2->payloadsz -= BL2HDR_SZ; 67 | 68 | bl2->totlen = bl2->payloadsz + BL2HDR_SZ; 69 | bl2->flag = 0; /* Not RSA signature support yet */ 70 | bl2->hash_start = BL2BLKHDR_SZ + BL2SHA2_SZ; 71 | bl2->hash_size = bl2->totlen - BL2IV_SZ - bl2->hash_start; 72 | 73 | return 0; 74 | } 75 | 76 | /** 77 | * Read a block of data from a file 78 | * 79 | * @param fd: File descriptor to read a block from 80 | * @param blk: Filled with read data 81 | * @param sz: Size of block to read from file 82 | * @return: Negative number on error, read size otherwise. The only reason that 83 | * return value could be different from sz on success is when EOF has been 84 | * encountered while reading the file. 85 | */ 86 | static ssize_t gi_bl2_read_blk(int fd, uint8_t *blk, size_t sz) 87 | { 88 | size_t i; 89 | ssize_t nr = 1; 90 | 91 | for(i = 0; (i < sz) && (nr != 0); i += nr) { 92 | nr = read(fd, blk + i, sz - i); 93 | if(nr < 0) 94 | goto out; 95 | } 96 | nr = i; 97 | out: 98 | return nr; 99 | } 100 | 101 | /** 102 | * Write a block of data into a file 103 | * 104 | * @param fd: File descriptor to write a block into 105 | * @param blk: Actual block data 106 | * @param sz: Size of block to write into file 107 | * @return: Negative number on error, sz otherwise. 108 | */ 109 | static ssize_t gi_bl2_write_blk(int fd, uint8_t *blk, size_t sz) 110 | { 111 | size_t i; 112 | ssize_t nr; 113 | 114 | for(i = 0; i < sz; i += nr) { 115 | nr = write(fd, blk + i, sz - i); 116 | if(nr < 0) 117 | goto out; 118 | } 119 | nr = i; 120 | out: 121 | return nr; 122 | } 123 | 124 | static int gi_bl2_dump_hdr(struct bl2 const *bl2, int fd) 125 | { 126 | uint8_t hdr[BL2BLKHDR_SZ] = {}; 127 | uint8_t rd[BL2IV_SZ]; 128 | ssize_t nr; 129 | off_t off; 130 | int ret; 131 | 132 | if(BF_IS_RSA(bl2)) { 133 | ERR("BL2 RSA signature not supported yet\n"); 134 | ret = -EINVAL; 135 | goto out; 136 | } 137 | 138 | ret = gi_random(rd, BL2IV_SZ); 139 | if(ret < 0) 140 | goto out; 141 | 142 | off = lseek(fd, 0, SEEK_SET); 143 | if(off < 0) { 144 | PERR("Cannot seek file: "); 145 | return (int)off; 146 | } 147 | bh_wr(hdr, 32, 0x00, BL2HDR_MAGIC); 148 | bh_wr(hdr, 32, 0x04, bl2->totlen - BL2IV_SZ); 149 | bh_wr(hdr, 8, 0x08, BL2BLKHDR_SZ); 150 | bh_wr(hdr, 8, 0x0a, 1); 151 | bh_wr(hdr, 8, 0x0b, 1); 152 | bh_wr(hdr, 32, 0x10, 0); /* SHA256 signature, no RSA */ 153 | bh_wr(hdr, 32, 0x14, BL2BLKHDR_SZ); 154 | bh_wr(hdr, 32, 0x18, BL2SIG_SZ); 155 | bh_wr(hdr, 32, 0x1c, bl2->hash_start); /* Beginning of hashed payload */ 156 | bh_wr(hdr, 32, 0x20, 0); /* Null RSA KEY type */ 157 | bh_wr(hdr, 32, 0x24, BL2BLKHDR_SZ + BL2SIG_SZ); /* RSA KEY Offset */ 158 | bh_wr(hdr, 32, 0x28, BL2HDR_SZ - BL2IV_SZ - BL2BLKHDR_SZ - BL2SIG_SZ); 159 | bh_wr(hdr, 32, 0x2c, bl2->hash_size); 160 | bh_wr(hdr, 32, 0x34, BL2HDR_SZ - BL2IV_SZ); /* Payload offset */ 161 | bh_wr(hdr, 32, 0x38, bl2->totlen - BL2HDR_SZ); 162 | 163 | nr = gi_bl2_write_blk(fd, rd, sizeof(rd)); 164 | if(nr != sizeof(rd)) { 165 | PERR("Failed to write random number in bl2 boot img: "); 166 | ret = (int)nr; 167 | goto out; 168 | } 169 | 170 | nr = gi_bl2_write_blk(fd, hdr, sizeof(hdr)); 171 | if(nr != sizeof(hdr)) { 172 | PERR("Failed to write header in bl2 boot img: "); 173 | ret = (int)nr; 174 | goto out; 175 | } 176 | ret = 0; 177 | out: 178 | return ret; 179 | } 180 | 181 | /** 182 | * Read BL2 header from image 183 | * 184 | * @param bl2: Filled up with BL2 header info 185 | * @param fd: File to read header from 186 | * 187 | * @return: 0 on success, negative number otherwise 188 | */ 189 | static int gi_bl2_read_hdr(struct bl2 *bl2, int fd) 190 | { 191 | uint8_t hdr[BL2BLKHDR_SZ]; 192 | off_t off; 193 | int ret; 194 | 195 | off = lseek(fd, BL2IV_SZ, SEEK_SET); /* Skip IV */ 196 | if(off < 0) { 197 | PERR("Cannot seek file: "); 198 | return (int)off; 199 | } 200 | 201 | ret = gi_bl2_read_blk(fd, hdr, sizeof(hdr)); 202 | if(ret < 0) { 203 | ERR("Cannot get header from bl2 file\n"); 204 | goto out; 205 | } 206 | 207 | ret = -EINVAL; 208 | if(bh_rd(hdr, 32, 0x00) != BL2HDR_MAGIC) { 209 | ERR("Invalid BL2 header\n"); 210 | goto out; 211 | } 212 | bl2->totlen = bh_rd(hdr, 32, 0x04) + BL2IV_SZ; 213 | bl2->hash_start = bh_rd(hdr, 32, 0x1c); /* Beginning of hashed payload */ 214 | bl2->hash_size = bh_rd(hdr, 32, 0x2c); 215 | bl2->payloadsz = bl2->totlen - BL2HDR_SZ; 216 | 217 | ret = 0; 218 | out: 219 | return ret; 220 | } 221 | 222 | static int gi_bl2_dump_key(struct bl2 const *bl2, int fd) 223 | { 224 | uint32_t val; 225 | off_t off; 226 | int ret; 227 | 228 | if(BF_IS_RSA(bl2)) { 229 | ERR("BL2 RSA signature not supported yet\n"); 230 | return -EINVAL; 231 | } 232 | 233 | off = lseek(fd, BL2IV_SZ + BL2BLKHDR_SZ + BL2SIG_SZ + 0x18, SEEK_SET); 234 | if(off < 0) { 235 | SEEK_ERR(off, ret); 236 | goto out; 237 | } 238 | val = htole32(0x298); 239 | gi_bl2_write_blk(fd, (uint8_t *)(&val), 4); 240 | 241 | /* TODO What is this offset */ 242 | off = lseek(fd, BL2IV_SZ + 0x8ec, SEEK_SET); 243 | if(off < 0) { 244 | SEEK_ERR(off, ret); 245 | goto out; 246 | } 247 | val = htole32(0x240); 248 | gi_bl2_write_blk(fd, (uint8_t *)(&val), 4); 249 | 250 | /* TODO What is this offset */ 251 | off = lseek(fd, BL2IV_SZ + 0xb20, SEEK_SET); 252 | if(off < 0) { 253 | SEEK_ERR(off, ret); 254 | goto out; 255 | } 256 | val = htole32(0x298); 257 | gi_bl2_write_blk(fd, (uint8_t *)(&val), 4); 258 | ret = 0; 259 | out: 260 | return ret; 261 | } 262 | 263 | static int gi_bl2_dump_binary(struct bl2 const *bl2, int fout, int fin) 264 | { 265 | uint8_t block[1024]; 266 | size_t nr; 267 | ssize_t rd, wr; 268 | off_t off; 269 | int ret; 270 | 271 | off = lseek(fin, 0, SEEK_SET); 272 | if(off < 0) { 273 | SEEK_ERR(off, ret); 274 | goto out; 275 | } 276 | off = lseek(fout, BL2HDR_SZ, SEEK_SET); 277 | if(off < 0) { 278 | SEEK_ERR(off, ret); 279 | goto out; 280 | } 281 | 282 | for(nr = 0; nr < bl2->payloadsz; nr += rd) { 283 | rd = gi_bl2_read_blk(fin, block, sizeof(block)); 284 | if(rd <= 0) { 285 | ret = (int)rd; 286 | goto out; 287 | } 288 | wr = gi_bl2_write_blk(fout, block, sizeof(block)); 289 | if(wr != rd) { 290 | ret = (int)wr; 291 | goto out; 292 | } 293 | } 294 | 295 | ret = 0; 296 | 297 | out: 298 | return ret; 299 | } 300 | 301 | static int gi_bl2_sign(struct bl2 const *bl2, int fd) 302 | { 303 | EVP_MD_CTX *ctx; 304 | uint8_t tmp[1024]; 305 | uint8_t hash[BL2SHA2_SZ]; 306 | size_t i; 307 | ssize_t nr; 308 | off_t off; 309 | int ret; 310 | 311 | ctx = EVP_MD_CTX_new(); 312 | if(ctx == NULL) { 313 | SSLERR(ret, "Cannot create digest context: "); 314 | goto out; 315 | } 316 | 317 | ret = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); 318 | if(ret != 1) { 319 | SSLERR(ret, "Cannot init digest context: "); 320 | goto out; 321 | } 322 | 323 | /* Hash header */ 324 | off = lseek(fd, BL2IV_SZ, SEEK_SET); 325 | if(off < 0) { 326 | SEEK_ERR(off, ret); 327 | goto out; 328 | } 329 | nr = gi_bl2_read_blk(fd, tmp, BL2BLKHDR_SZ); 330 | if((nr < 0) || (nr != BL2BLKHDR_SZ)) { 331 | PERR("Cannot read header from fd %d: ", fd); 332 | ret = (int)nr; 333 | goto out; 334 | } 335 | ret = EVP_DigestUpdate(ctx, tmp, nr); 336 | if(ret != 1) { 337 | SSLERR(ret, "Cannot hash header block: "); 338 | goto out; 339 | } 340 | 341 | /* Hash payload */ 342 | off = lseek(fd, BL2IV_SZ + bl2->hash_start, SEEK_SET); 343 | if(off < 0) { 344 | SEEK_ERR(off, ret); 345 | goto out; 346 | } 347 | for(i = 0; i < bl2->hash_size; i += nr) { 348 | nr = gi_bl2_read_blk(fd, tmp, sizeof(tmp)); 349 | if(nr < 0) { 350 | PERR("Cannot read fd %d:", fd); 351 | ret = (int)nr; 352 | goto out; 353 | } 354 | ret = EVP_DigestUpdate(ctx, tmp, nr); 355 | if(ret != 1) { 356 | SSLERR(ret, "Cannot hash data block: "); 357 | goto out; 358 | } 359 | } 360 | 361 | ret = EVP_DigestFinal_ex(ctx, hash, NULL); 362 | if(ret != 1) { 363 | SSLERR(ret, "Cannot finalize hash: "); 364 | goto out; 365 | } 366 | 367 | /* Only SHA256 signature is supported so far */ 368 | off = lseek(fd, BL2IV_SZ + BL2BLKHDR_SZ, SEEK_SET); 369 | if(off < 0) { 370 | SEEK_ERR(off, ret); 371 | goto out; 372 | } 373 | nr = gi_bl2_write_blk(fd, hash, BL2SHA2_SZ); 374 | if(nr != BL2SHA2_SZ) { 375 | PERR("Cannot write SHA sig in fd %d:", fd); 376 | ret = (int)nr; 377 | goto out; 378 | } 379 | 380 | ret = 0; 381 | out: 382 | EVP_MD_CTX_free(ctx); 383 | return ret; 384 | } 385 | 386 | int gi_bl2_sign_img(char const *fin, char const *fout) 387 | { 388 | struct bl2 bl2; 389 | int fdin = -1, fdout = -1, ret; 390 | 391 | DBG("Create bl2 boot image from %s in %s\n", fin, fout); 392 | 393 | fdin = open(fin, O_RDONLY); 394 | if(fdin < 0) { 395 | PERR("Cannot open file %s", fin); 396 | ret = -errno; 397 | goto out; 398 | } 399 | 400 | fdout = open(fout, O_RDWR | O_CREAT, FOUT_MODE_DFT); 401 | if(fdout < 0) { 402 | PERR("Cannot open file %s", fout); 403 | ret = -errno; 404 | goto out; 405 | } 406 | 407 | ret = ftruncate(fdout, 0); 408 | if(ret < 0) 409 | goto out; 410 | 411 | ret = gi_bl2_init(&bl2, fdin); 412 | if(ret < 0) 413 | goto out; 414 | 415 | /* Fill the whole file with zeros */ 416 | ret = ftruncate(fdout, bl2.totlen); 417 | if(ret < 0) 418 | goto out; 419 | 420 | ret = gi_bl2_dump_hdr(&bl2, fdout); 421 | if(ret < 0) 422 | goto out; 423 | 424 | ret = gi_bl2_dump_key(&bl2, fdout); 425 | if(ret < 0) 426 | goto out; 427 | 428 | ret = gi_bl2_dump_binary(&bl2, fdout, fdin); 429 | if(ret < 0) 430 | goto out; 431 | 432 | ret = gi_bl2_sign(&bl2, fdout); 433 | out: 434 | if(fdin >= 0) 435 | close(fdin); 436 | if(fdout >= 0) 437 | close(fdout); 438 | return ret; 439 | } 440 | 441 | int gi_bl2_unsign_img(char const *fin, char const *fout) 442 | { 443 | struct bl2 bl2 = {}; 444 | ssize_t rd, wr; 445 | size_t len; 446 | off_t off; 447 | int fdin = -1, fdout = -1, ret; 448 | uint8_t block[1024]; 449 | 450 | DBG("Extract bl2 boot image from signed %s in %s\n", fin, fout); 451 | 452 | fdin = open(fin, O_RDONLY); 453 | if(fdin < 0) { 454 | PERR("Cannot open file %s", fin); 455 | ret = -errno; 456 | goto out; 457 | } 458 | 459 | fdout = open(fout, O_RDWR | O_CREAT, FOUT_MODE_DFT); 460 | if(fdout < 0) { 461 | PERR("Cannot open file %s", fout); 462 | ret = -errno; 463 | goto out; 464 | } 465 | 466 | ret = gi_bl2_read_hdr(&bl2, fdin); 467 | if(ret != 0) { 468 | ERR("Cannot get BL2 header\n"); 469 | goto out; 470 | } 471 | 472 | ret = ftruncate(fdout, 0); 473 | if(ret < 0) 474 | goto out; 475 | 476 | off = lseek(fdin, BL2HDR_SZ, SEEK_SET); 477 | if(off < 0) { 478 | SEEK_ERR(off, ret); 479 | goto out; 480 | } 481 | 482 | len = bl2.payloadsz; 483 | if (len + BL2HDR_SZ == 0xc000) /* GXL */ 484 | len = 0xc000; 485 | 486 | ret = ftruncate(fdout, len); 487 | if(ret < 0) 488 | goto out; 489 | 490 | do { 491 | rd = gi_bl2_read_blk(fdin, block, MIN(len, sizeof(block))); 492 | if(rd <= 0) { 493 | ret = (int)rd; 494 | goto out; 495 | } 496 | wr = gi_bl2_write_blk(fdout, block, rd); 497 | if(wr != rd) { 498 | ret = (int)wr; 499 | goto out; 500 | } 501 | len -= rd; 502 | } while((rd != 0) && (len != 0)); 503 | 504 | ret = 0; 505 | out: 506 | if(fdin >= 0) 507 | close(fdin); 508 | if(fdout >= 0) 509 | close(fdout); 510 | return ret; 511 | } 512 | -------------------------------------------------------------------------------- /bl2.h: -------------------------------------------------------------------------------- 1 | #ifndef _BL2_H_ 2 | #define _BL2_H_ 3 | 4 | int gi_bl2_sign_img(char const *fin, char const *fout); 5 | int gi_bl2_unsign_img(char const *fin, char const *fout); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /bl3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "gxlimg.h" 10 | #include "bl3.h" 11 | #include "amlcblk.h" 12 | #include "amlsblk.h" 13 | 14 | #define FOUT_MODE_DFT (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) 15 | 16 | /** 17 | * Sign a BL3 boot image 18 | * 19 | * @param fin: Path of BL3 binary input file 20 | * @param fout: Path of BL3 boot image output file 21 | * @return: 0 on success, negative number otherwise 22 | */ 23 | int gi_bl3_sign_img(char const *fin, char const *fout) 24 | { 25 | struct amlsblk asb; 26 | int fdin = -1, fdout = -1, ret; 27 | 28 | DBG("Sign bl3x %s in %s\n", fin, fout); 29 | 30 | fdin = open(fin, O_RDONLY); 31 | if(fdin < 0) { 32 | PERR("Cannot open file %s", fin); 33 | ret = -errno; 34 | goto out; 35 | } 36 | 37 | fdout = open(fout, O_RDWR | O_CREAT, FOUT_MODE_DFT); 38 | if(fdout < 0) { 39 | PERR("Cannot open file %s", fout); 40 | ret = -errno; 41 | goto out; 42 | } 43 | 44 | ret = ftruncate(fdout, 0); 45 | if(ret < 0) 46 | goto out; 47 | 48 | ret = gi_amlsblk_init(&asb, fdin); 49 | if(ret < 0) 50 | goto out; 51 | 52 | ret = gi_amlsblk_hash_payload(&asb, fdin); 53 | if(ret < 0) 54 | goto out; 55 | 56 | ret = gi_amlsblk_build_header(&asb); 57 | if(ret < 0) 58 | goto out; 59 | 60 | ret = gi_amlsblk_flush_data(&asb, fdin, fdout); 61 | out: 62 | if(fdout >= 0) 63 | close(fdout); 64 | if(fdin >= 0) 65 | close(fdin); 66 | return ret; 67 | } 68 | 69 | /** 70 | * Encrypt a BL3 boot image 71 | * 72 | * @param fin: Path of BL3 binary input file 73 | * @param fout: Path of BL3 boot image output file 74 | * @return: 0 on success, negative number otherwise 75 | */ 76 | int gi_bl3_encrypt_img(char const *fin, char const *fout) 77 | { 78 | struct amlcblk acb; 79 | int fdin = -1, fdout = -1, ret; 80 | 81 | DBG("Encode bl3 %s in %s\n", fin, fout); 82 | 83 | fdin = open(fin, O_RDONLY); 84 | if(fdin < 0) { 85 | PERR("Cannot open file %s", fin); 86 | ret = -errno; 87 | goto out; 88 | } 89 | 90 | fdout = open(fout, O_RDWR | O_CREAT, FOUT_MODE_DFT); 91 | if(fdout < 0) { 92 | PERR("Cannot open file %s", fout); 93 | ret = -errno; 94 | goto out; 95 | } 96 | 97 | ret = ftruncate(fdout, 0); 98 | if(ret < 0) 99 | goto out; 100 | 101 | ret = gi_amlcblk_init(&acb, fdin); 102 | if(ret < 0) 103 | goto out; 104 | 105 | ret = gi_amlcblk_aes_enc(&acb, fdout, fdin); 106 | if(ret < 0) 107 | goto out; 108 | 109 | ret = gi_amlcblk_dump_hdr(&acb, fdout); 110 | out: 111 | if(fdout >= 0) 112 | close(fdout); 113 | if(fdin >= 0) 114 | close(fdin); 115 | return ret; 116 | } 117 | 118 | /** 119 | * Decrypt a BL3 boot image 120 | * 121 | * @param fin: Path of BL3 boot image to decode 122 | * @param fout: Path of result BL3 binary file 123 | * @return: 0 on success, negative number otherwise 124 | */ 125 | int gi_bl3_decrypt_img(char const *fin, char const *fout) 126 | { 127 | struct amlcblk acb; 128 | int fdin = -1, fdout = -1, ret; 129 | 130 | DBG("Decrypt bl3 %s in %s\n", fin, fout); 131 | 132 | fdin = open(fin, O_RDONLY); 133 | if(fdin < 0) { 134 | PERR("Cannot open file %s", fin); 135 | ret = -errno; 136 | goto out; 137 | } 138 | 139 | fdout = open(fout, O_WRONLY | O_CREAT, FOUT_MODE_DFT); 140 | if(fdout < 0) { 141 | PERR("Cannot open file %s", fout); 142 | ret = -errno; 143 | goto out; 144 | } 145 | 146 | ret = ftruncate(fdout, 0); 147 | if(ret < 0) 148 | goto out; 149 | 150 | ret = gi_amlcblk_read_hdr(&acb, fdin); 151 | if(ret < 0) 152 | goto out; 153 | 154 | ret = gi_amlcblk_aes_dec(&acb, fdout, fdin); 155 | if(ret < 0) 156 | goto out; 157 | 158 | out: 159 | if(fdout >= 0) 160 | close(fdout); 161 | if(fdin >= 0) 162 | close(fdin); 163 | return ret; 164 | } 165 | -------------------------------------------------------------------------------- /bl3.h: -------------------------------------------------------------------------------- 1 | #ifndef _BL3_H_ 2 | #define _BL3_H_ 3 | 4 | int gi_bl3_encrypt_img(char const *fin, char const *fout); 5 | int gi_bl3_decrypt_img(char const *fin, char const *fout); 6 | int gi_bl3_sign_img(char const *fin, char const *fout); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /fip.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "gxlimg.h" 14 | #include "fip.h" 15 | #include "amlcblk.h" 16 | #include "ssl.h" 17 | 18 | #define FOUT_MODE_DFT (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) 19 | 20 | #define BL31_ENTRY_MAGIC (0x87654321) 21 | #define BL31_MAGIC (0x12348765) 22 | #define AMLSBLK_KEY_MAGIC (*(uint32_t *)"@KEY") 23 | #define BL2SZ (0xc000) 24 | #define TOC_OFFSET_V3 (0x10) 25 | 26 | #define SHA2_SZ (0x20) 27 | 28 | /** 29 | * FIP Format Revision 30 | */ 31 | enum fip_rev { 32 | GI_FIP_V2, /* GXL, GXM */ 33 | GI_FIP_V3, /* G12A, G12B, SM1 */ 34 | }; 35 | 36 | /** 37 | * Read a block of data from a file 38 | * 39 | * @param fd: File descriptor to read a block from 40 | * @param blk: Filled with read data 41 | * @param sz: Size of block to read from file 42 | * @return: Negative number on error, read size otherwise. The only reason that 43 | * return value could be different from sz on success is when EOF has been 44 | * encountered while reading the file. 45 | */ 46 | static ssize_t gi_fip_read_blk(int fd, uint8_t *blk, size_t sz) 47 | { 48 | size_t i; 49 | ssize_t nr = 1; 50 | 51 | for(i = 0; (i < sz) && (nr != 0); i += nr) { 52 | nr = read(fd, blk + i, sz - i); 53 | if(nr < 0) 54 | goto out; 55 | } 56 | nr = i; 57 | out: 58 | return nr; 59 | } 60 | 61 | /** 62 | * Write a block of data into a file 63 | * 64 | * @param fd: File descriptor to write a block into 65 | * @param blk: Actual block data 66 | * @param sz: Size of block to write into file 67 | * @return: Negative number on error, sz otherwise. 68 | */ 69 | static ssize_t gi_fip_write_blk(int fd, uint8_t *blk, size_t sz) 70 | { 71 | size_t i; 72 | ssize_t nr; 73 | 74 | for(i = 0; i < sz; i += nr) { 75 | nr = write(fd, blk + i, sz - i); 76 | if(nr < 0) 77 | goto out; 78 | } 79 | nr = i; 80 | out: 81 | return nr; 82 | } 83 | 84 | /** 85 | * Safely create a temporary file 86 | * 87 | * @param path: Path of temporary file, should ends with 6 X. On success those 88 | * X will be replaced with the unique file suffix created. 89 | * @return: fd on success, negative number otherwise 90 | */ 91 | static int gi_fip_create_tmp(char *path) 92 | { 93 | mode_t oldmask; 94 | int fd; 95 | 96 | oldmask = umask(S_IXUSR | S_IRWXG | S_IRWXO); 97 | fd = mkstemp(path); 98 | (void)umask(oldmask); 99 | 100 | return fd; 101 | } 102 | 103 | /** 104 | * List of supported boot image 105 | */ 106 | enum FIP_BOOT_IMG { 107 | FBI_BL2, 108 | FBI_BL30, 109 | FBI_BL301, 110 | FBI_BL31, 111 | FBI_BL32, 112 | FBI_BL33, 113 | FBI_BL2_DATA, 114 | FBI_BL30_DATA, 115 | FBI_BL31_DATA, 116 | FBI_BL32_DATA, 117 | FBI_BL33_DATA, 118 | FBI_EMPTY, 119 | FBI_UNKNOWN, 120 | }; 121 | 122 | typedef uint8_t uuid_t[16]; 123 | 124 | /** 125 | * Default uuid for each boot image 126 | */ 127 | static uuid_t const uuid_list[] = { 128 | [FBI_BL2] = { 129 | 0x5f, 0xf9, 0xec, 0x0b, 0x4d, 0x22, 0x3e, 0x4d, 130 | 0xa5, 0x44, 0xc3, 0x9d, 0x81, 0xc7, 0x3f, 0x0a 131 | }, 132 | [FBI_BL30] = { 133 | 0x97, 0x66, 0xfd, 0x3d, 0x89, 0xbe, 0xe8, 0x49, 134 | 0xae, 0x5d, 0x78, 0xa1, 0x40, 0x60, 0x82, 0x13 135 | }, 136 | [FBI_BL301] = { 137 | 0xdd, 0xcc, 0xbb, 0xaa, 0xcd, 0xab, 0xef, 0xef, 138 | 0xab, 0xcd, 0x12, 0x34, 0x56, 0x78, 0xab, 0xcd 139 | }, 140 | [FBI_BL31] = { 141 | 0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46, 142 | 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x00 143 | }, 144 | [FBI_BL32] = { 145 | 0x05, 0xd0, 0xe1, 0x89, 0x53, 0xdc, 0x13, 0x47, 146 | 0x8d, 0x2b, 0x50, 0x0a, 0x4b, 0x7a, 0x3e, 0x38 147 | }, 148 | [FBI_BL33] = { 149 | 0xd6, 0xd0, 0xee, 0xa7, 0xfc, 0xea, 0xd5, 0x4b, 150 | 0x97, 0x82, 0x99, 0x34, 0xf2, 0x34, 0xb6, 0xe4 151 | }, 152 | [FBI_BL2_DATA] = { 153 | 0xf4, 0x1d, 0x14, 0x86, 0xcb, 0x95, 0xe6, 0x11, 154 | 0x84, 0x88, 0x84, 0x2b, 0x2b, 0x01, 0xca, 0x38 155 | }, 156 | [FBI_BL30_DATA] = { 157 | 0x48, 0x56, 0xcc, 0xc2, 0xcc, 0x85, 0xe6, 0x11, 158 | 0xa5, 0x36, 0x3c, 0x97, 0x0e, 0x97, 0xa0, 0xee 159 | }, 160 | [FBI_BL31_DATA] = { 161 | 0xca, 0xaf, 0xb0, 0x33, 0xce, 0x85, 0xe6, 0x11, 162 | 0x8c, 0x32, 0x00, 0x22, 0x19, 0xc7, 0x77, 0x2f 163 | }, 164 | [FBI_BL32_DATA] = { 165 | 0x34, 0xa1, 0x48, 0xb8, 0xbc, 0x90, 0xe6, 0x11, 166 | 0x8f, 0xef, 0xa4, 0xba, 0xdb, 0x19, 0xde, 0x03 167 | }, 168 | [FBI_BL33_DATA] = { 169 | 0x8e, 0x59, 0xd6 ,0x5d, 0x5e, 0x8b, 0xe6, 0x11, 170 | 0xbc, 0xb5, 0xf0, 0xde, 0xf1, 0x83, 0x72, 0x96, 171 | }, 172 | [FBI_EMPTY] = {}, 173 | }; 174 | 175 | /** 176 | * Get FIP image type from its uuid 177 | * 178 | * @param uuid: UUID to find image type from 179 | * @return: FIP image type if found FIP_UNKNOWN otherwise 180 | */ 181 | static enum FIP_BOOT_IMG uuid_get_type(uuid_t uuid) 182 | { 183 | size_t i; 184 | 185 | for(i = 0; i < ARRAY_SIZE(uuid_list); ++i) 186 | if(memcmp(uuid_list[i], uuid, sizeof(uuid_list[i])) == 0) 187 | goto out; 188 | 189 | i = FBI_UNKNOWN; 190 | 191 | out: 192 | return (enum FIP_BOOT_IMG)i; 193 | } 194 | 195 | #define FT_DATA_SIZE (0x468) 196 | /** 197 | * Image fixed data for V3, usage & meaning is unknown 198 | * We find some similar data in bl32.img header 199 | * Probably used for secure boot 200 | */ 201 | static uint8_t const uuid_data[][FT_DATA_SIZE] = { 202 | [FBI_BL2_DATA] = { 203 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 204 | 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00 205 | /* Rest is 0x0 */ 206 | }, 207 | [FBI_BL30_DATA] = { 208 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 209 | 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, 210 | 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 211 | 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, 212 | 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x00, 213 | /* Rest is 0x0 */ 214 | }, 215 | [FBI_BL31_DATA] = { 216 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 217 | 0x00, 0x00, 0x30, 0x05, 0x00, 0x00, 0x00, 0x00, 218 | 0x00, 0x00, 0x30, 0x05, 0x00, 0x00, 0x00, 0x00, 219 | 0x00, 0x00, 0x30, 0x05, 0x00, 0x00, 0x00, 0x00, 220 | 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x40, 0x01, 221 | /* Rest is 0x0 */ 222 | }, 223 | [FBI_BL32_DATA] = { 224 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 225 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 226 | /* Rest is 0x0 */ 227 | }, 228 | [FBI_BL33_DATA] = {}, 229 | }; 230 | #define FT_DATA_START (0x188) 231 | 232 | /** 233 | * FIP header structure 234 | */ 235 | #pragma pack(push, 1) 236 | struct fip_toc_header { 237 | /** 238 | * FIP magic 239 | */ 240 | uint32_t name; 241 | /** 242 | * Vendor specific number 243 | */ 244 | uint32_t serial_number; 245 | /** 246 | * Flags, reserved for later use 247 | */ 248 | uint64_t flags; 249 | }; 250 | #pragma pack(pop) 251 | #define FT_NAME 0xaa640001 252 | #define FT_SERIAL 0x12345678 253 | #define FIP_TOC_HEADER \ 254 | &((struct fip_toc_header){ \ 255 | .name = htole32(FT_NAME), \ 256 | .serial_number = htole32(FT_SERIAL) \ 257 | }) 258 | 259 | /** 260 | * Header for a FIP table of content entry 261 | */ 262 | #pragma pack(push, 1) 263 | struct fip_toc_entry { 264 | /** 265 | * uuid of the image entry 266 | */ 267 | uuid_t uuid; 268 | /** 269 | * Offset of the image from the FIP base address 270 | */ 271 | uint64_t offset; 272 | /** 273 | * Size of the FIP entry image 274 | */ 275 | uint64_t size; 276 | /** 277 | * Flags for the FIP entry image 278 | */ 279 | uint64_t flags; 280 | }; 281 | #pragma pack(pop) 282 | 283 | #define FTE_BL31HDR_SZ 0x50 284 | #define FTE_BL31HDR_OFF(nr) (0x430 + FTE_BL31HDR_SZ * (nr)) 285 | /* Get fip entry offset from fip base */ 286 | #define FTE_OFF(nr) \ 287 | (sizeof(struct fip_toc_header) + nr * sizeof(struct fip_toc_entry)) 288 | 289 | #define FIP_SZ 0x4000 290 | #define FIP_TEMPPATH "/tmp/fip.bin.XXXXXX" 291 | #define FIP_TEMPPATHSZ sizeof(FIP_TEMPPATH) 292 | /** 293 | * FIP handler 294 | */ 295 | struct fip { 296 | /** 297 | * Current image copied data size 298 | */ 299 | size_t cursz; 300 | /** 301 | * Number of entry in FIP table of content 302 | */ 303 | size_t nrentries; 304 | /** 305 | * Temporary Fip file descriptor 306 | */ 307 | int fd; 308 | /** 309 | * Temporary file path 310 | */ 311 | char path[FIP_TEMPPATHSZ]; 312 | }; 313 | 314 | /** 315 | * Init a FIP handler 316 | * 317 | * @param fip: FIP handler to init 318 | * @return: 0 on success, negative number otherwise 319 | */ 320 | static inline int fip_init(struct fip *fip, enum fip_rev rev) 321 | { 322 | unsigned long long init = 0xffffffffffffffffULL; 323 | ssize_t nr; 324 | size_t i; 325 | off_t off; 326 | int ret; 327 | 328 | strncpy(fip->path, FIP_TEMPPATH, sizeof(fip->path)); 329 | fip->cursz = FIP_SZ; 330 | fip->nrentries = 0; 331 | fip->fd = gi_fip_create_tmp(fip->path); 332 | if(fip->fd < 0) { 333 | PERR("Cannot create fip temp: "); 334 | ret = -errno; 335 | goto out; 336 | } 337 | 338 | switch (rev) { 339 | case GI_FIP_V2: 340 | ret = ftruncate(fip->fd, FIP_SZ - 0x200); 341 | if(ret < 0) { 342 | PERR("Cannot truncate fip toc header: "); 343 | close(fip->fd); 344 | ret = -errno; 345 | goto out; 346 | } 347 | 348 | nr = gi_fip_write_blk(fip->fd, (uint8_t *)FIP_TOC_HEADER, 349 | sizeof(*FIP_TOC_HEADER)); 350 | if(nr < 0) { 351 | PERR("Cannot write fip toc header: "); 352 | close(fip->fd); 353 | ret = -errno; 354 | goto out; 355 | } 356 | 357 | /* End of toc entry */ 358 | off = lseek(fip->fd, 0xc00, SEEK_SET); 359 | if(off < 0) { 360 | SEEK_ERR(off, ret); 361 | goto out; 362 | } 363 | for(i = 0; i < 0x80 / sizeof(init); ++i) { 364 | nr = gi_fip_write_blk(fip->fd, (uint8_t *)&init, 365 | sizeof(init)); 366 | if(nr < 0) { 367 | PERR("Cannot write fip toc last entry: "); 368 | close(fip->fd); 369 | ret = -errno; 370 | goto out; 371 | } 372 | } 373 | break; 374 | case GI_FIP_V3: 375 | ret = ftruncate(fip->fd, FIP_SZ - SHA2_SZ); 376 | if(ret < 0) { 377 | PERR("Cannot truncate fip toc header: "); 378 | close(fip->fd); 379 | ret = -errno; 380 | goto out; 381 | } 382 | 383 | off = lseek(fip->fd, TOC_OFFSET_V3, SEEK_SET); 384 | if(off < 0) { 385 | SEEK_ERR(off, ret); 386 | goto out; 387 | } 388 | 389 | nr = gi_fip_write_blk(fip->fd, (uint8_t *)FIP_TOC_HEADER, 390 | sizeof(*FIP_TOC_HEADER)); 391 | if(nr < 0) { 392 | PERR("Cannot write fip toc header: "); 393 | close(fip->fd); 394 | ret = -errno; 395 | goto out; 396 | } 397 | break; 398 | } 399 | ret = 0; 400 | 401 | out: 402 | return ret; 403 | } 404 | 405 | /** 406 | * Cleanup a FIP handler 407 | * 408 | * @param fip: FIP handler to clean 409 | */ 410 | static inline void fip_cleanup(struct fip *fip) 411 | { 412 | close(fip->fd); 413 | (void)remove(fip->path); 414 | } 415 | 416 | /** 417 | * Binary file info found in FIP ToC entry 418 | */ 419 | struct fip_entry_info { 420 | enum FIP_BOOT_IMG type; 421 | size_t offset; 422 | size_t size; 423 | }; 424 | 425 | /** 426 | * List of binaries file info found in FIP ToC 427 | */ 428 | #define MAX_FIP_FILE 10 429 | struct fip_toc_info { 430 | size_t nr_files; 431 | struct fip_entry_info files[MAX_FIP_FILE]; 432 | }; 433 | 434 | /** 435 | * Read a fip header from image 436 | * 437 | * @param fip: fill up with FIP informations 438 | * @param fd: FIP image to read header from 439 | * @param rev: FIP Format revision 440 | * @param bl2sz: BL2 Size 441 | * @return: 0 on success, negative number otherwise 442 | */ 443 | static int fip_read_toc(struct fip_toc_info *toc, int fd, enum fip_rev rev, 444 | size_t bl2sz) 445 | { 446 | struct fip_toc_header tochdr; 447 | struct fip_toc_entry entry; 448 | ssize_t nr; 449 | size_t i; 450 | off_t off; 451 | enum FIP_BOOT_IMG type; 452 | int ret; 453 | 454 | off = lseek(fd, rev == GI_FIP_V3 ? bl2sz + TOC_OFFSET_V3 : 0, 455 | SEEK_SET); 456 | if(off < 0) { 457 | SEEK_ERR(off, ret); 458 | goto out; 459 | } 460 | 461 | /* Verify FIP TOC header first */ 462 | ret = gi_fip_read_blk(fd, (uint8_t *)&tochdr, sizeof(tochdr)); 463 | if(ret < 0) { 464 | PERR("Cannot read FIP header\n"); 465 | ret = -errno; 466 | goto out; 467 | } 468 | 469 | if(memcmp((uint8_t *)&tochdr, (uint8_t *)FIP_TOC_HEADER, 470 | sizeof(tochdr)) != 0) { 471 | ERR("Invalid FIP Header\n"); 472 | ret = -EINVAL; 473 | goto out; 474 | } 475 | 476 | /* Now read table of content entries */ 477 | for(i = 0; i < ARRAY_SIZE(toc->files); ++i) { 478 | nr = gi_fip_read_blk(fd, (uint8_t *)&entry, sizeof(entry)); 479 | if(nr <= 0) { 480 | PERR("Cannot read TOC entry\n"); 481 | ret = -errno; 482 | goto out; 483 | } 484 | type = uuid_get_type(entry.uuid); 485 | toc->files[i].type = type; 486 | if(type == FBI_EMPTY) 487 | continue; 488 | if(type == FBI_UNKNOWN) 489 | break; 490 | toc->files[i].offset = entry.offset; 491 | toc->files[i].size = entry.size; 492 | } 493 | 494 | toc->nr_files = i; 495 | ret = 0; 496 | out: 497 | return ret; 498 | } 499 | 500 | /** 501 | * Copy part of a file as-is in another file at specific offset 502 | * 503 | * @param fdin: Src file to copy 504 | * @param fdout: Dest file to copy into 505 | * @param size: Maximum size to copy from fdin 506 | * @return: actual number of bytes copied from fdin on success, negative number 507 | * otherwise. 508 | */ 509 | static ssize_t gi_copy_file(int fdin, int fdout, size_t len) 510 | { 511 | ssize_t nrd, nwr, tot; 512 | uint8_t block[512]; 513 | 514 | tot = 0; 515 | do { 516 | nrd = gi_fip_read_blk(fdin, block, 517 | MIN(len - tot, sizeof(block))); 518 | if(nrd < 0) 519 | continue; 520 | nwr = gi_fip_write_blk(fdout, block, nrd); 521 | if(nwr < 0) { 522 | PERR("Cannot write to file\n"); 523 | tot = -errno; 524 | goto out; 525 | } 526 | tot += nrd; 527 | } while((nrd > 0) && ((size_t)tot < len)); 528 | 529 | if(nrd < 0) { 530 | PERR("Cannot read file\n"); 531 | tot = -errno; 532 | goto out; 533 | } 534 | out: 535 | return tot; 536 | } 537 | 538 | /** 539 | * Copy a file as-is in another file at specific offset 540 | * 541 | * @param fdin: Src file to copy 542 | * @param fdout: Dest file to copy into 543 | * @param off: Offset at which src file should be copy into dest file 544 | * @return: 0 on success, negative number otherwise 545 | */ 546 | static int gi_fip_dump_img(int fdin, int fdout, size_t off) 547 | { 548 | ssize_t len; 549 | off_t o; 550 | int ret; 551 | 552 | o = lseek(fdout, off, SEEK_SET); 553 | if(o < 0) { 554 | SEEK_ERR(o, ret); 555 | goto out; 556 | } 557 | 558 | len = gi_copy_file(fdin, fdout, (size_t)-1); 559 | if(len < 0) { 560 | ret = (int)len; 561 | goto out; 562 | } 563 | 564 | ret = 0; 565 | out: 566 | return ret; 567 | } 568 | 569 | /** 570 | * Add a bootloder image in boot image 571 | * 572 | * @param fip: Fip handler 573 | * @param fdout: Final boot image file 574 | * @param fdin: Bootloader image to add 575 | * @param type: Type of bootloader image 576 | * @param rev: FIP Format revision 577 | * @param bl2sz: BL2 Image Size 578 | */ 579 | static int gi_fip_add(struct fip *fip, int fdout, int fdin, 580 | enum FIP_BOOT_IMG type, enum fip_rev rev, size_t bl2sz) 581 | { 582 | static uint32_t const bl31magic[] = { 583 | BL31_ENTRY_MAGIC, 584 | 0x1, 585 | }; 586 | struct fip_toc_entry entry; 587 | size_t sz; 588 | ssize_t nr; 589 | size_t skip = 0; 590 | off_t off; 591 | int ret; 592 | uint8_t buf[FTE_BL31HDR_SZ]; 593 | 594 | if (fdin >= 0) { 595 | off = lseek(fdin, 0, SEEK_END); 596 | if(off < 0) { 597 | SEEK_ERR(off, ret); 598 | goto out; 599 | } 600 | sz = (size_t)off; 601 | 602 | /* Detect a AMLSBLK image a skip header if found on V3 */ 603 | if (rev == GI_FIP_V3) { 604 | off = lseek(fdin, 0, SEEK_SET); 605 | if(off < 0) { 606 | SEEK_ERR(off, ret); 607 | goto out; 608 | } 609 | 610 | nr = gi_fip_read_blk(fdin, buf, 4); 611 | if(nr <= 0) { 612 | PERR("Cannot read BL image entry\n"); 613 | ret = -errno; 614 | goto out; 615 | } 616 | 617 | if (le32toh(*(uint32_t *)buf) == AMLSBLK_KEY_MAGIC) { 618 | skip = 0x490; 619 | sz -= skip; 620 | } 621 | } 622 | } else 623 | sz = 0; 624 | 625 | memcpy(entry.uuid, uuid_list[type], sizeof(entry.uuid)); 626 | entry.offset = fip->cursz; 627 | entry.flags = 0; 628 | entry.size = sz; 629 | 630 | off = FTE_OFF(fip->nrentries); 631 | if (rev == GI_FIP_V3) 632 | off += TOC_OFFSET_V3; 633 | 634 | off = lseek(fip->fd, off, SEEK_SET); 635 | if(off < 0) { 636 | SEEK_ERR(off, ret); 637 | goto out; 638 | } 639 | nr = gi_fip_write_blk(fip->fd, (uint8_t *)&entry, sizeof(entry)); 640 | if(nr < 0) { 641 | PERR("Cannot write FIP entry\n"); 642 | ret = -errno; 643 | goto out; 644 | } 645 | 646 | /* Skip writting data if file is not available */ 647 | if (!sz) 648 | goto nofdin; 649 | 650 | off = lseek(fdin, 256, SEEK_SET); 651 | if(off < 0) { 652 | SEEK_ERR(off, ret); 653 | goto out; 654 | } 655 | nr = gi_fip_read_blk(fdin, buf, sizeof(buf)); 656 | if(nr <= 0) { 657 | PERR("Cannot read BL image entry\n"); 658 | ret = -errno; 659 | goto out; 660 | } 661 | 662 | /* 663 | * BL31 binary store information about load address and entry point in 664 | * the FIP data 665 | */ 666 | if(le32toh(*(uint32_t *)buf) == BL31_MAGIC) { 667 | off = lseek(fip->fd, 1024, SEEK_SET); 668 | if(off < 0) { 669 | SEEK_ERR(off, ret); 670 | goto out; 671 | } 672 | nr = gi_fip_write_blk(fip->fd, (uint8_t *)&bl31magic, 673 | sizeof(bl31magic)); 674 | if(nr < 0) { 675 | PERR("Cannot write BL31 entry header\n"); 676 | ret = -errno; 677 | goto out; 678 | } 679 | off = lseek(fip->fd, FTE_BL31HDR_OFF(fip->nrentries), 680 | SEEK_SET); 681 | if(off < 0) { 682 | SEEK_ERR(off, ret); 683 | goto out; 684 | } 685 | nr = gi_fip_write_blk(fip->fd, buf, sizeof(buf)); 686 | if(nr < 0) { 687 | PERR("Cannot write BL31 entry header data\n"); 688 | ret = -errno; 689 | goto out; 690 | } 691 | } 692 | 693 | off = lseek(fdin, skip, SEEK_SET); 694 | if(off < 0) { 695 | SEEK_ERR(off, ret); 696 | goto out; 697 | } 698 | gi_fip_dump_img(fdin, fdout, bl2sz + entry.offset); 699 | fip->cursz += ROUNDUP(sz, 0x4000); 700 | 701 | nofdin: 702 | ++fip->nrentries; 703 | ret = 0; 704 | 705 | out: 706 | return ret; 707 | } 708 | 709 | /** 710 | * Add a bootloader image data in boot image TOC 711 | * 712 | * @param fip: Fip handler 713 | * @param type: Type of bootloader image data 714 | * @param index: Index of bootloader image data 715 | */ 716 | static int gi_fip_data_add(struct fip *fip, enum FIP_BOOT_IMG type, 717 | unsigned int index) 718 | { 719 | struct fip_toc_entry entry; 720 | ssize_t nr; 721 | off_t off; 722 | int ret; 723 | 724 | memcpy(entry.uuid, uuid_list[type], sizeof(entry.uuid)); 725 | entry.offset = FT_DATA_START + (index * FT_DATA_SIZE); 726 | entry.flags = 0; 727 | entry.size = FT_DATA_SIZE; 728 | 729 | off = lseek(fip->fd, TOC_OFFSET_V3 + FTE_OFF(fip->nrentries), 730 | SEEK_SET); 731 | if(off < 0) { 732 | SEEK_ERR(off, ret); 733 | goto out; 734 | } 735 | nr = gi_fip_write_blk(fip->fd, (uint8_t *)&entry, 736 | sizeof(entry)); 737 | if(nr < 0) { 738 | PERR("Cannot write FIP entry\n"); 739 | ret = -errno; 740 | goto out; 741 | } 742 | 743 | off = lseek(fip->fd, entry.offset, SEEK_SET); 744 | if(off < 0) { 745 | SEEK_ERR(off, ret); 746 | goto out; 747 | } 748 | nr = gi_fip_write_blk(fip->fd, (uint8_t *)&uuid_data[type], 749 | FT_DATA_SIZE); 750 | if(nr < 0) { 751 | PERR("Cannot write FIP data entry\n"); 752 | ret = -errno; 753 | goto out; 754 | } 755 | 756 | ++fip->nrentries; 757 | ret = 0; 758 | 759 | out: 760 | return ret; 761 | } 762 | 763 | /** 764 | * DDRFW header structure 765 | */ 766 | #pragma pack(push, 1) 767 | struct fip_ddrfw_toc_header { 768 | /** 769 | * DDRFW magic 770 | */ 771 | uint32_t magic; 772 | /** 773 | * DDR Firmware count 774 | */ 775 | uint32_t count; 776 | /** 777 | * Flags, reserved for later use 778 | */ 779 | uint64_t flags; 780 | }; 781 | #pragma pack(pop) 782 | #define DDRFW_MAGIC (0x4d464440) 783 | #define FIP_DDRFW_TOC_HEADER(ddrfw_count) \ 784 | { \ 785 | .magic = htole32(DDRFW_MAGIC), \ 786 | .count = htole32(ddrfw_count), \ 787 | .flags = 0 \ 788 | } 789 | #define DDRFW_OFF (0x1790) 790 | 791 | /** 792 | * Initialize the DDR firmware TOC 793 | * 794 | * @param fip: Fip handler 795 | * @param ddrfw_count: Number of DDR firmwares 796 | */ 797 | static int gi_fip_ddrfw_init(struct fip *fip, unsigned int ddrfw_count) 798 | { 799 | struct fip_ddrfw_toc_header toc = FIP_DDRFW_TOC_HEADER(ddrfw_count); 800 | ssize_t nr; 801 | off_t off; 802 | int ret = 0; 803 | 804 | off = lseek(fip->fd, DDRFW_OFF, SEEK_SET); 805 | if(off < 0) { 806 | SEEK_ERR(off, ret); 807 | goto out; 808 | } 809 | 810 | nr = gi_fip_write_blk(fip->fd, (uint8_t *)&toc, sizeof(toc)); 811 | if(nr < 0) { 812 | PERR("Cannot write fip toc header: "); 813 | close(fip->fd); 814 | ret = -errno; 815 | goto out; 816 | } 817 | 818 | out: 819 | return ret; 820 | } 821 | 822 | /** 823 | * DDRFW entry structure 824 | */ 825 | #pragma pack(push, 1) 826 | struct fip_ddrfw_toc_entry { 827 | /** 828 | * DDRFW magic 829 | */ 830 | uint8_t magic[8]; 831 | /** 832 | * DDRFW offset 833 | */ 834 | uint32_t offset; 835 | /** 836 | * DDRFW size 837 | */ 838 | uint32_t size; 839 | /** 840 | * DDRFW properties 841 | */ 842 | uint8_t props[16]; 843 | /** 844 | * DDRFW hash 845 | */ 846 | uint8_t hash[SHA2_SZ]; 847 | }; 848 | #pragma pack(pop) 849 | 850 | #define FTE_DDRFW_OFF(i) \ 851 | (DDRFW_OFF + sizeof(struct fip_ddrfw_toc_header) + \ 852 | (i * sizeof(struct fip_ddrfw_toc_entry))) 853 | 854 | /** 855 | * Add a DDR Firmware data in boot image TOC 856 | * 857 | * @param fip: Fip handler 858 | * @param fdout: Final boot image file 859 | * @param fdin: Bootloader image to add 860 | * @param index: Index of DDR Firmware 861 | * @param bl2sz: Size of BL2 image 862 | */ 863 | static int gi_fip_ddrfw_add(struct fip *fip, int fdout, int fdin, 864 | size_t index, size_t bl2sz) 865 | { 866 | struct fip_ddrfw_toc_entry entry; 867 | uint8_t tmp[1024]; 868 | EVP_MD_CTX *ctx; 869 | size_t sz; 870 | ssize_t nr; 871 | off_t off; 872 | size_t i; 873 | int ret; 874 | 875 | ctx = EVP_MD_CTX_new(); 876 | if(ctx == NULL) { 877 | SSLERR(ret, "Cannot create digest context: "); 878 | goto out; 879 | } 880 | 881 | ret = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); 882 | if(ret != 1) { 883 | SSLERR(ret, "Cannot init digest context: "); 884 | goto out; 885 | } 886 | 887 | off = lseek(fdin, 0, SEEK_END); 888 | if(off < 0) { 889 | SEEK_ERR(off, ret); 890 | goto out; 891 | } 892 | sz = (size_t)off; 893 | 894 | entry.offset = fip->cursz; 895 | /* Align size on 16k */ 896 | entry.size = ((sz - 0x60) + 0x3fff) & 0xffffc000; 897 | 898 | off = lseek(fdin, SHA2_SZ, SEEK_SET); 899 | if(off < 0) { 900 | SEEK_ERR(off, ret); 901 | goto out; 902 | } 903 | 904 | nr = gi_fip_read_blk(fdin, (uint8_t *)&entry.magic, sizeof(entry.magic)); 905 | if(nr <= 0) { 906 | PERR("Cannot read DDRFW magic\n"); 907 | ret = -errno; 908 | goto out; 909 | } 910 | 911 | off = lseek(fdin, 0x30, SEEK_SET); 912 | if(off < 0) { 913 | SEEK_ERR(off, ret); 914 | goto out; 915 | } 916 | 917 | nr = gi_fip_read_blk(fdin, (uint8_t *)&entry.props, sizeof(entry.props)); 918 | if(nr <= 0) { 919 | PERR("Cannot read DDRFW properties\n"); 920 | ret = -errno; 921 | goto out; 922 | } 923 | 924 | off = lseek(fdin, 0x60, SEEK_SET); 925 | if(off < 0) { 926 | SEEK_ERR(off, ret); 927 | goto out; 928 | } 929 | 930 | for(i = 0; i < (sz - 0x60); i += nr) { 931 | nr = gi_fip_read_blk(fdin, tmp, sizeof(tmp)); 932 | if(nr < 0) { 933 | PERR("Cannot read fd %d:", fdin); 934 | ret = (int)nr; 935 | goto out; 936 | } 937 | ret = EVP_DigestUpdate(ctx, tmp, nr); 938 | if(ret != 1) { 939 | SSLERR(ret, "Cannot hash data block: "); 940 | goto out; 941 | } 942 | } 943 | 944 | /* Add potential padding to digest aswell */ 945 | while (i < entry.size) { 946 | memset(tmp, 0, sizeof(tmp)); 947 | nr = MIN(entry.size - i, sizeof(tmp)); 948 | 949 | ret = EVP_DigestUpdate(ctx, tmp, nr); 950 | if(ret != 1) { 951 | SSLERR(ret, "Cannot hash data block: "); 952 | goto out; 953 | } 954 | 955 | i += nr; 956 | } 957 | 958 | ret = EVP_DigestFinal_ex(ctx, (uint8_t *)&entry.hash, NULL); 959 | if(ret != 1) { 960 | SSLERR(ret, "Cannot finalize hash: "); 961 | goto out; 962 | } 963 | 964 | off = lseek(fip->fd, FTE_DDRFW_OFF(index), SEEK_SET); 965 | if(off < 0) { 966 | SEEK_ERR(off, ret); 967 | goto out; 968 | } 969 | nr = gi_fip_write_blk(fip->fd, (uint8_t *)&entry, sizeof(entry)); 970 | if(nr < 0) { 971 | PERR("Cannot write DDRFW entry\n"); 972 | ret = -errno; 973 | goto out; 974 | } 975 | 976 | off = lseek(fdin, 0x60, SEEK_SET); 977 | if(off < 0) { 978 | SEEK_ERR(off, ret); 979 | goto out; 980 | } 981 | 982 | gi_fip_dump_img(fdin, fdout, bl2sz + entry.offset); 983 | fip->cursz += entry.size; 984 | 985 | ret = 0; 986 | 987 | out: 988 | return ret; 989 | } 990 | 991 | /** 992 | * Calculate final FIP toc hash 993 | * 994 | * @param fip: Fip handler 995 | */ 996 | static int gi_fip_v3_fini(int fipfd) 997 | { 998 | EVP_MD_CTX *ctx; 999 | uint8_t tmp[1024]; 1000 | uint8_t hash[SHA2_SZ]; 1001 | size_t i; 1002 | ssize_t nr; 1003 | off_t off; 1004 | int ret; 1005 | 1006 | ctx = EVP_MD_CTX_new(); 1007 | if(ctx == NULL) { 1008 | SSLERR(ret, "Cannot create digest context: "); 1009 | goto out; 1010 | } 1011 | 1012 | ret = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); 1013 | if(ret != 1) { 1014 | SSLERR(ret, "Cannot init digest context: "); 1015 | goto out; 1016 | } 1017 | 1018 | off = lseek(fipfd, TOC_OFFSET_V3, SEEK_SET); 1019 | if(off < 0) { 1020 | SEEK_ERR(off, ret); 1021 | goto out; 1022 | } 1023 | 1024 | /* SHA256 is from 0x10 to (FIP_SZ - 0x40) */ 1025 | for(i = 0; i < (FIP_SZ - (2 * SHA2_SZ)); i += nr) { 1026 | nr = gi_fip_read_blk(fipfd, tmp, sizeof(tmp)); 1027 | if(nr < 0) { 1028 | PERR("Cannot read fd %d:", fipfd); 1029 | ret = (int)nr; 1030 | goto out; 1031 | } 1032 | ret = EVP_DigestUpdate(ctx, tmp, nr); 1033 | if(ret != 1) { 1034 | SSLERR(ret, "Cannot hash data block: "); 1035 | goto out; 1036 | } 1037 | } 1038 | 1039 | ret = EVP_DigestFinal_ex(ctx, hash, NULL); 1040 | if(ret != 1) { 1041 | SSLERR(ret, "Cannot finalize hash: "); 1042 | goto out; 1043 | } 1044 | 1045 | off = lseek(fipfd, (FIP_SZ - SHA2_SZ), SEEK_SET); 1046 | if(off < 0) { 1047 | SEEK_ERR(off, ret); 1048 | goto out; 1049 | } 1050 | 1051 | ret = gi_fip_write_blk(fipfd, hash, sizeof(hash)); 1052 | if (ret < 0) 1053 | goto out; 1054 | ret = 0; 1055 | 1056 | off = lseek(fipfd, 0, SEEK_SET); 1057 | if(off < 0) { 1058 | SEEK_ERR(off, ret); 1059 | goto out; 1060 | } 1061 | 1062 | out: 1063 | EVP_MD_CTX_free(ctx); 1064 | return ret; 1065 | } 1066 | 1067 | /** 1068 | * Create a Amlogic bootable image 1069 | * 1070 | * @param bl2: BL2 boot image to add 1071 | * @param bl30: BL30 boot image to add 1072 | * @param bl31: BL31 boot image to add 1073 | * @param bl33: BL33 boot image to add 1074 | * @return: 0 on success, negative number otherwise 1075 | */ 1076 | int gi_fip_create(char const *bl2, char const **ddrfw, 1077 | unsigned int ddrfw_count, char const *bl30, char const *bl301, 1078 | char const *bl31, char const *bl33, char const *fout, 1079 | char const *revc) 1080 | { 1081 | struct fip fip; 1082 | struct amlcblk acb; 1083 | enum fip_rev rev = GI_FIP_V2; 1084 | struct { 1085 | char const *path; 1086 | enum FIP_BOOT_IMG type; 1087 | } fip_bin_path[] = { 1088 | { 1089 | .path = bl30, 1090 | .type = FBI_BL30, 1091 | }, 1092 | { 1093 | .path = bl301, 1094 | .type = FBI_BL301, 1095 | }, 1096 | { 1097 | .path = bl31, 1098 | .type = FBI_BL31, 1099 | }, 1100 | /* BL32 entry, even empty is required needed for V3 */ 1101 | { 1102 | .path = NULL, 1103 | .type = FBI_BL32, 1104 | }, 1105 | { 1106 | .path = bl33, 1107 | .type = FBI_BL33, 1108 | }, 1109 | }; 1110 | size_t i; 1111 | off_t off; 1112 | int fdin = -1, fdout = -1, tmpfd = -1, ret; 1113 | char fippath[] = "/tmp/fip.enc.XXXXXX"; 1114 | off_t bl2sz; 1115 | 1116 | if (revc && !strcmp(revc, "v3")) { 1117 | DBG("Creating a v3 FIP\n"); 1118 | rev = GI_FIP_V3; 1119 | 1120 | if (ddrfw_count > 0) 1121 | DBG("Adding %d DDR firmwares in %s\n", 1122 | ddrfw_count, fout); 1123 | } 1124 | 1125 | DBG("Create FIP final image in %s\n", fout); 1126 | 1127 | ret = fip_init(&fip, rev); 1128 | if(ret < 0) 1129 | goto exit; 1130 | 1131 | fdout = open(fout, O_RDWR | O_CREAT, FOUT_MODE_DFT); 1132 | if(fdout < 0) { 1133 | PERR("Cannot open file %s", fout); 1134 | ret = -errno; 1135 | goto out; 1136 | } 1137 | 1138 | ret = ftruncate(fdout, 0); 1139 | if(ret < 0) 1140 | goto out; 1141 | 1142 | fdin = open(bl2, O_RDONLY); 1143 | if(fdin < 0) { 1144 | PERR("Cannot open bl2 %s", bl2); 1145 | ret = -errno; 1146 | goto out; 1147 | } 1148 | 1149 | off = lseek(fdin, 0, SEEK_END); 1150 | if(off < 0) { 1151 | SEEK_ERR(off, ret); 1152 | goto out; 1153 | } 1154 | 1155 | if (rev == GI_FIP_V2 && off != BL2SZ) { 1156 | PERR("Invalid bl2 size %lx", off); 1157 | ret = -EINVAL; 1158 | goto out; 1159 | } 1160 | bl2sz = off; 1161 | 1162 | off = lseek(fdin, 0, SEEK_SET); 1163 | if(off < 0) { 1164 | SEEK_ERR(off, ret); 1165 | goto out; 1166 | } 1167 | 1168 | ret = gi_fip_dump_img(fdin, fdout, 0); 1169 | if(ret < 0) 1170 | goto out; 1171 | 1172 | /* Add the DDR firmwares entries */ 1173 | if (rev == GI_FIP_V3 && ddrfw_count) { 1174 | ret = gi_fip_ddrfw_init(&fip, ddrfw_count); 1175 | if (ret < 0) 1176 | goto out; 1177 | 1178 | for (i = 0; i < ddrfw_count; ++i) { 1179 | close(fdin); 1180 | fdin = open(ddrfw[i], O_RDONLY); 1181 | if(fdin < 0) { 1182 | PERR("Cannot open bl %s: ", ddrfw[i]); 1183 | ret = -errno; 1184 | goto out; 1185 | } 1186 | ret = gi_fip_ddrfw_add(&fip, fdout, fdin, i, bl2sz); 1187 | if (ret < 0) 1188 | goto out; 1189 | } 1190 | } 1191 | 1192 | /* Add all BL3* images */ 1193 | for(i = 0; i < ARRAY_SIZE(fip_bin_path); ++i) { 1194 | /* FBI_BL301 is no more for V3 */ 1195 | if (rev == GI_FIP_V3 && fip_bin_path[i].type == FBI_BL301) 1196 | continue; 1197 | /* Do not add entry if missing on V2 */ 1198 | if (rev == GI_FIP_V2 && !fip_bin_path[i].path) 1199 | continue; 1200 | if (fdin >= 0) 1201 | close(fdin); 1202 | if(fip_bin_path[i].path) { 1203 | fdin = open(fip_bin_path[i].path, O_RDONLY); 1204 | if(fdin < 0) { 1205 | PERR("Cannot open bl %s: ", 1206 | fip_bin_path[i].path); 1207 | ret = -errno; 1208 | goto out; 1209 | } 1210 | } else 1211 | fdin = -1; 1212 | ret = gi_fip_add(&fip, fdout, fdin, fip_bin_path[i].type, 1213 | rev, bl2sz); 1214 | if(ret < 0) 1215 | goto out; 1216 | } 1217 | 1218 | /* Add the Image DATA entries, even if the image still boots without */ 1219 | if (rev == GI_FIP_V3) { 1220 | static enum FIP_BOOT_IMG const data_list[] = { 1221 | FBI_BL2_DATA, 1222 | FBI_BL30_DATA, 1223 | FBI_BL31_DATA, 1224 | FBI_BL32_DATA, 1225 | FBI_BL33_DATA 1226 | }; 1227 | 1228 | for(i = 0; i < ARRAY_SIZE(data_list); ++i) { 1229 | ret = gi_fip_data_add(&fip, data_list[i], i); 1230 | if (ret < 0) 1231 | goto out; 1232 | } 1233 | } 1234 | 1235 | if (rev == GI_FIP_V2) { 1236 | tmpfd = gi_fip_create_tmp(fippath); 1237 | if(tmpfd < 0) 1238 | goto out; 1239 | 1240 | ret = gi_amlcblk_init(&acb, fip.fd); 1241 | if(ret < 0) 1242 | goto out; 1243 | 1244 | ret = gi_amlcblk_aes_enc(&acb, tmpfd, fip.fd); 1245 | if(ret < 0) 1246 | goto out; 1247 | 1248 | ret = gi_amlcblk_dump_hdr(&acb, tmpfd); 1249 | if(ret < 0) 1250 | goto out; 1251 | 1252 | off = lseek(tmpfd, 0, SEEK_SET); 1253 | if(off < 0) { 1254 | SEEK_ERR(off, ret); 1255 | goto out; 1256 | } 1257 | ret = gi_fip_dump_img(tmpfd, fdout, BL2SZ); 1258 | } else { 1259 | ret = gi_fip_v3_fini(fip.fd); 1260 | if(ret < 0) 1261 | goto out; 1262 | 1263 | ret = gi_fip_dump_img(fip.fd, fdout, bl2sz); 1264 | } 1265 | out: 1266 | if(tmpfd >= 0) { 1267 | close(tmpfd); 1268 | (void)remove(fippath); 1269 | } 1270 | if(fdout >= 0) 1271 | close(fdout); 1272 | if(fdin >= 0) 1273 | close(fdin); 1274 | fip_cleanup(&fip); 1275 | exit: 1276 | return ret; 1277 | } 1278 | 1279 | /** 1280 | * Extract bl2 binary from an Amlogic bootable image 1281 | * 1282 | * @param fdin: Amlogic bootable image 1283 | * @param dir: Output directory 1284 | * @param bl2sz: BL2 Size 1285 | * @return: 0 on success, negative number otherwise 1286 | */ 1287 | static int gi_fip_extract_bl2(int fdin, char const *dir, int bl2sz) 1288 | { 1289 | int fdout = -1, ret; 1290 | char path[PATH_MAX]; 1291 | 1292 | ret = snprintf(path, sizeof(path) - 1, "%s/%s", dir, "bl2.sign"); 1293 | if((ret < 0) || (ret > (int)(sizeof(path) - 1))) { 1294 | ERR("Filename too long"); 1295 | ret = -EINVAL; 1296 | goto out; 1297 | } 1298 | 1299 | fdout = open(path, O_WRONLY | O_CREAT, FOUT_MODE_DFT); 1300 | if(fdout < 0) { 1301 | PERR("Cannot open file %s", path); 1302 | ret = -errno; 1303 | goto out; 1304 | } 1305 | 1306 | ret = gi_copy_file(fdin, fdout, bl2sz); 1307 | if(ret < 0) { 1308 | ERR("Cannot extract BL2 from fip\n"); 1309 | goto out; 1310 | } 1311 | if(ret < bl2sz) { 1312 | ERR("BL2 is too small\n"); 1313 | ret = -EINVAL; 1314 | goto out; 1315 | } 1316 | ret = 0; 1317 | 1318 | out: 1319 | if(fdout >= 0) 1320 | close(fdout); 1321 | return ret; 1322 | } 1323 | 1324 | /** 1325 | * Extract FIP decode it and read ToC from an Amlogic bootable image 1326 | * 1327 | * @param toc: Filled up with FIP ToC infos 1328 | * @param fdin: Amlogic bootable image 1329 | * @param dir: Output directory 1330 | * @param rev: FIP Format revision 1331 | * @return: 0 on success, negative number otherwise 1332 | */ 1333 | static int gi_fip_extract_fip(struct fip_toc_info *toc, int fdin, 1334 | char const *dir) 1335 | { 1336 | struct amlcblk acb; 1337 | char path[PATH_MAX]; 1338 | int fip_enc = -1, fip = -1, ret; 1339 | ssize_t off; 1340 | 1341 | /* Read Amlogic control block */ 1342 | off = lseek(fdin, BL2SZ, SEEK_SET); 1343 | if(off < 0) { 1344 | SEEK_ERR(off, ret); 1345 | goto out; 1346 | } 1347 | ret = gi_amlcblk_read_hdr(&acb, fdin); 1348 | if(ret < 0) 1349 | goto out; 1350 | 1351 | ret = snprintf(path, sizeof(path) - 1, "%s/%s", dir, "fip.enc"); 1352 | if((ret < 0) || (ret > (int)(sizeof(path) - 1))) { 1353 | ERR("Filename too long"); 1354 | ret = -EINVAL; 1355 | goto out; 1356 | } 1357 | 1358 | fip_enc = open(path, O_RDWR | O_CREAT, FOUT_MODE_DFT); 1359 | if(fip_enc < 0) { 1360 | PERR("Cannot open file %s", path); 1361 | ret = -errno; 1362 | goto out; 1363 | } 1364 | 1365 | off = lseek(fdin, BL2SZ, SEEK_SET); 1366 | if(off < 0) { 1367 | SEEK_ERR(off, ret); 1368 | goto out; 1369 | } 1370 | ret = gi_copy_file(fdin, fip_enc, FIP_SZ); 1371 | if(ret < 0) { 1372 | ERR("Cannot extract encrypted fip from boot image\n"); 1373 | goto out; 1374 | } 1375 | if(ret < FIP_SZ) { 1376 | ERR("Encrypted fip is too small\n"); 1377 | ret = -EINVAL; 1378 | goto out; 1379 | } 1380 | 1381 | ret = snprintf(path, sizeof(path) - 1, "%s/%s", dir, "fip"); 1382 | if((ret < 0) || (ret > (int)(sizeof(path) - 1))) { 1383 | ERR("Filename too long"); 1384 | ret = -EINVAL; 1385 | goto out; 1386 | } 1387 | 1388 | fip = open(path, O_RDWR | O_CREAT, FOUT_MODE_DFT); 1389 | if(fip < 0) { 1390 | PERR("Cannot open file %s", path); 1391 | ret = -errno; 1392 | goto out; 1393 | } 1394 | 1395 | ret = gi_amlcblk_aes_dec(&acb, fip, fip_enc); 1396 | if(ret != 0) { 1397 | ERR("Cannot decode FIP header"); 1398 | goto out; 1399 | } 1400 | 1401 | ret = fip_read_toc(toc, fip, GI_FIP_V2, BL2SZ); 1402 | if(ret != 0) { 1403 | ERR("Cannot read fip"); 1404 | goto out; 1405 | } 1406 | 1407 | out: 1408 | if(fip_enc >= 0) 1409 | close(fip_enc); 1410 | if(fip >= 0) 1411 | close(fip); 1412 | return ret; 1413 | } 1414 | 1415 | /** 1416 | * Extract DDR firmware binaries from an Amlogic bootable image. 1417 | * 1418 | * @param fd: Amlogic bootable image 1419 | * @param dir: Output directory 1420 | * @param bl2sz: BL2 Size 1421 | * @return: 0 on success, negative number otherwise 1422 | */ 1423 | static int gi_fip_extract_ddrfw(int fd, char const *dir, int bl2sz) 1424 | { 1425 | uint8_t *tmp, *fw_identifier, hash[SHA2_SZ] = { 0 }; 1426 | struct fip_ddrfw_toc_entry tocentry; 1427 | struct fip_ddrfw_toc_header tochdr; 1428 | char path[PATH_MAX], fw_name[32]; 1429 | int ddr_fw_fd = -1, ret; 1430 | ssize_t off, nr, size; 1431 | EVP_MD_CTX *ctx; 1432 | unsigned int i; 1433 | 1434 | off = lseek(fd, bl2sz + DDRFW_OFF, SEEK_SET); 1435 | if(off < 0) { 1436 | SEEK_ERR(off, ret); 1437 | return ret; 1438 | } 1439 | 1440 | ret = gi_fip_read_blk(fd, (uint8_t *)&tochdr, sizeof(tochdr)); 1441 | if(ret < 0) { 1442 | PERR("Cannot read DDR firmware header\n"); 1443 | return -errno; 1444 | } 1445 | 1446 | if(le32toh(tochdr.magic) != DDRFW_MAGIC) { 1447 | ERR("Invalid DDR firmware magic\n"); 1448 | return -EINVAL; 1449 | } 1450 | 1451 | ctx = EVP_MD_CTX_new(); 1452 | if(ctx == NULL) { 1453 | SSLERR(ret, "Cannot create digest context: "); 1454 | return ret; 1455 | } 1456 | 1457 | for(i = 0; i < tochdr.count; i++) { 1458 | ret = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); 1459 | if(ret != 1) { 1460 | SSLERR(ret, "Cannot init digest context: "); 1461 | goto out; 1462 | } 1463 | 1464 | off = lseek(fd, bl2sz + FTE_DDRFW_OFF(i), SEEK_SET); 1465 | if(off < 0) { 1466 | SEEK_ERR(off, ret); 1467 | goto out; 1468 | } 1469 | 1470 | ret = gi_fip_read_blk(fd, (uint8_t *)&tocentry, sizeof(tocentry)); 1471 | if(ret < 0) { 1472 | ERR("Cannot read DDR firmware entry header\n"); 1473 | goto out; 1474 | } 1475 | 1476 | size = sizeof(tocentry) + tocentry.size; 1477 | tmp = malloc(size); 1478 | if(tmp == NULL) { 1479 | ret = -ENOMEM; 1480 | goto out; 1481 | } 1482 | 1483 | off = lseek(fd, bl2sz + tocentry.offset, SEEK_SET); 1484 | if(off < 0) { 1485 | SEEK_ERR(off, ret); 1486 | goto out; 1487 | } 1488 | 1489 | memcpy(tmp, &tocentry, sizeof(tocentry)); 1490 | 1491 | ret = gi_fip_read_blk(fd, tmp + sizeof(tocentry), tocentry.size); 1492 | if(ret < 0) { 1493 | ERR("Cannot read DDR firmware data\n"); 1494 | goto out; 1495 | } 1496 | 1497 | ret = EVP_DigestUpdate(ctx, tmp, size); 1498 | if(ret != 1) { 1499 | SSLERR(ret, "Cannot hash DDR firmware header and data: "); 1500 | goto out; 1501 | } 1502 | 1503 | ret = EVP_DigestFinal_ex(ctx, hash, NULL); 1504 | if(ret != 1) { 1505 | SSLERR(ret, "Cannot finalize DDR firmware and header hash: "); 1506 | goto out; 1507 | } 1508 | 1509 | fw_identifier = tocentry.props + sizeof(uint32_t); 1510 | 1511 | if(!memcmp(fw_identifier, "d444", 4)) 1512 | strcpy(fw_name, "ddr4_1d.fw"); 1513 | else if(!memcmp(fw_identifier, "d422", 4)) 1514 | strcpy(fw_name, "ddr4_2d.fw"); 1515 | else if(!memcmp(fw_identifier, "d333", 4)) 1516 | strcpy(fw_name, "ddr3_1d.fw"); 1517 | else if(!memcmp(fw_identifier, "eaea", 4)) 1518 | strcpy(fw_name, "piei.fw"); 1519 | else if(!memcmp(fw_identifier, "dl44", 4)) 1520 | strcpy(fw_name, "lpddr4_1d.fw"); 1521 | else if(!memcmp(fw_identifier, "dl40", 4)) 1522 | strcpy(fw_name, "lpddr4_2d.fw"); 1523 | else if(!memcmp(fw_identifier, "ddg1", 4)) 1524 | strcpy(fw_name, "diag_lpddr4.fw"); 1525 | else if(!memcmp(fw_identifier, "AML0", 4)) 1526 | strcpy(fw_name, "aml_ddr.fw"); 1527 | else if(!memcmp(fw_identifier, "dl33", 4)) 1528 | strcpy(fw_name, "lpddr3_1d.fw"); 1529 | else 1530 | snprintf(fw_name, sizeof(fw_name), "ddrfw_%u.bin", i); 1531 | 1532 | ret = snprintf(path, sizeof(path) - 1, "%s/%s", dir, fw_name); 1533 | if((ret < 0) || (ret > (int)(sizeof(path) - 1))) { 1534 | ERR("Filename too long"); 1535 | ret = -EINVAL; 1536 | goto out; 1537 | } 1538 | 1539 | ddr_fw_fd = open(path, O_RDWR | O_CREAT, FOUT_MODE_DFT); 1540 | if(ddr_fw_fd < 0) { 1541 | PERR("Cannot open file %s", path); 1542 | ret = -errno; 1543 | goto out; 1544 | } 1545 | 1546 | nr = gi_fip_write_blk(ddr_fw_fd, hash, sizeof(hash)); 1547 | if(nr < 0) { 1548 | PERR("Cannot write DDR firmware sha256 checksum"); 1549 | ret = -errno; 1550 | goto out; 1551 | } 1552 | 1553 | ret = gi_fip_write_blk(ddr_fw_fd, tmp, size); 1554 | if(ret < 0) { 1555 | ERR("Cannot write extracted DDR firmware data\n"); 1556 | goto out; 1557 | } 1558 | 1559 | close(ddr_fw_fd); 1560 | ddr_fw_fd = -1; 1561 | } 1562 | 1563 | out: 1564 | if(ddr_fw_fd >= 0) 1565 | close(ddr_fw_fd); 1566 | return ret; 1567 | } 1568 | 1569 | /** 1570 | * Extract bl3x binaries from an Amlogic bootable image 1571 | * 1572 | * @param toc: FIP ToC infos 1573 | * @param fdin: Amlogic bootable image 1574 | * @param dir: Output directory 1575 | * @param bl2sz: BL2 Size 1576 | * @return: 0 on success, negative number otherwise 1577 | */ 1578 | static int gi_fip_extract_bl3x(struct fip_toc_info const *toc, int fdin, 1579 | char const *dir, int bl2sz) 1580 | { 1581 | static char const *_fname[] = { 1582 | [FBI_BL30] = "bl30.enc", 1583 | [FBI_BL301] = "bl301.enc", 1584 | [FBI_BL31] = "bl31.enc", 1585 | [FBI_BL32] = "bl32.enc", 1586 | [FBI_BL33] = "bl33.enc", 1587 | [FBI_BL2_DATA] = "bl2.dat", 1588 | [FBI_BL30_DATA] = "bl30.dat", 1589 | [FBI_BL31_DATA] = "bl31.dat", 1590 | [FBI_BL32_DATA] = "bl32.dat", 1591 | [FBI_BL33_DATA] = "bl33.dat", 1592 | }; 1593 | enum FIP_BOOT_IMG type; 1594 | char path[PATH_MAX]; 1595 | off_t off; 1596 | ssize_t len; 1597 | size_t i; 1598 | int binfd = -1, ret = 0; 1599 | 1600 | for(i = 0; i < toc->nr_files; ++i) { 1601 | type = toc->files[i].type; 1602 | if (type == FBI_EMPTY) 1603 | continue; 1604 | if((type >= ARRAY_SIZE(_fname)) || (_fname[type] == NULL)) { 1605 | DBG("Unknown binary %d\n", type); 1606 | continue; 1607 | } 1608 | ret = snprintf(path, sizeof(path) - 1, "%s/%s", dir, 1609 | _fname[type]); 1610 | if((ret < 0) || (ret > (int)(sizeof(path) - 1))) { 1611 | ERR("Filename too long"); 1612 | ret = -EINVAL; 1613 | goto out; 1614 | } 1615 | 1616 | binfd = open(path, O_WRONLY | O_CREAT, FOUT_MODE_DFT); 1617 | if(binfd < 0) { 1618 | PERR("Cannot open file %s", path); 1619 | ret = -errno; 1620 | goto out; 1621 | } 1622 | 1623 | off = lseek(fdin, bl2sz + toc->files[i].offset, SEEK_SET); 1624 | if(off < 0) { 1625 | SEEK_ERR(off, ret); 1626 | goto out; 1627 | } 1628 | 1629 | len = gi_copy_file(fdin, binfd, toc->files[i].size); 1630 | if(len < 0) { 1631 | PERR("Cannot copy binary file %s", path); 1632 | ret = -errno; 1633 | goto out; 1634 | } 1635 | if((size_t)len != toc->files[i].size) 1636 | DBG("Binary file is truncated %s", path); 1637 | close(binfd); 1638 | binfd=-1; 1639 | } 1640 | ret = 0; 1641 | 1642 | out: 1643 | if(binfd >= 0) 1644 | close(binfd); 1645 | return ret; 1646 | } 1647 | 1648 | /** 1649 | * Determine the FIP version and BL2 size 1650 | * 1651 | * @param fdin: Amlogic bootable image file descriptor 1652 | * @param rev: FIP Format revision 1653 | * @param bl2sz: BL2 Size 1654 | * @return: 0 on success, negative number otherwise 1655 | */ 1656 | int gi_fip_check(int fdin, enum fip_rev *rev, size_t *bl2sz) 1657 | { 1658 | struct fip_toc_header tochdr; 1659 | static size_t known_sizes[] = { 1660 | BL2SZ, 0x10000, 0 1661 | }; 1662 | int size_id = 0; 1663 | off_t off; 1664 | int ret; 1665 | 1666 | /* Default to V2 values */ 1667 | *rev = GI_FIP_V2; 1668 | *bl2sz = BL2SZ; 1669 | 1670 | while (known_sizes[size_id]) { 1671 | /* Check if v3 */ 1672 | off = lseek(fdin, known_sizes[size_id] + TOC_OFFSET_V3, 1673 | SEEK_SET); 1674 | if(off < 0) { 1675 | SEEK_ERR(off, ret); 1676 | goto out; 1677 | } 1678 | 1679 | /* Verify FIP TOC header first */ 1680 | ret = gi_fip_read_blk(fdin, (uint8_t *)&tochdr, 1681 | sizeof(tochdr)); 1682 | if(ret < 0) { 1683 | PERR("Cannot read FIP header\n"); 1684 | ret = -errno; 1685 | goto out; 1686 | } 1687 | 1688 | if(!memcmp((uint8_t *)&tochdr, (uint8_t *)FIP_TOC_HEADER, 1689 | sizeof(tochdr))) { 1690 | DBG("Detected FIP v3 format at offset 0x%lx\n", 1691 | known_sizes[size_id] + TOC_OFFSET_V3); 1692 | *rev = GI_FIP_V3; 1693 | *bl2sz = known_sizes[size_id]; 1694 | break; 1695 | } 1696 | 1697 | size_id++; 1698 | } 1699 | 1700 | off = lseek(fdin, 0, SEEK_SET); 1701 | if(off < 0) { 1702 | SEEK_ERR(off, ret); 1703 | goto out; 1704 | } 1705 | 1706 | out: 1707 | return ret; 1708 | } 1709 | 1710 | /** 1711 | * Extract encrypted binaries from an Amlogic bootable image 1712 | * 1713 | * @param fip: Amlogic bootable image 1714 | * @param dir: Output directory 1715 | * @return: 0 on success, negative number otherwise 1716 | */ 1717 | int gi_fip_extract(char const *fip, char const *dir) 1718 | { 1719 | struct fip_toc_info toc; 1720 | enum fip_rev rev = GI_FIP_V2; 1721 | size_t bl2sz = BL2SZ; 1722 | struct stat st; 1723 | int fdin = -1; 1724 | int ret; 1725 | 1726 | ret = stat(dir, &st); 1727 | if(ret != 0) { 1728 | PERR("Cannot open dir %s", dir); 1729 | ret = -errno; 1730 | goto out; 1731 | } 1732 | if((st.st_mode & S_IFDIR) == 0) { 1733 | ERR("%s is not a directory", dir); 1734 | ret = -EINVAL; 1735 | goto out; 1736 | } 1737 | 1738 | fdin = open(fip, O_RDONLY); 1739 | if(fdin < 0) { 1740 | PERR("Cannot open file %s", fip); 1741 | ret = -errno; 1742 | goto out; 1743 | } 1744 | 1745 | ret = gi_fip_check(fdin, &rev, &bl2sz); 1746 | if (ret < 0) { 1747 | PERR("Cannot check FIP revision\n"); 1748 | goto out; 1749 | } 1750 | 1751 | ret = gi_fip_extract_bl2(fdin, dir, bl2sz); 1752 | if(ret < 0) 1753 | goto out; 1754 | 1755 | if (rev == GI_FIP_V3) 1756 | ret = fip_read_toc(&toc, fdin, rev, bl2sz); 1757 | else 1758 | ret = gi_fip_extract_fip(&toc, fdin, dir); 1759 | if(ret < 0) 1760 | goto out; 1761 | 1762 | ret = gi_fip_extract_bl3x(&toc, fdin, dir, bl2sz); 1763 | if(ret < 0) 1764 | goto out; 1765 | 1766 | if (rev == GI_FIP_V3) 1767 | ret = gi_fip_extract_ddrfw(fdin, dir, bl2sz); 1768 | 1769 | out: 1770 | if(fdin >= 0) 1771 | close(fdin); 1772 | return ret; 1773 | } 1774 | -------------------------------------------------------------------------------- /fip.h: -------------------------------------------------------------------------------- 1 | #ifndef _FIP_H_ 2 | #define _FIP_H_ 3 | 4 | int gi_fip_create(char const *bl2, char const **ddrfw, 5 | unsigned int ddrfw_count, char const *bl30, char const *bl301, 6 | char const *bl31, char const *bl33, char const *fout, 7 | char const *rev); 8 | int gi_fip_extract(char const *fip, char const *dir); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /gxlimg.h: -------------------------------------------------------------------------------- 1 | #ifndef _GXLIMG_H_ 2 | #define _GXLIMG_H_ 3 | 4 | #define ROUNDUP(val, rnd) ((((val) + (rnd) - 1) / (rnd)) * (rnd)) 5 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a)) 6 | 7 | #define ERR(...) fprintf(stderr, __VA_ARGS__) 8 | #define PERR(...) do { \ 9 | fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \ 10 | fprintf(stderr, __VA_ARGS__); \ 11 | perror(""); \ 12 | } while(0) 13 | #define SEEK_ERR(off, ret) do { \ 14 | PERR("Cannot seek file: "); \ 15 | ret = (int)off; \ 16 | } while(0) 17 | #ifdef DEBUG 18 | #define DBG(...) printf(__VA_ARGS__) 19 | #else 20 | #define DBG(...) do {} while(0) 21 | #endif 22 | 23 | #ifndef MIN 24 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 25 | #endif 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "gxlimg.h" 8 | #include "bl2.h" 9 | #include "bl3.h" 10 | #include "fip.h" 11 | 12 | #define _PROGNAME_DFT "gxlimg" 13 | #define PROGNAME(argc, argv) (((argc) > 0) ? (argv)[0] : _PROGNAME_DFT) 14 | #define USAGE(argc, argv) usage(PROGNAME(argc, argv)) 15 | 16 | /** 17 | * Supported actions (e.g. create a boot image, ...) 18 | */ 19 | enum gi_act { 20 | GA_INVAL, 21 | GA_BLSIGN, 22 | GA_BLUNSIGN, 23 | GA_BLENC, 24 | GA_BLDEC, 25 | GA_BLDECRYPT, 26 | GA_FIPIMG, 27 | GA_EXTIMG, 28 | }; 29 | 30 | /** 31 | * Type of binary file (e.g. BL2, BL3) 32 | */ 33 | enum gi_type { 34 | GT_INVAL, 35 | GT_BL2, 36 | GT_DDRFW, 37 | GT_BL3X, 38 | GT_BL30, 39 | }; 40 | 41 | /** 42 | * Parsed options for BL2/BL3 image creation 43 | */ 44 | struct gi_blopt { 45 | enum gi_type type; 46 | char const *fin; 47 | char const *fout; 48 | }; 49 | #define GI_BLOPT_INIT(go) do \ 50 | { \ 51 | (go)->type = GT_INVAL; \ 52 | (go)->fin = NULL; \ 53 | (go)->fout = NULL; \ 54 | } while(0) 55 | 56 | /** 57 | * Maximum number of DDR Firmwares 58 | */ 59 | #define MAX_DDRFW 9 60 | 61 | /** 62 | * Parsed options for FIP image creation 63 | */ 64 | struct gi_fipopt { 65 | char const *bl2; 66 | char const *ddrfw[MAX_DDRFW]; 67 | unsigned int ddrfw_count; 68 | char const *bl30; 69 | char const *bl301; 70 | char const *bl31; 71 | char const *bl33; 72 | char const *fout; 73 | char const *rev; 74 | }; 75 | #define GI_FIPOPT_INIT(go) do \ 76 | { \ 77 | (go)->bl2 = NULL; \ 78 | memset(&(go)->ddrfw, 0, sizeof((go)->ddrfw)); \ 79 | (go)->ddrfw_count = 0; \ 80 | (go)->bl30 = NULL; \ 81 | (go)->bl301 = NULL; \ 82 | (go)->bl31 = NULL; \ 83 | (go)->bl33 = NULL; \ 84 | (go)->fout = NULL; \ 85 | (go)->rev = NULL; \ 86 | } while(0) 87 | 88 | /** 89 | * Parsed options for FIP image extraction 90 | */ 91 | struct gi_extopt { 92 | char const *fip; 93 | char const *dir; 94 | }; 95 | #define GI_EXTOPT_INIT(go) do \ 96 | { \ 97 | (go)->fip = NULL; \ 98 | (go)->dir = NULL; \ 99 | } while(0) 100 | 101 | 102 | /** 103 | * Parsed options 104 | */ 105 | struct gi_opt { 106 | enum gi_act act; 107 | union { 108 | struct gi_blopt blopt; 109 | struct gi_fipopt fipopt; 110 | struct gi_extopt extopt; 111 | }; 112 | }; 113 | #define GI_OPT_INIT(go) do \ 114 | { \ 115 | bzero(go, sizeof(*go)); \ 116 | (go)->act = GA_INVAL; \ 117 | } while(0) 118 | 119 | #define GI_FIPOPT_VALID(go) \ 120 | (((go)->act == GA_FIPIMG) && ((go)->fipopt.bl2 != NULL) && \ 121 | ((go)->fipopt.bl30 != NULL) && ((go)->fipopt.bl31 != NULL) && \ 122 | ((go)->fipopt.bl33 != NULL) && ((go)->fipopt.fout != NULL)) 123 | 124 | #define GI_EXTOPT_VALID(go) \ 125 | (((go)->act == GA_EXTIMG) && ((go)->extopt.fip != NULL) && \ 126 | ((go)->extopt.dir != NULL)) 127 | 128 | #define GI_BLOPT_VALID(go) \ 129 | (((go)->act != GA_INVAL) && ((go)->blopt.type != GT_INVAL) && \ 130 | ((go)->blopt.fin != NULL) && ((go)->blopt.fout != NULL)) 131 | 132 | #define GI_OPT_VALID(go) (GI_FIPOPT_VALID(go) || GI_EXTOPT_VALID(go) || \ 133 | GI_BLOPT_VALID(go)) 134 | 135 | static void usage(char const *progname) 136 | { 137 | ERR("Usage:\n"); 138 | ERR("\t%s [OPTION] \n", progname); 139 | ERR("\t%s -t fip [OPTION] \n\n", progname); 140 | ERR("\t-t, --type\n"); 141 | ERR("\t\ttype of file (bl2 or bl30 or bl3x or fip)\n"); 142 | ERR("\n\tbl2 and bl3x options :\n"); 143 | ERR("\t---------------------\n"); 144 | ERR("\t-e, --extract\n"); 145 | ERR("\t\textract the different binary images from boot image\n"); 146 | ERR("\t\tand store them in fout directory\n"); 147 | ERR("\t-s, --sign\n"); 148 | ERR("\t\tsign a boot image from binary image\n"); 149 | ERR("\t-u, --unsign\n"); 150 | ERR("\t\tget a boot image from signed binary image\n"); 151 | ERR("\t-c, --encrypt\n"); 152 | ERR("\t\tcreate and encrypt a bl boot image from binary image\n"); 153 | ERR("\t-d, --decrypt\n"); 154 | ERR("\t\tdecrypt a bl boot image from store it in \n"); 155 | ERR("\n\tfip options :\n"); 156 | ERR("\t--------------\n"); 157 | ERR("\t--bl2\n"); 158 | ERR("\t\tBL2 boot file to add in final boot image\n"); 159 | ERR("\t--ddrfw\n"); 160 | ERR("\t\tDDR Firmware(s) file(s) to add in final boot image\n"); 161 | ERR("\t--bl30\n"); 162 | ERR("\t\tBL30 boot file to add in final boot image\n"); 163 | ERR("\t--bl301\n"); 164 | ERR("\t\tBL301 optional boot file to add in final boot image\n"); 165 | ERR("\t--bl31\n"); 166 | ERR("\t\tBL31 boot file to add in final boot image\n"); 167 | ERR("\t--bl33\n"); 168 | ERR("\t\tBL31 boot file to add in final boot image\n"); 169 | ERR("\t--rev\n"); 170 | ERR("\t\tFIP format revision (v2 or v3)\n"); 171 | } 172 | 173 | /** 174 | * Sign a bootloader boot image ready to be flashed onto a SD card 175 | * 176 | * @param gopt: Boot image creating options 177 | * @return: 0 on success, negative number otherwise 178 | */ 179 | static int gi_sign_img(struct gi_opt *gopt) 180 | { 181 | int ret = -1; 182 | 183 | switch(gopt->blopt.type) { 184 | case GT_BL2: 185 | case GT_BL30: 186 | ret = gi_bl2_sign_img(gopt->blopt.fin, gopt->blopt.fout); 187 | break; 188 | case GT_BL3X: 189 | ret = gi_bl3_sign_img(gopt->blopt.fin, gopt->blopt.fout); 190 | break; 191 | default: 192 | break; 193 | } 194 | return ret; 195 | } 196 | 197 | /** 198 | * Extract a bootloader boot image from a signed one 199 | * 200 | * @param gopt: Boot image creating options 201 | * @return: 0 on success, negative number otherwise 202 | */ 203 | static int gi_unsign_img(struct gi_opt *gopt) 204 | { 205 | int ret = -1; 206 | 207 | switch(gopt->blopt.type) { 208 | case GT_BL2: 209 | case GT_BL30: 210 | ret = gi_bl2_unsign_img(gopt->blopt.fin, gopt->blopt.fout); 211 | break; 212 | default: 213 | break; 214 | } 215 | return ret; 216 | } 217 | 218 | /** 219 | * Encrypt a bootloader boot image ready to be flashed onto a SD card 220 | * 221 | * @param gopt: Boot image options 222 | * @return: 0 on success, negative number otherwise 223 | */ 224 | static int gi_encrypt_img(struct gi_opt *gopt) 225 | { 226 | int ret = -1; 227 | 228 | switch(gopt->blopt.type) { 229 | case GT_BL3X: 230 | ret = gi_bl3_encrypt_img(gopt->blopt.fin, gopt->blopt.fout); 231 | break; 232 | default: 233 | break; 234 | } 235 | return ret; 236 | } 237 | 238 | /** 239 | * Decrypt a bootloader boot image 240 | * 241 | * @param gopt: Boot image options 242 | * @return: 0 on success, negative number otherwise 243 | */ 244 | static int gi_decrypt_img(struct gi_opt *gopt) 245 | { 246 | int ret = -1; 247 | 248 | switch(gopt->blopt.type) { 249 | case GT_BL3X: 250 | ret = gi_bl3_decrypt_img(gopt->blopt.fin, gopt->blopt.fout); 251 | break; 252 | default: 253 | break; 254 | } 255 | return ret; 256 | } 257 | 258 | /** 259 | * Extract all bootloader binaries from a boot image 260 | * 261 | * @param gopt: Boot image extraction options 262 | * @return: 0 on success, negative number otherwise 263 | */ 264 | static int gi_extract(struct gi_opt *gopt) 265 | { 266 | int ret; 267 | 268 | DBG("Extract files from FIP boot image %s\n", gopt->extopt.fip); 269 | ret = gi_fip_extract(gopt->extopt.fip, gopt->extopt.dir); 270 | return ret; 271 | } 272 | 273 | /** 274 | * Create a FIP boot final image 275 | * 276 | * @param gopt: Boot image creation options 277 | * @return: 0 on success, negative number otherwise 278 | */ 279 | static int gi_fipimg_create(struct gi_opt *gopt) 280 | { 281 | int ret; 282 | 283 | DBG("Creating final FIP boot image %s\n", gopt->fipopt.fout); 284 | ret = gi_fip_create(gopt->fipopt.bl2, gopt->fipopt.ddrfw, 285 | gopt->fipopt.ddrfw_count, gopt->fipopt.bl30, 286 | gopt->fipopt.bl301, gopt->fipopt.bl31, 287 | gopt->fipopt.bl33, gopt->fipopt.fout, 288 | gopt->fipopt.rev); 289 | return ret; 290 | } 291 | /** 292 | * Parse program arguments 293 | * 294 | * @param gopt: Filled with parsed options 295 | * @param argc: number of argument to parse 296 | * @param argv: Array of argument 297 | * @return: 0 on success, negative number otherwise 298 | */ 299 | static int parse_args(struct gi_opt *gopt, int argc, char *argv[]) 300 | { 301 | struct option opt[] = { 302 | { 303 | .name = "type", 304 | .has_arg = 1, 305 | .flag = NULL, 306 | .val = 't', 307 | }, 308 | { 309 | .name = "sign", 310 | .has_arg = 0, 311 | .flag = NULL, 312 | .val = 's', 313 | }, 314 | { 315 | .name = "unsign", 316 | .has_arg = 0, 317 | .flag = NULL, 318 | .val = 'u', 319 | }, 320 | { 321 | .name = "encrypt", 322 | .has_arg = 0, 323 | .flag = NULL, 324 | .val = 'c', 325 | }, 326 | { 327 | .name = "decrypt", 328 | .has_arg = 0, 329 | .flag = NULL, 330 | .val = 'd', 331 | }, 332 | { 333 | .name = "extract", 334 | .has_arg = 0, 335 | .flag = NULL, 336 | .val = 'e', 337 | }, 338 | { 339 | .name = "bl2", 340 | .has_arg = 1, 341 | .flag = NULL, 342 | .val = '2', 343 | }, 344 | { 345 | .name = "ddrfw", 346 | .has_arg = 1, 347 | .flag = NULL, 348 | .val = '6', 349 | }, 350 | { 351 | .name = "bl30", 352 | .has_arg = 1, 353 | .flag = NULL, 354 | .val = '0', 355 | }, 356 | { 357 | .name = "bl301", 358 | .has_arg = 1, 359 | .flag = NULL, 360 | .val = '4', 361 | }, 362 | { 363 | .name = "bl31", 364 | .has_arg = 1, 365 | .flag = NULL, 366 | .val = '1', 367 | }, 368 | { 369 | .name = "bl33", 370 | .has_arg = 1, 371 | .flag = NULL, 372 | .val = '3', 373 | }, 374 | { 375 | .name = "rev", 376 | .has_arg = 1, 377 | .flag = NULL, 378 | .val = '5', 379 | }, 380 | }; 381 | struct gi_blopt blopt; 382 | struct gi_fipopt fipopt; 383 | struct gi_extopt extopt; 384 | int ret; 385 | int idx; 386 | 387 | GI_OPT_INIT(gopt); 388 | GI_BLOPT_INIT(&blopt); 389 | GI_FIPOPT_INIT(&fipopt); 390 | GI_EXTOPT_INIT(&extopt); 391 | 392 | while((ret = getopt_long(argc, argv, "ecdsut:", opt, &idx)) != -1) { 393 | switch(ret) { 394 | case 't': 395 | if(strcmp(optarg, "bl2") == 0) { 396 | blopt.type = GT_BL2; 397 | } else if(strcmp(optarg, "bl3x") == 0) { 398 | blopt.type = GT_BL3X; 399 | } else if(strcmp(optarg, "bl30") == 0) { 400 | blopt.type = GT_BL30; 401 | } else if(strcmp(optarg, "fip") == 0) { 402 | gopt->act = GA_FIPIMG; 403 | } else { 404 | ERR("%s: invalid type %s\n", 405 | PROGNAME(argc, argv), optarg); 406 | goto out; 407 | } 408 | break; 409 | case 's': 410 | gopt->act = GA_BLSIGN; 411 | break; 412 | case 'u': 413 | gopt->act = GA_BLUNSIGN; 414 | break; 415 | case 'c': 416 | gopt->act = GA_BLENC; 417 | break; 418 | case 'd': 419 | gopt->act = GA_BLDEC; 420 | break; 421 | case 'e': 422 | gopt->act = GA_EXTIMG; 423 | break; 424 | case '2': 425 | fipopt.bl2 = optarg; 426 | break; 427 | case '6': 428 | fipopt.ddrfw[fipopt.ddrfw_count++] = optarg; 429 | break; 430 | case '0': 431 | fipopt.bl30 = optarg; 432 | break; 433 | case '4': 434 | fipopt.bl301 = optarg; 435 | break; 436 | case '1': 437 | fipopt.bl31 = optarg; 438 | break; 439 | case '3': 440 | fipopt.bl33 = optarg; 441 | break; 442 | case '5': 443 | fipopt.rev = optarg; 444 | break; 445 | case '?': 446 | goto out; 447 | }; 448 | } 449 | 450 | if(gopt->act == GA_FIPIMG) { 451 | if(optind + 1 > argc) { 452 | ERR("%s: is mandatory\n", PROGNAME(argc, argv)); 453 | goto out; 454 | } 455 | 456 | fipopt.fout = argv[optind]; 457 | memcpy(&gopt->fipopt, &fipopt, sizeof(fipopt)); 458 | } else { 459 | if(optind + 2 > argc) { 460 | ERR("%s: and are mandatory\n", 461 | PROGNAME(argc, argv)); 462 | goto out; 463 | } 464 | 465 | if(gopt->act == GA_EXTIMG) { 466 | extopt.fip = argv[optind]; 467 | extopt.dir = argv[optind + 1]; 468 | memcpy(&gopt->extopt, &extopt, sizeof(extopt)); 469 | } else { 470 | blopt.fin = argv[optind]; 471 | blopt.fout = argv[optind + 1]; 472 | memcpy(&gopt->blopt, &blopt, sizeof(blopt)); 473 | } 474 | } 475 | 476 | out: 477 | return (GI_OPT_VALID(gopt)) ? 0 : -1; 478 | } 479 | 480 | int main(int argc, char *argv[]) 481 | { 482 | struct gi_opt opt; 483 | int ret; 484 | 485 | ret = parse_args(&opt, argc, argv); 486 | if(ret < 0) { 487 | USAGE(argc, argv); 488 | goto out; 489 | } 490 | 491 | switch(opt.act) { 492 | case GA_BLSIGN: 493 | ret = gi_sign_img(&opt); 494 | break; 495 | case GA_BLUNSIGN: 496 | ret = gi_unsign_img(&opt); 497 | break; 498 | case GA_BLENC: 499 | ret = gi_encrypt_img(&opt); 500 | break; 501 | case GA_BLDEC: 502 | ret = gi_decrypt_img(&opt); 503 | break; 504 | case GA_EXTIMG: 505 | ret = gi_extract(&opt); 506 | break; 507 | case GA_FIPIMG: 508 | ret = gi_fipimg_create(&opt); 509 | break; 510 | default: 511 | ERR("Invalid action %d\n", opt.act); 512 | ret = -1; 513 | break; 514 | } 515 | 516 | out: 517 | return ret; 518 | } 519 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('gxlimg', 'c', default_options: ['buildtype=debugoptimized', 'c_std=gnu99']) 2 | 3 | deps = [dependency('libssl'), dependency('libcrypto')] 4 | srcs = ['amlcblk.c', 'amlsblk.c', 'bl2.c', 'bl3.c', 'fip.c', 'main.c'] 5 | 6 | add_project_arguments('-D_GNU_SOURCE', language: 'c') 7 | 8 | if get_option('buildtype') == 'debug' 9 | add_project_arguments('-DDEBUG=1', language: 'c') 10 | endif 11 | 12 | executable('gxlimg', srcs, dependencies : deps, install : true) 13 | -------------------------------------------------------------------------------- /reveng/aml_encrypt_gxl.decompiled: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | static int aml_file_copy(FILE *fin, FILE *fout, int sz) 7 | { 8 | /* fin -> -0xc8 */ 9 | /* fout -> -0xd0 */ 10 | /* sz -> -0xd4 */ 11 | int fout_off; /* -0xac */ 12 | uint8_t *buf; /* -0xa8 */ 13 | int ret = 0x427; /* -0xb8 */ 14 | int len = 0; /* -0xb4 */ 15 | int fincur; /* -0xb0 */ 16 | 17 | buf = malloc(0x400); 18 | if((fin == NULL) || (fout == NULL)) 19 | goto out; 20 | 21 | ret = 0x42f; 22 | fincur = ftell(fin); 23 | fout_off = ftell(fout); 24 | fseek(fin, 0, SEEK_END); 25 | len = ftell(fin); 26 | 27 | if(len - fincur < sz) 28 | goto out; 29 | 30 | fseek(fin, fincur, SEEK_SET); 31 | ret = 0x43e; 32 | while(sz > 0) { 33 | len = fread(buf, 0x1, (sz < 0x400) ? sz : 0x400, fin); 34 | if(len == 0) 35 | break; 36 | fwrite(buf, 0x1, len, fout); 37 | sz -= len; 38 | } 39 | 40 | ret = 0; 41 | 42 | out: 43 | if(buf) { 44 | free(buf); 45 | buf = NULL; 46 | } 47 | return ret; 48 | } 49 | 50 | enum bl2sig_optid { 51 | B2S_INPUT, /* 0x0 */ 52 | B2S_AMLUSERKEY, /* 0x8 */ 53 | B2S_USERKEY, /* 0x10 */ 54 | B2S_KEYMAX, /* 0x18 */ 55 | B2S_AESKEY, /* 0x20 */ 56 | B2S_OUTPUT, /* 0x28 */ 57 | B2S_EFUSE, /* 0x30 */ 58 | B2S_SKIPSECTOR, /* 0x38 */ 59 | B2S_LEVEL, /* 0x40 */ 60 | B2S_NR, 61 | }; 62 | 63 | static int aml_bl2_sig_file(char *argval[]) 64 | { 65 | /* argval = -0x2898 */ 66 | int i; 67 | int ret; /* -0x2880 */ 68 | int skip; /* -0x2878 */ 69 | int rsakey_sz; /* -0x287c */ 70 | uint64_t unknow1 = 0; /* -0x2874 */ 71 | int usekey; /* -0x2870 */ 72 | int rdbufsz; /* -0x286c */ 73 | int newsz; /* -0x2868 */ 74 | int useless; /* -0x2864 */ 75 | FILE *fin = NULL; /* -0x2860 */ 76 | FILE *fout = NULL; /* -0x2858 */ 77 | FILE *fkey = NULL; /* -0x2850 */ 78 | /* WWWWWW NULL */ 79 | char *outdata_ptr = NULL; /* -0x2848 */ 80 | char *outdata = NULL; /* -0x2840 */ 81 | char *rdbuf; /* -0x2838 */ 82 | char * rsa_sig_data = 0; /* -0x2830 */ 83 | char * payload_data = 0; /* -0x2828 */ 84 | char *wtfptr; /* 0x2820 */ 85 | char *wtfbuf; /* 0x2818 */ 86 | uint8_t *magic; /* -0x2810 */ 87 | uint8_t *rsakey_data; /* -0x2808 */ 88 | uint8_t *chk1; /* -0x2800 */ 89 | uint8_t *chk3; /* -0x27f8 */ 90 | uint8_t *chk2; /* -0x27f0 */ 91 | uint8_t *chk4; /* -0x27e8 */ 92 | rsa_context ctx; /* -0x27e0 -> ~~~~~-0x26c4 */ 93 | char rsakey_hdr[0x30]; /* -0x26c0 -> 0x2690 */ 94 | char *hdr[0x40]; /* -0x2690 -> 0x2650 */ 95 | struct sha2_context shactx; /* 0x2650 */ 96 | char rand[16]; /* -0x2560 -> 0x2550 */ 97 | char discard[0x20]; /* 0x2550 -> 0x2530 Is it rsa private ??? */ 98 | char rsahash[0x20]; /* 0x2530 -> 0x2510 */ 99 | char pathout[0x100]; /* -0x2510 -> -0x2410 */ 100 | char uselessbuf[0x400]; /* -0x2410 -> 0x2010 */ 101 | char rsakey_buf[0x2000]; /* -0x2010 -> 0x10 */ 102 | 103 | ret = 0x2d73; 104 | 105 | usekey = 0; 106 | if((argval[B2S_AMLUSERKEY] != NULL) || 107 | ((argval[B2S_USERKEY] != NULL) && (argval[B2S_KEYMAX] != NULL))) 108 | usekey = 1; 109 | rsakey_sz = 0; 110 | rdbufsz = 0; 111 | 112 | wtfbuf = malloc(0xc000); 113 | wtfptr = wtfbuf; 114 | 115 | if(argval[B2S_INPUT] == NULL) 116 | goto out; 117 | 118 | skip = 0; 119 | if(argval[B2S_SKIPSECTOR] != NULL) 120 | skip = strtoul(argval[B2S_SKIPSECTOR], NULL, 16); 121 | if(skip > 4) 122 | skip = 4; 123 | 124 | ret = 0x2d95; 125 | if(usekey && skip) 126 | goto out; 127 | 128 | bzero(pathout, 0x100); 129 | bzero(wtfbuf, 0xc000); 130 | 131 | if(argval[B2S_OUTPUT]) { 132 | (.plt)(pathout, argval[B2S_OUTPUT]); /* strcpy ??? */ 133 | } else { 134 | sprintf(pathout, "%s%s", argval[B2S_INPUT], 135 | (usekey) ? ".encrypt" : ".pkg"); 136 | } 137 | 138 | fin = fopen(argval[B2S_INPUT], "rb"); 139 | remove(pathout); 140 | fout = fopen(pathout, "wb+"); 141 | 142 | ret = 0x2db5; 143 | rdbufsz = 0xb000; 144 | rdbuf = malloc(rdbufsz); /* TODO tmp is inited ??? */ 145 | 146 | if(fin == NULL || fout = NULL || rdbuf == NULL) 147 | goto out; 148 | 149 | for(i = 0; i < 16; ++i) 150 | rand[i] = rand(); 151 | 152 | if(usekey) { 153 | if(argval[B2S_AMLUSERKEY]) { 154 | ret = 0x2dc9; 155 | fkey = fopen(argval[B2S_AMLUSERKEY], "rb"); 156 | if(!fkey) 157 | goto out; 158 | fseek(fkey, 0, SEEK_END); 159 | ret = 0x2dd3; 160 | if(ftell(fkey) != 0x1b40) 161 | goto out; 162 | fseek(fkey, 0x1b20, SEEK_SET); 163 | fread(discard, 1, 0x20, fkey); 164 | fseek(fkey, 0x1248, SEEK_SET); 165 | } else if(argval[B2S_KEYMAX] != NULL) { 166 | ret = 0x2de5; 167 | fkey = fopen(argval[B2S_KEYMAX], "rb"); 168 | if(!fkey) 169 | goto out; 170 | fseek(fkey, 0, SEEK_END); 171 | ret = 0x2ded; 172 | if(ftell(fkey) != 0x8d8) 173 | goto out; 174 | fseek(fkey, 0, SEEK_SET); 175 | } else { 176 | printf("aml log: error %d\n", 0x2df6); 177 | } 178 | ret = 0x2dfa; 179 | } 180 | 181 | bzero(hdr, 0x40); 182 | *(uint32_t *)(hdr) = "@AML"; 183 | hdr[10] = 1; 184 | hdr[11] = 1; 185 | hdr[8] = 0x40; /* <----- HDR SIZE (uint8_t) ---- */ 186 | *(uint32_t *)(hdr + 4) = 0x40; /* <------ Total SIZE (uint32_t) */ 187 | 188 | err = 0x2e0a; 189 | /* Copy rand + usekey if any in file */ 190 | if(usekey && fkey) { 191 | bzero(rdbuf, rdbufsz); 192 | fread(rdbuf, 1, 0x8d8, fkey); 193 | memcpy(rdbuf, rand, 16); 194 | fwrite(rdbuf, 1, 0x8d8, fout); 195 | memcpy(wtfptr, rdbuf, 0x8d8); 196 | wtfptr += 0x8d8; 197 | fclose(fkey); 198 | fkey = NULL; 199 | if((rdbuf[0x6b0] != 0x40) && (rdbuf[0x6b0] != 0x80) && 200 | (rdbuf[0x6b0] != 0x20)) { 201 | ret = 0x2e22; 202 | goto out; 203 | } 204 | } else { 205 | bzero(rdbuf, rdbufsz); 206 | memcpy(rdbuf, rand, 16); 207 | fwrite(rdbuf, 1, 16, fout); 208 | memcpy(wtfptr, rdbuf, 16); 209 | wtfptr += 16 210 | } 211 | 212 | bzero(rdbuf, rdbufsz); 213 | fread(rdbuf, 1, rdbufsz, fin); 214 | 215 | /* Skip 4096 HEADER ??? */ 216 | magic = rdbuf + 16; 217 | if((*(uint32_t *)magic == "@AML") && (*(uint32_t *)magic[4] == 218 | *(uint32_t *)magic[0x2c] + *(uint32_t *)magic[0x1c])) { 219 | fseek(fin, 0x1000, SEEK_SET); 220 | fread(rdbuf, 1, rdbufsz, fin); 221 | } 222 | fclose(fin); 223 | 224 | bzero(rsahash, 0x20); 225 | bzero(uselessbuf, 0x400); 226 | 227 | rsa_init(&ctx, 0, 0); 228 | bzero(rsakey_buf, 0x2000); 229 | 230 | if(usekey) { 231 | if(aml_gx_load_rsa_key_file(&ctx, (argval[B2S_AMLUSERKEY] ? 232 | argval[B2S_AMLUSERKEY] : argval[B2S_USERKEY])) != 0) 233 | goto out; 234 | bzero(rsakey_hdr, 0x30); 235 | *(uint32_t *)rsakey_hdr = "@KEY"; 236 | rsakey_hdr[8] = 1; 237 | rsakey_hdr[10] = 0x30; 238 | rsakey_hdr[4] = 0x30; 239 | newsz = hdr[8] + 0x230; 240 | rsakey_data = &rsakey_buf; 241 | useless = 0; 242 | rsakey_sz = aml_create_key_from_file_x(0, (argval[B2S_AMLUSERKEY] ? argval[B2S_AMLUSERKEY] : argval[B2S_USERKEY]), rsakey_data, rsakey_hdr, newsz); 243 | bzero(rsakey_buf + rsakey_sz, 0xf); 244 | rsakey_sz = (rsakey_sz + 0xf) & 0xfffffff0; /* ROUND UP to 16 */ 245 | if(rsakey_sz != 0x4b8) { 246 | bzero(rsakey_buf + rsakey_sz, rsakey_sz - 0x4b8); 247 | rsakey_sz = 0x4b8; 248 | } 249 | *(uint32_t *)(hdr + 16) = 0x1; /* RSA KEY */ 250 | *(uint32_t *)(hdr +0x20) = rsakey_hdr[9]; /* KEY TYPE ???? */ 251 | *(uint32_t *)(hdr +0x28) = rsakey_sz + 0x30; 252 | } else { 253 | if(skip != 0) 254 | ctx.DP = skip * 512 - 0x50; 255 | else 256 | ctx.DP = 0x20; 257 | *(uint32_t *)(hdr + 16) = 0x0; /* NO RSA KEY */ 258 | *(uint32_t *)(hdr + 20) = 0x0; /* NO RSA KEY */ 259 | rsakey_sz = 0xd80; 260 | *(uint32_t *)(hdr + 0x28) = rsakey_sz + 0x30; 261 | } 262 | 263 | /* TODO HDR MAGIC */ 264 | *(uint64_t *)(hdr + 0x18) = 0x200; /* IS IT VERSION OR HEADER SIZE */ 265 | *(uint32_t *)(hdr + 0x14) = hdr[8]; 266 | *(uint32_t *)(hdr + 4) = *(uint32_t *)(hdr + 4) + *(uint64_t *)(hdr + 0x18); 267 | *(uint32_t *)(hdr + 0x1c) = *(uint32_t *)(hdr + 0x14) + ctx.DP; 268 | *(uint32_t *)(hdr + 0x24) = *(uint32_t *)(hdr + 0x14) + 0x200; 269 | *(uint32_t *)(hdr + 4) = *(uint32_t *)(hdr + 4) + *(uint64_t *)(hdr + 0x24); 270 | *(uint32_t *)(hdr + 0x38) = rdbufsz; 271 | *(uint32_t *)(hdr + 0x34) = *(uint32_t *)(hdr + 0x28) + *(uint32_t *)(hdr + 0x24); 272 | *(uint32_t *)(hdr + 4) = *(uint32_t *)(hdr + 4) + *(uint64_t *)(hdr + 0x38); 273 | *(uint32_t *)(hdr + 0x2c) = *(uint32_t *)(hdr + 4) - *(uint32_t *)(hdr + 0x1c); 274 | 275 | outdata_ptr = malloc(*(uint32_t *)(hdr + 4)); 276 | if(outdata_ptr == NULL) 277 | goto out; 278 | bzero(outdata_ptr, *(uint32_t *)(hdr + 4)); 279 | outdata = outdata_ptr; 280 | rsa_sig_data = 0; 281 | memcpy(outdata_ptr, hdr, 0x40); 282 | outdata_ptr += 0x40; 283 | rsa_sig_data = outdata_ptr; 284 | outdata_ptr += *(uint32_t *)(hdr + 0x18); 285 | payload_data = rsa_sig_data + ctx.DP; 286 | 287 | if(usekey) { 288 | memcpy(outdata_ptr, rsakey_hdr, 0x30); 289 | outdata_ptr += 0x30; 290 | memcpy(outdata_ptr, rsakey_buf, rsakey_sz); 291 | outdata_ptr += rsakey_sz; 292 | } else { 293 | outdata_ptr += 0x30; 294 | outdata_ptr += rsakey_sz; 295 | } 296 | 297 | memcpy(outdata_ptr, rdbuf, rdbufsz); 298 | outdata_ptr += rdbufsz; 299 | 300 | if(*(uint32_t *)(hdr + 4) + outdata != outdata_ptr) { 301 | puts("aml log : internal error!"); 302 | while(1); /* LOOOOOOOL */ 303 | } 304 | 305 | chk1 = outdata + 0x24; 306 | chk3 = outdata_ptr + 0x258; 307 | chk2 = outdata_ptr + 0x8ec; 308 | chk4 = outdata_ptr + 0xb20; 309 | 310 | if(usekey) { 311 | /* Consistency check */ 312 | if(*chk1 != 0x240) { 313 | puts("aml log : sig fail! Ln=%d ch1=0x%x\n", 314 | 0x2ed6, *chk1); 315 | goto out; 316 | } 317 | if(*chk3 != 0x298) { 318 | puts("aml log : sig fail! Ln=%d chk3=0x%x chk4 = 0x%x\n", 319 | 0x2edc, *chk3, *chk4); 320 | } 321 | } else { 322 | *(uint32_t *)chk1 = 0x240; 323 | *(uint32_t *)chk3 = 0x298; 324 | *(uint32_t *)chk2 = 0x240; 325 | *(uint32_t *)chk4 = 0x298; 326 | } 327 | 328 | sha2_start(&shactx, 0); 329 | sha2_update(&shactx, outdata, hdr[8] /*HDR sz (0x40) */); 330 | sha2_update(&shactx, payload_data, *(uint32_t *)(hdr + 0x2c) /* payload len */); 331 | sha2_finish(&shactx, rsahash); 332 | 333 | if(usekey) { 334 | /* 335 | * 1 = 336 | * 0xb = POLARSSL_MD_* 337 | * 0x20 = hashlen 338 | */ 339 | ret = rsa_pkcs1_sign(&ctx, 0, 0, 1, 0xb, 0x20, rsahash, rsa_sig_data); 340 | if(ret != 0) { 341 | ret = 0x2f00; 342 | goto out; 343 | } 344 | } else { 345 | memcpy(rsa_sig_data, rsahash, 0x20); 346 | } 347 | 348 | fwrite(outdata, 1, *(uint32_t *)(hdr + 4), fout); 349 | ret = 0; 350 | 351 | out: 352 | if(fin) 353 | fclose(fin); 354 | if(fkey) 355 | fclose(fkey); 356 | if(fout) 357 | fclose(fout); 358 | if(rdbuf) 359 | free(rdbuf); 360 | if(outdata) 361 | free(outdata); 362 | if(wtfbuf) 363 | free(wtfbuf); 364 | return ret; 365 | } 366 | 367 | static int aml_bl2_sig(int argc, char *argv[]) 368 | { 369 | /* argv = -0x550 */ 370 | /* argc = -0x544 */ 371 | int optidx; /* -0x53c */ 372 | int ret; /* -0x538 */ 373 | int optret; /* -0x534 */ 374 | void *argval[10]; /* -0x530 -> -0x4e0 */ 375 | struct stat st; /* -0x4e0 */ 376 | struct option opt[] = { /* -0x450 -> -0x310 */ 377 | { 378 | .name = "input", 379 | .has_arg = 1, 380 | .flag = NULL, 381 | .val = '0', 382 | }, 383 | { 384 | .name = "amluserkey", 385 | .has_arg = 1, 386 | .flag = NULL, 387 | .val = '1', 388 | }, 389 | { 390 | .name = "userkey", 391 | .has_arg = 1, 392 | .flag = NULL, 393 | .val = '2', 394 | }, 395 | { 396 | .name = "keymax", 397 | .has_arg = 1, 398 | .flag = NULL, 399 | .val = '3', 400 | }, 401 | { 402 | .name = "aeskey", 403 | .has_arg = 1, 404 | .flag = NULL, 405 | .val = '4', 406 | }, 407 | { 408 | .name = "output", 409 | .has_arg = 1, 410 | .flag = NULL, 411 | .val = '5', 412 | }, 413 | { 414 | .name = "efuse", 415 | .has_arg = 1, 416 | .flag = NULL, 417 | .val = '6', 418 | }, 419 | { 420 | .name = "skipsector", 421 | .has_arg = 1, 422 | .flag = NULL, 423 | .val = '7', 424 | }, 425 | { 426 | .name = "level", 427 | .has_arg = 1, 428 | .flag = NULL, 429 | .val = '8', 430 | }, 431 | {}, 432 | }; 433 | char buf1[0x100]; /* -0x310 -> -0x210 */ 434 | char buf2[0x100]; /* -0x210 -> -0x110 */ 435 | char pathout[/*sz ?*/]; /* -0x110 */ 436 | 437 | ret = 0x2f93; 438 | 439 | if(argc < 4) 440 | goto out; 441 | 442 | bzero(argval, sizeof(argval)); 443 | 444 | while((optret = getopt_long(argc, argv, "", opt, &optidx)) != -1) 445 | argval[optidx] = optarg; 446 | 447 | ret = 0x2fa7; 448 | if((argval[B2S_AMLUSERKEY] != NULL) && argc < 5) 449 | goto out; 450 | 451 | /* Is it v3 level ??? */ 452 | if((argval[B2S_LEVEL] != 0) && 453 | ((.plt + 0x70)(argval[B2S_LEVEL], "v3") == 0)) { /* strcmp ?? */ 454 | ret = amvl_bl2_sig_file_v3(argval[B2S_INPUT]); 455 | goto out; 456 | } 457 | 458 | for(optidx = 0; optidx < 10; ++optidx) { 459 | if(argval[optidx] == NULL) 460 | continue 461 | if(optidx > B2S_KEYMAX) 462 | continue; 463 | if(stat(argval[optidx], &st) == 0) 464 | continue; 465 | 466 | printf("aml log: error! Illegal [%s] %s\n", opt[optidx].name, 467 | argval[optidx]); 468 | goto out; 469 | } 470 | 471 | if(argval[B2S_INPUT] == NULL) { 472 | puts("aml log: error! Please set input first!"); 473 | goto out; 474 | } 475 | 476 | bzero(buf1, 0x100); 477 | bzero(buf2, 0x100); 478 | 479 | if(argval[B2S_OUTPUT] == NULL) { 480 | sprintf(pathout, "%s.sig", argval[B2S_OUTPUT]); 481 | argval[B2S_OUTPUT] = pathout; 482 | } 483 | 484 | ret = aml_bl2_sig_file(argval); 485 | out: 486 | return ret; 487 | } 488 | 489 | enum optid { 490 | OI_INPUT, /* 0x0 */ 491 | OI_AESKEY, /* 0x8 */ 492 | OI_OUTPUT, /* 0x10 */ 493 | OI_UCLCOMPACT, /* 0x18 */ 494 | OI_COMPRESS, /* 0x20 */ 495 | OI_NR, 496 | }; 497 | 498 | struct aml_ctrl_blk { 499 | uint32_t magic1; 500 | .... 501 | }; 502 | 503 | enum cipher { 504 | C_AES, 505 | C_RSA, 506 | }; 507 | 508 | static int aml_file_boom(char const *path, uint32_t filesz, char fill) 509 | { 510 | /* fill = 0xd0 */ 511 | /* filesz == -0xcc */ 512 | /* path == -0xc8 */ 513 | int ret; /* -0xbc */ 514 | int boomsz; /* -0xb8 */ 515 | int sz; /* -0xb4 */ 516 | FILE *fd; /* -0xb0 */ 517 | char *buf; /* -0xa8 */ 518 | struct stat st; 519 | 520 | ret = 0x4b9; 521 | unknow = 0; 522 | 523 | buf = malloc(0x400); 524 | if(path == NULL) 525 | goto out; 526 | 527 | ret = 0x4c2; 528 | if(stat(path, &st) != 0) 529 | goto out; 530 | 531 | ret = 0x4c7; 532 | fd = fopen(path, "rb+"); 533 | if(fd == NULL) 534 | goto out; 535 | 536 | sz = 0; 537 | fseek(fd, 0, SEEK_END); 538 | sz = ftell(fd); 539 | 540 | ret = 0x4d3; 541 | boomsz = filesz - sz; 542 | if(boomsz < 0) 543 | goto out; 544 | 545 | (.plt + 0x60)(buf, fill, 0x400); /* memset ??? */ 546 | while(boomsz > 0) { 547 | if(boomsz >= 0x400) { 548 | fwrite(buf, 1, 0x400, fd); 549 | boomsz -= 0x400; 550 | } else { 551 | fwrite(buf, 1, boomsz, fd); 552 | boomsz = 0; 553 | } 554 | } 555 | ret = 0; 556 | out: 557 | if(buf) 558 | free(buf); 559 | if(fd) 560 | fclose(fd); 561 | 562 | return ret; 563 | } 564 | 565 | static void aml_set_blk_time_stamp(enum cipher cipher, char *blk) 566 | { 567 | /* blk == -0x90 */ 568 | /* cipher == -0x84 */ 569 | uint32_t ret = 0x389; /* -0x7c */ 570 | struct tm *localt; /* 0x78 */ 571 | time_t t; /* -0x70 */ 572 | char *cipher_desc[] = { /* -0x30 */ 573 | [C_AES] = "AES-CBC", 574 | [C_RSA] = "RSA-SIG", 575 | }; 576 | char *ptr[] = { /* -0x20 */ 577 | &blk[0x88], 578 | &blk[0xb0], 579 | }; 580 | 581 | if(blk == NULL) 582 | goto out; 583 | 584 | if(cipher > 1) 585 | goto out; 586 | 587 | time(&t); 588 | localt = localtime(&t); 589 | 590 | sprintf(ptr[cipher], "%s%04d/%02d/%02d %02d:%02d:%02d", 591 | cipher_desc[cipher], tm->tm_year + 1900, 592 | tm->tm_mon + 1, tm->tm_hour, 593 | tm->tm_min, tm->tm_sec); 594 | 595 | ret = 0; 596 | out: 597 | return ret; 598 | } 599 | 600 | static int aml_file_aes(FILE *file, int filesz, char key[0x30], char enc) 601 | { 602 | /* key -0x1a8 */ 603 | /* enc -0x1a0 */ 604 | /* filesz -0x19c */ 605 | /* file -0x198 */ 606 | int ret; /* -0x184 */ 607 | int curend; /* -0x180 */ 608 | long cur; /* -0x17c */ 609 | int fsz; /* -0x178 */ 610 | int i; /* -0x174 */ 611 | void *buf = NULL; /* -0x170 */ 612 | char *iv = _key + 0x20; /* -0x168 */ 613 | aes_context ctx; /* -0x160 */ 614 | char _key[0x30]; /* -0x40 TODO sz */ 615 | 616 | ret = 0x3c0; 617 | if(file == NULL) 618 | goto out; 619 | 620 | if(key == NULL) 621 | goto out; 622 | 623 | cur = ftell(file); 624 | fsz = filesz; 625 | fseek(file, 0, SEEK_END); 626 | curend = ftell(file); 627 | fseek(file, cur, SEEK_SET); 628 | curend = curend - cur; 629 | ret = 0x3d4; 630 | if(curend < fsz) 631 | goto out; 632 | 633 | curend = fsz; 634 | if(curend & 0xf) 635 | goto out; 636 | 637 | buf = malloc(0x400); 638 | if(buf == NULL) 639 | goto out; 640 | 641 | memcpy(_key, key, 0x30); 642 | if(enc == 1) 643 | aes_setkey_enc(&ctx, _key, 256); 644 | else 645 | aes_setkey_dec(&ctx, _key, 256); 646 | 647 | i = 0 648 | while(curend) { 649 | i = fread(buf, 1, (curend < 0x400) ? curend : 0x400, file) 650 | if(i == 0) 651 | break; 652 | curend -= i; 653 | aes_crypt_cbc(ctx, enc, i, iv, buf, buf); 654 | fseek(file, -i, SEEK_CUR); /* rewind current block */ 655 | fwrite(buf, i, 1, file); 656 | } 657 | 658 | fflush(file); 659 | fseek(file, cur, SEEK_SET); 660 | ret = 0; 661 | out: 662 | if(buf) { 663 | free(buf); 664 | buf = NULL; 665 | } 666 | return ret; 667 | } 668 | 669 | static int aml_ctrl_blk_check(char const *buf) 670 | { 671 | } 672 | 673 | static int aml_bl3_enc_file(argval[OI_NR + 1]) /* argval is at -0xc58 */ 674 | { 675 | char lz4[0x400]; /* -0x410 */ 676 | char pathlz4[0x100]; /* 0x510 */ 677 | char pathout[0x100]; /* -0x610 */ 678 | char rd[0x30]; /* -0x640 rsakey + iv */ 679 | char firstblk[0x200] /* 0x840 */ 680 | /* END OF buf <- 0x840 */ 681 | char buf[0x200]; /* -0xa40 */ 682 | struct stat st2; /* -0xad0 */ 683 | struct stat st; /* -0xb60 */ 684 | struct aml_ctrl_blk cblk; /* -0xbb0 uint64_t[10] */ 685 | char *lz4data; /* 0xbf8 */ 686 | time t; /* 0xbf0 */ 687 | FILE *lz4out; /*0xc00*/ 688 | FILE *lz4file; /*0xc08*/ 689 | char *filedata; /*0xc10*/ 690 | struct tm *tm; /*0xc18 */ 691 | FILE *fout; /* 0xc20 */ 692 | FILE *file; /* -0xc28 */ 693 | uint32_t lz4sz; /* -0xc2c */ 694 | uint32_t lz4maxsz; /* -0xc30 */ 695 | size_t filesz; /* -0xc38 */ 696 | int i; /* -0xc40 */ 697 | int ret = 0x16e0; /* -0xc44 */ 698 | size_t offset; /* -0xc3c */ 699 | 700 | if(argval[OI_INPUT] == NULL) 701 | goto out; 702 | 703 | if(stat(argval[OI_INPUT], &st) != 0) 704 | goto out; 705 | 706 | ret = 0x16e6; 707 | 708 | file = fopen(argval[OI_INPUT], "rb"); 709 | if(file == NULL) 710 | goto out; 711 | 712 | ret = 0x16eb; 713 | bzero(buf, 0x200); 714 | fread(buf, 1, 512, file); 715 | 716 | ret = aml_ctrl_blk_check(buf); 717 | fclose(file); 718 | file = NULL; 719 | if(ret == 0) 720 | goto out; 721 | 722 | memcpy(&cblk, buf, 80); 723 | offset = 0; 724 | if(cblk.magic1 == 0x12348765) 725 | offset = 0x200; 726 | 727 | bzero(rd, 0x30); 728 | for(i = 0; i < 0x30; ++i) 729 | rd[i] = (char)rand(); 730 | 731 | if(argval[OI_AESKEY] != NULL) { 732 | /* fetch key .... */ 733 | fseek(); /* TODO */ 734 | file = fopen(argval[OI_AESKEY], "rb"); 735 | if(file == NULL) 736 | goto out; 737 | fread(rd, 1, 0x30, file); 738 | fclose(file); 739 | file = NULL; 740 | } 741 | 742 | filesz = 0; 743 | bzero(pathout, 0x100); 744 | 745 | if(argval[OI_OUTPUT] != NULL) 746 | .plt(pathout, argval[OI_OUTPUT]); /* strcpy ??? */ 747 | else 748 | sprintf(pathout, "%s.enc", argval[OI_INTPU]); 749 | 750 | ret = 0x1739; 751 | fout = fopen(pathout, "wb"); 752 | if(fout == NULL) 753 | goto out; 754 | 755 | ret = 0x173e; 756 | file = fopen(argval[OI_INPUT], "rb"); 757 | if(file == NULL) { 758 | fclose(fout); 759 | goto out; 760 | } 761 | 762 | fseek(file, 0x0, SEEK_END); 763 | filesz = ftell(file); 764 | filesz -= offset; 765 | fseek(file, offset, SEEK_SET); 766 | 767 | ret = aml_file_copy(file, fout, filesz); 768 | fclose(file); 769 | fclose(fout); 770 | file = NULL; 771 | fout = NULL; 772 | 773 | if(argval[OI_COMPRESS] != NULL) { 774 | /* TODO */ 775 | if((.plt + 0x70)(argval[OI_COMPRESS], "lz4") == 0) {/* strcmp ?? */ 776 | bzero(lz4, 0x400); 777 | bzero(pathlz4, 0x100); 778 | sprintf(pathlz4, "%s.lz4", pathout); 779 | bzero(firstblk, 0x80); 780 | *(uint32_t *)(firstblk + 92) = "LZ4C"; 781 | *(uint32_t *)(firstblk) = "LZ4C"; 782 | *(uint32_t *)(firstblk + 6) = 0x80; /* Hdr size ? */ 783 | for(i = 0; i < 10; ++i) 784 | firstblk[80 + i] = rand(); 785 | time(&t); 786 | tm = localtime(&t); 787 | sprintf(firstblk + 0x30, "%04d%02d%02d%02d:%02d:%02d", 788 | tm->tm_year + 1900, tm->tm_mon + 1, 789 | tm->tm_mday, tm->tm_hour, tm->tm_min, 790 | tm->tm_tm_sec); 791 | if(stat(pathout, &st2) == 0) { 792 | aml_file_sha2(pathout, firstblk + 0x10); 793 | filedata = malloc(st2.fsz); 794 | if(filedata) { 795 | lz4file = fopen(pathout, "rb"); 796 | if(lz4file == NULL) 797 | goto out; 798 | fread(filedata, 1, st2.fsz, lz4file); 799 | lz4out = fopen(lz4path, "wb"); 800 | if(lz4out != NULL) { 801 | lz4maxsz = st2.fsz + 0x400; 802 | lz4data = malloc(lz4maxsz); 803 | if(lz4data) { 804 | bzero(lz4data, lz4maxsz); 805 | *(uint32_t *)(firstblk + 8) = st2.fsz; 806 | lz4sz = Lz4_compress_HC(filedata, lz4data, st2.fsz, lz4maxsz, 12); 807 | *(uint32_t *)(firstblk + 10) = lz4sz; 808 | sha2(firstblk, 0x60, firstblk + 0x60, 0) 809 | fwrite(firstblk, 1, 0x80, lz4out); 810 | fwrite(lz4data, 1, lz4sz, lz4out); 811 | fclose(lz4out); 812 | lz4out = NULL; 813 | } 814 | fclose(lz4out); 815 | lz4out = NULL; 816 | } 817 | free(filedata); 818 | filedata = NULL; 819 | if(stat(pathlz4, &st2) == 0) { 820 | filesz = st2.filesz 821 | aml_file_duplicate(pathlz4, pathout, 0); 822 | remove(pathlz4); 823 | } 824 | } 825 | } 826 | } 827 | } 828 | 829 | if(ret != 0) 830 | goto out; 831 | 832 | /* If file is not 512 byte align, pad it */ 833 | if(filesz & 0x1ff) { 834 | /* Not aligned on 512 */ 835 | filesz = (filesz + 0x1ff) & ~0x1ff; /* Roundup */ 836 | if(aml_file_boom(pathout, filesz, 0) != 0) { 837 | ret = 0x1802; 838 | remove(pathout); 839 | goto out; 840 | } 841 | } 842 | 843 | file = fopen(pathout, "rb+"); 844 | ret = 0x180a; 845 | 846 | /* Prepare header */ 847 | bzero(buf, 0x200); 848 | *(uint16_t *)(blk + 2) = 0x200; 849 | *(uint16_t *)(blk + 250) = 0x200; 850 | *(uint16_t *)(blk + 4) = 1; /* AES Encrypted */ 851 | *(uint16_t *)(blk + 8) = 1; 852 | *(uint32_t *)(blk + 12) = (uint32_t)"AMLC"; 853 | *(uint32_t *)(blk + 252) = (uint32_t)"AMLC"; 854 | *(uint32_t *)(blk + 16) = filesz; 855 | *(uint32_t *)(blk + 20) = 0x200; 856 | *(uint32_t *)(blk + 24) = filesz; /* Extracted size ? */ 857 | *(uint32_t *)(blk + 28) = filesz; /* AES cipher size ?? */ 858 | /* XXX End of offset */ 859 | if(offset != 0) 860 | memcpy(blk + 256, cblk, sizeof(clbk)); 861 | memcpy(blk + 64, rd, sizeof(rd)); 862 | 863 | fseek(file, 0x0, SEEK_SET); 864 | 865 | if(*(blk + 4)) { /* AES crypt */ 866 | ret = aml_file_aes(file, filesz, rd, 1); 867 | if(ret != 0) 868 | goto out; 869 | } else { 870 | *(uint32_t *)(blk + 28) = 0; 871 | } 872 | 873 | aml_set_blk_time_stamp(C_AES, blk); 874 | fseek(file, 0x0, SEEK_SET); 875 | 876 | ret = aml_file_sha2(file, buf + 0x20, filesz); 877 | if(ret != 0) 878 | goto out; 879 | 880 | 881 | /* Put first block to the end and write header */ 882 | fseek(file, 0, SEEK_SET); 883 | fread(firstblk, 1, 0x200, file); 884 | fseek(file, 0, SEEK_SET); 885 | fwrite(buf, 1, 0x200, file); 886 | fseek(file, 0, SEEK_END); 887 | fwrite(firstblk, 1, 0x200, file); 888 | ret = 0; 889 | out: 890 | if(file != NULL) 891 | fclose(file); 892 | return ret; 893 | } 894 | 895 | static int aml_bl3_enc(int argc, char **argv) 896 | { 897 | struct option opt[] = { 898 | { 899 | .name = "input", 900 | .has_arg = 1, 901 | .flag = NULL, 902 | .val = '0', 903 | }, 904 | { 905 | .name = "aeskey", 906 | .has_arg = 1, 907 | .flag = NULL, 908 | .val = '1', 909 | }, 910 | { 911 | .name = "output", 912 | .has_arg = 1, 913 | .flag = NULL, 914 | .val = '2', 915 | }, 916 | { 917 | .name = "uclcompact", 918 | .has_arg = 1, 919 | .flag = NULL, 920 | .val = '3', 921 | }, 922 | { 923 | .name = "compress", 924 | .has_arg = 1, 925 | .flag = NULL 926 | .val = '4', 927 | }, 928 | {}, 929 | 930 | }; 931 | char const *argvalue[OI_NR + 1] = {}; 932 | int optidx; 933 | int ret = 0x2ffa; 934 | 935 | while(getopt_long(argc, argv, "", opt, &optidx) != -1) 936 | argvalue[optidx] = optarg; 937 | 938 | ret = aml_bl3_enc_file(argvalue); 939 | return ret; 940 | } 941 | 942 | struct aml_uboot_arg { 943 | char *path; 944 | char *key; 945 | /* Extra space 0x30 */ 946 | }; 947 | 948 | struct aml_sig_arg { 949 | char *path; 950 | char *key; 951 | char *desc; 952 | }; 953 | 954 | static int aml_uboot_process(struct aml_uboot_arg *arg, int v3) 955 | { 956 | /* arg -> -0x188 */ 957 | /* v3 -> -0x18c */ 958 | uint32_t ret = 0x1cd4; /* -0x174 */ 959 | uint64_t i = 0; /* -0x170 */ 960 | uint32_t nr; /* -0x16c */ 961 | FILE *fuboot = NULL; /* -0x168 */ 962 | FILE *fout = NULL; /* -0x160 */ 963 | uint8_t *buf; /* -0x158 */ 964 | struct aml_sig_arg asa; /* -0x150 -> -0x120 */ 965 | uint8_t path[0x100]; /* -0x120 -> -0x20 */ 966 | 967 | if(arg == NULL) 968 | goto out; 969 | 970 | fuboot = fopen(arg->path, "rb"); 971 | buf = malloc(0xc000); 972 | 973 | if((fuboot == NULL) || (buf == NULL)) /* Yep leak */ 974 | goto out; 975 | 976 | /* Copy u-boot.usb.bl2 */ 977 | bzero(path, 0x100); 978 | sprintf(path, "%s.usb.bl2", arg->path); 979 | bzero(buf, 0xc000); 980 | fout = fopen(path, "wb"); 981 | if(fout) { 982 | fread(buf, 0x1, 0xc000, fuboot); 983 | fwrite(buf, 0x1, 0xc000, fout); 984 | fclose(fout); 985 | fout = NULL; 986 | } 987 | 988 | /* Copy u-boot.usb.tpl */ 989 | sprintf(path, "%s.usb.tpl", arg->path); 990 | fout = fopen(path, "wb"); 991 | if(fout) { 992 | nr = fread(buf, 0x1, 0xc000, fuboot); 993 | while(nr != 0) { 994 | fwrite(buf, 0x1, nr, fout); 995 | nr = fread(buf, 0x1, 0xc000, fuboot); 996 | } 997 | fclose(fout); 998 | fout = NULL; 999 | } 1000 | 1001 | /* Copy u-boot.sd.bin */ 1002 | fseek(fuboot, 0x0, SEEK_SET); 1003 | sprintf(path, "%s.sd.bin", arg->path); 1004 | fout = fopen(path, "wb"); 1005 | if(fout) { 1006 | for(i = 0; i < 0x200; ++i) 1007 | buf[i] = rand(); 1008 | fwrite(buf, 0x1, 0x200, fout); 1009 | while((i = fread(buf, 0x1, 0xc000, fuboot)) != 0) 1010 | fwrite(buf, 0x1, i, fout); 1011 | fclose(fout); 1012 | fout = NULL; 1013 | } 1014 | ret = 0; 1015 | if(arg->key == NULL) 1016 | goto out; /* Exit normally */ 1017 | 1018 | bzero(asa, sizeof(asa)/*0x30*/); 1019 | asa.path = arg->path; 1020 | asa.key = arg->key; 1021 | asa.desc = "sig"; 1022 | aml_upgrade_sig_file(&asa); 1023 | out: 1024 | if(fuboot) { 1025 | fclose(fuboot); 1026 | fuboot = NULL; 1027 | } 1028 | 1029 | if(fout) { 1030 | fclose(fout); 1031 | fout = NULL; 1032 | } 1033 | 1034 | return ret; 1035 | } 1036 | 1037 | enum bootmk_optid { 1038 | BMK_BL2, /* 0x0 */ 1039 | BMK_BL30, /* 0x8 */ 1040 | BMK_BL31, /* 0x10 */ 1041 | BMK_BL32, /* 0x18 */ 1042 | BMK_BL33, /* 0x20 */ 1043 | BMK_BL3X, /* 0x28 */ 1044 | BMK_OUTPUT, /* 0x30 */ 1045 | BMK_USERKEY, /* 0x38 */ 1046 | BMK_LEVEL, /* 0x40 */ 1047 | BMK_INPUT, /* 0x48 */ 1048 | BMK_NR, 1049 | }; 1050 | 1051 | static uuid_t const uuid_list[] = { /* 0x716700 */ 1052 | [BMK_BL2] = { 1053 | 0x5f, 0xf9, 0xec, 0x0b, 0x4d, 0x22, 0x3e, 0x4d, 1054 | 0xa5, 0x44, 0xc3, 0x9d, 0x81, 0xc7, 0x3f, 0x0a 1055 | }, 1056 | [BMK_BL30] = { 1057 | 0x97, 0x66, 0xfd, 0x3d, 0x89, 0xbe, 0xe8, 0x49, 1058 | 0xae, 0x5d, 0x78, 0xa1, 0x40, 0x60, 0x82, 0x13 1059 | }, 1060 | [BMK_BL31] = { 1061 | 0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46, 1062 | 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x00 1063 | }, 1064 | [BMK_BL32] = { 1065 | 0x05, 0xd0, 0xe1, 0x89, 0x53, 0xdc, 0x13, 0x47, 1066 | 0x8d, 0x2b, 0x50, 0x0a, 0x4b, 0x7a, 0x3e, 0x38 1067 | }, 1068 | [BMK_BL33] = { 1069 | 0xd6, 0xd0, 0xee, 0xa7, 0xfc, 0xea, 0xd5, 0x4b, 1070 | 0x97, 0x82, 0x99, 0x34, 0xf2, 0x34, 0xb6, 0xe4 1071 | }, 1072 | }; 1073 | 1074 | struct fip_toc_header { 1075 | uint32_t name; 0xaa640001 1076 | uint32_t serial_number; 0x12345678 1077 | uint64_t flags; 1078 | }; 1079 | FT_NAME 0xaa640001 /* Should use htole32 */ 1080 | FT_SERIAL 0x12345678 /* Should use htole32 */ 1081 | 1082 | #define FIP_TOC_HEADER \ 1083 | ((struct fip_toc_header)({ \ 1084 | .name = FT_NAME, \ 1085 | .serial_number = FT_SERIAL \ 1086 | }) 1087 | 1088 | struct fip_toc_entry { 1089 | uuid_t uuid; 1090 | uint64_t offset; 1091 | uint64_t size; 1092 | uint64_t flags; 1093 | }; 1094 | 1095 | /* Get fip entry offset from fip base */ 1096 | #define FTE_GET(fbase, nr) \ 1097 | ((struct fip_toc_entry *)((fbase) + sizeof(struct fip_toc_header))[nr]) 1098 | 1099 | static int aml_boot_make(int argc, char **argv) 1100 | { 1101 | /* argc = -0x4bb4 */ 1102 | /* argv = -0x4bc0 */ 1103 | int32_t longoptidx; /* -0x4ba4 */ 1104 | uint32_t ret = 0x325e; /* -0x4ba0 */ 1105 | int32_t i; /* -0x4b9c */ 1106 | uint32_t key = 0; /* -0x4b98 */ 1107 | uint32_t bl3id; /* -0x4b94 */ 1108 | uint32_t bloff; /* -0x4b90 */ 1109 | uint32_t a; /* -0x4b8c */ 1110 | uint32_t z3; /* -0x4b88 */ 1111 | uint32_t w0; /* -0x4b84 */ 1112 | uint32_t padlen; /* -0x4b80 */ 1113 | uint32_t blcur; /* -0x4b7c */ 1114 | FILE *fd = NULL; /* -0x4b78 */ 1115 | FILE *fbl; /* -0x4b70 */ 1116 | uint8_t *ptr; /* -0x4b68 */ 1117 | uint8_t *padbuf; /* -0x4b60 */ 1118 | uint8_t buf[0x50]; /* -0x4b50 -> -0x4b00 */ 1119 | struct rsa_context *ctx; /* -0x4b00 -> ~-0x49e4 */ 1120 | char *argval[11] = {}; /* -0x49e0 -> -0x4988 */ 1121 | struct stat st; /* -0x4980 -> -0x48f0 */ 1122 | struct option opt[] = { /* -0x48f0 -> -0x4790 */ 1123 | { 1124 | .name = "bl2", 1125 | .has_arg = 1, 1126 | .flag = NULL, 1127 | .val = '0', 1128 | }, 1129 | { 1130 | .name = "bl30", 1131 | .has_arg = 1, 1132 | .flag = NULL, 1133 | .val = '1', 1134 | }, 1135 | { 1136 | .name = "bl31", 1137 | .has_arg = 1, 1138 | .flag = NULL, 1139 | .val = '3', 1140 | }, 1141 | { 1142 | .name = "bl32", 1143 | .has_arg = 1, 1144 | .flag = NULL, 1145 | .val = '4', 1146 | }, 1147 | { 1148 | .name = "bl33", 1149 | .has_arg = 1, 1150 | .flag = NULL, 1151 | .val = '5', 1152 | }, 1153 | { 1154 | .name = "bl3x", 1155 | .has_arg = 1, 1156 | .flag = NULL, 1157 | .val = '6', 1158 | }, 1159 | { 1160 | .name = "output", 1161 | .has_arg = 1, 1162 | .flag = NULL, 1163 | .val = '7', 1164 | }, 1165 | { 1166 | .name = "userkey", 1167 | .has_arg = 1, 1168 | .flag = NULL, 1169 | .val = '8', 1170 | }, 1171 | { 1172 | .name = "input", 1173 | .has_arg = 1, 1174 | .flag = NULL, 1175 | .val = '9', 1176 | }, 1177 | { 1178 | .name = "level", 1179 | .has_arg = 1, 1180 | .flag = NULL, 1181 | .val = 'a', 1182 | }, 1183 | {}, 1184 | }; 1185 | uint8_t fiphdr[0x200]; /* -0x4790 -> -0x4590 */ 1186 | uint8_t hdr[0x200]; /* -0x4590 -> -0x4390 */ 1187 | char fip[0x4000]; /* -0x4150 -> -0x150 */ 1188 | char rd[0x30]; /* -0x150 -> -0x120 */ 1189 | char path[/*????*/]; /* -0x120 */ 1190 | 1191 | for(i = 0; i < argc; i++); 1192 | 1193 | while((i = getopt_long(argc, argv, "", opt, &longoptidx)) != -1) 1194 | argval[longoptidx] = optarg; 1195 | 1196 | ret = 0x3287; 1197 | if((argval[BMK_LEVEL] != NULL) && 1198 | ((.plt + 0x70)(argval[BMK_LEVEL], "v3"))) { /* strcmp */ 1199 | optind = 0x3; /* Reset getopt_long() */ 1200 | aml_boot_make_v3(argc, argv); 1201 | return; 1202 | } 1203 | 1204 | rsa_init(&ctx, 0, 0); 1205 | 1206 | if((stat64(argval[BMK_USERKEY], &st) == 0) && 1207 | (aml_gx_load_rsa_key_file(ctx, argval[BMK_USERKEY]) == 0)) { 1208 | key = 1; 1209 | } 1210 | 1211 | if(argval[BMK_OUTPUT] != NULL) 1212 | (.plt)(path, argval[BMK_OUTPUT]); /* XXX Buf overflow */ 1213 | else 1214 | sprintf(path, "%s%s", argval[BMK_BL2], (key) ? ".encrypt" : ".pkg"); 1215 | 1216 | ret = 0x32a5; 1217 | remove(path); 1218 | fd = fopen(path, "wb+"); 1219 | if(fd == NULL) 1220 | goto out; 1221 | 1222 | fbl = NULL; 1223 | bzero(fip, sizeof(fip)); 1224 | 1225 | memcpy(fip, &FIP_TOC_HEADER, sizeof(struct fip_toc_header)); 1226 | 1227 | (.plt + 0x60)(fip + 0xc00, 0xff, 0x80); /* memset */ 1228 | 1229 | ptr = fip + 16; 1230 | bl3id = 0; 1231 | bloff = sizeof(fip); 1232 | z3 = 0; 1233 | 1234 | /* For bl2, bl30, bl31, bl32, bl33 */ 1235 | for(a = 0; a <= 4; ++a) { 1236 | if(stat64(argval[a], &st) != 0) 1237 | continue; 1238 | 1239 | fbl = fopen(argval[a], "rb"); 1240 | if(fbl == NULL) 1241 | continue; 1242 | 1243 | if(a == BMK_BL2) { 1244 | ret = 0x32d4; 1245 | aml_file_copy(fbl, fd, 0xc000); 1246 | fwrite(fip, 0x1, 0x4000, fd); 1247 | fclose(fbl); 1248 | fbl = NULL; 1249 | } else { 1250 | fseek(fbl, 0x0, SEEK_SET); 1251 | bzero(buf, sizeof(buf)); 1252 | fread(buf, 0x1, 0x50, fbl); 1253 | w0 = 0x58; 1254 | if(RD_32(buf, 0) == 0x12348765) { /* Should not happen */ 1255 | w0 += 0x50; 1256 | WR_32(fip, 0x400, 0x87654321); 1257 | WR_32(fip, 0x404, 0x1); 1258 | memcpy(fip + 0x430 + 0x50 * bl3id, buf, 0x50); 1259 | } 1260 | 1261 | /* Set Fip Toc entry */ 1262 | memcpy(&FTE_GET(fip, bl3id).uuid, uuid_list[a], 1263 | sizeof(uuid_list[a])); 1264 | FTE_GET(fip, bl3id).offset = htole64(bloff); 1265 | if(st.st_size != 0) { 1266 | rax = w0 + 0x438; 1267 | } else { 1268 | rax = 0; 1269 | } 1270 | FTE_GET(fip, bl3id).size = st.st_size - rax; 1271 | if(key) { 1272 | /* XXX TODO */ 1273 | } else { 1274 | ret = 0x3325; 1275 | bzero(hdr, sizeof(hdr)); 1276 | fseek(fbl, 0x0, SEEK_SET); 1277 | blcur = ftell(fbl); 1278 | fread(hdr, 0x1, 0x200, fbl); 1279 | if(aml_ctrl_blk_check(hdr) != 0) 1280 | goto out; 1281 | fseek(fbl, blcur, SEEK_SET); 1282 | if(RD_32(hdr, 0x100) == 0x12348765) { /* BL31 */ 1283 | WR_32(fip, 0x400, 0x87654321); 1284 | WR_32(fip, 0x404, 0x1); 1285 | memcpy(fip + (bl3id * 4 + bl3id) * 16 + 0x430, hdr + 100, 0x50); 1286 | } 1287 | } 1288 | aml_file_copy(fbl, fd, FTE_GET(fip, bl3id).size); 1289 | fclose(fbl); 1290 | fbl = NULL; 1291 | bloff += FTE_GET(fip, bl3id).size; 1292 | if(bloff & 0x3fff) { 1293 | padlen = 0x4000 - (bloff & 0x3fff); 1294 | } else { 1295 | padlen = 0; 1296 | } 1297 | 1298 | if(padlen) { 1299 | padbuf = malloc(padlen); 1300 | if(padbuf) { 1301 | for(i = 0; i < padlen; ++i) 1302 | padbuf = rand(); 1303 | fwrite(padbuf, 1, padlen, fd); 1304 | free(padbuf); 1305 | padbuf = NULL; 1306 | } 1307 | } 1308 | bloff += pad; 1309 | ++bl3id; 1310 | } 1311 | } 1312 | 1313 | fseek(fd, 0xc000, SEEK_SET); 1314 | fwrite(fip, 0x1, 0x4000, fd); 1315 | bzero(fiphdr, 0x200); 1316 | WR_16(fiphdr, 2, 0x200); 1317 | WR_16(fiphdr, 250, 0x200); 1318 | WR_16(fiphdr, 4, 0x1); /* AES CBC */ 1319 | WR_16(fiphdr, 6, 0x1); 1320 | WR_32(fiphdr, 12, *((uint32_t *)"AMLC")); 1321 | WR_32(fiphdr, 252, *((uint32_t *)"AMLC")); 1322 | WR_32(fiphdr, 16, 0x4000 - 0x200); /* aessz */ 1323 | WR_32(fiphdr, 20, 0x200); 1324 | WR_32(fiphdr, 28, 0x4000 - 0x200); 1325 | WR_32(fiphdr, 32, 0x4000 - 0x200); /* shasz */ 1326 | bzero(rd, sizeof(rd)); 1327 | 1328 | if(RD_16(fiphdr, 4) == 0x1) { /* AES CBC */ 1329 | for(i = 0; i < sizeof(rd); ++i) 1330 | rd[i] = rand(); 1331 | memcpy(fiphdr + 0x40, rd, sizeof(rd)); 1332 | fseek(fd, 0xc000, SEEK_SET); 1333 | ret = aml_file_aes(fd, RD_32(fiphdr, 0x10) /* aessz */, rd, 1); 1334 | if(ret != 0) 1335 | goto out; 1336 | aml_set_blk_time_stamp(C_AES, fiphdr); 1337 | } 1338 | 1339 | fseek(fd, 0xc000, SEEK_SET); 1340 | ret = aml_file_sha2(fd, RD_32(fiphdr, 0x20) /*shasz */, RD_32(fiphdr, 0x10) /* aessz */); 1341 | if(ret != 0) 1342 | goto out; 1343 | 1344 | fseek(fd, 0xc000, SEEK_SET); 1345 | fread(hdr, 0x1, sizeof(hdr), fd); 1346 | if(key) { 1347 | /* XXX TODO */ 1348 | } 1349 | 1350 | fseek(fd, 0xc000, SEEK_SET); 1351 | fwrite(fiphdr, 0x1, 0x200, fd); 1352 | fseek(fd, 0xfe00, SEEK_SET); 1353 | fwrite(hdr, 0x200, fd); /* Put first block at the end */ 1354 | 1355 | ret = 0; 1356 | out: 1357 | if(fd) 1358 | fclose(fd); 1359 | 1360 | if(ret = 0) { 1361 | bzero(hdr, 0x40); 1362 | struct aml_uboot_arg *a = (aml_uboot_arg *)hdr; 1363 | a->path = &path; 1364 | if(key) 1365 | a.key = &argval[BMK_USERKEY]; 1366 | ret = aml_uboot_process(a, 0); 1367 | } else { 1368 | remove(path); 1369 | } 1370 | 1371 | return; 1372 | } 1373 | 1374 | int main(int argc, char **argv) 1375 | { 1376 | /* argv == -0x500 */ 1377 | /* argc == -0x4f4 */ 1378 | int optidx = -1; /* -0x4f0 */ 1379 | int ret = 0; /* -0x4ec */ 1380 | time_t t; /* -0x4e8 */ 1381 | int (*action)(int argc, char **argv)[] = { /* -0x4e0 -> -0x3f0 */ 1382 | aml_sig_check, 1383 | aml_rsa_check, 1384 | aml_rsa_gen, 1385 | aml_key_sig, 1386 | aml_key_bnd, 1387 | aml_key_sig_v3, 1388 | aml_key_bnd_v3, 1389 | aml_bin_sig, 1390 | aml_boot_sig, 1391 | aml_boot_sig_v3, 1392 | aml_img_sig, 1393 | aml_mk_sd, 1394 | aml_sha, 1395 | aml_upgrade_sig, 1396 | aml_mk_password, 1397 | aml_mk_customer_id, 1398 | aml_bl2_sig, 1399 | aml_bl2_enc, 1400 | aml_bl2_sig_v3, 1401 | aml_bl2_enc, 1402 | aml_bl3_enc, 1403 | aml_bl3_sig, 1404 | aml_bl3_sig_v3, 1405 | aml_boot_make, 1406 | aml_boot_make_v3, 1407 | aml_efuse_gen, 1408 | aml_efuse_gen_v3, 1409 | aml_bin2hex, 1410 | aml_efuse_process, 1411 | aml_show_help, 1412 | }; 1413 | struct option opt[] = { /* -0x3f0 */ 1414 | { .name = "sigchk", }, /* aml_sig_check */ 1415 | { .name = "rsacheck", }, /* aml_rsa_check */ 1416 | { .name = "rsagen", }, /* aml_rsa_gen */ 1417 | { .name = "keysig", }, /* aml_key_sig */ 1418 | { .name = "keybnd", }, /* aml_key_bnd */ 1419 | { .name = "keysig3", }, /* aml_key_sig_v3 */ 1420 | { .name = "keybnd3", }, /* aml_key_bnd_v3 */ 1421 | { .name = "binsig", }, /* aml_bin_sig */ 1422 | { .name = "bootsig", }, /* aml_boot_sig */ 1423 | { .name = "bootsig3", }, /* aml_boot_sig_v3 */ 1424 | { .name = "imgsig", }, /* aml_img_sig */ 1425 | { .name = "mksd", }, /* aml_mk_sd */ 1426 | { .name = "sha", }, /* aml_sha */ 1427 | { .name = "upsig", }, /* aml_upgrade_sig */ 1428 | { .name = "mkpsd", }, /* aml_mk_password */ 1429 | { .name = "mkcusid", }, /* aml_mk_customer_id */ 1430 | { .name = "bl2sig", }, /* aml_bl2_sig */ 1431 | { .name = "bl2enc", }, /* aml_bl2_enc */ 1432 | { .name = "bl2sig3", }, /* aml_bl2_sig_v3 */ 1433 | { .name = "bl2enc3", }, /* aml_bl2_enc */ 1434 | { .name = "bl3enc", }, /* aml_bl3_enc */ 1435 | { .name = "bl3sig", }, /* aml_bl3_sig */ 1436 | { .name = "bl3sig3", }, /* aml_bl3_sig_v3 */ 1437 | { .name = "bootmk", }, /* aml_boot_make */ 1438 | { .name = "bootmk3", }, /* aml_boot_make_v3 */ 1439 | { .name = "efsgen", }, /* aml_efuse_gen */ 1440 | { .name = "efsgen3", }, /* aml_efuse_gen_v3 */ 1441 | { .name = "bin2hex", }, /* aml_bin2hex */ 1442 | { .name = "efsproc", }, /* aml_efuse_process */ 1443 | { .name = "help", }, /* aml_show_help */ 1444 | {}, 1445 | 1446 | }; 1447 | int optidx; 1448 | 1449 | t = time(); 1450 | srand(t); 1451 | if(argc <= 1) { 1452 | aml_show_help(0, 0); 1453 | goto out; 1454 | } 1455 | 1456 | 1457 | if(getopt_long(2, argv, "", opt, &optidx) == -1) { 1458 | aml_show_help(0, 0); 1459 | goto out; 1460 | } 1461 | 1462 | if(optidx < 0) { 1463 | aml_show_help(0, 0); 1464 | goto out; 1465 | } 1466 | 1467 | if(optidx > sizeof(action) / sizeof(*action)) 1468 | optidx = sizeof(action) / sizeof(*action); 1469 | 1470 | ret = (*action)(argc, argv); 1471 | if(ret != 0) { 1472 | aml_show_help(0, 0); 1473 | goto out; 1474 | } 1475 | out: 1476 | return ret; 1477 | } 1478 | -------------------------------------------------------------------------------- /ssl.h: -------------------------------------------------------------------------------- 1 | #ifndef _SSL_H_ 2 | #define _SSL_H_ 3 | 4 | #include 5 | #include 6 | 7 | #define SSLERR(ret, ...) do \ 8 | { \ 9 | char __sslerrbuf[256]; \ 10 | fprintf(stderr, __VA_ARGS__); \ 11 | ret = -ERR_get_error(); \ 12 | ERR_error_string_n(-ret, __sslerrbuf, sizeof(__sslerrbuf)); \ 13 | fprintf(stderr, "%s\n", __sslerrbuf); \ 14 | } while(0) 15 | 16 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 17 | static inline void *OPENSSL_zalloc(size_t num) 18 | { 19 | void *ret = OPENSSL_malloc(num); 20 | 21 | if (ret != NULL) 22 | memset(ret, 0, num); 23 | return ret; 24 | } 25 | 26 | static inline EVP_MD_CTX *EVP_MD_CTX_new(void) 27 | { 28 | return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); 29 | } 30 | 31 | static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) 32 | { 33 | EVP_MD_CTX_cleanup(ctx); 34 | OPENSSL_free(ctx); 35 | } 36 | #endif 37 | 38 | /** 39 | * Fill a buffer with random data 40 | * 41 | * @param buf: Buffer to fill with random data 42 | * @param sz: Number of random byte to read 43 | * 44 | * @return: 0 on success negative number otherwise 45 | */ 46 | static inline int gi_random(uint8_t *buf, size_t sz) 47 | { 48 | int ret, fd = -1; 49 | size_t i; 50 | ssize_t nr; 51 | 52 | ret = open("/dev/urandom", O_RDONLY); 53 | if(ret < 0) { 54 | PERR("Cannot open /dev/urandom: "); 55 | goto out; 56 | } 57 | fd = ret; 58 | 59 | for(i = 0; i < sz; i += nr) { 60 | nr = read(fd, buf, sz - i); 61 | if(nr < 0) { 62 | PERR("Cannot read /dev/urandom: "); 63 | ret = (int)nr; 64 | goto out; 65 | } 66 | } 67 | ret = 0; 68 | out: 69 | if(fd >= 0) 70 | close(fd); 71 | return ret; 72 | } 73 | 74 | #endif 75 | --------------------------------------------------------------------------------