├── .clang-format ├── .github └── workflows │ └── tests.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── src ├── README.md ├── arch │ ├── common.c │ ├── common.h │ ├── zynq.c │ ├── zynq.h │ ├── zynqmp.c │ └── zynqmp.h ├── bif.c ├── bif.h ├── bootrom.c ├── bootrom.h ├── common.c ├── common.h ├── exbootimage.c ├── file │ ├── bitstream.c │ ├── bitstream.h │ ├── elf.c │ └── elf.h └── mkbootimage.c └── tests ├── extraction └── files ├── offsets ├── bad_1.bin ├── bad_10.bin ├── bad_2.bin ├── bad_3.bin ├── bad_4.bin ├── bad_5.bin ├── bad_6.bin ├── bad_7.bin ├── bad_8.bin ├── bad_9.bin └── boot.bin ├── parser ├── bad_attribute.bif ├── bad_bracket.bif ├── bad_comment_in_comment.bif ├── bad_comments.bif ├── bad_device_name.bif ├── bad_empty_file_list.bif ├── bad_forgotten_attribute.bif ├── bad_forgotten_filename.bif ├── bad_lacking_attribute_after_comma.bif ├── bad_lacking_attribute_value.bif ├── bad_unclosed_c_comment.bif ├── boot1.bif ├── boot10.bif ├── boot11.bif ├── boot12.bif ├── boot2.bif ├── boot3.bif ├── boot4.bif ├── boot5.bif ├── boot6.bif ├── boot7.bif ├── boot8.bif ├── boot9.bif ├── comment_in_comment.bif ├── cpp_comment_on_eof.bif ├── long_file_list.bif └── oneliner.bif └── tester.sh /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveMacros: true 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveBitFields: true 9 | AlignConsecutiveDeclarations: false 10 | AlignEscapedNewlines: DontAlign 11 | AlignOperands: AlignAfterOperator 12 | AlignTrailingComments: true 13 | AllowAllArgumentsOnNextLine: true 14 | AllowAllConstructorInitializersOnNextLine: true 15 | AllowAllParametersOfDeclarationOnNextLine: true 16 | AllowShortEnumsOnASingleLine: false 17 | AllowShortBlocksOnASingleLine: Never 18 | AllowShortCaseLabelsOnASingleLine: false 19 | AllowShortFunctionsOnASingleLine: None 20 | AllowShortIfStatementsOnASingleLine: Never 21 | AllowShortLoopsOnASingleLine: false 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: true 24 | BinPackArguments: false 25 | BinPackParameters: false 26 | BraceWrapping: 27 | AfterCaseLabel: false 28 | AfterClass: false 29 | AfterControlStatement: Never 30 | AfterEnum: false 31 | AfterFunction: false 32 | AfterNamespace: false 33 | AfterObjCDeclaration: false 34 | AfterStruct: false 35 | AfterUnion: false 36 | AfterExternBlock: false 37 | BeforeCatch: false 38 | BeforeElse: false 39 | BeforeLambdaBody: false 40 | BeforeWhile: false 41 | IndentBraces: false 42 | SplitEmptyFunction: false 43 | SplitEmptyRecord: false 44 | SplitEmptyNamespace: false 45 | BreakBeforeBinaryOperators: None 46 | BreakBeforeBraces: Attach 47 | BreakInheritanceList: BeforeColon 48 | BreakBeforeTernaryOperators: true 49 | BreakStringLiterals: true 50 | ColumnLimit: 100 51 | ContinuationIndentWidth: 2 52 | DeriveLineEnding: true 53 | DerivePointerAlignment: true 54 | DisableFormat: false 55 | ExperimentalAutoDetectBinPacking: false 56 | FixNamespaceComments: false 57 | IncludeBlocks: Regroup 58 | IncludeCategories: 59 | - Regex: '^' 60 | Priority: 1 61 | - Regex: '^' 62 | Priority: 4 63 | - Regex: '^' 64 | Priority: 3 65 | - Regex: '^' 66 | Priority: 5 67 | - Regex: '^<.*\.h>' 68 | Priority: 6 69 | - Regex: '.*' 70 | Priority: 2 71 | IndentCaseLabels: false 72 | IndentCaseBlocks: false 73 | IndentGotoLabels: false 74 | IndentPPDirectives: BeforeHash 75 | IndentWidth: 2 76 | IndentWrappedFunctionNames: false 77 | InsertTrailingCommas: Wrapped 78 | JavaScriptQuotes: Leave 79 | KeepEmptyLinesAtTheStartOfBlocks: false 80 | MacroBlockBegin: '' 81 | MacroBlockEnd: '' 82 | MaxEmptyLinesToKeep: 1 83 | PenaltyBreakAssignment: 2 84 | PenaltyBreakBeforeFirstCallParameter: 1 85 | PenaltyBreakComment: 300 86 | PenaltyBreakFirstLessLess: 120 87 | PenaltyBreakString: 100 88 | PenaltyBreakTemplateDeclaration: 10 89 | PenaltyExcessCharacter: 10000 90 | PenaltyReturnTypeOnItsOwnLine: 200 91 | PointerAlignment: Left 92 | ReflowComments: true 93 | SortIncludes: true 94 | SpaceAfterCStyleCast: true 95 | SpaceAfterLogicalNot: false 96 | SpaceBeforeAssignmentOperators: true 97 | SpaceBeforeCpp11BracedList: false 98 | SpaceBeforeCtorInitializerColon: true 99 | SpaceBeforeParens: ControlStatements 100 | SpaceInEmptyBlock: false 101 | SpaceInEmptyParentheses: false 102 | SpacesBeforeTrailingComments: 1 103 | SpacesInAngles: false 104 | SpacesInConditionalStatement: false 105 | SpacesInContainerLiterals: true 106 | SpacesInCStyleCastParentheses: false 107 | SpacesInParentheses: false 108 | SpacesInSquareBrackets: false 109 | SpaceBeforeSquareBrackets: false 110 | Standard: Auto 111 | TabWidth: 2 112 | TypenameMacros: ['FORMAT'] 113 | UseCRLF: false 114 | UseTab: Never 115 | WhitespaceSensitiveMacros: ['FORMAT'] 116 | ... 117 | 118 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Test mkbootimage 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | check-formatting: 6 | runs-on: ubuntu-20.04 7 | steps: 8 | - name: Install dependencies 9 | run: | 10 | sudo apt-get install --yes clang-format-12 11 | clang-format-12 --version 12 | 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | 16 | - name: Run formatting check 17 | run: | 18 | make format FMT=clang-format-12 19 | git diff --exit-code > formatting.log 20 | if [ $? -ne 0 ]; then 21 | echo The code formatting is not proper 22 | exit 1 23 | fi 24 | 25 | - name: Save diff 26 | uses: actions/upload-artifact@v2 27 | with: 28 | name: formatting 29 | path: formatting.log 30 | 31 | test-zynq-mkbootimage: 32 | runs-on: ubuntu-20.04 33 | steps: 34 | - name: Install dependencies 35 | run: | 36 | sudo apt-get install --yes libelf-dev 37 | 38 | - name: Checkout 39 | uses: actions/checkout@v2 40 | 41 | - name: Build the project 42 | run: | 43 | make 44 | test -f mkbootimage 45 | 46 | - name: Run integration tests 47 | run: make test 48 | 49 | - name: Save logs 50 | uses: actions/upload-artifact@v2 51 | with: 52 | name: results 53 | path: tests/results.log 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | mkbootimage 2 | exbootimage 3 | *.bif 4 | *.bin 5 | *.bit 6 | *.elf 7 | *.dtb 8 | *.o 9 | *.vim 10 | *.log 11 | tags 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2021 Antmicro 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 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * 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" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean distclean 2 | 3 | CC=gcc 4 | 5 | FMT=clang-format 6 | 7 | MKBOOTIMAGE_NAME:=mkbootimage 8 | EXBOOTIMAGE_NAME:=exbootimage 9 | 10 | VERSION_MAJOR:=2.3 11 | VERSION_MINOR:=$(shell git rev-parse --short HEAD) 12 | 13 | VERSION:=$(MKBOOTIMAGE_NAME) $(VERSION_MAJOR)-$(VERSION_MINOR) 14 | 15 | COMMON_SRCS:=src/bif.c src/bootrom.c src/common.c \ 16 | $(wildcard src/arch/*.c) $(wildcard src/file/*.c) 17 | 18 | COMMON_HDRS:=src/bif.h src/bootrom.h src/common.h \ 19 | $(wildcard src/arch/*.h) $(wildcard src/file/*.h) 20 | 21 | MKBOOTIMAGE_SRCS:=$(COMMON_SRCS) src/mkbootimage.c 22 | MKBOOTIMAGE_OBJS:=$(MKBOOTIMAGE_SRCS:.c=.o) 23 | 24 | EXBOOTIMAGE_SRCS:=$(COMMON_SRCS) src/exbootimage.c 25 | EXBOOTIMAGE_OBJS:=$(EXBOOTIMAGE_SRCS:.c=.o) 26 | 27 | ALL_SRCS:=$(COMMON_SRCS) src/mkbootimage.c src/exbootimage.c 28 | ALL_HDRS:=$(COMMON_HDRS) 29 | 30 | INCLUDE_DIRS:=src 31 | 32 | override CFLAGS += $(foreach includedir,$(INCLUDE_DIRS),-I$(includedir)) \ 33 | -DMKBOOTIMAGE_VER="\"$(VERSION)\"" \ 34 | -Wall -Wextra -Wpedantic \ 35 | --std=c11 36 | 37 | LDLIBS = -lelf 38 | 39 | all: $(MKBOOTIMAGE_NAME) $(EXBOOTIMAGE_NAME) 40 | 41 | $(MKBOOTIMAGE_NAME): $(MKBOOTIMAGE_OBJS) 42 | $(CC) $(CFLAGS) $(LDFLAGS) $(MKBOOTIMAGE_OBJS) -o $(MKBOOTIMAGE_NAME) $(LDLIBS) 43 | 44 | $(EXBOOTIMAGE_NAME): $(EXBOOTIMAGE_OBJS) 45 | $(CC) $(CFLAGS) $(LDFLAGS) $(EXBOOTIMAGE_OBJS) -o $(EXBOOTIMAGE_NAME) $(LDLIBS) 46 | 47 | format: 48 | $(FMT) -i $(ALL_SRCS) $(ALL_HDRS) 49 | 50 | test: 51 | ./tests/tester.sh 52 | 53 | clean: 54 | @- $(RM) $(MKBOOTIMAGE_NAME) 55 | @- $(RM) $(MKBOOTIMAGE_OBJS) 56 | @- $(RM) $(EXBOOTIMAGE_NAME) 57 | @- $(RM) $(EXBOOTIMAGE_OBJS) 58 | 59 | distclean: clean 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zynq mkbootimage 2 | 3 | Copyright (c) 2015-2022 [Antmicro](https://antmicro.com) 4 | 5 | This is an open-source replacement for the Xilinx `bootgen` application. 6 | The package provides sources of two binaries `mkbootimage` and `exbootimage` 7 | for both creation and extraction of Zynq boot images. 8 | 9 | The tools are written entirely in C. 10 | 11 | Requires the `libelf` C library. 12 | 13 | To build these the tools run: 14 | ``` 15 | make 16 | ``` 17 | 18 | For details on project organization and contribution tips, see `src/README.md`. 19 | 20 | ## mkbootimage 21 | `mkbootimage` parses a `.bif` file and creates a Zynq boot image in the `.bin` format. 22 | 23 | To use it, type in: 24 | ``` 25 | ./mkbootimage [--parse-only|-p] [--zynqmp|-u] 26 | ``` 27 | 28 | To see all available options, run: 29 | ``` 30 | ./mkbootimage --help 31 | ``` 32 | 33 | ### Zynq-7000 34 | 35 | For Zynq-7000 series, `zynq-mkbootimage` currently supports creating boot images 36 | containing the FSBL, bitstream, U-Boot, and Linux-related binary files. 37 | 38 | For loading Linux-related images, both the `[load]` and the `[offset]` attributes 39 | are supported. 40 | As opposed to the original `bootgen` utility, file extensions are not required. 41 | 42 | For example the following `.bif` file: 43 | ``` 44 | the_ROM_image: 45 | { 46 | [bootloader]fsbl.elf 47 | fpga.bit 48 | u-boot.elf 49 | [load=0x2a00000]devicetree.dtb 50 | [load=0x2000000]uramdisk 51 | [load=0x3000000]uImage 52 | } 53 | ``` 54 | 55 | used with the following command: 56 | ``` 57 | ./mkbootimage boot.bif boot.bin 58 | ``` 59 | 60 | will generate a `.bin` image which can be used in U-Boot, as follows: 61 | ``` 62 | bootm 0x3000000 0x2000000 0x2a00000 63 | ``` 64 | 65 | ### Zynq UltraScale+ 66 | 67 | For Zynq UltraScale+, `zynq-mkbootimage` currently supports creating boot images 68 | containing the FSBL, bitstream, U-Boot, ARM trusted software and Linux-related binary images. 69 | 70 | For example the following `.bif` file: 71 | ``` 72 | the_ROM_image: 73 | { 74 | [fsbl_config] a53_x64 75 | [bootloader] fsbl.elf 76 | [destination_device=pl] fpga.bit 77 | [, destination_cpu=a53-0, exception_level=el-2] bl31.elf 78 | [, destination_cpu=a53-0, exception_level=el-2] u-boot.elf 79 | [load=0x2a00000]devicetree.dtb 80 | [load=0x2000000]uramdisk 81 | [load=0x3000000]uImage 82 | } 83 | ``` 84 | 85 | used with the following command: 86 | ``` 87 | ./mkbootimage --zynqmp boot.bif boot.bin 88 | ``` 89 | 90 | will generate a `.bin` image, which can be used to successfully boot a Zynq 91 | UltraScale+ machine, and to boot Linux using the following U-Boot command: 92 | ``` 93 | bootm 0x3000000 0x2000000 0x2a00000 94 | ``` 95 | 96 | Encryption certificates are not supported. 97 | 98 | ## exbootimage 99 | `exbootimage` parses a boot ROM file and extracts desired information out of it. 100 | 101 | To use it, type in: 102 | ``` 103 | ./exbootimage [--zynqmp|-u] [--extract|-x] [--force|-f] [--list|-l] 104 | [--describe|-d] [--header|-h] [--images|-i] [--parts|-p] 105 | [--bitstream|-d DESIGN,PART-NAME] 106 | [extract_file...] 107 | ``` 108 | 109 | To see all available options, run: 110 | ``` 111 | ./exbootimage --help 112 | ``` 113 | 114 | Three of the main functionalities of the tool are described below. 115 | 116 | ### Listing the contents of the boot image 117 | 118 | To list the contents of the boot image use the `-l` option. It 119 | is especially useful before extracting partitions. 120 | 121 | To obtain the list, run: 122 | ``` 123 | ./exbootimage -l boot.bin 124 | ``` 125 | 126 | ### Printing a description of headers of the boot image 127 | You can print a readable description of all headers in a boot image 128 | by using the `-d` option. 129 | 130 | The output is divided into sections dedicated to various header types: 131 | 1. Main file header 132 | 2. Image header table 133 | 3. Image headers 134 | 4. Partition headers 135 | 136 | To see it working, run: 137 | ``` 138 | ./exbootimage -d boot.bin 139 | ``` 140 | 141 | Or for ZynqMP bootimages: 142 | ``` 143 | ./exbootimage -u -d boot.bin 144 | ``` 145 | 146 | ### Extracting partition data 147 | To extract partition contents use the `-x` option. The partitions 148 | will be extracted into files named after each partition's image name. 149 | 150 | To perform this operation, run: 151 | ``` 152 | ./exbootimage -x boot.bin 153 | ``` 154 | 155 | Or for ZynqMP boot files: 156 | ``` 157 | ./exbootimage -ux boot.bin 158 | ``` 159 | 160 | The tool stops if a file of that name is already present, this behaviour 161 | can be bypassed with the `-f` flag: 162 | ``` 163 | ./exbootimage -uxf boot.bin 164 | ``` 165 | 166 | To extract only some of the partitions, type their names after 167 | the boot image name: 168 | ``` 169 | ./exbootimage -x boot.bin fpga.bit rootfs.img 170 | ``` 171 | 172 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Zynq mkbootimage project organization 2 | 3 | This file is meant to help contributors getting familiar with organization of this project. 4 | 5 | ## Source tree 6 | The project source tree has the following structure: 7 | 8 | ``` 9 | / - the root project directory 10 | LICENSE - projects license file (BSD 2-clause) 11 | Makefile - supports `make [all|exbootimage|makebootimage|format|test]` 12 | README.md - this file 13 | .clang-format - project formatter config 14 | 15 | tests/ - tests 16 | tester.sh - testing script 17 | 18 | src/ - project source code 19 | bif.c - BIF file parser 20 | bootrom.c - boot image generator 21 | common.c - common tool routines used by the whole project 22 | common.h - as above + definitions of error codes 23 | exbootimage.c - main routine of `exbootimage` and its most important routines 24 | mkbootimage.c - main routine of `mkbootimage` 25 | 26 | src/arch/ - architecture-specific header initializers 27 | common.c - common initilization routines 28 | zynq.c - routines for Zynq 29 | zynqmp.c - routines for ZynqMP 30 | 31 | src/file/ - sources for file formats that need special operations 32 | bitstream.c - bitstream creation and extraction routines 33 | elf.c - routines extracting ELF information 34 | ``` 35 | 36 | Most of header files haven't been mentioned since they have the same roles as their corresponding C files. 37 | 38 | ## Code style 39 | The code style is specified in `.clang-format` file, generally it resembles the Linux style with two spaces wide indent and 100 characters column limit. 40 | 41 | To assure correctness of formatting, run 42 | ``` 43 | make format 44 | ``` 45 | before committing and pushing your changes. 46 | 47 | It is recommended to use `clang-format` version 12 as it is used by the formatting checks on GitHub, any newer version should be appropriate as well though. 48 | 49 | ## Tests 50 | To start tests, run: 51 | ``` 52 | make test 53 | ``` 54 | 55 | All kinds of tests are implemented as routines in `tests/tester.sh`. 56 | Tests-specific files are put into subdirectories of `tests/` and named after functionality they are testing. 57 | 58 | Further details on implementing new tests can be found in the comments of the `tests/tester.sh` script itself. 59 | 60 | ## Error codes 61 | The error codes are defined in `common.h` and used by the whole project. 62 | They are values of the `error` type, and so almost every routine that can fail has the `error` return value. 63 | 64 | All the error message follow the schema below: 65 | ``` 66 | error: [where: ] 67 | ``` 68 | 69 | For BIF parser the `[where: ]` clause is `filename:line:column: `, for image extractor it is a hexadecimal offset of a wrong value encountered in a boot image file. 70 | If the place in which the error occured cannot be precisely addressed, the `[where: ]` clause can be omitted. 71 | 72 | All errors are printed with the use of `errorf` routine, which is of the same type as `printf`, only it prints the output to `stderr` and begins every message with an `error: ` prefix. 73 | The only exception is the BIF parser which uses `perrof` routine which uses lexer state to specify the error source. 74 | 75 | The error messages should be printed as early as the error is detected and the error code should be returned and passed to the caller, until it is handled or returned as an exit value in `main`. 76 | -------------------------------------------------------------------------------- /src/arch/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | error bootrom_init_header(bootrom_hdr_t *hdr) { 12 | unsigned int i = 0; 13 | for (i = 0; i < sizeof(hdr->interrupt_table) / sizeof(hdr->interrupt_table[0]); i++) { 14 | hdr->interrupt_table[i] = BOOTROM_INT_TABLE_DEFAULT; 15 | } 16 | hdr->width_detect = BOOTROM_WIDTH_DETECT; 17 | memcpy(&(hdr->img_id), BOOTROM_IMG_ID, strlen(BOOTROM_IMG_ID)); 18 | hdr->encryption_status = BOOTROM_ENCRYPTED_NONE; 19 | 20 | /* BootROM does not interpret the field below */ 21 | hdr->src_offset = 0x0; /* Will be filled elsewhere */ 22 | hdr->img_len = 0x0; /* Will be filled elsewhere */ 23 | hdr->reserved_0 = BOOTROM_RESERVED_0; 24 | hdr->start_of_exec = 0x0; /* Will be filled elsewhere */ 25 | hdr->total_img_len = 0x0; /* Will be filled elsewhere */ 26 | hdr->reserved_1 = BOOTROM_RESERVED_1_RL; 27 | 28 | return SUCCESS; 29 | } 30 | 31 | void bootrom_calc_hdr_checksum(bootrom_hdr_t *hdr) { 32 | /* the checksum skips the interupt vector and starts 33 | * at the width detect field */ 34 | hdr->checksum = calc_checksum(&(hdr->width_detect), &(hdr->checksum) - 1); 35 | } 36 | 37 | error bootrom_init_img_hdr_tab(bootrom_img_hdr_tab_t *img_hdr_tab, bootrom_offs_t *offs) { 38 | /* Prepare image header table */ 39 | img_hdr_tab->version = BOOTROM_IMG_VERSION; 40 | img_hdr_tab->part_hdr_off = 0x0; /* filled below */ 41 | img_hdr_tab->part_img_hdr_off = 0x0; /* filled below */ 42 | img_hdr_tab->auth_hdr_off = 0x0; /* auth not implemented */ 43 | 44 | /* The data will be copied to the reserved space later 45 | * when we know all the required offsets, 46 | * save the pointer for that */ 47 | offs->hoff = offs->poff; 48 | offs->poff += sizeof(*img_hdr_tab) / sizeof(uint32_t); 49 | 50 | return SUCCESS; 51 | } 52 | -------------------------------------------------------------------------------- /src/arch/common.h: -------------------------------------------------------------------------------- 1 | #ifndef ARCH_COMMON_H 2 | #define ARCH_COMMON_H 3 | 4 | #include 5 | 6 | error bootrom_init_header(bootrom_hdr_t*); 7 | void bootrom_calc_hdr_checksum(bootrom_hdr_t*); 8 | error bootrom_init_img_hdr_tab(bootrom_img_hdr_tab_t*, bootrom_offs_t*); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/arch/zynq.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | error zynq_bootrom_init_offs(uint32_t *img_ptr, int hdr_count, bootrom_offs_t *offs) { 19 | (void) hdr_count; 20 | /* Copy the image pointer */ 21 | offs->img_ptr = img_ptr; 22 | 23 | /* Init constant offsets */ 24 | offs->img_hdr_off = BOOTROM_IMG_HDR_OFF; 25 | offs->part_hdr_off = BOOTROM_PART_HDR_OFF; 26 | offs->part_hdr_end_off = BOOTROM_PART_HDR_END_PADD; 27 | offs->bins_off = BOOTROM_BINS_OFF; 28 | 29 | /* Move the offset to reserve the space for headers */ 30 | offs->poff = (offs->img_hdr_off) / sizeof(uint32_t) + img_ptr; 31 | offs->coff = (offs->bins_off) / sizeof(uint32_t) + img_ptr; 32 | 33 | return SUCCESS; 34 | } 35 | 36 | error zynq_bootrom_init_header(bootrom_hdr_t *hdr, bootrom_offs_t *offs) { 37 | error err; 38 | int i; 39 | 40 | /* Call the common init */ 41 | if ((err = bootrom_init_header(hdr))) 42 | return err; 43 | 44 | hdr->user_defined_0 = BOOTROM_USER_0; 45 | 46 | memset(hdr->user_defined_zynq_0, 0x0, sizeof(hdr->user_defined_zynq_0)); 47 | 48 | hdr->user_defined_zynq_0[19] = offs->img_hdr_off; 49 | hdr->user_defined_zynq_0[20] = offs->part_hdr_off; 50 | ; 51 | 52 | /* Memory acces ranges - set to full (0x0 - 0xFFFFFFFF range) */ 53 | for (i = 0; i < 256; i++) { 54 | hdr->reg_init_zynq[2 * i] = 0xFFFFFFFF; 55 | hdr->reg_init_zynq[(2 * i) + 1] = 0x0; 56 | } 57 | 58 | memset(hdr->user_defined_zynq_1, 0xFF, sizeof(hdr->user_defined_zynq_1)); 59 | 60 | /* Calculate the checksum */ 61 | bootrom_calc_hdr_checksum(hdr); 62 | 63 | return SUCCESS; 64 | } 65 | 66 | error zynq_bootrom_setup_fsbl_at_curr_off(bootrom_hdr_t *hdr, 67 | bootrom_offs_t *offs, 68 | uint32_t img_len) { 69 | /* Update the header to point at the bootloader */ 70 | hdr->src_offset = (offs->coff - offs->img_ptr) * sizeof(uint32_t); 71 | 72 | /* Image length needs to be in words not bytes */ 73 | hdr->img_len = img_len; 74 | hdr->total_img_len = hdr->img_len; 75 | 76 | /* Recalculate the checksum */ 77 | bootrom_calc_hdr_checksum(hdr); 78 | 79 | return SUCCESS; 80 | } 81 | 82 | error zynq_bootrom_init_img_hdr_tab(bootrom_img_hdr_tab_t *img_hdr_tab, 83 | bootrom_img_hdr_t *img_hdr, 84 | bootrom_partition_hdr_t *ihdr, 85 | bootrom_offs_t *offs) { 86 | unsigned int i; 87 | uint32_t img_hdr_size = 0; 88 | 89 | /* Retrieve the header */ 90 | bootrom_partition_hdr_zynq_t *part_hdr; 91 | part_hdr = (bootrom_partition_hdr_zynq_t *) ihdr; 92 | 93 | /* Call the common code */ 94 | bootrom_init_img_hdr_tab(img_hdr_tab, offs); 95 | 96 | for (i = 0; i < img_hdr_tab->hdrs_count; i++) { 97 | img_hdr_size = sizeof(img_hdr[i]) / sizeof(uint32_t); 98 | memset(&img_hdr[i].padding, 0xFF, sizeof(img_hdr->padding)); 99 | 100 | /* Calculate the next img hdr offsets */ 101 | if (i + 1 == img_hdr_tab->hdrs_count) { 102 | img_hdr[i].next_img_off = 0x0; 103 | } else { 104 | img_hdr[i].next_img_off = offs->poff + img_hdr_size - offs->img_ptr; 105 | } 106 | 107 | img_hdr[i].part_hdr_off = (offs->part_hdr_off / sizeof(uint32_t)) + 108 | (i * sizeof(bootrom_partition_hdr_t) / sizeof(uint32_t)); 109 | 110 | /* Write the actual img_hdr data */ 111 | memcpy(offs->poff, &(img_hdr[i]), sizeof(img_hdr[i])); 112 | 113 | /* Keep the offset for later use */ 114 | part_hdr[i].img_hdr_off = (offs->poff - offs->img_ptr); 115 | 116 | /* Calculate the checksum */ 117 | part_hdr[i].checksum = calc_checksum(&(part_hdr[i].pd_len), &(part_hdr[i].checksum) - 1); 118 | 119 | if (i == 0) { 120 | img_hdr_tab->part_img_hdr_off = (offs->poff - offs->img_ptr); 121 | } 122 | 123 | offs->poff += img_hdr_size; 124 | } 125 | 126 | /* Fill the partition header offset in img header */ 127 | img_hdr_tab->part_hdr_off = offs->part_hdr_off / sizeof(uint32_t); 128 | 129 | /* Fill padding */ 130 | memset(img_hdr_tab->padding, 0xFFFFFFFF, sizeof(img_hdr_tab->padding)); 131 | 132 | return SUCCESS; 133 | } 134 | 135 | error zynq_init_part_hdr_generic(bootrom_partition_hdr_t *ihdr, 136 | bif_node_t *node, 137 | uint32_t extra_args) { 138 | /* Retrieve the header */ 139 | bootrom_partition_hdr_zynq_t *hdr; 140 | hdr = (bootrom_partition_hdr_zynq_t *) ihdr; 141 | 142 | /* set destination device as the only attribute */ 143 | hdr->attributes = (0x1 << BOOTROM_PART_ATTR_DEST_DEV_OFF) | extra_args; 144 | 145 | /* No load/execution address */ 146 | hdr->dest_load_addr = node->load; 147 | hdr->dest_exec_addr = 0x0; 148 | return SUCCESS; 149 | } 150 | 151 | error zynq_init_part_hdr_default(bootrom_partition_hdr_t *ihdr, bif_node_t *node) { 152 | return zynq_init_part_hdr_generic(ihdr, node, BINARY_ATTR_GENERAL); 153 | } 154 | 155 | error zynq_init_part_hdr_dtb(bootrom_partition_hdr_t *ihdr, bif_node_t *node) { 156 | return zynq_init_part_hdr_generic(ihdr, node, BINARY_ATTR_RAMDISK); 157 | } 158 | 159 | error zynq_init_part_hdr_elf(bootrom_partition_hdr_t *ihdr, 160 | bif_node_t *node, 161 | uint32_t *size, 162 | uint32_t load, 163 | uint32_t entry, 164 | uint8_t nbits) { 165 | /* Handle unused parameters warning */ 166 | (void) node; 167 | (void) size; 168 | (void) nbits; 169 | 170 | /* Retrieve the header */ 171 | bootrom_partition_hdr_zynq_t *hdr; 172 | hdr = (bootrom_partition_hdr_zynq_t *) ihdr; 173 | 174 | /* Set the load and execution address */ 175 | hdr->dest_load_addr = load; 176 | hdr->dest_exec_addr = entry; 177 | 178 | /* set destination device as the only attribute */ 179 | hdr->attributes = BOOTROM_PART_ATTR_DEST_DEV_PS; 180 | 181 | return SUCCESS; 182 | } 183 | 184 | error zynq_init_part_hdr_bitstream(bootrom_partition_hdr_t *ihdr, bif_node_t *node) { 185 | /* Handle unused parameters warning */ 186 | (void) node; 187 | 188 | /* Retrieve the header */ 189 | bootrom_partition_hdr_zynq_t *hdr; 190 | hdr = (bootrom_partition_hdr_zynq_t *) ihdr; 191 | 192 | /* Set destination device as the only attribute */ 193 | hdr->attributes = BOOTROM_PART_ATTR_DEST_DEV_PL; 194 | 195 | /* No execution address for bitstream */ 196 | hdr->dest_load_addr = 0x0; 197 | hdr->dest_exec_addr = 0x0; 198 | return SUCCESS; 199 | } 200 | 201 | error zynq_init_part_hdr_linux(bootrom_partition_hdr_t *ihdr, 202 | bif_node_t *node, 203 | linux_image_header_t *img) { 204 | /* Retrieve the header */ 205 | bootrom_partition_hdr_zynq_t *hdr; 206 | hdr = (bootrom_partition_hdr_zynq_t *) ihdr; 207 | if (img->type == FILE_LINUX_IMG_TYPE_UIM) { 208 | hdr->attributes = BINARY_ATTR_LINUX; 209 | } 210 | 211 | if (img->type == FILE_LINUX_IMG_TYPE_URD) 212 | hdr->attributes = 0x00; /* despite what the doc says */ 213 | 214 | if (img->type == FILE_LINUX_IMG_TYPE_SCR) 215 | hdr->attributes = BINARY_ATTR_GENERAL; 216 | 217 | /* Set destination device attribute */ 218 | hdr->attributes |= BOOTROM_PART_ATTR_DEST_DEV_PS; 219 | 220 | /* No load/execution address */ 221 | hdr->dest_load_addr = node->load; 222 | hdr->dest_exec_addr = 0x0; 223 | return SUCCESS; 224 | } 225 | 226 | error zynq_finish_part_hdr(bootrom_partition_hdr_t *ihdr, 227 | uint32_t *img_size, 228 | bootrom_offs_t *offs) { 229 | /* Retrieve the header */ 230 | bootrom_partition_hdr_zynq_t *hdr; 231 | hdr = (bootrom_partition_hdr_zynq_t *) ihdr; 232 | 233 | /* Is bitstream? */ 234 | if (hdr->attributes == BOOTROM_PART_ATTR_DEST_DEV_PL) { 235 | /* For some reason, a noop is appended after bitstream (only for zynq) */ 236 | const uint8_t noop[4] = {0, 0, 0, 0x20}; 237 | memcpy(offs->coff + (*img_size), noop, sizeof(noop)); 238 | (*img_size)++; 239 | } 240 | 241 | hdr->pd_len = *img_size; 242 | hdr->ed_len = *img_size; 243 | hdr->total_len = *img_size; 244 | 245 | /* Section count is always set to 1 */ 246 | hdr->section_count = 0x1; 247 | 248 | /* Fill remaining fields that don't seem to be used */ 249 | hdr->checksum_off = 0x0; 250 | hdr->cert_off = 0x0; 251 | memset(hdr->reserved, 0x00, sizeof(hdr->reserved)); 252 | 253 | /* Fill the offset */ 254 | hdr->data_off = (offs->coff - offs->img_ptr); 255 | 256 | /* Continue adding padding util it hits the correct alignement */ 257 | while (*img_size % (BOOTROM_IMG_PADDING_SIZE / sizeof(uint32_t))) { 258 | memset(offs->coff + (*img_size), 0xFF, sizeof(uint32_t)); 259 | (*img_size)++; 260 | } 261 | 262 | return SUCCESS; 263 | } 264 | 265 | /* Define ops */ 266 | bootrom_ops_t zynq_bops = { 267 | .init_offs = zynq_bootrom_init_offs, 268 | .init_header = zynq_bootrom_init_header, 269 | .setup_fsbl_at_curr_off = zynq_bootrom_setup_fsbl_at_curr_off, 270 | .init_img_hdr_tab = zynq_bootrom_init_img_hdr_tab, 271 | .init_part_hdr_default = zynq_init_part_hdr_default, 272 | .init_part_hdr_dtb = zynq_init_part_hdr_dtb, 273 | .init_part_hdr_elf = zynq_init_part_hdr_elf, 274 | .init_part_hdr_bitstream = zynq_init_part_hdr_bitstream, 275 | .init_part_hdr_linux = zynq_init_part_hdr_linux, 276 | .finish_part_hdr = zynq_finish_part_hdr, 277 | .append_null_part = 0 /* Zynq does not use null part */ 278 | }; 279 | -------------------------------------------------------------------------------- /src/arch/zynq.h: -------------------------------------------------------------------------------- 1 | #ifndef ARCH_ZYNQ_H 2 | #define ARCH_ZYNQ_H 3 | 4 | #include 5 | 6 | #include "bootrom.h" 7 | 8 | extern bootrom_ops_t zynq_bops; 9 | 10 | typedef struct bootrom_partition_hdr_zynq_t { 11 | uint32_t pd_len; /* encrypted partiton data length */ 12 | uint32_t ed_len; /* unecrypted data length */ 13 | uint32_t total_len; /* total encrypted, padding,expansion, auth length */ 14 | 15 | uint32_t dest_load_addr; /* RAM addr where the part will be loaded */ 16 | uint32_t dest_exec_addr; 17 | uint32_t data_off; 18 | uint32_t attributes; 19 | uint32_t section_count; 20 | 21 | uint32_t checksum_off; 22 | uint32_t img_hdr_off; 23 | uint32_t cert_off; 24 | 25 | uint32_t reserved[4]; /* set to 0 */ 26 | uint32_t checksum; 27 | } bootrom_partition_hdr_zynq_t; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/arch/zynqmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define BOOTROM_ZYNQMP_OFFSET_AFTER_HEADERS 0x40 18 | 19 | error zynqmp_bootrom_init_offs(uint32_t *img_ptr, int hdr_count, bootrom_offs_t *offs) { 20 | /* Copy the image pointer */ 21 | offs->img_ptr = img_ptr; 22 | 23 | /* Init constant offsets */ 24 | offs->img_hdr_off = BOOTROM_IMG_HDR_OFF; 25 | offs->part_hdr_end_off = 0; /* Not needed by zynqmp */ 26 | offs->part_hdr_off = 27 | offs->img_hdr_off + sizeof(bootrom_img_hdr_tab_t) + sizeof(bootrom_img_hdr_t) * hdr_count; 28 | offs->bins_off = offs->part_hdr_off + sizeof(bootrom_partition_hdr_t) * hdr_count + 29 | BOOTROM_ZYNQMP_OFFSET_AFTER_HEADERS; 30 | 31 | /* Move the offset to reserve the space for headers */ 32 | offs->poff = (offs->img_hdr_off) / sizeof(uint32_t) + img_ptr; 33 | offs->coff = (offs->bins_off) / sizeof(uint32_t) + img_ptr; 34 | 35 | return SUCCESS; 36 | } 37 | 38 | error zynqmp_bootrom_init_header(bootrom_hdr_t *hdr, bootrom_offs_t *offs) { 39 | int i; 40 | error err; 41 | 42 | /* Call the common init */ 43 | if ((err = bootrom_init_header(hdr))) 44 | return err; 45 | 46 | hdr->fsbl_execution_addr = BOOTROM_FSBL_EXEC_ADDR; 47 | 48 | /* Obfuscated keys not yet supported */ 49 | memset(hdr->obfuscated_key, 0x0, sizeof(hdr->obfuscated_key)); 50 | 51 | hdr->reserved_zynqmp = BOOTROM_RESERVED_ZMP_RL; 52 | 53 | memset(hdr->user_defined_zynqmp_0, 0x0, sizeof(hdr->user_defined_zynqmp_0)); 54 | 55 | hdr->user_defined_zynqmp_0[10] = offs->img_hdr_off; 56 | hdr->user_defined_zynqmp_0[11] = offs->part_hdr_off; 57 | 58 | memset(hdr->sec_hdr_init_vec, 0x0, sizeof(hdr->sec_hdr_init_vec)); 59 | memset(hdr->obf_key_init_vec, 0x0, sizeof(hdr->obf_key_init_vec)); 60 | 61 | /* Memory acces ranges - set to full (0x0 - 0xFFFFFFFF range) */ 62 | for (i = 0; i < 256; i++) { 63 | hdr->reg_init_zynqmp[2 * i] = 0xFFFFFFFF; 64 | hdr->reg_init_zynqmp[(2 * i) + 1] = 0x0; 65 | } 66 | 67 | /* Fill padding */ 68 | memset(hdr->padding, 0xFFFFFFFF, sizeof(hdr->padding)); 69 | 70 | /* Calculate the checksum */ 71 | bootrom_calc_hdr_checksum(hdr); 72 | 73 | return SUCCESS; 74 | } 75 | 76 | error zynqmp_bootrom_setup_fsbl_at_curr_off(bootrom_hdr_t *hdr, 77 | bootrom_offs_t *offs, 78 | uint32_t img_len) { 79 | /* Update the header to point at the bootloader */ 80 | hdr->src_offset = (offs->coff - offs->img_ptr) * sizeof(uint32_t); 81 | 82 | /* Zynqmp seems to round out the len to 8B */ 83 | while (img_len % 8) 84 | img_len++; 85 | 86 | /* Image length needs to be in words not bytes */ 87 | hdr->fsbl_img_len = img_len; 88 | hdr->total_img_len = img_len; 89 | 90 | /* Set target cpu */ 91 | hdr->fsbl_target_cpu = BOOTROM_FSBL_CPU_A53_64; 92 | 93 | /* Recalculate the checksum */ 94 | bootrom_calc_hdr_checksum(hdr); 95 | 96 | return SUCCESS; 97 | } 98 | 99 | error zynqmp_bootrom_init_img_hdr_tab(bootrom_img_hdr_tab_t *img_hdr_tab, 100 | bootrom_img_hdr_t *img_hdr, 101 | bootrom_partition_hdr_t *ihdr, 102 | bootrom_offs_t *offs) { 103 | unsigned int i; 104 | uint32_t img_hdr_size = 0; 105 | 106 | bootrom_partition_hdr_zynqmp_t *part_hdr; 107 | part_hdr = (bootrom_partition_hdr_zynqmp_t *) ihdr; 108 | 109 | /* Call the common code */ 110 | bootrom_init_img_hdr_tab(img_hdr_tab, offs); 111 | 112 | for (i = 0; i < img_hdr_tab->hdrs_count; i++) { 113 | img_hdr_size = sizeof(img_hdr[i]) / sizeof(uint32_t); 114 | memset(&img_hdr[i].padding, 0xFF, sizeof(img_hdr->padding)); 115 | 116 | img_hdr[i].part_hdr_off = (offs->part_hdr_off / sizeof(uint32_t)) + 117 | (i * sizeof(bootrom_partition_hdr_t) / sizeof(uint32_t)); 118 | 119 | /* Calculate the next img hdr offsets */ 120 | if (i + 1 == img_hdr_tab->hdrs_count) { 121 | img_hdr[i].next_img_off = 0x0; 122 | } else { 123 | img_hdr[i].next_img_off = offs->poff + img_hdr_size - offs->img_ptr; 124 | } 125 | 126 | part_hdr[i].next_part_hdr_off = 0x0; 127 | if (i > 0) { 128 | part_hdr[i - 1].next_part_hdr_off = img_hdr[i].part_hdr_off; 129 | part_hdr[i - 1].checksum = 130 | calc_checksum(&(part_hdr[i - 1].pd_len), &(part_hdr[i - 1].checksum) - 1); 131 | } 132 | 133 | /* Write the actual img_hdr data */ 134 | memcpy(offs->poff, &(img_hdr[i]), sizeof(img_hdr[i])); 135 | 136 | /* Keep the offset for later use */ 137 | part_hdr[i].img_hdr_off = (offs->poff - offs->img_ptr); 138 | 139 | /* Calculate the checksum if this is the last header*/ 140 | if (i + 1 == img_hdr_tab->hdrs_count) { 141 | part_hdr[i].checksum = calc_checksum(&(part_hdr[i].pd_len), &(part_hdr[i].checksum) - 1); 142 | } 143 | 144 | if (i == 0) { 145 | img_hdr_tab->part_img_hdr_off = (offs->poff - offs->img_ptr); 146 | } 147 | 148 | offs->poff += img_hdr_size; 149 | } 150 | 151 | /* Fill the partition header offset in img header */ 152 | img_hdr_tab->part_hdr_off = offs->part_hdr_off / sizeof(uint32_t); 153 | 154 | /* Set boot device */ 155 | img_hdr_tab->boot_dev = BOOTROM_IMG_HDR_BOOT_SAME; 156 | 157 | /* Fill reserved fields with zeroes */ 158 | memset(img_hdr_tab->reserved, 0x0, sizeof(img_hdr_tab->reserved)); 159 | 160 | /* Recalculate the checksum */ 161 | img_hdr_tab->checksum = calc_checksum(&img_hdr_tab->version, &(img_hdr_tab->checksum) - 1); 162 | 163 | return SUCCESS; 164 | } 165 | 166 | uint32_t zynqmp_calc_part_hdr_attr(bif_node_t *node) { 167 | uint32_t attr = 0; 168 | attr |= node->partition_owner; 169 | attr |= node->destination_device; 170 | attr |= node->destination_cpu; 171 | attr |= node->exception_level; 172 | 173 | return attr; 174 | } 175 | 176 | error zynqmp_init_part_hdr_default(bootrom_partition_hdr_t *ihdr, bif_node_t *node) { 177 | /* Retrieve the header */ 178 | bootrom_partition_hdr_zynqmp_t *hdr; 179 | hdr = (bootrom_partition_hdr_zynqmp_t *) ihdr; 180 | 181 | /* set destination device as the only attribute */ 182 | hdr->attributes = zynqmp_calc_part_hdr_attr(node); 183 | 184 | hdr->dest_load_addr_lo = node->load; 185 | hdr->dest_exec_addr_hi = 0x0; 186 | return SUCCESS; 187 | } 188 | 189 | error zynqmp_init_part_hdr_elf(bootrom_partition_hdr_t *ihdr, 190 | bif_node_t *node, 191 | uint32_t *size, 192 | uint32_t load, 193 | uint32_t entry, 194 | uint8_t nbits) { 195 | /* Retrieve the header */ 196 | bootrom_partition_hdr_zynqmp_t *hdr; 197 | hdr = (bootrom_partition_hdr_zynqmp_t *) ihdr; 198 | 199 | /* Set the load and execution address */ 200 | hdr->dest_load_addr_lo = load; 201 | hdr->dest_exec_addr_lo = entry; 202 | 203 | /* Size needs to be rounded after conversion to words */ 204 | *size = (*size + 3) & ~3u; 205 | 206 | /* Set sizes (in words) */ 207 | hdr->pd_len = *size / 4; 208 | hdr->ed_len = *size / 4; 209 | hdr->total_len = *size / 4; 210 | 211 | /* Set destination device as the only attribute */ 212 | hdr->attributes = zynqmp_calc_part_hdr_attr(node); 213 | 214 | /* Set the execution state arguments */ 215 | if (nbits == 32) { 216 | hdr->attributes |= BOOTROM_PART_ATTR_A5X_EXEC_S_32; 217 | /* Additionally, for some reason, 32bit elfs seem to always overwrite 218 | * the exception level to EL2 */ 219 | hdr->attributes &= (~BOOTROM_PART_ATTR_EXC_LVL_MASK); 220 | hdr->attributes |= BOOTROM_PART_ATTR_EXC_LVL_EL2; 221 | } else if (nbits == 64) { 222 | hdr->attributes |= BOOTROM_PART_ATTR_A5X_EXEC_S_64; 223 | } 224 | 225 | return SUCCESS; 226 | } 227 | 228 | error zynqmp_init_part_hdr_bitstream(bootrom_partition_hdr_t *ihdr, bif_node_t *node) { 229 | (void) node; 230 | /* Retrieve the header */ 231 | bootrom_partition_hdr_zynqmp_t *hdr; 232 | hdr = (bootrom_partition_hdr_zynqmp_t *) ihdr; 233 | 234 | /* Set destination device as the only attribute */ 235 | hdr->attributes = zynqmp_calc_part_hdr_attr(node); 236 | 237 | /* No execution address for bitstream */ 238 | hdr->dest_load_addr_lo = 0xffffffff; /* Don't ask me why */ 239 | hdr->dest_load_addr_hi = 0x0; 240 | hdr->dest_exec_addr_lo = 0x0; 241 | hdr->dest_exec_addr_hi = 0x0; 242 | return SUCCESS; 243 | } 244 | 245 | error zynqmp_init_part_hdr_linux(bootrom_partition_hdr_t *ihdr, 246 | bif_node_t *node, 247 | linux_image_header_t *img) { 248 | bootrom_partition_hdr_zynqmp_t *hdr; 249 | hdr = (bootrom_partition_hdr_zynqmp_t *) ihdr; 250 | 251 | /* Despite what ug1283 p. 21 says, sometimes attributes need to be zeroed */ 252 | if (img->type == FILE_LINUX_IMG_TYPE_URD) { 253 | hdr->attributes = 0x0; 254 | } else if (img->type == FILE_LINUX_IMG_TYPE_SCR) { 255 | hdr->attributes = 0x0; 256 | } else { 257 | /* Add explicitly defined attributes if they're given */ 258 | hdr->attributes = zynqmp_calc_part_hdr_attr(node); 259 | 260 | /* Set destination device attribute */ 261 | hdr->attributes |= BOOTROM_PART_ATTR_DEST_DEV_PS; 262 | 263 | if (img->type == FILE_LINUX_IMG_TYPE_UIM) 264 | hdr->attributes |= BINARY_ATTR_LINUX; 265 | } 266 | 267 | /* No load/execution address */ 268 | hdr->dest_load_addr_lo = node->load; 269 | hdr->dest_exec_addr_lo = 0x0; 270 | return SUCCESS; 271 | } 272 | 273 | error zynqmp_finish_part_hdr(bootrom_partition_hdr_t *ihdr, 274 | uint32_t *img_size, 275 | bootrom_offs_t *offs) { 276 | /* Retrieve the header */ 277 | bootrom_partition_hdr_zynqmp_t *hdr; 278 | hdr = (bootrom_partition_hdr_zynqmp_t *) ihdr; 279 | 280 | /* Set lengths to basic img_len if not set earlier */ 281 | if (hdr->pd_len == 0) 282 | hdr->pd_len = *img_size; 283 | 284 | if (hdr->ed_len == 0) 285 | hdr->ed_len = *img_size; 286 | 287 | if (hdr->total_len == 0) 288 | hdr->total_len = *img_size; 289 | 290 | /* Fill remaining fields that don't seem to be used */ 291 | hdr->checksum_off = 0x0; 292 | 293 | /* Section count is always set to 1 */ 294 | hdr->section_count = 0x1; 295 | 296 | hdr->next_part_hdr_off = 0x0; 297 | hdr->actual_part_off = (offs->coff - offs->img_ptr); 298 | 299 | /* Add 0xFF padding */ 300 | while (*img_size % (BOOTROM_IMG_PADDING_SIZE / sizeof(uint32_t))) { 301 | memset(offs->coff + (*img_size), 0xFF, sizeof(uint32_t)); 302 | (*img_size)++; 303 | } 304 | 305 | return SUCCESS; 306 | } 307 | 308 | /* Define ops */ 309 | bootrom_ops_t zynqmp_bops = { 310 | .init_offs = zynqmp_bootrom_init_offs, 311 | .init_header = zynqmp_bootrom_init_header, 312 | .setup_fsbl_at_curr_off = zynqmp_bootrom_setup_fsbl_at_curr_off, 313 | .init_img_hdr_tab = zynqmp_bootrom_init_img_hdr_tab, 314 | .init_part_hdr_default = zynqmp_init_part_hdr_default, 315 | .init_part_hdr_dtb = zynqmp_init_part_hdr_default, /* XXX unsupported yet */ 316 | .init_part_hdr_elf = zynqmp_init_part_hdr_elf, 317 | .init_part_hdr_bitstream = zynqmp_init_part_hdr_bitstream, 318 | .init_part_hdr_linux = zynqmp_init_part_hdr_linux, 319 | .finish_part_hdr = zynqmp_finish_part_hdr, 320 | .append_null_part = 1 /* yes */ 321 | }; 322 | -------------------------------------------------------------------------------- /src/arch/zynqmp.h: -------------------------------------------------------------------------------- 1 | #ifndef ARCH_ZYNQMP_H 2 | #define ARCH_ZYNQMP_H 3 | 4 | #include 5 | 6 | #include "bootrom.h" 7 | 8 | extern bootrom_ops_t zynqmp_bops; 9 | 10 | typedef struct bootrom_partition_hdr_zynqmp_t { 11 | uint32_t pd_len; 12 | uint32_t ed_len; 13 | uint32_t total_len; 14 | 15 | uint32_t next_part_hdr_off; 16 | uint32_t dest_exec_addr_lo; 17 | uint32_t dest_exec_addr_hi; 18 | uint32_t dest_load_addr_lo; 19 | uint32_t dest_load_addr_hi; 20 | uint32_t actual_part_off; 21 | uint32_t attributes; 22 | union { 23 | uint32_t reserved0; 24 | uint32_t section_count; 25 | }; 26 | uint32_t checksum_off; 27 | union { 28 | uint32_t img_hdr_off; 29 | uint32_t reserved1; 30 | }; 31 | union { 32 | uint32_t reserved2; 33 | uint32_t cert_off; 34 | }; 35 | uint32_t reserved3; 36 | uint32_t checksum; 37 | } bootrom_partition_hdr_zynqmp_t; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/bif.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013-2021, Antmicro Ltd 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 | * * Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 10 | * * 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" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | static int perrorf(lexer_t *lex, const char *fmt, ...); 41 | 42 | static inline char *get_token_name(int type); 43 | 44 | static inline void update_pos(lexer_t *lex, char ch); 45 | static inline error append_token(lexer_t *lex, char ch); 46 | 47 | static error bif_scan(lexer_t *lex); 48 | static inline error bif_consume(lexer_t *lex, int type); 49 | static inline error bif_expect(lexer_t *lex, int type); 50 | static error bif_parse_file(lexer_t *lex, bif_cfg_t *cfg, bif_node_t *node); 51 | static error bif_parse_attribute(lexer_t *lex, bif_cfg_t *cfg, bif_node_t *node); 52 | 53 | static const char *special_chars = ":{}[],=\\"; 54 | 55 | /* errorf equivalent for parser errors */ 56 | static int perrorf(lexer_t *lex, const char *fmt, ...) { 57 | int n; 58 | va_list args; 59 | 60 | va_start(args, fmt); 61 | fprintf(stderr, "error: %s:%d:%d: ", lex->fname, lex->line, lex->column); 62 | n = vfprintf(stderr, fmt, args); 63 | va_end(args); 64 | 65 | return n; 66 | } 67 | 68 | error init_bif_cfg(bif_cfg_t *cfg) { 69 | /* Initially setup 8 nodes */ 70 | cfg->nodes_num = 0; 71 | cfg->nodes_avail = 8; 72 | 73 | /* Alloc memory for it */ 74 | cfg->nodes = malloc(sizeof(bif_node_t) * cfg->nodes_avail); 75 | if (!cfg->nodes) { 76 | return ERROR_NOMEM; 77 | } 78 | 79 | return SUCCESS; 80 | } 81 | 82 | error deinit_bif_cfg(bif_cfg_t *cfg) { 83 | cfg->nodes_num = 0; 84 | cfg->nodes_avail = 0; 85 | 86 | free(cfg->nodes); 87 | 88 | return SUCCESS; 89 | } 90 | 91 | static error init_lexer(lexer_t *lex, const char *fname) { 92 | error err; 93 | 94 | if (!(lex->file = fopen(fname, "r"))) { 95 | errorf("could not open file \"%s\"\n", fname); 96 | return ERROR_BIF_NOFILE; 97 | } 98 | 99 | lex->fname = malloc(strlen(fname) + 1); 100 | strcpy(lex->fname, fname); 101 | 102 | lex->line = lex->column = 1; 103 | lex->type = 0; 104 | 105 | lex->len = lex->cap = 32; 106 | lex->buffer = malloc(lex->cap * sizeof(char)); 107 | 108 | /* Scan a first token 109 | as the lexer is assumed to always contain a next token 110 | information in the buffer and type attributes */ 111 | if ((err = bif_scan(lex))) 112 | return err; 113 | 114 | return SUCCESS; 115 | } 116 | 117 | static error deinit_lexer(lexer_t *lex) { 118 | fclose(lex->file); 119 | free(lex->fname); 120 | free(lex->buffer); 121 | 122 | return SUCCESS; 123 | } 124 | 125 | static inline char *get_token_name(int type) { 126 | switch (type) { 127 | case ':': 128 | return "':' operator"; 129 | case '{': 130 | return "'{' operator"; 131 | case '}': 132 | return "'}' operator"; 133 | case '[': 134 | return "'[' operator"; 135 | case ']': 136 | return "']' operator"; 137 | case ',': 138 | return "',' operator"; 139 | case '=': 140 | return "'=' operator"; 141 | case '/': 142 | return "'/' operator"; 143 | case '\\': 144 | return "'\\' operator"; 145 | 146 | case TOKEN_EOF: 147 | return "end of file"; 148 | case TOKEN_NAME: 149 | return "a string"; 150 | } 151 | return "unknown token"; 152 | } 153 | 154 | static inline void update_pos(lexer_t *lex, char ch) { 155 | /* Calculate the lexer position after reading ch */ 156 | 157 | if (ch == '\t') { 158 | lex->column += 8; 159 | } else if (ch == '\n') { 160 | lex->column = 1; 161 | lex->line++; 162 | } else { 163 | lex->column++; 164 | } 165 | } 166 | 167 | static inline error append_token(lexer_t *lex, char ch) { 168 | lex->len++; 169 | 170 | if (lex->len >= lex->cap) { 171 | lex->cap = 2 * (lex->len); 172 | lex->buffer = realloc(lex->buffer, lex->cap); 173 | if (!lex->buffer) { 174 | errorf("out of memory\n"); 175 | return ERROR_NOMEM; 176 | } 177 | } 178 | 179 | lex->buffer[lex->len - 1] = ch; 180 | lex->buffer[lex->len] = '\0'; 181 | return SUCCESS; 182 | } 183 | 184 | static inline error bif_scan_comment(lexer_t *lex, char type) { 185 | int ch = 0, prev; 186 | 187 | if (type == '*') { 188 | for (;;) { 189 | prev = ch; 190 | /* Break if its the end of file */ 191 | if ((ch = fgetc(lex->file)) < 0) { 192 | perrorf(lex, "file ended while scanning a C-style comment\n"); 193 | return ERROR_BIF_LEXER; 194 | } 195 | /* Break if its the end of the comment */ 196 | if (prev == '*' && ch == '/') 197 | break; 198 | update_pos(lex, ch); 199 | } 200 | /* Don't update the pos after the last ch, it will happen in bif_scan */ 201 | } else if (type == '/') { 202 | while ((ch = fgetc(lex->file)) >= 0 && ch != '\n') 203 | update_pos(lex, ch); 204 | /* Don't update the pos after the last ch, it will happen in bif_scan */ 205 | } 206 | return SUCCESS; 207 | } 208 | 209 | static error bif_scan(lexer_t *lex) { 210 | /* Scan a single token from a BIF file */ 211 | 212 | int ch = 0, prev, err; 213 | bool esc = false; 214 | 215 | /* Skip white chars and comments */ 216 | for (;;) { 217 | prev = ch; 218 | ch = fgetc(lex->file); 219 | 220 | if (ch < 0) { 221 | lex->type = TOKEN_EOF; 222 | return SUCCESS; 223 | } else if (prev == '/') { 224 | if (ch == '/' || ch == '*') { 225 | /* Update the pos before reading the comment */ 226 | update_pos(lex, ch); 227 | 228 | if ((err = bif_scan_comment(lex, ch))) 229 | return err; 230 | 231 | /* Replace ch with a more neutral char than '/' and '*' */ 232 | ch = ch == '/' ? '\n' : ' '; 233 | } else { 234 | /* The char after initial '/' didn't start a comment, 235 | so pretend it wasn't read */ 236 | ungetc(ch, lex->file); 237 | ch = prev; 238 | break; 239 | } 240 | } else if (prev == '*' && ch == '/') { 241 | perrorf(lex, "comment end without start\n"); 242 | return ERROR_BIF_LEXER; 243 | } else if (ch == '/' || ch == '*') { 244 | /* Ignore a single '/' as it might start a comment 245 | '*' might end a comment that didn't start */ 246 | ; 247 | } else if (!isspace(ch)) { 248 | break; 249 | } 250 | 251 | update_pos(lex, ch); 252 | } 253 | 254 | lex->len = 0; 255 | 256 | update_pos(lex, ch); 257 | if ((err = append_token(lex, ch))) 258 | return err; 259 | 260 | if (strchr(special_chars, ch)) { 261 | /* Parse a special character */ 262 | lex->type = ch; /* This is why ASCII is skipped in the enum */ 263 | } else if (ch == '\"') { 264 | /* Scan a string delimited by " */ 265 | 266 | /* Ignore the leading quotation mark */ 267 | lex->len = 0; 268 | 269 | for (;;) { 270 | ch = fgetc(lex->file); 271 | 272 | if (ch < 0) { 273 | perrorf(lex, "file ended while scanning a string\n"); 274 | return ERROR_BIF_LEXER; 275 | } else if (ch == '\\') { 276 | esc = true; 277 | continue; 278 | } else if (ch == '\"' && !esc) { 279 | lex->type = TOKEN_NAME; 280 | break; 281 | } else if (esc) { 282 | perrorf(lex, "only escape for '\"' char is supported\n"); 283 | } 284 | 285 | update_pos(lex, ch); 286 | if ((err = append_token(lex, ch))) 287 | return err; 288 | } 289 | } else { 290 | /* Scan a string being defined without " marks */ 291 | for (;;) { 292 | ch = fgetc(lex->file); 293 | 294 | if (ch < 0 || strchr(special_chars, ch) || isspace(ch)) { 295 | ungetc(ch, lex->file); /* we don't want that char */ 296 | lex->type = TOKEN_NAME; 297 | break; 298 | } 299 | 300 | update_pos(lex, ch); 301 | if ((err = append_token(lex, ch))) 302 | return err; 303 | } 304 | } 305 | 306 | return SUCCESS; 307 | } 308 | 309 | static inline error bif_consume(lexer_t *lex, int type) { 310 | /* Read next token if its of the assumed type */ 311 | error err; 312 | 313 | if (lex->type != type) 314 | return ERROR_BIF_PARSER; 315 | if ((err = bif_scan(lex))) 316 | return err; 317 | return SUCCESS; 318 | } 319 | 320 | static inline error bif_expect(lexer_t *lex, int type) { 321 | /* Throw errors if the next token is unexpected */ 322 | error err = bif_consume(lex, type); 323 | 324 | if (err == ERROR_BIF_PARSER) 325 | perrorf(lex, "expected %s, got %s\n", get_token_name(type), get_token_name(lex->type)); 326 | return err; 327 | } 328 | 329 | static error bif_parse_file(lexer_t *lex, bif_cfg_t *cfg, bif_node_t *node) { 330 | error err; 331 | 332 | node->load = 0; 333 | node->offset = 0; 334 | node->bootloader = 0; 335 | node->fsbl_config = 0; 336 | node->pmufw_image = 0; 337 | node->exception_level = BOOTROM_PART_ATTR_EXC_LVL_EL0; 338 | node->partition_owner = BOOTROM_PART_ATTR_OWNER_FSBL; 339 | node->destination_cpu = BOOTROM_PART_ATTR_DEST_CPU_NONE; 340 | node->destination_device = BOOTROM_PART_ATTR_DEST_DEV_NONE; 341 | node->is_file = 1; 342 | 343 | /* Parse the attribute list if it's present */ 344 | if (!bif_consume(lex, '[')) { 345 | /* If the bracket was opened, at least one attribute must be present */ 346 | 347 | /* Ignore the return value as the initial comma is optional */ 348 | bif_consume(lex, ','); 349 | 350 | do { 351 | if ((err = bif_parse_attribute(lex, cfg, node))) 352 | return err; 353 | } while (!bif_consume(lex, ',')); 354 | 355 | if ((err = bif_expect(lex, ']'))) 356 | return err; 357 | } 358 | 359 | /* Expect a filename */ 360 | if (lex->type != TOKEN_NAME) 361 | return bif_expect(lex, TOKEN_NAME); 362 | strcpy(node->fname, lex->buffer); 363 | if ((err = bif_consume(lex, TOKEN_NAME))) 364 | return err; 365 | 366 | return SUCCESS; 367 | } 368 | 369 | static error bif_parse_attribute(lexer_t *lex, bif_cfg_t *cfg, bif_node_t *node) { 370 | error err; 371 | char *key = NULL, *value = NULL; 372 | 373 | /* Parse an attribute name */ 374 | if (lex->type != TOKEN_NAME) 375 | return bif_expect(lex, TOKEN_NAME); 376 | key = malloc(lex->len); 377 | strcpy(key, lex->buffer); 378 | if ((err = bif_consume(lex, TOKEN_NAME))) 379 | return err; 380 | 381 | /* Parse the attribute value if it was present */ 382 | if (!bif_consume(lex, '=')) { 383 | if (lex->type != TOKEN_NAME) 384 | return bif_expect(lex, TOKEN_NAME); 385 | value = malloc(lex->len); 386 | strcpy(value, lex->buffer); 387 | if ((err = bif_consume(lex, TOKEN_NAME))) 388 | return err; 389 | } 390 | 391 | /* If the value wasn't present, it stays NULL */ 392 | err = bif_node_set_attr(lex, cfg, node, key, value); 393 | 394 | free(key); 395 | free(value); 396 | 397 | return err; 398 | } 399 | 400 | error bif_parse(const char *fname, bif_cfg_t *cfg) { 401 | error err; 402 | lexer_t lex; 403 | bif_node_t node; 404 | 405 | /* Initialize the lexer */ 406 | if ((err = init_lexer(&lex, fname))) 407 | return err; 408 | 409 | /* First parse the name */ 410 | if ((err = bif_expect(&lex, TOKEN_NAME))) 411 | return err; 412 | if ((err = bif_expect(&lex, ':'))) 413 | return err; 414 | if ((err = bif_expect(&lex, '{'))) 415 | return err; 416 | /* Parse the file list */ 417 | do { 418 | if ((err = bif_parse_file(&lex, cfg, &node))) 419 | return err; 420 | if ((err = bif_cfg_add_node(cfg, &node))) 421 | return err; 422 | } while (lex.type == TOKEN_NAME || lex.type == '['); 423 | if ((err = bif_expect(&lex, '}'))) 424 | return err; 425 | 426 | deinit_lexer(&lex); 427 | 428 | return SUCCESS; 429 | } 430 | 431 | error bif_node_set_attr( 432 | lexer_t *lex, bif_cfg_t *cfg, bif_node_t *node, char *attr_name, char *value) { 433 | uint32_t mask; 434 | /* TODO: parser errors on wrong scans */ 435 | 436 | if (strcmp(attr_name, "bootloader") == 0) { 437 | node->bootloader = 0xFF; 438 | return SUCCESS; 439 | } 440 | 441 | if (strcmp(attr_name, "load") == 0) { 442 | if (!value) { 443 | perrorf(lex, "the \"%s\" attribute requires an argument\n", attr_name); 444 | return ERROR_BIF_PARSER; 445 | } 446 | if (sscanf(value, "0x%08x", &(node->load)) < 1) { 447 | perrorf(lex, "the value \"%s\" in an improper format, expected '0xhhhhhhhh' form\n", value); 448 | return ERROR_BIF_PARSER; 449 | } 450 | return SUCCESS; 451 | } 452 | 453 | if (strcmp(attr_name, "offset") == 0) { 454 | if (!value) { 455 | perrorf(lex, "the \"%s\" attribute requires an argument\n", attr_name); 456 | return ERROR_BIF_PARSER; 457 | } 458 | if (sscanf(value, "0x%08x", &(node->offset)) < 1) { 459 | perrorf(lex, "the value \"%s\" in an improper format, expected '0xhhhhhhhh' form\n", value); 460 | return ERROR_BIF_PARSER; 461 | } 462 | return SUCCESS; 463 | } 464 | 465 | if (strcmp(attr_name, "partition_owner") == 0) { 466 | if (!value) { 467 | perrorf(lex, "the \"%s\" attribute requires an argument\n", attr_name); 468 | return ERROR_BIF_PARSER; 469 | } 470 | mask = map_name_to_mask(bootrom_part_attr_owner_names, value); 471 | if (mask == NOMASK) { 472 | perrorf(lex, "value: \"%s\" not supported for the \"%s\" attribute\n", value, attr_name); 473 | return ERROR_BIF_UNSUPPORTED_VAL; 474 | } 475 | return SUCCESS; 476 | } 477 | 478 | /* Only handle these for zynqmp arch */ 479 | if (cfg->arch & BIF_ARCH_ZYNQMP) { 480 | if (strcmp(attr_name, "fsbl_config") == 0) { 481 | node->fsbl_config = 0xFF; 482 | 483 | /* This attribute does not refer to file */ 484 | node->is_file = 0x00; 485 | return SUCCESS; 486 | } 487 | 488 | if (strcmp(attr_name, "pmufw_image") == 0) { 489 | node->pmufw_image = 0xFF; 490 | return SUCCESS; 491 | } 492 | 493 | if (strcmp(attr_name, "destination_device") == 0) { 494 | if (!value) { 495 | perrorf(lex, "the \"%s\" attribute requires an argument\n", attr_name); 496 | return ERROR_BIF_PARSER; 497 | } 498 | mask = map_name_to_mask(bootrom_part_attr_dest_dev_names, value); 499 | if (mask == NOMASK) { 500 | perrorf(lex, "value: \"%s\" not supported for the \"%s\" attribute\n", value, attr_name); 501 | return ERROR_BIF_UNSUPPORTED_VAL; 502 | } 503 | node->destination_device = mask; 504 | return SUCCESS; 505 | } 506 | 507 | if (strcmp(attr_name, "destination_cpu") == 0) { 508 | if (!value) { 509 | perrorf(lex, "the \"%s\" attribute requires an argument\n", attr_name); 510 | return ERROR_BIF_PARSER; 511 | } 512 | mask = map_name_to_mask(bootrom_part_attr_dest_cpu_names, value); 513 | if (mask == NOMASK) { 514 | perrorf(lex, "value: \"%s\" not supported for the \"%s\" attribute\n", value, attr_name); 515 | return ERROR_BIF_UNSUPPORTED_VAL; 516 | } 517 | node->destination_cpu = mask; 518 | return SUCCESS; 519 | } 520 | 521 | if (strcmp(attr_name, "exception_level") == 0) { 522 | if (!value) { 523 | perrorf(lex, "the \"%s\" attribute requires an argument\n", attr_name); 524 | return ERROR_BIF_PARSER; 525 | } 526 | mask = map_name_to_mask(bootrom_part_attr_exc_lvl_names, value); 527 | if (mask == NOMASK) { 528 | perrorf(lex, "value: \"%s\" not supported for the \"%s\" attribute\n", value, attr_name); 529 | return ERROR_BIF_UNSUPPORTED_VAL; 530 | } 531 | node->exception_level = mask; 532 | return SUCCESS; 533 | } 534 | } 535 | 536 | perrorf(lex, "node attribute not supported: \"%s\"\n", attr_name); 537 | return ERROR_BIF_UNSUPPORTED_ATTR; 538 | } 539 | 540 | error bif_cfg_add_node(bif_cfg_t *cfg, bif_node_t *node) { 541 | uint16_t pos; 542 | bif_node_t tmp_node; 543 | 544 | /* Check if initialized */ 545 | if (cfg->nodes_avail == 0) { 546 | errorf("the BIF config is not initialized\n"); 547 | return ERROR_BIF_UNINITIALIZED; 548 | } 549 | 550 | pos = cfg->nodes_num; 551 | cfg->nodes[pos] = *node; 552 | 553 | /* Move nodes without offset to the top */ 554 | while (pos >= 1 && cfg->nodes[pos - 1].offset && !cfg->nodes[pos].offset) { 555 | tmp_node = cfg->nodes[pos - 1]; 556 | cfg->nodes[pos - 1] = cfg->nodes[pos]; 557 | cfg->nodes[pos] = tmp_node; 558 | pos--; 559 | } 560 | 561 | pos = cfg->nodes_num; 562 | 563 | /* Move special nodes to the top as well */ 564 | while (pos && (cfg->nodes[pos].fsbl_config || cfg->nodes[pos].pmufw_image)) { 565 | tmp_node = cfg->nodes[pos - 1]; 566 | cfg->nodes[pos - 1] = cfg->nodes[pos]; 567 | cfg->nodes[pos] = tmp_node; 568 | pos--; 569 | } 570 | 571 | pos = cfg->nodes_num; 572 | 573 | /* Sort nodes via offset */ 574 | while (pos >= 1 && cfg->nodes[pos - 1].offset > cfg->nodes[pos].offset) { 575 | tmp_node = cfg->nodes[pos - 1]; 576 | cfg->nodes[pos - 1] = cfg->nodes[pos]; 577 | cfg->nodes[pos] = tmp_node; 578 | pos--; 579 | } 580 | 581 | (cfg->nodes_num)++; 582 | 583 | /* Allocate more space if needed */ 584 | if (cfg->nodes_num >= cfg->nodes_avail) { 585 | cfg->nodes_avail *= 2; 586 | cfg->nodes = realloc(cfg->nodes, sizeof(bif_node_t) * cfg->nodes_avail); 587 | if (!cfg->nodes) { 588 | return ERROR_NOMEM; 589 | } 590 | } 591 | return SUCCESS; 592 | } 593 | -------------------------------------------------------------------------------- /src/bif.h: -------------------------------------------------------------------------------- 1 | #ifndef BIF_PARSER_H 2 | #define BIF_PARSER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #define BIF_ARCH_ZYNQ (1 << 0) 12 | #define BIF_ARCH_ZYNQMP (1 << 1) 13 | 14 | enum token_type 15 | { 16 | TOKEN_EOF = 0, 17 | 18 | TOKEN_UNKNOWN = 256, /* skip ASCII */ 19 | TOKEN_NAME, 20 | }; 21 | 22 | typedef struct lexer_t { 23 | FILE *file; /* the file being parsed */ 24 | char *fname; 25 | 26 | int line, column; 27 | 28 | int type; /* type of the last token read */ 29 | struct { 30 | char *buffer; /* tokens of the token */ 31 | int len, cap; /* current length and allocated size */ 32 | }; 33 | } lexer_t; 34 | 35 | typedef struct bif_node_t { 36 | char fname[PATH_MAX]; 37 | 38 | /* supported common attributes */ 39 | uint8_t bootloader; /* boolean */ 40 | uint32_t load; 41 | uint32_t offset; 42 | uint32_t partition_owner; 43 | 44 | /* supported zynqmp attributes */ 45 | uint8_t fsbl_config; /* boolean */ 46 | uint8_t pmufw_image; /* boolean */ 47 | uint32_t destination_device; 48 | uint32_t destination_cpu; 49 | uint32_t exception_level; 50 | 51 | /* special, non-bootgen features */ 52 | uint8_t is_file; /* for now equal to !fsbl_config */ 53 | uint8_t numbits; 54 | } bif_node_t; 55 | 56 | typedef struct bif_cfg_t { 57 | uint8_t arch; 58 | 59 | uint16_t nodes_num; 60 | uint16_t nodes_avail; 61 | 62 | bif_node_t *nodes; 63 | } bif_cfg_t; 64 | 65 | error init_bif_cfg(bif_cfg_t *cfg); 66 | error deinit_bif_cfg(bif_cfg_t *cfg); 67 | 68 | error bif_cfg_add_node(bif_cfg_t *cfg, bif_node_t *node); 69 | error bif_node_set_attr( 70 | lexer_t *lex, bif_cfg_t *cfg, bif_node_t *node, char *attr_name, char *value); 71 | 72 | error bif_parse(const char *fname, bif_cfg_t *cfg); 73 | 74 | #endif /* BIF_PARSER_H */ 75 | -------------------------------------------------------------------------------- /src/bootrom.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013-2021, Antmicro Ltd 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 | * * Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 10 | * * 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" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | /* clang-format off */ 46 | mask_name_t bootrom_part_attr_owner_names[] = { 47 | {"fsbl", BOOTROM_PART_ATTR_OWNER_FSBL, NULL}, 48 | {"uboot", BOOTROM_PART_ATTR_OWNER_UBOOT, NULL}, 49 | {0}, 50 | }; 51 | 52 | mask_name_t bootrom_part_attr_rsa_used_names[] = { 53 | {"used", BOOTROM_PART_ATTR_RSA_USED, NULL}, 54 | {"not used", BOOTROM_PART_ATTR_RSA_NOT_USED, NULL}, 55 | {0}, 56 | }; 57 | 58 | mask_name_t bootrom_part_attr_dest_cpu_names[] = { 59 | {"none", BOOTROM_PART_ATTR_DEST_CPU_NONE, NULL}, 60 | {"a53-0", BOOTROM_PART_ATTR_DEST_CPU_A53_0, NULL}, 61 | {"a53-1", BOOTROM_PART_ATTR_DEST_CPU_A53_1, NULL}, 62 | {"a53-2", BOOTROM_PART_ATTR_DEST_CPU_A53_2, NULL}, 63 | {"a53-3", BOOTROM_PART_ATTR_DEST_CPU_A53_3, NULL}, 64 | {"r5-0", BOOTROM_PART_ATTR_DEST_CPU_R5_0, NULL}, 65 | {"r5-1", BOOTROM_PART_ATTR_DEST_CPU_R5_1, NULL}, 66 | {"r5-lockstep", BOOTROM_PART_ATTR_DEST_CPU_R5_L, NULL}, 67 | {0}, 68 | }; 69 | 70 | mask_name_t bootrom_part_attr_encryption_names[] = { 71 | {"yes", BOOTROM_PART_ATTR_ENCRYPTION_YES, NULL}, 72 | {"no", BOOTROM_PART_ATTR_ENCRYPTION_NO, NULL}, 73 | {0}, 74 | }; 75 | 76 | mask_name_t bootrom_part_attr_dest_dev_names[] = { 77 | {"none", BOOTROM_PART_ATTR_DEST_DEV_NONE, NULL}, 78 | {"ps", BOOTROM_PART_ATTR_DEST_DEV_PS, NULL}, 79 | {"pl", BOOTROM_PART_ATTR_DEST_DEV_PL, NULL}, 80 | {"int", BOOTROM_PART_ATTR_DEST_DEV_INT, NULL}, 81 | {0}, 82 | }; 83 | 84 | mask_name_t bootrom_part_attr_a5x_exec_s_names[] = { 85 | {"32-bit", BOOTROM_PART_ATTR_A5X_EXEC_S_32, NULL}, 86 | {"64-bit", BOOTROM_PART_ATTR_A5X_EXEC_S_64, NULL}, 87 | {0}, 88 | }; 89 | 90 | mask_name_t bootrom_part_attr_exc_lvl_names[] = { 91 | {"el-0", BOOTROM_PART_ATTR_EXC_LVL_EL0, NULL}, 92 | {"el-1", BOOTROM_PART_ATTR_EXC_LVL_EL1, NULL}, 93 | {"el-2", BOOTROM_PART_ATTR_EXC_LVL_EL2, NULL}, 94 | {"el-3", BOOTROM_PART_ATTR_EXC_LVL_EL3, NULL}, 95 | {0}, 96 | }; 97 | 98 | mask_name_t bootrom_part_attr_trust_zone_names[] = { 99 | {"yes", BOOTROM_PART_ATTR_TRUST_ZONE_YES, NULL}, 100 | {"no", BOOTROM_PART_ATTR_TRUST_ZONE_NO, NULL}, 101 | {0}, 102 | }; 103 | 104 | mask_name_t bootrom_part_attr_mask_names[] = { 105 | {"Owner", BOOTROM_PART_ATTR_OWNER_MASK, bootrom_part_attr_owner_names}, 106 | {"RSA", BOOTROM_PART_ATTR_RSA_USED_MASK, bootrom_part_attr_rsa_used_names}, 107 | {"Destination CPU", BOOTROM_PART_ATTR_DEST_CPU_MASK, bootrom_part_attr_dest_cpu_names}, 108 | {"Encryption", BOOTROM_PART_ATTR_ENCRYPTION_MASK, bootrom_part_attr_encryption_names}, 109 | {"Destination Device", BOOTROM_PART_ATTR_DEST_DEV_MASK, bootrom_part_attr_dest_dev_names}, 110 | {"A5x Execution State", BOOTROM_PART_ATTR_A5X_EXEC_S_MASK, bootrom_part_attr_a5x_exec_s_names}, 111 | {"Exception Level", BOOTROM_PART_ATTR_EXC_LVL_MASK, bootrom_part_attr_exc_lvl_names}, 112 | {"Trust Zone", BOOTROM_PART_ATTR_TRUST_ZONE_MASK, bootrom_part_attr_trust_zone_names}, 113 | {0} 114 | }; 115 | /* clang-format on */ 116 | 117 | uint32_t map_name_to_mask(mask_name_t mask_names[], char *name) { 118 | int i; 119 | 120 | for (i = 0; mask_names[i].name; i++) 121 | if (strcmp(mask_names[i].name, name) == 0) 122 | return mask_names[i].mask; 123 | return NOMASK; 124 | } 125 | 126 | char *map_mask_to_name(mask_name_t mask_names[], uint32_t mask) { 127 | int i; 128 | 129 | for (i = 0; mask_names[i].name; i++) 130 | if (mask_names[i].mask == mask) 131 | return mask_names[i].name; 132 | return "INVALID"; 133 | } 134 | 135 | /* Returns the offset by which the addr parameter should be moved 136 | * and partition header info via argument pointers. 137 | * The regular return value is the error code. */ 138 | error append_file_to_image(uint32_t *addr, 139 | bootrom_ops_t *bops, 140 | bootrom_offs_t *offs, 141 | bif_node_t node, 142 | bootrom_partition_hdr_t *part_hdr, 143 | uint32_t *img_size) { 144 | uint32_t file_header; 145 | struct stat cfile_stat; 146 | FILE *cfile; 147 | uint32_t elf_load; 148 | uint32_t elf_entry; 149 | uint8_t elf_nbits; 150 | uint32_t img_size_init; 151 | linux_image_header_t linux_img; 152 | error err; 153 | 154 | /* Initialize header with zeroes */ 155 | memset(part_hdr, 0x0, sizeof(*part_hdr)); 156 | 157 | img_size_init = *img_size; 158 | *img_size = 0; 159 | 160 | if (stat(node.fname, &cfile_stat)) { 161 | errorf("could not stat file: %s\n", node.fname); 162 | return ERROR_BOOTROM_NOFILE; 163 | } 164 | if (!S_ISREG(cfile_stat.st_mode)) { 165 | errorf("not a regular file: %s\n", node.fname); 166 | return ERROR_BOOTROM_NOFILE; 167 | } 168 | cfile = fopen(node.fname, "rb"); 169 | 170 | if (cfile == NULL) { 171 | errorf("could not open file: %s\n", node.fname); 172 | return ERROR_BOOTROM_NOFILE; 173 | } 174 | 175 | /* Check file format */ 176 | fread(&file_header, 1, sizeof(file_header), cfile); 177 | 178 | switch (file_header) { 179 | case FILE_MAGIC_ELF: 180 | /* Init elf file (img_size_init is non-zero for a bootloader if there 181 | * is PMU firmware waiting). File size is used as result size limit 182 | * as estimate_boot_image_size() makes that same assumption when 183 | * allocating the memory area for the boot image */ 184 | err = elf_append(addr + img_size_init / sizeof(uint32_t), 185 | node.fname, 186 | cfile_stat.st_size, 187 | img_size, 188 | &elf_nbits, 189 | &elf_load, 190 | &elf_entry); 191 | if (err) { 192 | errorf("ELF file reading failed\n"); 193 | 194 | /* Close the file */ 195 | fclose(cfile); 196 | return err; 197 | } 198 | 199 | /* Init partition header, the img_size_init is non-zero only if this file 200 | * is a bootloader, and we have the PMU firmware waiting. In this case, the 201 | * partition header has to take the PMU size into account. That's why the 202 | * 'init' size is added before initializing the partition header and 203 | * subtracted after the initialization is done */ 204 | *img_size += img_size_init; 205 | bops->init_part_hdr_elf(part_hdr, &node, img_size, elf_load, elf_entry, elf_nbits); 206 | *img_size -= img_size_init; 207 | 208 | break; 209 | case FILE_MAGIC_XILINXBIT_0: 210 | /* Verify file */ 211 | if ((err = bitstream_verify(cfile))) { 212 | errorf("not a valid bitstream file: %s.\n", node.fname); 213 | 214 | /* Close the file */ 215 | fclose(cfile); 216 | return err; 217 | } 218 | 219 | /* It matches, append it to the image */ 220 | err = bitstream_append(addr, cfile, img_size); 221 | 222 | if (err) 223 | return err; 224 | 225 | /* Init partition header */ 226 | bops->init_part_hdr_bitstream(part_hdr, &node); 227 | 228 | break; 229 | case FILE_MAGIC_LINUX: 230 | fseek(cfile, 0, SEEK_SET); 231 | fread(&linux_img, 1, sizeof(linux_img), cfile); 232 | 233 | fseek(cfile, 0, SEEK_SET); 234 | *img_size = fread(addr, 1, cfile_stat.st_size, cfile); 235 | 236 | /* Init partition header */ 237 | bops->init_part_hdr_linux(part_hdr, &node, &linux_img); 238 | 239 | break; 240 | case FILE_MAGIC_DTB: 241 | fseek(cfile, 0, SEEK_SET); 242 | *img_size = fread(addr, 1, cfile_stat.st_size, cfile); 243 | 244 | bops->init_part_hdr_dtb(part_hdr, &node); 245 | break; 246 | default: /* Treat as a binary file */ 247 | 248 | fseek(cfile, 0, SEEK_SET); 249 | *img_size = fread(addr, 1, cfile_stat.st_size, cfile); 250 | 251 | bops->init_part_hdr_default(part_hdr, &node); 252 | }; 253 | 254 | *img_size += img_size_init; 255 | /* Convert size to 32bit words */ 256 | *img_size = (*img_size + 3) / 4; 257 | 258 | /* Finish partition header */ 259 | bops->finish_part_hdr(part_hdr, img_size, offs); 260 | 261 | /* Close the file */ 262 | fclose(cfile); 263 | 264 | return SUCCESS; 265 | } 266 | 267 | /* Returns an estimation of the output image size */ 268 | uint32_t estimate_boot_image_size(bif_cfg_t *bif_cfg) { 269 | uint8_t i; 270 | uint32_t estimated_size; 271 | struct stat st_file; 272 | 273 | /* TODO the offset used hereshould be more 274 | * dependent on the target architecture */ 275 | estimated_size = BOOTROM_BINS_OFF; 276 | 277 | for (i = 0; i < bif_cfg->nodes_num; i++) { 278 | /* Skip if param will not include a file */ 279 | if (!bif_cfg->nodes[i].is_file) 280 | continue; 281 | 282 | if (stat(bif_cfg->nodes[i].fname, &st_file)) { 283 | errorf("could not stat %s\n", bif_cfg->nodes[i].fname); 284 | return 0; 285 | } 286 | 287 | if (bif_cfg->nodes[i].offset) 288 | estimated_size = bif_cfg->nodes[i].offset; 289 | 290 | estimated_size += st_file.st_size; 291 | } 292 | 293 | /* Add 3% to make sure padding is covered */ 294 | estimated_size *= 1.03; 295 | 296 | return estimated_size; 297 | } 298 | 299 | /* Returns total size of the created image via the last argument. 300 | * The regular return value is the error code. */ 301 | error create_boot_image(uint32_t *img_ptr, 302 | bif_cfg_t *bif_cfg, 303 | bootrom_ops_t *bops, 304 | uint32_t *total_size) { 305 | /* declare variables */ 306 | bootrom_hdr_t hdr; 307 | bootrom_offs_t offs; 308 | uint16_t i, j, f; 309 | error err; 310 | int img_term_n = 0; 311 | uint8_t img_name[BOOTROM_IMG_MAX_NAME_LEN]; 312 | uint8_t pmufw_img[BOOTROM_PMUFW_MAX_SIZE]; 313 | uint32_t pmufw_img_load; 314 | uint32_t pmufw_img_entry; 315 | uint32_t pmufw_img_size; 316 | uint8_t pmufw_img_nbits; 317 | struct stat pmufile_stat; 318 | uint8_t part_hdr_count; 319 | 320 | if (bops->append_null_part) 321 | part_hdr_count = bif_cfg->nodes_num + 1; 322 | else 323 | part_hdr_count = bif_cfg->nodes_num; 324 | 325 | bootrom_partition_hdr_t part_hdr[part_hdr_count]; 326 | bootrom_img_hdr_t img_hdr[bif_cfg->nodes_num]; 327 | uint32_t img_size; 328 | 329 | bootrom_img_hdr_tab_t img_hdr_tab; 330 | 331 | img_hdr_tab.hdrs_count = 0; 332 | for (i = 0; i < bif_cfg->nodes_num; i++) { 333 | if (bif_cfg->nodes[i].is_file && !bif_cfg->nodes[i].pmufw_image) 334 | img_hdr_tab.hdrs_count++; 335 | } 336 | 337 | /* Initialize offsets */ 338 | bops->init_offs(img_ptr, img_hdr_tab.hdrs_count, &offs); 339 | 340 | /* Initialize header */ 341 | bops->init_header(&hdr, &offs); 342 | 343 | /* Iterate through the images and write them */ 344 | for (i = 0, f = 0; i < bif_cfg->nodes_num; i++) { 345 | /* i - index of all bif nodes 346 | * f - index of bif node excluding non-file ones */ 347 | 348 | /* Skip if param will not include a file */ 349 | if (!bif_cfg->nodes[i].is_file) 350 | continue; 351 | 352 | if (bif_cfg->nodes[i].pmufw_image) { 353 | /* For now just assume the firmware is always the maximum length */ 354 | hdr.pmufw_len = BOOTROM_PMUFW_MAX_SIZE; 355 | hdr.pmufw_total_len = hdr.pmufw_len; 356 | 357 | /* Prepare the array for the firmware */ 358 | memset(pmufw_img, 0x00, sizeof(pmufw_img)); 359 | 360 | /* Open pmu file */ 361 | if (stat(bif_cfg->nodes[i].fname, &pmufile_stat)) { 362 | errorf("could not stat file: %s\n", bif_cfg->nodes[i].fname); 363 | return ERROR_BOOTROM_NOFILE; 364 | } 365 | if (!S_ISREG(pmufile_stat.st_mode)) { 366 | errorf("not a regular file: %s\n", bif_cfg->nodes[i].fname); 367 | return ERROR_BOOTROM_NOFILE; 368 | } 369 | err = elf_append(pmufw_img, 370 | bif_cfg->nodes[i].fname, 371 | hdr.pmufw_len, 372 | &pmufw_img_size, 373 | &pmufw_img_nbits, 374 | &pmufw_img_load, 375 | &pmufw_img_entry); 376 | if (err) { 377 | errorf("failed to parse ELF file: %s\n", bif_cfg->nodes[i].fname); 378 | return ERROR_BOOTROM_ELF; 379 | } 380 | continue; 381 | } 382 | 383 | if (bif_cfg->nodes[i].offset != 0 && 384 | (img_ptr + bif_cfg->nodes[i].offset / sizeof(uint32_t)) < offs.coff) { 385 | errorf("binary sections overlapping.\n"); 386 | return ERROR_BOOTROM_SEC_OVERLAP; 387 | } else { 388 | /* Add 0xFF padding until this binary */ 389 | while (offs.coff < (bif_cfg->nodes[i].offset / sizeof(uint32_t) + img_ptr)) { 390 | memset(offs.coff, 0xFF, sizeof(uint32_t)); 391 | offs.coff++; 392 | } 393 | } 394 | 395 | /* Append file content to memory */ 396 | if (bif_cfg->nodes[i].bootloader && hdr.pmufw_len) { 397 | /* This is a bootloader image and we have a pmu fw waiting */ 398 | memcpy(offs.coff, pmufw_img, hdr.pmufw_len); 399 | img_size = hdr.pmufw_len; 400 | } else { 401 | img_size = 0; 402 | } 403 | 404 | err = 405 | append_file_to_image(offs.coff, bops, &offs, bif_cfg->nodes[i], &(part_hdr[f]), &img_size); 406 | 407 | if (err) { 408 | return err; 409 | } 410 | 411 | /* Check if dealing with bootloader (size is in words - thus x 4) */ 412 | if (bif_cfg->nodes[i].bootloader) { 413 | bops->setup_fsbl_at_curr_off(&hdr, &offs, (part_hdr[f].pd_len * 4) - hdr.pmufw_len); 414 | } 415 | 416 | /* Update the offset, skip padding for the last image */ 417 | if (i == bif_cfg->nodes_num - 1) { 418 | offs.coff += part_hdr[f].pd_len; 419 | } else { 420 | offs.coff += img_size; 421 | } 422 | 423 | /* Create image headers for all of them */ 424 | img_hdr[f].part_count = 0x0; 425 | 426 | /* filling this field as a helper */ 427 | img_hdr[f].name_len = strlen(basename(bif_cfg->nodes[i].fname)); 428 | 429 | /* Fill the name variable with zeroes */ 430 | memset(img_name, 0x0, BOOTROM_IMG_MAX_NAME_LEN); 431 | 432 | /* Temporarily read the name */ 433 | memcpy(img_name, basename(bif_cfg->nodes[i].fname), img_hdr[f].name_len); 434 | 435 | /* Calculate number of string terminators, this should be 32b 436 | * however if the name length is divisible by 4 the bootgen 437 | * binary makes it 64b and thats what we're going to do here */ 438 | if (strlen((char *) img_name) % 4 == 0) { 439 | img_term_n = 2; 440 | } else { 441 | img_term_n = 1; 442 | } 443 | 444 | /* Make the name len be divisible by 4 */ 445 | while (img_hdr[f].name_len % 4) 446 | img_hdr[f].name_len++; 447 | 448 | /* The name is packed in big-endian order. To reconstruct 449 | * the string, unpack 4 bytes at a time, reverse 450 | * the order, and concatenate. */ 451 | for (j = 0; j < img_hdr[f].name_len; j += 4) { 452 | img_hdr[f].name[j + 0] = img_name[j + 3]; 453 | img_hdr[f].name[j + 1] = img_name[j + 2]; 454 | img_hdr[f].name[j + 2] = img_name[j + 1]; 455 | img_hdr[f].name[j + 3] = img_name[j + 0]; 456 | } 457 | 458 | /* Append the actual terminators */ 459 | memset(&(img_hdr[f].name[img_hdr[f].name_len]), 0x00, img_term_n * sizeof(uint32_t)); 460 | 461 | /* Fill the rest with 0xFF padding */ 462 | for (j = img_hdr[f].name_len + img_term_n * sizeof(uint32_t); j < BOOTROM_IMG_MAX_NAME_LEN; 463 | j++) { 464 | img_hdr[f].name[j] = 0xFF; 465 | } 466 | 467 | /* Name length is not really the length of the name. 468 | * According to the documentation it is the value of the 469 | * actual partition count, however the bootgen binary 470 | * always sets this field to 1. */ 471 | img_hdr[f].name_len = 0x1; 472 | 473 | f++; 474 | } 475 | 476 | /* Create the image header table */ 477 | bops->init_img_hdr_tab(&img_hdr_tab, img_hdr, part_hdr, &offs); 478 | 479 | /* Copy the image header as all the fields should be filled by now */ 480 | memcpy(offs.hoff, &(img_hdr_tab), sizeof(img_hdr_tab)); 481 | 482 | /* Add 0xFF padding until partition header offset */ 483 | while ((uint32_t) (offs.poff - img_ptr) < offs.part_hdr_off / sizeof(uint32_t)) { 484 | memset(offs.poff, 0xFF, sizeof(uint32_t)); 485 | offs.poff++; 486 | } 487 | 488 | /* Add null partition at the end */ 489 | if (bops->append_null_part) { 490 | bootrom_partition_hdr_t *null_hdr = &part_hdr[img_hdr_tab.hdrs_count++]; 491 | 492 | memset(null_hdr, 0x0, sizeof(bootrom_partition_hdr_t)); 493 | null_hdr->checksum = 0xffffffff; 494 | } 495 | 496 | /* Write the partition headers */ 497 | for (i = 0; i < img_hdr_tab.hdrs_count; i++) { 498 | memcpy(offs.poff, &(part_hdr[i]), sizeof(part_hdr[i])); 499 | 500 | /* Partition header is aligned, so no padding needed */ 501 | offs.poff += sizeof(part_hdr[i]) / sizeof(uint32_t); 502 | } 503 | 504 | /* Recalculate partition hdr end offset/padding */ 505 | if (offs.part_hdr_end_off) { 506 | offs.part_hdr_end_off = BOOTROM_PART_HDR_OFF + 507 | (img_hdr_tab.hdrs_count * sizeof(struct bootrom_partition_hdr_t)) + 508 | BOOTROM_PART_HDR_END_PADD; 509 | } 510 | 511 | /* Add 0x00 padding until end of partition header */ 512 | while ((uint32_t) (offs.poff - img_ptr) < offs.part_hdr_end_off / sizeof(uint32_t)) { 513 | memset(offs.poff, 0x00, sizeof(uint32_t)); 514 | offs.poff++; 515 | } 516 | 517 | /* Add 0xFF padding until BOOTROM_BINS_OFF */ 518 | while ((uint32_t) (offs.poff - img_ptr) < offs.bins_off / sizeof(uint32_t)) { 519 | memset(offs.poff, 0xFF, sizeof(uint32_t)); 520 | offs.poff++; 521 | } 522 | 523 | /* Finally write the header to the image */ 524 | memcpy(img_ptr, &(hdr), sizeof(hdr)); 525 | 526 | *total_size = offs.coff - img_ptr; 527 | 528 | return SUCCESS; 529 | } 530 | -------------------------------------------------------------------------------- /src/bootrom.h: -------------------------------------------------------------------------------- 1 | #ifndef BOOTROM_H 2 | #define BOOTROM_H 3 | 4 | #include 5 | #include 6 | 7 | #define NOMASK 0xFFFFFFFF 8 | 9 | /* BootROM Header based on ug585 and ug1085 */ 10 | typedef struct bootrom_hdr_t { 11 | uint32_t interrupt_table[8]; 12 | uint32_t width_detect; 13 | uint32_t img_id; 14 | uint32_t encryption_status; 15 | union { 16 | uint32_t user_defined_0; 17 | uint32_t fsbl_defined_0; 18 | uint32_t fsbl_execution_addr; 19 | }; 20 | uint32_t src_offset; 21 | union { 22 | struct { 23 | uint32_t img_len; 24 | uint32_t reserved_0; /* set to 0 */ 25 | }; 26 | struct { 27 | uint32_t pmufw_len; 28 | uint32_t pmufw_total_len; 29 | }; 30 | }; 31 | union { 32 | uint32_t start_of_exec; 33 | uint32_t fsbl_img_len; 34 | }; 35 | uint32_t total_img_len; 36 | union { 37 | uint32_t reserved_1; 38 | uint32_t fsbl_target_cpu; 39 | }; 40 | uint32_t checksum; 41 | /* The rest of the header is different for zynq and zynqmp*/ 42 | union { 43 | /* This is the zynq part */ 44 | struct { 45 | union { 46 | uint32_t user_defined_zynq_0[21]; 47 | uint32_t fsbl_defined_zynq_0[21]; 48 | }; 49 | uint32_t reg_init_zynq[512]; 50 | union { 51 | uint32_t user_defined_zynq_1[8]; 52 | uint32_t fsbl_defined_zynq_1[8]; 53 | }; 54 | }; 55 | /* This is the zynqmp part */ 56 | struct { 57 | uint32_t obfuscated_key[8]; 58 | uint32_t reserved_zynqmp; 59 | union { 60 | uint32_t user_defined_zynqmp_0[12]; 61 | uint32_t fsbl_defined_zynqmp_0[12]; 62 | }; 63 | uint32_t sec_hdr_init_vec[3]; 64 | uint32_t obf_key_init_vec[3]; 65 | uint32_t reg_init_zynqmp[512]; 66 | /* 0xFF padding ?? */ 67 | uint32_t padding[2]; 68 | }; 69 | }; 70 | } bootrom_hdr_t; 71 | 72 | /* BootROM image header table based on ug821 and ug1137 */ 73 | typedef struct bootrom_img_hdr_tab_t { 74 | uint32_t version; 75 | uint32_t hdrs_count; 76 | uint32_t part_hdr_off; /* word offset to the partition header */ 77 | uint32_t part_img_hdr_off; /* word offset to first image header */ 78 | uint32_t auth_hdr_off; /* word offset to header authentication */ 79 | union { 80 | /* just padding for zynq */ 81 | uint32_t padding[11]; 82 | 83 | /* zynqmp additions */ 84 | struct { 85 | uint32_t boot_dev; 86 | uint32_t reserved[9]; 87 | uint32_t checksum; 88 | }; 89 | }; 90 | } bootrom_img_hdr_tab_t; 91 | 92 | /* BootROM partition header based on ug821 */ 93 | /* All offsets are relative to the start of the boot image */ 94 | typedef struct bootrom_partition_hdr_t { 95 | uint32_t pd_len; 96 | uint32_t ed_len; 97 | uint32_t total_len; 98 | 99 | uint32_t arch_specific[12]; 100 | 101 | uint32_t checksum; 102 | } bootrom_partition_hdr_t; 103 | 104 | /* Output image specific parameters */ 105 | #define BOOTROM_IMG_MAX_NAME_LEN 32 106 | #define BOOTROM_IMG_PADDING_SIZE 64 107 | 108 | /* BootROM image header based on ug821 */ 109 | typedef struct bootrom_img_hdr_t { 110 | uint32_t next_img_off; /* 0 if last */ 111 | uint32_t part_hdr_off; 112 | uint32_t part_count; /* always set to 0 */ 113 | /* Name length is not really the length of the name. 114 | * According to the documentation it is the value of the 115 | * actual partition count, however the bootgen binary 116 | * always sets this field to 1. */ 117 | uint32_t name_len; 118 | uint8_t name[BOOTROM_IMG_MAX_NAME_LEN]; 119 | uint8_t padding[16]; 120 | } bootrom_img_hdr_t; 121 | 122 | typedef struct linux_image_header_t { 123 | uint32_t magic; 124 | uint32_t hcrc; 125 | uint32_t time; 126 | uint32_t size; 127 | uint32_t load; 128 | uint32_t ep; 129 | uint32_t dcrc; 130 | uint8_t os; 131 | uint8_t arch; 132 | uint8_t type; 133 | uint8_t comp; 134 | uint8_t name[32]; 135 | } linux_image_header_t; 136 | 137 | /* attributes of the bootrom partition header */ 138 | #define BOOTROM_PART_ATTR_OWNER_OFF 16 139 | #define BOOTROM_PART_ATTR_OWNER_MASK (3 << BOOTROM_PART_ATTR_OWNER_OFF) 140 | #define BOOTROM_PART_ATTR_OWNER_FSBL (0 << BOOTROM_PART_ATTR_OWNER_OFF) 141 | #define BOOTROM_PART_ATTR_OWNER_UBOOT (1 << BOOTROM_PART_ATTR_OWNER_OFF) 142 | 143 | #define BOOTROM_PART_ATTR_RSA_USED_OFF 15 144 | #define BOOTROM_PART_ATTR_RSA_USED_MASK (1 << BOOTROM_PART_ATTR_RSA_USED_OFF) 145 | #define BOOTROM_PART_ATTR_RSA_USED (1 << BOOTROM_PART_ATTR_RSA_USED_OFF) 146 | #define BOOTROM_PART_ATTR_RSA_NOT_USED (0 << BOOTROM_PART_ATTR_RSA_USED_OFF) 147 | 148 | #define BOOTROM_PART_ATTR_DEST_CPU_OFF 8 149 | #define BOOTROM_PART_ATTR_DEST_CPU_MASK (7 << BOOTROM_PART_ATTR_DEST_CPU_OFF) 150 | #define BOOTROM_PART_ATTR_DEST_CPU_NONE (0 << BOOTROM_PART_ATTR_DEST_CPU_OFF) 151 | #define BOOTROM_PART_ATTR_DEST_CPU_A53_0 (1 << BOOTROM_PART_ATTR_DEST_CPU_OFF) 152 | #define BOOTROM_PART_ATTR_DEST_CPU_A53_1 (2 << BOOTROM_PART_ATTR_DEST_CPU_OFF) 153 | #define BOOTROM_PART_ATTR_DEST_CPU_A53_2 (3 << BOOTROM_PART_ATTR_DEST_CPU_OFF) 154 | #define BOOTROM_PART_ATTR_DEST_CPU_A53_3 (4 << BOOTROM_PART_ATTR_DEST_CPU_OFF) 155 | #define BOOTROM_PART_ATTR_DEST_CPU_R5_0 (5 << BOOTROM_PART_ATTR_DEST_CPU_OFF) 156 | #define BOOTROM_PART_ATTR_DEST_CPU_R5_1 (6 << BOOTROM_PART_ATTR_DEST_CPU_OFF) 157 | #define BOOTROM_PART_ATTR_DEST_CPU_R5_L (7 << BOOTROM_PART_ATTR_DEST_CPU_OFF) 158 | 159 | #define BOOTROM_PART_ATTR_ENCRYPTION_OFF 7 160 | #define BOOTROM_PART_ATTR_ENCRYPTION_MASK (1 << BOOTROM_PART_ATTR_ENCRYPTION_OFF) 161 | #define BOOTROM_PART_ATTR_ENCRYPTION_YES (1 << BOOTROM_PART_ATTR_ENCRYPTION_OFF) 162 | #define BOOTROM_PART_ATTR_ENCRYPTION_NO (0 << BOOTROM_PART_ATTR_ENCRYPTION_OFF) 163 | 164 | #define BOOTROM_PART_ATTR_DEST_DEV_OFF 4 165 | #define BOOTROM_PART_ATTR_DEST_DEV_MASK (7 << BOOTROM_PART_ATTR_DEST_DEV_OFF) 166 | #define BOOTROM_PART_ATTR_DEST_DEV_NONE (0 << BOOTROM_PART_ATTR_DEST_DEV_OFF) 167 | #define BOOTROM_PART_ATTR_DEST_DEV_PS (1 << BOOTROM_PART_ATTR_DEST_DEV_OFF) 168 | #define BOOTROM_PART_ATTR_DEST_DEV_PL (2 << BOOTROM_PART_ATTR_DEST_DEV_OFF) 169 | #define BOOTROM_PART_ATTR_DEST_DEV_INT (3 << BOOTROM_PART_ATTR_DEST_DEV_OFF) 170 | 171 | #define BOOTROM_PART_ATTR_A5X_EXEC_S_OFF 3 172 | #define BOOTROM_PART_ATTR_A5X_EXEC_S_MASK (1 << BOOTROM_PART_ATTR_A5X_EXEC_S_OFF) 173 | #define BOOTROM_PART_ATTR_A5X_EXEC_S_64 (0 << BOOTROM_PART_ATTR_A5X_EXEC_S_OFF) 174 | #define BOOTROM_PART_ATTR_A5X_EXEC_S_32 (1 << BOOTROM_PART_ATTR_A5X_EXEC_S_OFF) 175 | 176 | #define BOOTROM_PART_ATTR_EXC_LVL_OFF 1 177 | #define BOOTROM_PART_ATTR_EXC_LVL_MASK (3 << BOOTROM_PART_ATTR_EXC_LVL_OFF) 178 | #define BOOTROM_PART_ATTR_EXC_LVL_EL0 (0 << BOOTROM_PART_ATTR_EXC_LVL_OFF) 179 | #define BOOTROM_PART_ATTR_EXC_LVL_EL1 (1 << BOOTROM_PART_ATTR_EXC_LVL_OFF) 180 | #define BOOTROM_PART_ATTR_EXC_LVL_EL2 (2 << BOOTROM_PART_ATTR_EXC_LVL_OFF) 181 | #define BOOTROM_PART_ATTR_EXC_LVL_EL3 (3 << BOOTROM_PART_ATTR_EXC_LVL_OFF) 182 | 183 | #define BOOTROM_PART_ATTR_TRUST_ZONE_OFF 0 184 | #define BOOTROM_PART_ATTR_TRUST_ZONE_MASK (3 << BOOTROM_PART_ATTR_TRUST_ZONE_OFF) 185 | #define BOOTROM_PART_ATTR_TRUST_ZONE_YES (1 << BOOTROM_PART_ATTR_TRUST_ZONE_OFF) 186 | #define BOOTROM_PART_ATTR_TRUST_ZONE_NO (0 << BOOTROM_PART_ATTR_TRUST_ZONE_OFF) 187 | 188 | /* values taken from boot.bin generated with bootgen 189 | * so there is no understanding of them yet */ 190 | #define BOOTROM_INT_TABLE_DEFAULT 0xEAFFFFFE 191 | #define BOOTROM_RESERVED_1_RL 0x00000001 /* MUST be set to 0 but is not */ 192 | #define BOOTROM_RESERVED_ZMP_RL 0x01000020 /* The same as img version? */ 193 | 194 | /* user defined 0 / fsbl execution address */ 195 | #define BOOTROM_USER_0 0x01010000 /* used by zynq */ 196 | #define BOOTROM_FSBL_EXEC_ADDR 0xfffc0000 /* used by zynqmp */ 197 | 198 | /* PMU firmware related */ 199 | /* The maximum PMU FW size should be 0x00020000 (128kB) 200 | * according to the TRM, but for some reason bootgen uses 201 | * the value used below */ 202 | #define BOOTROM_PMUFW_MAX_SIZE 0x0001fae0 203 | 204 | /* these values are also taken from bootm.bin 205 | * however these might not be the only valid 206 | * option - that is, in theory they could be 207 | * dynamic */ 208 | #define BOOTROM_IMG_HDR_OFF 0x000008c0 209 | #define BOOTROM_PART_HDR_OFF 0x00000c80 210 | #define BOOTROM_PART_HDR_END_PADD 0x0000003c 211 | #define BOOTROM_BINS_OFF 0x00001700 212 | 213 | /* The same defines as above but for zynqmp */ 214 | #define BOOTROM_BINS_OFF_ZMP 0x00000b40 215 | 216 | /* values from the documentation */ 217 | #define BOOTROM_WIDTH_DETECT 0xAA995566 218 | #define BOOTROM_IMG_ID "XNLX" 219 | #define BOOTROM_ENCRYPTED_EFUSE 0xA5C3C5A3 220 | #define BOOTROM_ENCRYPTED_OEFUSE 0xA5C3C5A7 /* obfuscated key in eFUSE */ 221 | #define BOOTROM_ENCRYPTED_RAMKEY 0x3A5C3C5A /* bram */ 222 | #define BOOTROM_ENCRYPTED_OBHDR 0xA35C7CA5 /* obfuscated key in boot hdr */ 223 | #define BOOTROM_ENCRYPTED_NONE 0x00000000 /* anything but efuse, ramkey*/ 224 | #define BOOTROM_MIN_SRC_OFFSET 0x000008C0 225 | #define BOOTROM_RESERVED_0 0x00000000 /* MUST be set to 0 */ 226 | #define BOOTROM_RESERVED_1 0x00000000 /* MUST be set to 0 */ 227 | 228 | #define BOOTROM_IMG_VERSION 0x01020000 229 | 230 | #define BOOTROM_IMG_HDR_BOOT_SAME 0x0 231 | #define BOOTROM_IMG_HDR_BOOT_QSPI 0x1 232 | #define BOOTROM_IMG_HDR_BOOT_NAND 0x2 233 | #define BOOTROM_IMG_HDR_BOOT_SD 0x3 234 | #define BOOTROM_IMG_HDR_BOOT_MMC 0x4 235 | #define BOOTROM_IMG_HDR_BOOT_USB 0x5 236 | #define BOOTROM_IMG_HDR_BOOT_ETH 0x6 237 | #define BOOTROM_IMG_HDR_BOOT_PCIE 0x7 238 | #define BOOTROM_IMG_HDR_BOOT_SATA 0x8 239 | 240 | /* TODO find definitions for other CPUs */ 241 | #define BOOTROM_FSBL_CPU_R5 0x001 242 | #define BOOTROM_FSBL_CPU_A53_64 0x800 243 | 244 | /* Other file specific files */ 245 | #define FILE_MAGIC_ELF 0x464C457F 246 | 247 | #define FILE_MAGIC_XILINXBIT_0 0xf00f0900 248 | #define FILE_MAGIC_XILINXBIT_1 0xf00ff00f 249 | 250 | #define FILE_MAGIC_LINUX 0x56190527 251 | #define FILE_MAGIC_DTB 0xedfe0dd0 252 | 253 | #define FILE_XILINXBIT_SEC_START 13 254 | #define FILE_XILINXBIT_SEC_DATA 'e' 255 | 256 | #define FILE_LINUX_IMG_TYPE_UIM 2 257 | #define FILE_LINUX_IMG_TYPE_URD 3 258 | #define FILE_LINUX_IMG_TYPE_SCR 6 259 | 260 | #define BINARY_ATTR_LINUX 0x00 261 | #define BINARY_ATTR_RAMDISK 0x02 262 | #define BINARY_ATTR_GENERAL 0x01 263 | 264 | typedef struct bootrom_offs_t { 265 | /* pointer to the image in memory */ 266 | uint32_t *img_ptr; 267 | 268 | /* offsets used as pointers */ 269 | uint32_t *coff; /* current offset/ptr */ 270 | uint32_t *poff; /* current partiton offset */ 271 | uint32_t *hoff; /* partition header table offset */ 272 | 273 | /* offsets used as regular values */ 274 | uint32_t img_hdr_off; 275 | uint32_t part_hdr_off; 276 | uint32_t part_hdr_end_off; 277 | uint32_t bins_off; 278 | } bootrom_offs_t; 279 | 280 | typedef struct mask_name_t { 281 | char *name; 282 | uint32_t mask; 283 | struct mask_name_t *submasks; 284 | } mask_name_t; 285 | 286 | extern mask_name_t bootrom_part_attr_mask_names[]; 287 | extern mask_name_t bootrom_part_attr_owner_names[]; 288 | extern mask_name_t bootrom_part_attr_rsa_used_names[]; 289 | extern mask_name_t bootrom_part_attr_dest_cpu_names[]; 290 | extern mask_name_t bootrom_part_attr_encryption_names[]; 291 | extern mask_name_t bootrom_part_attr_dest_dev_names[]; 292 | extern mask_name_t bootrom_part_attr_a5x_exec_s_names[]; 293 | extern mask_name_t bootrom_part_attr_exc_lvl_names[]; 294 | extern mask_name_t bootrom_part_attr_trust_zone_names[]; 295 | 296 | uint32_t map_name_to_mask(mask_name_t mask_names[], char *name); 297 | char *map_mask_to_name(mask_name_t mask_names[], uint32_t mask); 298 | 299 | /* bootrom operations */ 300 | typedef struct bootrom_ops_t { 301 | /* Initialize offsets - image pointer should be 302 | * set before this one is called */ 303 | error (*init_offs)(uint32_t *, int, bootrom_offs_t *); 304 | 305 | /* Initialize the main bootrom header */ 306 | error (*init_header)(bootrom_hdr_t *, bootrom_offs_t *); 307 | 308 | /* Setup bootloader at the current offset */ 309 | error (*setup_fsbl_at_curr_off)(bootrom_hdr_t *, bootrom_offs_t *, uint32_t img_len); 310 | 311 | /* Prepare image header table */ 312 | error (*init_img_hdr_tab)(bootrom_img_hdr_tab_t *, 313 | bootrom_img_hdr_t *, 314 | bootrom_partition_hdr_t *, 315 | bootrom_offs_t *); 316 | 317 | /* Partition header related callbacks */ 318 | error (*init_part_hdr_default)(bootrom_partition_hdr_t *, bif_node_t *); 319 | error (*init_part_hdr_dtb)(bootrom_partition_hdr_t *, bif_node_t *); 320 | error (*init_part_hdr_elf)(bootrom_partition_hdr_t *, 321 | bif_node_t *, 322 | uint32_t *size, 323 | uint32_t load, 324 | uint32_t entry, 325 | uint8_t nbits); 326 | error (*init_part_hdr_bitstream)(bootrom_partition_hdr_t *, bif_node_t *); 327 | error (*init_part_hdr_linux)(bootrom_partition_hdr_t *, bif_node_t *, linux_image_header_t *); 328 | /* The finish function is common for all partition types */ 329 | error (*finish_part_hdr)(bootrom_partition_hdr_t *, uint32_t *img_size, bootrom_offs_t *); 330 | 331 | /* Some archs require a null partition at the end */ 332 | uint8_t append_null_part; 333 | } bootrom_ops_t; 334 | 335 | uint32_t estimate_boot_image_size(bif_cfg_t *); 336 | error create_boot_image(uint32_t *, bif_cfg_t *, bootrom_ops_t *, uint32_t *); 337 | 338 | #endif /* BOOTROM_H */ 339 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | int errorf(const char *fmt, ...) { 11 | int n; 12 | va_list args; 13 | 14 | va_start(args, fmt); 15 | fprintf(stderr, "error: "); 16 | n = vfprintf(stderr, fmt, args); 17 | va_end(args); 18 | 19 | return n; 20 | } 21 | 22 | uint32_t calc_checksum(uint32_t *start_addr, uint32_t *end_addr) { 23 | uint32_t *ptr; 24 | uint32_t sum; 25 | 26 | sum = 0; 27 | ptr = start_addr; 28 | 29 | while (ptr <= end_addr) { 30 | sum += *ptr; 31 | ptr++; 32 | } 33 | 34 | return ~sum; 35 | } 36 | 37 | /* Check if pfix is a postifx of string */ 38 | bool is_postfix(char *string, char *pfix) { 39 | return strcmp(string + strlen(string) - strlen(pfix), pfix) == 0; 40 | } 41 | 42 | /* Check if a string is present on a list */ 43 | bool is_on_list(char *list[], char *s) { 44 | int i; 45 | 46 | for (i = 0; list[i]; i++) 47 | if (strcmp(list[i], s) == 0) 48 | return true; 49 | return false; 50 | } 51 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef MKBOOTIMAGE_COMMON_H 2 | #define MKBOOTIMAGE_COMMON_H 3 | 4 | typedef enum error 5 | { 6 | /* The job was ended sucessfully */ 7 | SUCCESS = 0, 8 | 9 | /* Common error codes */ 10 | ERROR_NOMEM = 1, 11 | ERROR_CANT_READ, 12 | ERROR_CANT_WRITE, 13 | ERROR_ITERATION_END, 14 | 15 | /* BIF parser specific errors */ 16 | ERROR_BIF_NOFILE, 17 | ERROR_BIF_PARSER, 18 | ERROR_BIF_UNSUPPORTED_ATTR, 19 | ERROR_BIF_UNINITIALIZED, 20 | ERROR_BIF_UNSUPPORTED_VAL, 21 | ERROR_BIF_LEXER, 22 | 23 | /* Bootrom compiler specific errors */ 24 | ERROR_BOOTROM_NOFILE, 25 | ERROR_BOOTROM_BITSTREAM, 26 | ERROR_BOOTROM_ELF, 27 | ERROR_BOOTROM_SEC_OVERLAP, 28 | ERROR_BOOTROM_UNSUPPORTED, 29 | ERROR_BOOTROM_NOMEM, 30 | 31 | /* Bootrom parser specific errors */ 32 | ERROR_BIN_FILE_EXISTS, 33 | ERROR_BIN_NOFILE, 34 | ERROR_BIN_WADDR, 35 | } error; 36 | 37 | int errorf(const char *fmt, ...); 38 | uint32_t calc_checksum(uint32_t *, uint32_t *); 39 | bool is_postfix(char *, char *); 40 | bool is_on_list(char **, char *); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/exbootimage.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013-2021, Antmicro Ltd 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 | * * Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 10 | * * 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" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | /* A macro constructor of struct fmt */ 44 | #define FORMAT(name, type, field, fmt) \ 45 | { name, offsetof(type, field), print_##fmt } 46 | 47 | /* Calculate absolute value of a byte or word address */ 48 | #define ABS_BADDR(base, addr) ((void *) ((uint8_t *) (base) + (addr))) 49 | #define ABS_WADDR(base, addr) ((void *) ((uint32_t *) (base) + (addr))) 50 | 51 | /* Calculate relative byte addres from an absolute address */ 52 | #define REL_BADDR(base, addr) ((uint32_t) ((unsigned long) (addr) - (unsigned long) (base))) 53 | 54 | /* Check if a byte or word address is correct */ 55 | #define IS_REL_BADDR(bsize, addr) ((addr) < (bsize)) 56 | #define IS_REL_WADDR(bsize, addr) ((addr) * sizeof(uint32_t) < (bsize)) 57 | 58 | /* Check if an absolute address is relatively NULL */ 59 | #define IS_REL_NULL(base, addr) ((void *) (addr) <= (void *) (base)) 60 | 61 | /* A struct for describing binary data layout */ 62 | struct format { 63 | char *name; 64 | char offset; 65 | int (*print)(FILE *, void *, int); 66 | }; 67 | 68 | /* Prepare struct for holding parsed arguments */ 69 | struct arguments { 70 | bool list; 71 | bool describe; 72 | bool header; 73 | bool images; 74 | bool partitions; 75 | bool zynqmp; 76 | 77 | bool force; 78 | bool extract; 79 | int extract_count; 80 | char **extract_names; 81 | 82 | char *design; 83 | char *part; 84 | bool swap; 85 | 86 | char *fname; 87 | }; 88 | 89 | /* Type definitions for local usage */ 90 | typedef bootrom_img_hdr_t img_hdr_t; 91 | typedef bootrom_partition_hdr_zynq_t zynq_hdr_t; 92 | typedef bootrom_partition_hdr_zynqmp_t zynqmp_hdr_t; 93 | typedef bootrom_hdr_t hdr_t; 94 | typedef bootrom_img_hdr_tab_t img_hdr_tab_t; 95 | typedef bootrom_img_hdr_t img_hdr_t; 96 | 97 | typedef bootrom_partition_hdr_t part_hdr_t; 98 | 99 | int print_dec(FILE *f, void *base, int offset); 100 | int print_word(FILE *f, void *base, int offset); 101 | int print_dbl_word(FILE *f, void *base, int offset); 102 | int print_name(FILE *f, void *base, int offset); 103 | int print_attr(FILE *f, void *base, int offset); 104 | 105 | static int name_to_string(char *dst, void *base, int offset); 106 | static int print_padding(FILE *f, int times, char ch); 107 | 108 | static error verify_waddr(void *base, uint32_t size, uint32_t *poffset); 109 | static error get_next_image(void *base, uint32_t size, img_hdr_t **img); 110 | 111 | /* Prepare global variables for arg parser */ 112 | const char *argp_program_version = MKBOOTIMAGE_VER; 113 | static char doc[] = "Extract data and files from Xilinx Zynq boot images."; 114 | static char args_doc[] = 115 | "[--zynqmp|-u] " 116 | "[--extract|-x] " 117 | "[--list|-l] " 118 | "[--describe|-d] " 119 | "[--header|-h] " 120 | "[--images|-i] " 121 | "[--parts|-p] " 122 | "[--bitstream|-bDESIGN,PART-NAME] " 123 | "[--swap|-s] " 124 | " "; 125 | 126 | static struct argp_option argp_options[] = { 127 | {"zynqmp", 'u', 0, 0, "Expect files for ZynqMP (default is Zynq)", 0}, 128 | {"extract", 'x', 0, 0, "Extract files embed in the image", 0}, 129 | {"force", 'f', 0, 0, "Don't avoid overwriting an extracted file", 0}, 130 | {"list", 'l', 0, 0, "List files embedded in the image", 0}, 131 | {"describe", 'd', 0, 0, "Describe the boot image (-hip equivalent)", 0}, 132 | {"header", 'h', 0, 0, "Print main boot image header", 0}, 133 | {"images", 'i', 0, 0, "Print partition image headers", 0}, 134 | {"parts", 'p', 0, 0, "Print partition headers", 0}, 135 | {"bitstream", 'b', "DESIGN,PART-NAME", 0, "Reconstruct bitstream with headers on extraction", 0}, 136 | {"swap", 's', 0, 0, "Swap bitstream bytes but don't reconstruct headers", 0}, 137 | {0}, 138 | }; 139 | 140 | /* clang-format off */ 141 | static struct format hdr_fmt[] = { 142 | FORMAT("Width Detection Word", hdr_t, width_detect, word), 143 | FORMAT("Header Signature", hdr_t, img_id, word), 144 | FORMAT("Key Source", hdr_t, encryption_status, word), 145 | FORMAT("Header Version", hdr_t, fsbl_defined_0, word), 146 | FORMAT("Source Byte Offset", hdr_t, src_offset, word), 147 | FORMAT("FSBL Image Length", hdr_t, img_len, dec), 148 | FORMAT("FSBL Load Address", hdr_t, pmufw_total_len, word), 149 | FORMAT("FSBL Execution Address", hdr_t, start_of_exec, word), 150 | FORMAT("Total FSBL Length", hdr_t, total_img_len, dec), 151 | FORMAT("QSPI configuration Word", hdr_t, reserved_1, word), 152 | FORMAT("Boot Header Checksum", hdr_t, checksum, word), 153 | {0}, 154 | }; 155 | 156 | static struct format img_hdr_tab_fmt[] = { 157 | FORMAT("Version", img_hdr_tab_t, version, word), 158 | FORMAT("Header Count", img_hdr_tab_t, hdrs_count, dec), 159 | FORMAT("Partition Header Offset", img_hdr_tab_t, part_hdr_off, word), 160 | FORMAT("Partition Image Header Offset", img_hdr_tab_t, part_img_hdr_off, word), 161 | FORMAT("Header Authentication Offset", img_hdr_tab_t, auth_hdr_off, word), 162 | {0}, 163 | }; 164 | 165 | static struct format zynqmp_img_hdr_tab_fmt[] = { 166 | FORMAT("(ZynqMP) Boot Device", img_hdr_tab_t, boot_dev, word), 167 | FORMAT("(ZynqMP) Checksum", img_hdr_tab_t, checksum, word), 168 | {0}, 169 | }; 170 | 171 | static struct format img_hdr_fmt[] = { 172 | FORMAT("Next Image Offset", img_hdr_t, next_img_off, word), 173 | FORMAT("Partition Header Offset", img_hdr_t, part_hdr_off, word), 174 | FORMAT("Partition Count (always 0)", img_hdr_t, part_count, dec), 175 | FORMAT("Name Length (usually 1)", img_hdr_t, name_len, dec), 176 | FORMAT("Image Name", img_hdr_t, name, name), 177 | {0}, 178 | }; 179 | 180 | static struct format zynq_hdr_fmt[] = { 181 | FORMAT("Encrypted Data Length", zynq_hdr_t, pd_len, dec), 182 | FORMAT("Unencrypted Data Length", zynq_hdr_t, ed_len, dec), 183 | FORMAT("Total Length", zynq_hdr_t, total_len, dec), 184 | FORMAT("Load Address", zynq_hdr_t, dest_load_addr, word), 185 | FORMAT("Execution Address", zynq_hdr_t, dest_exec_addr, word), 186 | FORMAT("Partition Data Offset", zynq_hdr_t, data_off, word), 187 | FORMAT("Attributes", zynq_hdr_t, attributes, attr), 188 | FORMAT("Section Count", zynq_hdr_t, section_count, dec), 189 | FORMAT("Checksum Offset", zynq_hdr_t, checksum_off, word), 190 | FORMAT("Image Header Offset", zynq_hdr_t, img_hdr_off, word), 191 | FORMAT("Certificate Offset", zynq_hdr_t, cert_off, word), 192 | FORMAT("Checksum", zynq_hdr_t, checksum, word), 193 | {0}, 194 | }; 195 | 196 | static struct format zynqmp_hdr_fmt[] = { 197 | FORMAT("Encrypted Data Length", zynqmp_hdr_t, pd_len, dec), 198 | FORMAT("Unencrypted Data Length", zynqmp_hdr_t, ed_len, dec), 199 | FORMAT("Total Length", zynqmp_hdr_t, total_len, dec), 200 | FORMAT("Next Header Offset", zynqmp_hdr_t, next_part_hdr_off, word), 201 | FORMAT("Load Address", zynqmp_hdr_t, dest_load_addr_lo, dbl_word), 202 | FORMAT("Execution Address", zynqmp_hdr_t, dest_exec_addr_lo, dbl_word), 203 | FORMAT("Partition Data Offset", zynqmp_hdr_t, actual_part_off, word), 204 | FORMAT("Attributes", zynqmp_hdr_t, attributes, attr), 205 | FORMAT("Section Count", zynqmp_hdr_t, section_count, dec), 206 | FORMAT("Checksum Offset", zynqmp_hdr_t, checksum_off, word), 207 | FORMAT("Image Header Offset", zynqmp_hdr_t, img_hdr_off, word), 208 | FORMAT("Certificate Offset", zynqmp_hdr_t, cert_off, word), 209 | FORMAT("Checksum", zynqmp_hdr_t, checksum, word), 210 | {0}, 211 | }; 212 | /* clang-format on */ 213 | 214 | /* Print a word as a decimal*/ 215 | int print_dec(FILE *f, void *base, int offset) { 216 | return fprintf(f, "%d", *(uint32_t *) ABS_BADDR(base, offset)); 217 | } 218 | 219 | /* Print a word as a hexadecimal*/ 220 | int print_word(FILE *f, void *base, int offset) { 221 | return fprintf(f, "0x%08x", *(uint32_t *) ABS_BADDR(base, offset)); 222 | } 223 | 224 | /* Print double word as a decimal*/ 225 | int print_dbl_word(FILE *f, void *base, int lo_offset) { 226 | uint32_t lo, hi; 227 | 228 | lo = *((uint32_t *) ABS_BADDR(base, lo_offset)); 229 | hi = *((uint32_t *) ABS_BADDR(base, lo_offset) + 1); 230 | 231 | return fprintf(f, "0x%08x%08x", hi, lo); 232 | } 233 | 234 | /* Print a string of chars encoded as big-endian 32bit words */ 235 | int print_name(FILE *f, void *base, int offset) { 236 | char name[BOOTROM_IMG_MAX_NAME_LEN]; 237 | 238 | name_to_string(name, base, offset); 239 | fputs(name, f); 240 | 241 | return 0; 242 | } 243 | 244 | int print_attr(FILE *f, void *base, int offset) { 245 | mask_name_t *subm; 246 | uint32_t attr = *(uint32_t *) ABS_BADDR(base, offset); 247 | uint32_t mask; 248 | char *name; 249 | int i; 250 | 251 | if (!attr) { 252 | print_word(f, base, offset); 253 | return 0; 254 | } 255 | 256 | fprintf(f, "explained below\n"); 257 | 258 | print_padding(f, 13, ' '); 259 | fprintf(f, "Hex Value: "); 260 | print_word(f, base, offset); 261 | fprintf(f, "\n"); 262 | 263 | for (i = 0; bootrom_part_attr_mask_names[i].name; i++) { 264 | name = bootrom_part_attr_mask_names[i].name; 265 | mask = bootrom_part_attr_mask_names[i].mask; 266 | subm = bootrom_part_attr_mask_names[i].submasks; 267 | 268 | print_padding(f, 13, ' '); 269 | fprintf(f, "%s: %s", name, map_mask_to_name(subm, attr & mask)); 270 | 271 | /* Start a newline if there are more attribute values to be printed */ 272 | if (bootrom_part_attr_mask_names[i + 1].name) 273 | fputc('\n', f); 274 | } 275 | 276 | return 0; 277 | } 278 | 279 | /* Print contents of a struct */ 280 | int print_struct(FILE *f, void *base, struct format fmt[]) { 281 | int i, n, maxlen = 0; 282 | 283 | for (i = 0; fmt[i].name; i++) 284 | if ((n = strlen(fmt[i].name)) > maxlen) 285 | maxlen = n; 286 | for (i = 0; fmt[i].name; i++) { 287 | fprintf(f, "[0x%08x] %s", fmt[i].offset, fmt[i].name); 288 | 289 | n = strlen(fmt[i].name); 290 | n = maxlen - n; 291 | print_padding(f, n, '.'); 292 | fputc(' ', f); 293 | 294 | fmt[i].print(f, base, fmt[i].offset); 295 | fprintf(f, "\n"); 296 | } 297 | 298 | return 0; 299 | } 300 | 301 | /* Print desired number of repetitions of the same char */ 302 | int print_padding(FILE *f, int times, char ch) { 303 | error err = SUCCESS; 304 | 305 | while (times-- > 0) 306 | if ((err = fputc(ch, f)) < 0) 307 | return ERROR_CANT_WRITE; 308 | return SUCCESS; 309 | } 310 | 311 | /* Print a section header in program's output */ 312 | int print_section(FILE *f, char *section) { 313 | fprintf(f, "\n%s\n", section); 314 | print_padding(f, 55, '='); 315 | fprintf(f, "\n\n"); 316 | return 0; 317 | } 318 | 319 | error print_file_list(FILE *f, hdr_t *base, uint32_t size) { 320 | error err = SUCCESS; 321 | img_hdr_t *img; 322 | 323 | for (img = NULL; (err = get_next_image(base, size, &img)) == SUCCESS;) { 324 | print_name(f, img, offsetof(img_hdr_t, name)); 325 | fputc('\n', f); 326 | } 327 | 328 | return err == ERROR_ITERATION_END ? SUCCESS : err; 329 | } 330 | 331 | error print_file_header(FILE *f, hdr_t *base, int zynqmp) { 332 | img_hdr_tab_t *tab = ABS_BADDR(base, sizeof(hdr_t)); 333 | 334 | print_section(f, "MAIN FILE HEADER SECTION"); 335 | print_struct(f, base, hdr_fmt); 336 | fputc('\n', f); 337 | 338 | print_section(f, "IMAGE HEADER TAB SECTION"); 339 | print_struct(f, tab, img_hdr_tab_fmt); 340 | if (zynqmp) 341 | print_struct(f, tab, zynqmp_img_hdr_tab_fmt); 342 | fputc('\n', f); 343 | 344 | return SUCCESS; 345 | } 346 | 347 | error print_image_headers(FILE *f, hdr_t *base, uint32_t size) { 348 | error err = SUCCESS; 349 | img_hdr_t *img; 350 | 351 | print_section(f, "IMAGE HEADERS SECTION"); 352 | for (img = NULL; (err = get_next_image(base, size, &img)) == SUCCESS;) { 353 | print_struct(f, img, img_hdr_fmt); 354 | fputc('\n', f); 355 | } 356 | 357 | return err == ERROR_ITERATION_END ? SUCCESS : err; 358 | } 359 | 360 | error print_partition_headers(FILE *f, hdr_t *base, uint32_t size, int zynqmp) { 361 | error err = SUCCESS; 362 | img_hdr_t *img; 363 | part_hdr_t *part; 364 | 365 | print_section(f, "PARTITION HEADERS SECTION"); 366 | for (img = NULL; (err = get_next_image(base, size, &img)) == SUCCESS;) { 367 | if ((err = verify_waddr(base, size, &img->part_hdr_off))) 368 | return err; 369 | part = ABS_WADDR(base, img->part_hdr_off); 370 | 371 | print_name(f, img, offsetof(img_hdr_t, name)); 372 | fprintf(f, ":\n"); 373 | 374 | if (zynqmp) 375 | print_struct(f, part, zynqmp_hdr_fmt); 376 | else 377 | print_struct(f, part, zynq_hdr_fmt); 378 | fputc('\n', f); 379 | } 380 | 381 | return err == ERROR_ITERATION_END ? SUCCESS : err; 382 | } 383 | 384 | error print_partition_contents(FILE *f, hdr_t *base, uint32_t size, struct arguments *arguments) { 385 | error err = SUCCESS; 386 | uint32_t partsize; 387 | img_hdr_t *img; 388 | part_hdr_t *part; 389 | void *data; 390 | struct stat bstat; 391 | FILE *bfile; 392 | char name[BOOTROM_IMG_MAX_NAME_LEN]; 393 | 394 | for (img = NULL; (err = get_next_image(base, size, &img)) == SUCCESS;) { 395 | name_to_string(name, img, offsetof(img_hdr_t, name)); 396 | 397 | /* Check if we're interested */ 398 | if (arguments->extract_names && !is_on_list(arguments->extract_names, name)) 399 | continue; 400 | 401 | /* Get partition header pointer */ 402 | if ((err = verify_waddr(base, size, &img->part_hdr_off))) 403 | return err; 404 | part = ABS_WADDR(base, img->part_hdr_off); 405 | 406 | /* Get partition size in bytes */ 407 | partsize = part->total_len; 408 | 409 | /* Get partition data pointer */ 410 | if (arguments->zynqmp) { 411 | if ((err = verify_waddr(base, size, &((zynqmp_hdr_t *) part)->actual_part_off))) 412 | return err; 413 | data = ABS_WADDR(base, ((zynqmp_hdr_t *) part)->actual_part_off); 414 | } else { 415 | if ((err = verify_waddr(base, size, &((zynq_hdr_t *) part)->data_off))) 416 | return err; 417 | data = ABS_WADDR(base, ((zynq_hdr_t *) part)->data_off); 418 | } 419 | 420 | /* Check if the file is fine */ 421 | if (!stat(name, &bstat) && !arguments->force) { 422 | errorf("file %s already exists, use -f to force\n", name); 423 | return ERROR_BIN_FILE_EXISTS; 424 | } 425 | if (!(bfile = fopen(name, "wb"))) { 426 | errorf("could not open file: %s\n", name); 427 | return ERROR_BIN_NOFILE; 428 | } 429 | 430 | fprintf(f, "Extracting %s... ", name); 431 | 432 | /* Treat bitstream files in a separate way */ 433 | if (is_postfix(name, ".bit")) { 434 | /* Zynq bitstream partisions are appended with an extra noop */ 435 | if (!arguments->zynqmp) 436 | partsize--; 437 | 438 | /* Reconstruct headers for bit files if it was requested */ 439 | if (arguments->part) 440 | bitstream_write_header(bfile, partsize, arguments->design, arguments->part); 441 | 442 | /* Perform byteswapped extraction if it was requested */ 443 | if (arguments->swap) 444 | bitstream_write(bfile, partsize, data); 445 | else 446 | fwrite(data, partsize, sizeof(uint32_t), bfile); 447 | } else { 448 | fwrite(data, partsize, sizeof(uint32_t), bfile); 449 | } 450 | 451 | fclose(bfile); 452 | 453 | fprintf(f, "done\n"); 454 | } 455 | 456 | return err == ERROR_ITERATION_END ? SUCCESS : err; 457 | } 458 | 459 | /* Convert a name encoded as big-endian 32bit words to string */ 460 | static int name_to_string(char *dst, void *base, int offset) { 461 | int i, j, p = 0; 462 | char *s = (char *) base + offset; 463 | 464 | for (i = 0; i < BOOTROM_IMG_MAX_NAME_LEN; i += sizeof(uint32_t)) { 465 | if (*(uint32_t *) (s + i) == 0) 466 | break; 467 | 468 | for (j = i + 3; j >= i; j--) 469 | if (s[j] > 0) 470 | dst[p++] = s[j]; 471 | } 472 | dst[p] = '\0'; 473 | 474 | return p; 475 | } 476 | 477 | /* Checkes wether poffset points to a correct word offset */ 478 | static error verify_waddr(void *base, uint32_t size, uint32_t *poffset) { 479 | uint32_t rel; 480 | 481 | if (*poffset < size) 482 | return SUCCESS; 483 | rel = REL_BADDR(base, poffset); 484 | errorf("0x%08x: wrong offset 0x%08x\n", rel, *poffset); 485 | return ERROR_BIN_WADDR; 486 | } 487 | 488 | /* Get next image absolute pointer or pointer to the first one if *img is NULL*/ 489 | static error get_next_image(void *base, uint32_t size, img_hdr_t **img) { 490 | error err; 491 | img_hdr_tab_t *tab; 492 | 493 | if (!*img) { 494 | /* Initialize *img if it was NULL */ 495 | tab = ABS_BADDR(base, sizeof(hdr_t)); 496 | 497 | if ((err = verify_waddr(base, size, &tab->part_img_hdr_off))) 498 | return err; 499 | *img = ABS_WADDR(base, tab->part_img_hdr_off); 500 | } else if ((err = verify_waddr(base, size, &(*img)->next_img_off))) { 501 | /* Return error if the next image offset is incorrect */ 502 | return err; 503 | } else { 504 | /* Get next image pointer */ 505 | *img = ABS_WADDR((base), (*img)->next_img_off); 506 | } 507 | if (IS_REL_NULL(base, *img)) { 508 | return ERROR_ITERATION_END; 509 | } 510 | return SUCCESS; 511 | } 512 | 513 | /* Define argument parser */ 514 | static error_t argp_parser(int key, char *arg, struct argp_state *state) { 515 | struct arguments *arguments = state->input; 516 | char *s; 517 | 518 | switch (key) { 519 | case 'u': 520 | arguments->zynqmp = true; 521 | break; 522 | case 'x': 523 | arguments->extract = true; 524 | break; 525 | case 'f': 526 | arguments->force = true; 527 | break; 528 | case 'l': 529 | arguments->list = true; 530 | break; 531 | case 'd': 532 | arguments->header = true; 533 | arguments->images = true; 534 | arguments->partitions = true; 535 | break; 536 | case 'h': 537 | arguments->header = true; 538 | break; 539 | case 'i': 540 | arguments->images = true; 541 | break; 542 | case 'p': 543 | arguments->partitions = true; 544 | break; 545 | case 's': 546 | arguments->swap = true; 547 | break; 548 | case 'b': 549 | if (!(s = strchr(arg, ','))) 550 | argp_usage(state); 551 | 552 | *s = '\0'; 553 | 554 | arguments->design = arg; 555 | arguments->part = s + 1; 556 | arguments->swap = true; 557 | break; 558 | case ARGP_KEY_ARG: 559 | if (state->arg_num == 0) { 560 | arguments->fname = arg; 561 | } else if (arguments->extract) { 562 | if (arguments->extract_count <= 0) { 563 | arguments->extract_names = calloc(state->argc + 1, sizeof(char *)); 564 | if (!arguments->extract_names) 565 | return EXIT_FAILURE; 566 | arguments->extract_count = 0; 567 | } 568 | arguments->extract_names[arguments->extract_count++] = arg; 569 | } else { 570 | argp_usage(state); 571 | } 572 | break; 573 | case ARGP_KEY_END: 574 | if (state->arg_num < 1) 575 | argp_usage(state); 576 | else if (arguments->extract_names) 577 | arguments->extract_names[arguments->extract_count] = NULL; 578 | break; 579 | default: 580 | return ARGP_ERR_UNKNOWN; 581 | } 582 | return 0; 583 | } 584 | 585 | /* Finally initialize argp struct */ 586 | static struct argp argp = {argp_options, argp_parser, args_doc, doc, 0, 0, 0}; 587 | 588 | /* Declare the main function */ 589 | int main(int argc, char *argv[]) { 590 | error err; 591 | struct arguments arguments; 592 | struct stat bfile_stat; 593 | uint32_t size; 594 | FILE *bfile; 595 | hdr_t *base; 596 | 597 | /* Init non-string arguments */ 598 | memset(&arguments, 0, sizeof(arguments)); 599 | 600 | /* Parse program arguments */ 601 | argp_parse(&argp, argc, argv, 0, 0, &arguments); 602 | 603 | if (stat(arguments.fname, &bfile_stat)) { 604 | errorf("could not stat file: %s\n", arguments.fname); 605 | return ERROR_BIN_NOFILE; 606 | } 607 | if (!(bfile = fopen(arguments.fname, "rb"))) { 608 | errorf("could not open file: %s\n", arguments.fname); 609 | return ERROR_BIN_NOFILE; 610 | } 611 | 612 | /* Load the image */ 613 | size = bfile_stat.st_size; 614 | 615 | if (!(base = malloc(size))) 616 | return ERROR_NOMEM; 617 | 618 | fread(base, sizeof(uint8_t), bfile_stat.st_size, bfile); 619 | fclose(bfile); 620 | 621 | if (arguments.list) 622 | /* Print partition names */ 623 | if ((err = print_file_list(stdout, base, size))) 624 | return err; 625 | 626 | if (arguments.header) 627 | /* Print boot image's header */ 628 | if ((err = print_file_header(stdout, base, arguments.zynqmp))) 629 | return err; 630 | 631 | if (arguments.images) 632 | /* Print parition image headers */ 633 | if ((err = print_image_headers(stdout, base, size))) 634 | return err; 635 | 636 | if (arguments.partitions) 637 | /* Print partition headers */ 638 | if ((err = print_partition_headers(stdout, base, size, arguments.zynqmp))) 639 | return err; 640 | 641 | if (arguments.extract) 642 | /* Write partition contents to files */ 643 | if ((err = print_partition_contents(stdout, base, size, &arguments))) 644 | return err; 645 | 646 | return EXIT_SUCCESS; 647 | } 648 | -------------------------------------------------------------------------------- /src/file/bitstream.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013-2021, Antmicro Ltd 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 | * * Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 10 | * * 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" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | error bitstream_verify(FILE *bitfile) { 38 | uint32_t fhdr; 39 | 40 | /* Seek to the start of the file */ 41 | rewind(bitfile); 42 | 43 | fread(&fhdr, 1, sizeof(fhdr), bitfile); 44 | if (fhdr != FILE_MAGIC_XILINXBIT_0) 45 | return ERROR_BOOTROM_BITSTREAM; 46 | 47 | /* Xilinx header is 64b, check the other half */ 48 | fread(&fhdr, 1, sizeof(fhdr), bitfile); 49 | if (fhdr != FILE_MAGIC_XILINXBIT_1) 50 | return ERROR_BOOTROM_BITSTREAM; 51 | 52 | /* Both halves match */ 53 | return SUCCESS; 54 | } 55 | 56 | int bitstream_write_header_part(FILE *bitfile, const uint8_t tag, const char *data) { 57 | uint16_t len = strlen(data) + 1; 58 | uint8_t n; 59 | 60 | fwrite(&tag, sizeof(uint8_t), 1, bitfile); 61 | 62 | n = (len >> 8) & 0xFF; 63 | fwrite(&n, sizeof(uint8_t), 1, bitfile); 64 | n = len & 0xFF; 65 | fwrite(&n, sizeof(uint8_t), 1, bitfile); 66 | 67 | fwrite(data, sizeof(uint8_t), len, bitfile); 68 | 69 | return 0; 70 | } 71 | 72 | error bitstream_write_header(FILE *bitfile, uint32_t size, const char *design, const char *part) { 73 | const uint8_t header[] = { 74 | 0x00, 75 | 0x09, 76 | 0x0f, 77 | 0xf0, 78 | 0x0f, 79 | 0xf0, 80 | 0x0f, 81 | 0xf0, 82 | 0x0f, 83 | 0xf0, 84 | 0x00, 85 | 0x00, 86 | 0x01, 87 | }; 88 | 89 | int i; 90 | uint8_t n; 91 | 92 | char stime[80]; 93 | time_t gtime; 94 | struct tm ltime; 95 | 96 | gtime = time(NULL); 97 | ltime = *localtime(>ime); 98 | 99 | /* TODO: the header sections are similar, generalize it */ 100 | 101 | /* Write magic numbers */ 102 | fwrite(header, sizeof(uint8_t), sizeof(header), bitfile); 103 | 104 | /* Write the section 'a' */ 105 | 106 | bitstream_write_header_part(bitfile, 'a', design); 107 | 108 | /* Write the section 'b' */ 109 | bitstream_write_header_part(bitfile, 'b', part); 110 | 111 | /* Write the section 'c' */ 112 | strftime(stime, sizeof(stime) - 1, "%Y/%m/%d", <ime); 113 | bitstream_write_header_part(bitfile, 'c', stime); 114 | 115 | /* Write the section 'd' */ 116 | strftime(stime, sizeof(stime) - 1, "%H:%M:%S", <ime); 117 | bitstream_write_header_part(bitfile, 'd', stime); 118 | 119 | /* Write the start of the section 'e' */ 120 | n = 'e'; 121 | fwrite(&n, sizeof(uint8_t), 1, bitfile); 122 | 123 | for (i = 3; i >= 0; i--) { 124 | n = (size >> (i * 8)) & 0xFF; 125 | fwrite(&n, sizeof(uint8_t), 1, bitfile); 126 | } 127 | 128 | return SUCCESS; 129 | } 130 | 131 | error bitstream_write(FILE *bitfile, uint32_t size, uint32_t *data) { 132 | uint32_t swapped; 133 | unsigned int i; 134 | 135 | for (i = 0; i < size; i++) { 136 | swapped = __builtin_bswap32(data[i]); 137 | fwrite(&swapped, 1, sizeof(swapped), bitfile); 138 | } 139 | return SUCCESS; 140 | } 141 | 142 | error bitstream_append(uint32_t *addr, FILE *bitfile, uint32_t *img_size) { 143 | uint32_t *dest = addr; 144 | uint32_t chunk, rchunk; 145 | uint8_t section_size; 146 | char section_data[255]; 147 | char section_hdr[2]; 148 | unsigned int i; 149 | 150 | /* Skip the header - it is already checked */ 151 | fseek(bitfile, FILE_XILINXBIT_SEC_START, SEEK_SET); 152 | while (1) { 153 | fread(§ion_hdr, 1, sizeof(section_hdr), bitfile); 154 | if (section_hdr[1] != 0x1 && section_hdr[1] != 0x0) { 155 | fclose(bitfile); 156 | errorf("bitstream file seems to have mismatched sections.\n"); 157 | return ERROR_BOOTROM_BITSTREAM; 158 | } 159 | 160 | if (section_hdr[0] == FILE_XILINXBIT_SEC_DATA) 161 | break; 162 | 163 | fread(§ion_size, 1, sizeof(uint8_t), bitfile); 164 | fread(§ion_data, 1, section_size, bitfile); 165 | } 166 | 167 | fseek(bitfile, -1, SEEK_CUR); 168 | fread(img_size, 1, 4, bitfile); 169 | 170 | *img_size = __builtin_bswap32(*img_size); 171 | uint32_t read_size = (*img_size + 3) & ~3; 172 | 173 | for (i = 0; i < read_size; i += sizeof(chunk)) { 174 | fread(&chunk, 1, sizeof(chunk), bitfile); 175 | rchunk = __builtin_bswap32(chunk); 176 | memcpy(dest, &rchunk, sizeof(rchunk)); 177 | dest++; 178 | } 179 | 180 | return SUCCESS; 181 | } 182 | -------------------------------------------------------------------------------- /src/file/bitstream.h: -------------------------------------------------------------------------------- 1 | #ifndef BITSTREAM_H 2 | #define BITSTREAM_H 3 | 4 | /* Check if this really is a bitstream file */ 5 | error bitstream_verify(FILE *bitfile); 6 | 7 | error bitstream_write_header(FILE *bfile, uint32_t size, const char *design, const char *part); 8 | error bitstream_write(FILE *bfile, uint32_t size, uint32_t *data); 9 | 10 | /* Returns the appended bitstream size via the last argument. 11 | * The regular return value is the error code. */ 12 | error bitstream_append(uint32_t *addr, FILE *bitfile, uint32_t *img_size); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/file/elf.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013-2021, Antmicro Ltd 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 | * * Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 10 | * * 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" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | static bool elf_is_loadable_section(const GElf_Shdr *elf_shdr) { 38 | return elf_shdr->sh_type != SHT_NOBITS && (elf_shdr->sh_flags & SHF_ALLOC) && 39 | elf_shdr->sh_size != 0; 40 | } 41 | 42 | static error elf_get_startaddr_endaddr(Elf *elf, uint32_t *start_addr, uint32_t *end_addr) { 43 | Elf_Scn *elf_scn = NULL; 44 | GElf_Shdr elf_shdr; 45 | *start_addr = -1; 46 | *end_addr = 0; 47 | 48 | while ((elf_scn = elf_nextscn(elf, elf_scn)) != NULL) { 49 | if (gelf_getshdr(elf_scn, &elf_shdr) != &elf_shdr) 50 | return ERROR_BOOTROM_ELF; 51 | 52 | if (!elf_is_loadable_section(&elf_shdr)) 53 | continue; 54 | 55 | if (*start_addr > elf_shdr.sh_addr) 56 | *start_addr = elf_shdr.sh_addr; 57 | 58 | if (elf_shdr.sh_addr + elf_shdr.sh_size > *end_addr) 59 | *end_addr = elf_shdr.sh_addr + elf_shdr.sh_size; 60 | } 61 | 62 | return SUCCESS; 63 | } 64 | 65 | static error elf_create_image(Elf *elf, uint32_t start_addr, uint8_t *out_buf) { 66 | Elf_Scn *elf_scn = NULL; 67 | Elf_Data *elf_data; 68 | GElf_Shdr elf_shdr; 69 | 70 | while ((elf_scn = elf_nextscn(elf, elf_scn)) != NULL) { 71 | if (gelf_getshdr(elf_scn, &elf_shdr) != &elf_shdr) 72 | return ERROR_BOOTROM_ELF; 73 | 74 | if (!elf_is_loadable_section(&elf_shdr)) 75 | continue; 76 | 77 | elf_data = NULL; 78 | 79 | while ((elf_data = elf_rawdata(elf_scn, elf_data)) != NULL) { 80 | if (!elf_data->d_buf) 81 | return ERROR_BOOTROM_ELF; 82 | 83 | memcpy(out_buf + elf_shdr.sh_addr + elf_data->d_off - start_addr, 84 | elf_data->d_buf, 85 | elf_data->d_size); 86 | } 87 | } 88 | 89 | return SUCCESS; 90 | } 91 | 92 | error elf_append(void *addr, 93 | const char *fname, 94 | uint32_t img_max_size, 95 | uint32_t *img_size, 96 | uint8_t *elf_nbits, 97 | uint32_t *elf_load, 98 | uint32_t *elf_entry) { 99 | error err; 100 | int fd_elf; 101 | Elf *elf; 102 | GElf_Ehdr elf_ehdr; 103 | uint32_t start_addr; 104 | uint32_t end_addr; 105 | 106 | /* Init elf library */ 107 | if (elf_version(EV_CURRENT) == EV_NONE) 108 | return ERROR_BOOTROM_ELF; 109 | 110 | /* Open file descriptor used by elf library */ 111 | if ((fd_elf = open(fname, O_RDONLY, 0)) < 0) 112 | return ERROR_BOOTROM_ELF; 113 | 114 | /* Init elf */ 115 | if ((elf = elf_begin(fd_elf, ELF_C_READ, NULL)) == NULL) { 116 | close(fd_elf); 117 | return ERROR_BOOTROM_ELF; 118 | } 119 | 120 | /* Make sure it is an elf (despite magic byte check) */ 121 | if (elf_kind(elf) != ELF_K_ELF) { 122 | elf_end(elf); 123 | close(fd_elf); 124 | return ERROR_BOOTROM_ELF; 125 | } 126 | 127 | if ((err = elf_get_startaddr_endaddr(elf, &start_addr, &end_addr))) { 128 | elf_end(elf); 129 | close(fd_elf); 130 | return err; 131 | } 132 | 133 | if (end_addr - start_addr > img_max_size) { 134 | elf_end(elf); 135 | close(fd_elf); 136 | return ERROR_BOOTROM_ELF; 137 | } 138 | 139 | memset(addr, 0, end_addr - start_addr); 140 | 141 | if ((err = elf_create_image(elf, start_addr, addr))) { 142 | elf_end(elf); 143 | close(fd_elf); 144 | return err; 145 | } 146 | 147 | if (gelf_getehdr(elf, &elf_ehdr) != &elf_ehdr) { 148 | elf_end(elf); 149 | close(fd_elf); 150 | return ERROR_BOOTROM_ELF; 151 | } 152 | 153 | *elf_load = start_addr; 154 | *elf_entry = elf_ehdr.e_entry; 155 | *elf_nbits = (elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64) ? 64 : 32; 156 | *img_size = end_addr - start_addr; 157 | 158 | elf_end(elf); 159 | close(fd_elf); 160 | 161 | return SUCCESS; 162 | } 163 | -------------------------------------------------------------------------------- /src/file/elf.h: -------------------------------------------------------------------------------- 1 | #ifndef ELF_H 2 | #define ELF_H 3 | 4 | /* Returns the appended file size and the elf header info via arguments. 5 | * The regular return value is the error code. */ 6 | error elf_append(void *addr, 7 | const char *fname, 8 | uint32_t img_max_size, 9 | uint32_t *img_size, 10 | uint8_t *elf_nbits, 11 | uint32_t *elf_load, 12 | uint32_t *elf_entry); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/mkbootimage.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013-2021, Antmicro Ltd 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 | * * Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 10 | * * 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" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | /* Prepare global variables for arg parser */ 40 | const char *argp_program_version = MKBOOTIMAGE_VER; 41 | static char doc[] = "Generate bootloader images for Xilinx Zynq based platforms."; 42 | static char args_doc[] = "[--parse-only|-p] [--zynqmp|-u] "; 43 | 44 | static struct argp_option argp_options[] = { 45 | {"zynqmp", 'u', 0, 0, "Generate files for ZyqnMP (default is Zynq)", 0}, 46 | {"parse-only", 'p', 0, 0, "Analyze BIF grammar, but don't generate any files", 0}, 47 | {0}, 48 | }; 49 | 50 | /* Prapare struct for holding parsed arguments */ 51 | struct arguments { 52 | bool zynqmp; 53 | bool parse_only; 54 | char *bif_filename; 55 | char *bin_filename; 56 | }; 57 | 58 | /* Define argument parser */ 59 | static error_t argp_parser(int key, char *arg, struct argp_state *state) { 60 | struct arguments *arguments = state->input; 61 | 62 | switch (key) { 63 | case 'u': 64 | arguments->zynqmp = true; 65 | break; 66 | case 'p': 67 | arguments->parse_only = true; 68 | break; 69 | case ARGP_KEY_ARG: 70 | switch (state->arg_num) { 71 | case 0: 72 | arguments->bif_filename = arg; 73 | break; 74 | case 1: 75 | arguments->bin_filename = arg; 76 | break; 77 | default: 78 | argp_usage(state); 79 | } 80 | break; 81 | case ARGP_KEY_END: 82 | if (state->arg_num < 1) 83 | argp_usage(state); 84 | else if (state->arg_num < 2 && !arguments->parse_only) 85 | argp_usage(state); 86 | break; 87 | default: 88 | return ARGP_ERR_UNKNOWN; 89 | } 90 | return 0; 91 | } 92 | 93 | /* Finally initialize argp struct */ 94 | static struct argp argp = {argp_options, argp_parser, args_doc, doc, 0, 0, 0}; 95 | 96 | /* Declare the main function */ 97 | int main(int argc, char *argv[]) { 98 | FILE *ofile; 99 | uint32_t ofile_size; 100 | uint32_t *file_data; 101 | uint32_t esize, esize_aligned; 102 | struct arguments arguments; 103 | bootrom_ops_t *bops; 104 | bif_cfg_t cfg; 105 | error err; 106 | int i; 107 | 108 | /* Init non-string arguments */ 109 | memset(&arguments, 0, sizeof(arguments)); 110 | 111 | /* Parse program arguments */ 112 | argp_parse(&argp, argc, argv, 0, 0, &arguments); 113 | 114 | /* Print program version info */ 115 | printf("%s\n", MKBOOTIMAGE_VER); 116 | 117 | init_bif_cfg(&cfg); 118 | 119 | /* Give bif parser the info about arch */ 120 | cfg.arch = (arguments.zynqmp) ? BIF_ARCH_ZYNQMP : BIF_ARCH_ZYNQ; 121 | bops = (arguments.zynqmp) ? &zynqmp_bops : &zynq_bops; 122 | 123 | err = bif_parse(arguments.bif_filename, &cfg); 124 | if (err) 125 | return err; 126 | if (cfg.nodes_num == 0) 127 | return ERROR_BOOTROM_NOFILE; 128 | 129 | printf("Nodes found in the %s file:\n", arguments.bif_filename); 130 | for (i = 0; i < cfg.nodes_num; i++) { 131 | printf(" %s", cfg.nodes[i].fname); 132 | if (cfg.nodes[i].bootloader) 133 | printf(" (bootloader)\n"); 134 | else 135 | printf("\n"); 136 | if (cfg.nodes[i].load) 137 | printf(" load: %08x\n", cfg.nodes[i].load); 138 | if (cfg.nodes[i].offset) 139 | printf(" offset: %08x\n", cfg.nodes[i].offset); 140 | } 141 | 142 | if (arguments.parse_only) { 143 | printf("The source BIF has a correct syntax\n"); 144 | return EXIT_SUCCESS; 145 | } 146 | 147 | /* Estimate memory required to fit all the binaries */ 148 | esize = estimate_boot_image_size(&cfg); 149 | if (!esize) 150 | return ERROR_BOOTROM_NOFILE; 151 | 152 | /* Align estimated size to powers of two */ 153 | esize_aligned = 2; 154 | while (esize_aligned < esize) 155 | esize_aligned *= 2; 156 | 157 | /* Allocate memory for output image */ 158 | file_data = malloc(sizeof *file_data * esize_aligned); 159 | if (!file_data) { 160 | return ERROR_NOMEM; 161 | } 162 | 163 | /* Generate bin file */ 164 | err = create_boot_image(file_data, &cfg, bops, &ofile_size); 165 | if (err) { 166 | free(file_data); 167 | return err; 168 | } 169 | 170 | ofile = fopen(arguments.bin_filename, "wb"); 171 | if (ofile == NULL) { 172 | errorf("could not open output file: %s\n", arguments.bin_filename); 173 | return ERROR_CANT_WRITE; 174 | } 175 | 176 | fwrite(file_data, sizeof(uint32_t), ofile_size, ofile); 177 | 178 | fclose(ofile); 179 | free(file_data); 180 | deinit_bif_cfg(&cfg); 181 | 182 | printf("All done, quitting\n"); 183 | return EXIT_SUCCESS; 184 | } 185 | -------------------------------------------------------------------------------- /tests/extraction/files: -------------------------------------------------------------------------------- 1 | README.md 2 | LICENSE 3 | Makefile 4 | 5 | -------------------------------------------------------------------------------- /tests/offsets/bad_1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/bad_1.bin -------------------------------------------------------------------------------- /tests/offsets/bad_10.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/bad_10.bin -------------------------------------------------------------------------------- /tests/offsets/bad_2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/bad_2.bin -------------------------------------------------------------------------------- /tests/offsets/bad_3.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/bad_3.bin -------------------------------------------------------------------------------- /tests/offsets/bad_4.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/bad_4.bin -------------------------------------------------------------------------------- /tests/offsets/bad_5.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/bad_5.bin -------------------------------------------------------------------------------- /tests/offsets/bad_6.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/bad_6.bin -------------------------------------------------------------------------------- /tests/offsets/bad_7.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/bad_7.bin -------------------------------------------------------------------------------- /tests/offsets/bad_8.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/bad_8.bin -------------------------------------------------------------------------------- /tests/offsets/bad_9.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/bad_9.bin -------------------------------------------------------------------------------- /tests/offsets/boot.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antmicro/zynq-mkbootimage/872363ce32c249f8278cf107bc6d3bdeb38d849f/tests/offsets/boot.bin -------------------------------------------------------------------------------- /tests/parser/bad_attribute.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] a53_x64 4 | [pmufw_image] pmu.elf 5 | [boatloader] fsbl.elf 6 | [destination_device=pl] fpga.bit 7 | [destination_cpu=a53-0,exception_level=el-2] bl31.elf 8 | [destination_cpu=a53-0,exception_level=el-2] u-boot.elf 9 | } 10 | -------------------------------------------------------------------------------- /tests/parser/bad_bracket.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [[bootloader]fsbl.elf 4 | fpga.bit 5 | u-boot.elf 6 | } 7 | -------------------------------------------------------------------------------- /tests/parser/bad_comment_in_comment.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader]fsbl.elf 4 | fpga.bit 5 | /* 6 | u-boot.elf 7 | [offset=0x00600000]uImage.bin 8 | /* [offset=0x00b00000]devicetree.dtb */ 9 | [offset=0x00c00000]uboot_ramdisk.scr 10 | */ 11 | [offset=0x00c40000]uramdisk.bin 12 | } 13 | -------------------------------------------------------------------------------- /tests/parser/bad_device_name.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] a53_x64 4 | [pmufw_image] pmu.elf 5 | [bootloader]fsbl.elf 6 | [destination_device=pl] fpga.bit 7 | [destination_cpu=a5a3-0,exception_level=el-2] bl31.elf 8 | [destination_cpu=a53-0,exception_level=el-2] u-boot.elf 9 | [offset=0x1D00000] Image.img 10 | [offset=0x2B00000] devicetree.dtb 11 | [offset=0x2B80000] uboot_ramdisk.scr 12 | [offset=0x2C00000] uramdisk.img 13 | } 14 | -------------------------------------------------------------------------------- /tests/parser/bad_empty_file_list.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /tests/parser/bad_forgotten_attribute.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] a53_x64 4 | [pmufw_image] pmu.elf 5 | [bootloader]fsbl.elf 6 | [] fpga.bit 7 | [destination_cpu=a53-0,exception_level=el-2] bl31.elf 8 | [destination_cpu=a53-0,exception_level=el-2] u-boot.elf 9 | [offset=0x1D00000] Image.img 10 | [offset=0x2B00000] devicetree.dtb 11 | [offset=0x2B80000] uboot_ramdisk.scr 12 | [offset=0x2C00000] uramdisk.img 13 | } 14 | -------------------------------------------------------------------------------- /tests/parser/bad_forgotten_filename.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] 4 | [load=0x0210000] minimal.cpio.uboot 5 | } 6 | -------------------------------------------------------------------------------- /tests/parser/bad_lacking_attribute_after_comma.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader,]fsbl.elf 4 | fpga.bit 5 | u-boot.elf 6 | } 7 | -------------------------------------------------------------------------------- /tests/parser/bad_lacking_attribute_value.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader]fsbl.elf 4 | fpga.bit 5 | u-boot.elf 6 | [offset=0x00600000]uImage.bin 7 | [offset]devicetree.dtb 8 | [offset=0x00c00000]uboot_ramdisk.scr 9 | [offset=0x00c40000]uramdisk.bin 10 | } 11 | -------------------------------------------------------------------------------- /tests/parser/bad_unclosed_c_comment.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] a53_x64 4 | [pmufw_image] pmu.elf 5 | [bootloader] fsbl.elf 6 | /*[destination_device=pl] fpga.bit/ 7 | [destination_cpu=a53-0,exception_level=el-2] bl31.elf 8 | [destination_cpu=a53-0,exception_level=el-2] u-boot.elf 9 | } 10 | -------------------------------------------------------------------------------- /tests/parser/boot1.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] a53_x64 4 | [pmufw_image] pmu.elf 5 | [bootloader] fsbl.elf 6 | [destination_device=pl] fpga.bit 7 | [destination_cpu=a53-0,exception_level=el-2] bl31.elf 8 | [destination_cpu=a53-0,exception_level=el-2] u-boot.elf 9 | } 10 | -------------------------------------------------------------------------------- /tests/parser/boot10.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader]fsbl.elf 4 | fpga.bit 5 | u-boot.elf 6 | } -------------------------------------------------------------------------------- /tests/parser/boot11.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader]fsbl.elf 4 | fpga.bit 5 | u-boot.elf 6 | } -------------------------------------------------------------------------------- /tests/parser/boot12.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] a53_x64 4 | [bootloader] fsbl.elf 5 | [destination_device=pl] fpga.bit 6 | [, destination_cpu=a53-0, exception_level=el-2] bl31.elf 7 | [, destination_cpu=a53-0, exception_level=el-2] u-boot.elf 8 | } 9 | -------------------------------------------------------------------------------- /tests/parser/boot2.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader]fsbl.elf 4 | fpga.bit 5 | u-boot.elf 6 | } -------------------------------------------------------------------------------- /tests/parser/boot3.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader]fsbl.elf 4 | fpga.bit 5 | u-boot.elf 6 | [offset=0x00600000]uImage.bin 7 | [offset=0x00b00000]devicetree.dtb 8 | [offset=0x00c00000]uboot_ramdisk.scr 9 | [offset=0x00c40000]uramdisk.bin 10 | } 11 | -------------------------------------------------------------------------------- /tests/parser/boot4.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader]fsbl.elf 4 | fpga.bit 5 | u-boot.elf 6 | } -------------------------------------------------------------------------------- /tests/parser/boot5.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader]fsbl.elf 4 | HelloWorld.elf 5 | } -------------------------------------------------------------------------------- /tests/parser/boot6.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader]fsbl.elf 4 | fpga.bit 5 | u-boot.elf 6 | } -------------------------------------------------------------------------------- /tests/parser/boot7.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] a53_x64 4 | [pmufw_image] pmu.elf 5 | [bootloader] fsbl.elf 6 | [destination_device=pl] fpga.bit 7 | [destination_cpu=a53-0,exception_level=el-2] bl31.elf 8 | [destination_cpu=a53-0,exception_level=el-2] u-boot.elf 9 | } 10 | -------------------------------------------------------------------------------- /tests/parser/boot8.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] a53_x64 4 | [pmufw_image] pmu.elf 5 | [bootloader] fsbl.elf 6 | [destination_device=pl] fpga.bit 7 | [destination_cpu=a53-0,exception_level=el-2] bl31.elf 8 | [destination_cpu=a53-0,exception_level=el-2] u-boot.elf 9 | } 10 | -------------------------------------------------------------------------------- /tests/parser/boot9.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] a53_x64 4 | [load=0x0210000] minimal.cpio.uboot 5 | } 6 | -------------------------------------------------------------------------------- /tests/parser/comment_in_comment.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [bootloader]fsbl.elf 4 | fpga.bit 5 | /* 6 | u-boot.elf 7 | [offset=0x00600000]uImage.bin 8 | // [offset=0x00b00000]devicetree.dtb 9 | [offset=0x00c00000]uboot_ramdisk.scr 10 | */ 11 | [offset=0x00c40000]uramdisk.bin 12 | } 13 | -------------------------------------------------------------------------------- /tests/parser/cpp_comment_on_eof.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image: 2 | { 3 | [fsbl_config] a53_x64 4 | [pmufw_image] pmu.elf 5 | [bootloader] fsbl.elf 6 | [destination_device=pl] fpga.bit 7 | [destination_cpu=a53-0,exception_level=el-2] bl31.elf 8 | [destination_cpu=a53-0,exception_level=el-2] u-boot.elf 9 | } 10 | // 11 | -------------------------------------------------------------------------------- /tests/parser/oneliner.bif: -------------------------------------------------------------------------------- 1 | the_ROM_image:{[fsbl_config]a53_x64[pmufw_image]pmu.elf[bootloader]fsbl.elf[destination_device=pl]fpga.bit[destination_cpu=a53-0,exception_level=el-2]bl31.elf[destination_cpu=a53-0,exception_level=el-2]u-boot.elf} 2 | -------------------------------------------------------------------------------- /tests/tester.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # CONFIG -------------------------------------------------- # 4 | # Remember useful directories 5 | ORIGIN=$(pwd) 6 | DIR=$(git rev-parse --show-toplevel) 7 | TESTS=$DIR/tests 8 | LOG=$TESTS/results.log 9 | PARSER=$TESTS/parser 10 | EXTRACT=$TESTS/extraction 11 | OFFSETS=$TESTS/offsets 12 | 13 | cd $DIR 14 | 15 | # Set aliases to color control sequences 16 | RESET=$(printf "\e[1;0m") 17 | GREEN=$(printf "\e[1;32m") 18 | RED=$(printf "\e[1;31m") 19 | 20 | # Set pass/fail counters 21 | pass=0 22 | fail=0 23 | 24 | # Define routines for passing and failing 25 | passtest() { 26 | pass=$(expr $pass + 1) 27 | printf "${GREEN}pass${RESET}: %s\n" "$1" 28 | } 29 | 30 | failtest() { 31 | fail=$(expr $fail + 1) 32 | printf "${RED}fail${RESET}: %s\n" "$1" 33 | } 34 | 35 | # The routine implements a test pair. The kind of test 36 | # is determined by the $1 argument. 37 | # 38 | # Negative tests work only on parser/bad_* files. 39 | # The other files are expected to be correct. 40 | testparser() { 41 | for file in $PARSER/*; do 42 | printf "\nLogs for $(basename $file) parsing:\n" >> $LOG 43 | 44 | base=$(basename $file) 45 | negative=$(expr 0 "<" "(" $base : "^bad_" ")" ) 46 | 47 | # XOR between program's success and test positivity 48 | $DIR/mkbootimage -p -u $file 1> /dev/null 2>> $LOG 49 | if [ $(expr $? = 0) != $negative ]; then 50 | passtest $(basename $file) 51 | else 52 | failtest $(basename $file) 53 | fi 54 | done 55 | } 56 | 57 | # Create a fake boot image out of project's files, then 58 | # unpack it and compar with the original ones. 59 | testextraction() { 60 | # Create a simple BIF from the list in the "files" file 61 | BIF=$EXTRACT/boot.bif 62 | BIN=$EXTRACT/boot.bin 63 | TMP=$EXTRACT/tmp 64 | 65 | printf "the_rom_image:{" > $BIF 66 | for file in $(cat $EXTRACT/files); do 67 | printf "%s " $file >> $BIF 68 | done 69 | printf "}" >> $BIF 70 | 71 | # Create a dummy boot image and unpack it 72 | printf "\nLogs for image extraction:\n" >> $LOG 73 | $DIR/mkbootimage -u $BIF $BIN 1> /dev/null 2>> $LOG 74 | 75 | # Extract the files back 76 | mkdir $TMP 77 | cd $TMP 78 | $DIR/exbootimage -ux $BIN 1> /dev/null 2>> $LOG 79 | 80 | # Test the files 81 | for file in $(cat $EXTRACT/files); do 82 | printf "\nLogs for $(basename $file) extraction:\n" >> $LOG 83 | 84 | if diff $file $TMP/$(basename $file) 1> /dev/null 2>> $LOG; then 85 | passtest $(basename $file) 86 | else 87 | failtest $(basename $file) 88 | fi 89 | done 90 | 91 | rm $BIF $BIN 92 | rm -rf $TMP 93 | cd $DIR 94 | } 95 | 96 | # Check if wrong offsets are detected properly 97 | testoffseterrors() { 98 | for file in $OFFSETS/*; do 99 | printf "\nLogs for $(basename $file) offsets:\n" >> $LOG 100 | 101 | base=$(basename $file) 102 | negative=$(expr 0 "<" "(" $base : "^bad_" ")" ) 103 | $DIR/exbootimage -l $file 1> /dev/null 2>> $LOG 104 | 105 | # XOR between program's success and test positivity 106 | if [ $(expr $? = 0) != $negative ]; then 107 | passtest $(basename $file) 108 | else 109 | failtest $(basename $file) 110 | fi 111 | done 112 | } 113 | 114 | # It is encouraged for future tests to be placed here 115 | # and implemented in an analogous way with the `testparser` 116 | # test routine, with both negative and positive tests. 117 | 118 | # The test files should be placed in a subdirectory of 119 | # a name describing their role, like parser/ is for 120 | # parser test files. 121 | 122 | # TESTING ------------------------------------------------- # 123 | # Check whether the mkbootimage binary exists 124 | if [ ! -f "$DIR/mkbootimage" ]; then 125 | printf "Build a mkbootimage binary before testing\n" 126 | exit 1 127 | fi 128 | 129 | if [ ! -f "$DIR/exbootimage" ]; then 130 | printf "Build an exbootimage binary before testing\n" 131 | exit 1 132 | fi 133 | 134 | # Init the log with a date information 135 | printf "Performed on $(date)\n" > $LOG 136 | 137 | # Perform parser tests 138 | testparser 139 | testextraction 140 | testoffseterrors 141 | 142 | # RESULT INFORMATION -------------------------------------- # 143 | printf "\npassed: %s\nfailed: %s\n\n" $pass $fail 144 | if [ $fail -eq 0 ]; then 145 | printf "Everything ${GREEN}PASSED${RESET}\n" 146 | exit 0 147 | else 148 | printf "Something ${RED}FAILED${RESET}\n" 149 | printf "Read %s to investigate details\n" $(basename $LOG) 150 | exit 1 151 | fi 152 | 153 | --------------------------------------------------------------------------------