1#!/usr/bin/awk -f 2#- 3# SPDX-License-Identifier: BSD-2-Clause 4# 5# Copyright 2019 Ian Lepore <ian@freebsd.org> 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28 29BEGIN { 30 # Init global vars. 31 gBytesOut = 0; # How many output bytes we've written so far 32 gKernbase = 0; # Address of first byte of loaded kernel image 33 gStart = 0; # Address of _start symbol 34 gStartOff = 0; # Offset of _start symbol from start of image 35 gEnd = 0; # Address of _end symbol 36 gEndOff = 0; # Offset of _end symbol from start of image 37 38 # The type of header we're writing is set using -v hdrtype= on 39 # the command line, ensure we got a valid value for it. 40 if (hdrtype != "v7jump" && 41 hdrtype != "v7bootz" && 42 hdrtype != "v8jump" && 43 hdrtype != "v8booti") { 44 print "arm_kernel_boothdr.awk: " \ 45 "missing or invalid '-v hdrtype=' argument" >"/dev/stderr" 46 gHdrType = "error_reported" 47 exit 1 48 } 49 50 gHdrType = hdrtype 51 for (i = 0; i < 16; i++) { 52 hex[sprintf("%x", i)] = i; 53 hex[sprintf("%X", i)] = i; 54 } 55} 56 57function addr_to_offset(addr) { 58 # Turn an address into an offset from the start of the loaded image. 59 return addr % gKernbase 60} 61 62function hexstr_to_num(str) { 63 64 sum = 0; 65 len = length(str); 66 for (i = 1; i <= len; i++) { 67 sum = sum * 16 + hex[substr(str, i, 1)]; 68 } 69 70 return sum; 71} 72 73function write_le32(num) { 74 75 for (i = 0; i < 4; i++) { 76 printf("%c", num % 256); 77 num /= 256 78 } 79 gBytesOut += 4 80} 81 82function write_le64(num) { 83 84 for (i = 0; i < 8; i++) { 85 printf("%c", num % 256); 86 num /= 256 87 } 88 gBytesOut += 8 89} 90 91function write_padding() { 92 93 # Write enough padding bytes so that the header fills all the 94 # remaining space before the _start symbol. 95 96 while (gBytesOut++ < gStartOff) { 97 printf("%c", 0); 98 } 99} 100 101function write_v7jump() { 102 103 # Write the machine code for "b _start"... 104 # 0xea is armv7 "branch always" and the low 24 bits is the signed 105 # offset from the current PC, in words. We know the gStart offset 106 # is in the first 2mb, so it'll fit in 24 bits. 107 108 write_le32(hexstr_to_num("ea000000") + (gStartOff / 4) - 2) 109} 110 111function write_v7bootz() { 112 113 # We are writing this struct... 114 # 115 # struct BootZ_header { 116 # uint32_t code0; /* Executable code */ 117 # uint32_t dummy[8]; /* dummy */ 118 # uint32_t magic; /* Magic number 0x016f2818*/ 119 # uint32_t load_offset; /* Image load offset, LE */ 120 # uint32_t image_size; /* Effective Image size, LE */ 121 # }; 122 # 123 # We write 'b _start' into code0. The image size is everything from 124 # the start of the loaded image to the offset given by the _end symbol. 125 126 write_v7jump() # code0 127 write_le32(0) # dummy[0] 128 write_le32(0) # dummy[1] 129 write_le32(0) # dummy[2] 130 write_le32(0) # dummy[3] 131 write_le32(0) # dummy[4] 132 write_le32(0) # dummy[5] 133 write_le32(0) # dummy[6] 134 write_le32(0) # dummy[7] 135 write_le32(hexstr_to_num("016f2818")) # magic marker 136 write_le32(0) # load_offset (0 -> auto) 137 write_le32(gEndOff) # image_size 138} 139 140function write_v8jump() { 141 142 # Write the machine code for "b _start"... 143 # 0x14 is armv8 "branch always" and the low 26 bits is the signed 144 # offset from the current PC, in words. We know the gStart offset 145 # is in the first 2mb, so it'll fit in 26 bits. 146 147 write_le32(hexstr_to_num("14000000") + (gStartOff / 4)) 148} 149 150function write_v8booti() { 151 152 # We are writing this struct... 153 # 154 # struct Image_header { 155 # uint32_t code0; /* Executable code */ 156 # uint32_t code1; /* Executable code */ 157 # uint64_t text_offset; /* Image load offset, LE */ 158 # uint64_t image_size; /* Effective Image size, LE */ 159 # uint64_t flags; /* Kernel flags, LE */ 160 # uint64_t res1[3]; /* reserved */ 161 # uint32_t magic; /* Magic number */ 162 # uint32_t res2; 163 # }; 164 # 165 # We write 'b _start' into code0. The image size is everything from 166 # the start of the loaded image to the offset given by the _end symbol. 167 168 write_v8jump() # code0 169 write_le32(0) # code1 170 write_le64(0) # text_offset 171 write_le64(gEndOff) # image_size 172 write_le64(hexstr_to_num("8")) # flags 173 write_le64(0) # res1[0] 174 write_le64(0) # res1[1] 175 write_le64(0) # res1[2] 176 write_le32(hexstr_to_num("644d5241")) # magic (LE "ARMd" (d is 0x64)) 177 write_le32(0) # res2 178} 179 180/kernbase/ { 181 # If the symbol name is exactly "kernbase" save its address. 182 if ($3 == "kernbase") { 183 gKernbase = hexstr_to_num($1) 184 } 185} 186 187/_start/ { 188 # If the symbol name is exactly "_start" save its address. 189 if ($3 == "_start") { 190 gStart = hexstr_to_num($1) 191 } 192} 193 194/_end/ { 195 # If the symbol name is exactly "_end" remember its value. 196 if ($3 == "_end") { 197 gEnd = hexstr_to_num($1) 198 } 199} 200 201END { 202 # Note that this function runs even if BEGIN calls exit(1)! 203 if (gHdrType == "error_reported") { 204 exit 1 205 } 206 207 # Make sure we got all three required symbols. 208 if (gKernbase == 0 || gStart == 0 || gEnd == 0) { 209 print "arm_kernel_boothdr.awk: " \ 210 "missing kernbase/_start/_end symbol(s)" >"/dev/stderr" 211 exit 1 212 } 213 214 gStartOff = addr_to_offset(gStart) 215 gEndOff = addr_to_offset(gEnd) 216 217 if (gHdrType == "v7jump") { 218 write_v7jump() 219 } else if (gHdrType == "v7bootz") { 220 write_v7bootz() 221 } else if (gHdrType == "v8jump") { 222 write_v8jump() 223 } else if (gHdrType == "v8booti") { 224 write_v8booti() 225 } 226 write_padding() 227} 228