Index: sys/conf/Makefile.arm64 =================================================================== --- sys/conf/Makefile.arm64 +++ sys/conf/Makefile.arm64 @@ -23,6 +23,7 @@ .if !defined(S) S= ../../.. .endif +.include "src.lua.mk" .include "$S/conf/kern.pre.mk" INCLUDES+= -I$S/contrib/libfdt -I$S/contrib/device-tree/include @@ -74,14 +75,14 @@ # stripped and a custom binary head blob is prepended), saving the # output in a temp file. We also strip arm "marker" symbols which are # used only by elf toolchains. Read the symbols from kernel.full and pass -# them to arm_kernel_boothdr.awk, which generates a binary header blob +# them to kernel_boothdr.lua, which generates a binary header blob # that goes on the front of the stripped kernel. Cat the header blob # and the temp file together to make the kernel.bin file. ${KERNEL_KO}.bin: ${FULLKERNEL} @${OBJCOPY} --wildcard --strip-symbol='$$[adtx]*' \ --output-target=binary ${.ALLSRC} ${.TARGET}.temp - @{ readelf -s ${.ALLSRC} | \ - ${AWK} -f $S/tools/arm_kernel_boothdr.awk -v hdrtype=v8booti && \ + @{ + ${LUA} $S/tools/kernel_boothdr.lua ${.ALLSRC} v8booti && \ cat ${.TARGET}.temp; \ } > ${.TARGET} @rm ${.TARGET}.temp Index: sys/conf/Makefile.riscv =================================================================== --- sys/conf/Makefile.riscv +++ sys/conf/Makefile.riscv @@ -24,6 +24,7 @@ .if !defined(S) S= ../../.. .endif +.include "src.lua.mk" .include "$S/conf/kern.pre.mk" INCLUDES+= -I$S/contrib/libfdt -I$S/contrib/device-tree/include @@ -37,6 +38,24 @@ KERNEL_LMA?= 0x80200000 LDFLAGS+= --defsym='kernel_lma=${KERNEL_LMA}' +# Use a custom SYSTEM_LD command to generate the elf kernel, so we can +# set the text segment start address, and also strip the "arm mapping +# symbols" which have names like $a.0 and $d.2; see the document +# "ELF for the ARM architecture" for more info on the mapping symbols. +SYSTEM_LD= \ + ${SYSTEM_LD_BASECMD} \ + --defsym='text_start=kernbase + SIZEOF_HEADERS' \ + -o ${.TARGET} ${SYSTEM_OBJS} vers.o; \ + $(OBJCOPY) \ + --wildcard \ + --strip-symbol='$$[adtx]*' \ + ${.TARGET} + +# Generate the .bin (booti images) kernel as an extra build output. +# The targets and rules to generate these appear near the end of the file. +KERNEL_EXTRA+= ${KERNEL_KO}.bin +KERNEL_EXTRA_INSTALL+= ${KERNEL_KO}.bin + .if !empty(DDB_ENABLED) CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls .endif @@ -59,3 +78,21 @@ %RULES .include "$S/conf/kern.post.mk" + +# Create a kernel.bin file... +# Copy the kernel to u-boot's booti image format (the elf headers are +# stripped and a custom binary head blob is prepended), saving the +# output in a temp file. We also strip arm "marker" symbols which are +# used only by elf toolchains. Read the symbols from kernel.full and pass +# them to kernel_boothdr.lua, which generates a binary header blob +# that goes on the front of the stripped kernel. Cat the header blob +# and the temp file together to make the kernel.bin file. +${KERNEL_KO}.bin: ${FULLKERNEL} + @${OBJCOPY} --wildcard --strip-symbol='$$[adtx]*' \ + --output-target=binary ${.ALLSRC} ${.TARGET}.temp + @{ + ${LUA} $S/tools/kernel_boothdr.lua ${.ALLSRC} riscvbooti && \ + cat ${.TARGET}.temp; \ + } > ${.TARGET} + @rm ${.TARGET}.temp + @echo "created ${.TARGET} from ${.ALLSRC}" Index: sys/conf/ldscript.riscv =================================================================== --- sys/conf/ldscript.riscv +++ sys/conf/ldscript.riscv @@ -6,9 +6,9 @@ SECTIONS { /* Read-only sections, merged into text segment: */ - . = kernbase; + . = text_start; /* The load address kernel_lma is set using --defsym= on the command line. */ - .text : AT(kernel_lma) + .text : { *(.text) *(.stub) Index: sys/tools/arm_kernel_boothdr.awk =================================================================== --- sys/tools/arm_kernel_boothdr.awk +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/awk -f -#- -# SPDX-License-Identifier: BSD-2-Clause-FreeBSD -# -# Copyright 2019 Ian Lepore -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ - -BEGIN { - # Init global vars. - gBytesOut = 0; # How many output bytes we've written so far - gKernbase = 0; # Address of first byte of loaded kernel image - gStart = 0; # Address of _start symbol - gStartOff = 0; # Offset of _start symbol from start of image - gEnd = 0; # Address of _end symbol - gEndOff = 0; # Offset of _end symbol from start of image - - # The type of header we're writing is set using -v hdrtype= on - # the command line, ensure we got a valid value for it. - if (hdrtype != "v7jump" && - hdrtype != "v8jump" && - hdrtype != "v8booti") { - print "arm_kernel_boothdr.awk: " \ - "missing or invalid '-v hdrtype=' argument" >"/dev/stderr" - gHdrType = "error_reported" - exit 1 - } - - gHdrType = hdrtype -} - -function addr_to_offset(addr) { - # Turn an address into an offset from the start of the loaded image. - return addr % gKernbase -} - -function hexstr_to_num(str) { - - # Prepend a 0x onto the string, then coerce it to a number by doing - # arithmetic with it, which makes awk run it through strtod(), - # which handles hex numbers that have a 0x prefix. - - return 0 + ("0x" str) -} - -function write_le32(num) { - - for (i = 0; i < 4; i++) { - printf("%c", num % 256); - num /= 256 - } - gBytesOut += 4 -} - -function write_le64(num) { - - for (i = 0; i < 8; i++) { - printf("%c", num % 256); - num /= 256 - } - gBytesOut += 8 -} - -function write_padding() { - - # Write enough padding bytes so that the header fills all the - # remaining space before the _start symbol. - - while (gBytesOut++ < gStartOff) { - printf("%c", 0); - } -} - -function write_v7jump() { - - # Write the machine code for "b _start"... - # 0xea is armv7 "branch always" and the low 24 bits is the signed - # offset from the current PC, in words. We know the gStart offset - # is in the first 2mb, so it'll fit in 24 bits. - - write_le32(hexstr_to_num("ea000000") + (gStartOff / 4) - 2) -} - -function write_v8jump() { - - # Write the machine code for "b _start"... - # 0x14 is armv8 "branch always" and the low 26 bits is the signed - # offset from the current PC, in words. We know the gStart offset - # is in the first 2mb, so it'll fit in 26 bits. - - write_le32(hexstr_to_num("14000000") + (gStartOff / 4)) -} - -function write_v8booti() { - - # We are writing this struct... - # - # struct Image_header { - # uint32_t code0; /* Executable code */ - # uint32_t code1; /* Executable code */ - # uint64_t text_offset; /* Image load offset, LE */ - # uint64_t image_size; /* Effective Image size, LE */ - # uint64_t flags; /* Kernel flags, LE */ - # uint64_t res1[3]; /* reserved */ - # uint32_t magic; /* Magic number */ - # uint32_t res2; - # }; - # - # We write 'b _start' into code0. The image size is everything from - # the start of the loaded image to the offset given by the _end symbol. - - write_v8jump() # code0 - write_le32(0) # code1 - write_le64(0) # text_offset - write_le64(gEndOff) # image_size - write_le64(0) # flags - write_le64(0) # res1[0] - write_le64(0) # res1[1] - write_le64(0) # res1[2] - write_le32(hexstr_to_num("644d5241")) # magic (LE "ARMd" (d is 0x64)) - write_le32(0) # res2 -} - -/kernbase/ { - # If the symbol name is exactly "kernbase" save its address. - if ($8 == "kernbase") { - gKernbase = hexstr_to_num($2) - } -} - -/_start/ { - # If the symbol name is exactly "_start" save its address. - if ($8 == "_start") { - gStart = hexstr_to_num($2) - } -} - -/_end/ { - # If the symbol name is exactly "_end" remember its value. - if ($8 == "_end") { - gEnd = hexstr_to_num($2) - } -} - -END { - # Note that this function runs even if BEGIN calls exit(1)! - if (gHdrType == "error_reported") { - exit 1 - } - - # Make sure we got all three required symbols. - if (gKernbase == 0 || gStart == 0 || gEnd == 0) { - print "arm_kernel_boothdr.awk: " \ - "missing kernbase/_start/_end symbol(s)" >"/dev/stderr" - exit 1 - } - - gStartOff = addr_to_offset(gStart) - gEndOff = addr_to_offset(gEnd) - - if (gHdrType == "v7jump") { - write_v7jump() - } else if (gHdrType == "v8jump") { - write_v8jump() - } else if (gHdrType == "v8booti") { - write_v8booti() - } - write_padding() -} Index: sys/tools/kernel_boothdr.lua =================================================================== --- /dev/null +++ sys/tools/kernel_boothdr.lua @@ -0,0 +1,157 @@ +-- +-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD +-- +-- Copyright (c) 2021 Conclusive Engineering Sp. z o. o. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS"" AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +-- SUCH DAMAGE. +-- +-- $FreeBSD$ +-- + +function printf(s, ...) + return io.stderr:write(s:format(...)) +end + +function write_packed(fmt, ...) + io.write(fmt:pack(...)) + g_written = g_written + fmt:packsize(...) +end + +function read_symbols(kernel) + local pipe = io.popen("nm " .. kernel) + + for line in pipe:lines() do + address, t, name = line:match("(%x+)%s(%g)%s(%g+)") + + if name == "_start" then + g_start = tonumber(address, 16) + elseif name == "_end" then + g_end = tonumber(address, 16) + elseif name == "kernbase" then + g_kernbase = tonumber(address, 16) + end + end + + pipe:close() + + if g_start == nil or g_end == nil or g_kernbase == nil then + printf("error: failed to read nm output\n"); + os.exit(1) + end +end + +function addr_to_offset(addr) + return math.abs(math.fmod(g_kernbase, addr)) +end + +function write_armv7_jump() + local insn = 0xea000000 | (g_start_off / 4) - 2 + write_packed("> 12) & 0xff + local imm11 = (g_start_off) >> 11 & 0x1 + local imm1001 = (g_start_off) >> 1 & 0x3ff + local imm20 = (g_start_off) >> 20 & 0x1 + + write_packed(" \n", arg[0]) + os.exit(1) +end + +read_symbols(arg[1]) +g_written = 0 +g_start_off = addr_to_offset(g_start) +g_end_off = addr_to_offset(g_end) + +if arg[2] == "v7jump" then + write_armv7_jump() + write_padding() + return +end + +if arg[2] == "v8jump" then + write_armv8_jump() + write_padding() + return +end + +if arg[2] == "v8booti" then + write_armv8_booti() + write_padding() + return +end + +if arg[2] == "riscvbooti" then + write_riscv_booti() + write_padding() + return +end + +printf("error: unknown action %s\n", arg[2]) +os.exit(1)