#!/usr/bin/env bash # helper script: create ROM images for a given mainboard # # Copyright (C) 2020,2021 Leah Rowe # Copyright (C) 2021 Vitali64 # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # This script assumes that the working directory is the root # of git or release archive [ "x${DEBUG+set}" = 'xset' ] && set -v set -u -e projectname="$(cat projectname)" if (( $# != 1 )); then printf "Usage: ./build boot roms boardname\n" printf "Example: ./build boot roms x60\n" printf "Example: ./build boot roms x60 x200_8mb\n" printf "Example: ./build boot roms all\n" printf "You need to specify exactly 1 argument\n" exit 1 fi board="${1}" if [ ! -d "resources/coreboot/${board}" ]; then printf "build/roms: Target %s does not exist in the %s build system. Skipping build.\n" "${projectname}" "${board}" exit 1 fi if [ ! -f "resources/coreboot/${board}/board.cfg" ]; then printf "build/roms: Target %s does not have a board.cfg. Skipping build.\n" "${board}" exit 1 fi # Workaround to grub's slow boot grub_scan_disk="undefined" # both: scan ata and ahci (slow), there is ata and ahci too # as an option cbtree="undefined" romtype="normal" # optional parameter in board.cfg. "normal" is default arch="undefined" # Disable all payloads by default. # board.cfg files have to specifically enable [a] payload(s) payload_grub="n" payload_grub_withseabios="n" # seabios chainloaded from grub payload_seabios="n" payload_seabios_withgrub="n" # i386-coreboot grub accessible from SeaBIOS boot menu seabios_opromloadonly="0" payload_memtest="n" # Override the above defaults using board.cfg source "resources/coreboot/${board}/board.cfg" if [ "${grub_scan_disk}" = "undefined" ]; then printf "build/roms: Target %s does not define grub_scan_disk. Defaulting to 'both'.\n" "${board}" grub_scan_disk="both" fi if [ "${grub_scan_disk}" != "both" ] && \ [ "${grub_scan_disk}" != "ata" ] && \ [ "${grub_scan_disk}" != "ahci" ]; then printf "build/roms: Target %s defines an invalid grub_scan_disk setting. Defaulting to 'both'.\n" "${board}" grub_scan_disk="both" # erroring out would be silly. just use the default fi if [ "${cbtree}" = "undefined" ]; then printf "build/roms: Target %s does not define a coreboot tree. Skipping build.\n" "${board}" exit 1 fi if [ "${arch}" = "undefined" ]; then printf "build/roms: Target %s does not define a CPU type. Skipping build.\n" "${board}" exit 1 fi if [ "${seabios_opromloadonly}" != "0" ] && \ [ "${seabios_opromloadonly}" != "1" ]; then seabios_opromloadonly="0" fi if [ "${payload_memtest}" != "n" ] && \ [ "${payload_memtest}" != "y" ]; then payload_memtest="n" fi if [ "${payload_grub_withseabios}" = "y" ]; then payload_grub="y" fi if [ "${payload_grub_withseabios}" = "y" ]; then payload_seabios="y" payload_seabios_withgrub="y" # if grub-first works, then seabios-with-grub will also work fi if [ "${payload_seabios_withgrub}" = "y" ]; then payload_seabios="y" # if seabios-with-grub works, then SeaBIOS-alone should also work fi # NOTE: reverse logic must not be applied. If SeaBIOS-with-GRUB works, that doesn't # necessarily mean GRUB-with-SeaBIOS will work nicely. for example, the board might # only have an add-on GPU available, where it's recommended to boot SeaBIOS first if [ "${payload_grub}" != "y" ] && [ "${payload_seabios}" != "y" ]; then while true; do for configfile in "resources/coreboot/${board}/config/"*; do if [ -f "${configfile}" ]; then printf "ERROR build/roms: Target '%s' does not define a payload. Exiting.\n" "${board}" exit 1 fi done break done fi if [ "${payload_memtest}" = "y" ]; then if [ ! -f "memtest86plus/memtest" ]; then ./build module memtest86plus fi fi romdir="bin/${board}" cbdir="coreboot/${board}" if [ "${board}" != "${cbtree}" ]; then cbdir="coreboot/${cbtree}" fi cbfstool="${cbdir}/util/cbfstool/cbfstool" corebootrom="${cbdir}/build/coreboot.rom" seavgabiosrom="payload/seabios/seavgabios.bin" if [ ! -d "${cbdir}" ]; then ./download coreboot ${cbtree} fi if [ "${arch}" = "x86_32" ] || [ "${arch}" = "x86_64" ]; then if [ ! -d "${cbdir}/util/crossgcc/xgcc/i386-elf/" ]; then ( cat version > "${cbdir}/.coreboot-version" cd "${cbdir}" make crossgcc-i386 CPUS=$(nproc) # even for 64-bit machines, coreboot builds # 32-bit ROM images, so we only need to worry about i386-elf ) fi elif [ "${arch}" = "ARMv7" ]; then cat version > "${cbdir}/.coreboot-version" cd "${cbdir}" make crossgcc-arm CPUS=$(nproc) # This is for armv7, doesn't apply to aarch64 elif [ "${arch}" = "AArch64" ]; then cat version > "${cbdir}/.coreboot-version" cd "${cbdir}" make crossgcc-aarch64 CPUS=$(nproc) # This is for aarch64, doesn't apply to armv7 fi if [ ! -f "${cbfstool}" ]; then ./build module cbutils ${cbtree} fi if [ ! -f "${seavgabiosrom}" ] \ || [ ! -f payload/seabios/seabios_libgfxinit.elf ] \ || [ ! -f payload/seabios/seabios_vgarom.elf ]; then if [ "${payload_seabios}" = "y" ]; then ./build payload seabios elif [ "${payload_grub}" = "y" ] \ && [ "${payload_grub_withseabios}" = "y" ]; then ./build payload seabios fi fi [ -d "${romdir}/" ] || mkdir -p "${romdir}/" rm -f "${romdir}"/* if [ "${payload_grub}" = "y" ] || [ "${payload_seabios_withgrub}" = "y" ]; then if [ -f "payload/grub/grub_usqwerty.cfg" ]; then grubrefchecksum="$(sha1sum resources/grub/config/grub.cfg)" grubrefchecksum="${grubrefchecksum% resources/grub/config/grub.cfg}" grubbuildchecksum="$(sha1sum payload/grub/grub_usqwerty.cfg)" grubbuildchecksum="${grubbuildchecksum% payload/grub/grub_usqwerty.cfg}" if [ "${grubrefchecksum}" != "${grubbuildchecksum}" ]; then rm -Rf payload/grub/ printf "Changes detected to GRUB. Re-building now:\n" fi else printf "Required GRUB payloads not yet built. Building now:\n" rm -Rf payload/grub/ # just in case fi for keymapfile in resources/grub/keymap/*; do if [ ! -f "${keymapfile}" ]; then continue fi keymap="${keymapfile##*/}" keymap="${keymap%.gkb}" grubelf="payload/grub/grub_${keymap}.elf" grubcfg="payload/grub/grub_${keymap}.cfg" grubtestcfg="payload/grub/grub_${keymap}_test.cfg" if [ ! -f "${grubelf}" ] || [ ! -f "${grubcfg}" ] || \ [ ! -f "${grubtestcfg}" ]; then ./build payload grub fi done fi # it is assumed that no other work will be done on the ROM # after calling this function. therefore this function is "final" moverom() { rompath="$1" newrompath="$2" cuttype="$3" printf "\nCreating new ROM image: %s\n" "${newrompath}" if [ "${cuttype}" = "4MiB IFD BIOS region" ]; then dd if=${rompath} of=${newrompath} bs=1 skip=$[$(stat -c %s ${rompath}) - 0x400000] count=4194304 else cp ${rompath} ${newrompath} fi # pike2008 cards cause a system hang when loading the option rom in seabios # if there is an empty option rom in cbfs, no option rom will be loaded if [ "${cuttype}" = "d8d16sas" ]; then emptyrom=$(mktemp -t coreboot_rom.XXXXXXXXXX) rm -f "${emptyrom}" touch "${emptyrom}" for deviceID in "0072" "3050"; do "${cbfstool}" "${newrompath}" add -f "${emptyrom}" -n pci1000,${deviceID}.rom -t raw done rm -f "${emptyrom}" fi for romsize in 4 8 16; do if [ "${cuttype}" = "${romsize}MiB ICH9 IFD NOR flash" ]; then if [ ! -f "descriptors/ich9m/ich9fdgbe_${romsize}m.bin" ]; then ./build build descriptors fi dd if=descriptors/ich9m/ich9fdgbe_${romsize}m.bin of=${newrompath} bs=1 count=12k conv=notrunc fi if [ "${cuttype}" = "${romsize}MiB ICH9 IFD NOGBE NOR flash" ]; then if [ ! -f "descriptors/ich9m/ich9fdnogbe_${romsize}m.bin" ]; then ./build build descriptors fi dd if=descriptors/ich9m/ich9fdnogbe_${romsize}m.bin of=${newrompath} bs=1 count=4k conv=notrunc fi done } # expected: configs must not specify a payload mkCoreboot() { cbdir="${1}" # e.g. coreboot/default cbcfgpath="${2}" # e.g. resources/coreboot/x200_8mb/config/txtmode if [ ! -f "${cbcfgpath}" ]; then printf "\nmkCoreboot: Coreboot config '%s' does not exist. Skipping build.\n" \ "${cbcfgpath}" return 0 fi printf "%s-%s\n" "$(cat projectname)" "$(cat version)" > "${cbdir}/.coreboot-version" ( cd "${cbdir}" make distclean ) cp "${cbcfgpath}" "${cbdir}"/.config ( cd "${cbdir}" make -j$(nproc) ) } # make a rom in /tmp/ and then print the path of that ROM make_seabios_rom() { target_cbrom="${1}" # rom to insert seabios in. this rom won't be touched # a tmpfile will be made instead target_seabios_cbfs_path="${2}" # e.g. fallback/payload target_opromloadonly="${3}" # 0 or 1. if 1, only load but don't execute oproms cbfstool_path="${4}" # etc/pci-optionrom-exec will be set to 2 # etc/pci-optionrom-exec will be set to 0 target_seabioself="payload/seabios/seabios.elf" target_seavgabios_rom="payload/seabios/seavgabios.bin" tmprom=$(mktemp -t coreboot_rom.XXXXXXXXXX) cp "${target_cbrom}" "${tmprom}" "${cbfstool}" "${tmprom}" add-payload -f "${target_seabioself}" -n ${target_seabios_cbfs_path} -c lzma "${cbfstool}" "${tmprom}" add-int -i 3000 -n etc/ps2-keyboard-spinup "${cbfstool}" "${tmprom}" add-int -i 2 -n etc/pci-optionrom-exec "${cbfstool}" "${tmprom}" add-int -i 0 -n etc/optionroms-checksum "${cbfstool}" "${tmprom}" add-int -i ${target_opromloadonly} -n etc/only-load-option-roms "${cbfstool_path}" "${tmprom}" add -f "${target_seavgabios_rom}" -n vgaroms/seavgabios.bin -t raw printf "%s\n" "${tmprom}" } # make a rom in /tmp/ and then print the path of that ROM make_grubrom_from_keymap() { target_keymap="${1}" target_cbrom="${2}" cbfstool_path="${3}" target_grubelf_cbfs_path="${4}" # e.g. fallback/payload grubelf="payload/grub/grub_${target_keymap}.elf" grubcfg="payload/grub/grub_${target_keymap}.cfg" grubtestcfg="payload/grub/grub_${target_keymap}_test.cfg" tmprom=$(mktemp -t coreboot_rom.XXXXXXXXXX) cp "${target_cbrom}" "${tmprom}" "${cbfstool_path}" "${tmprom}" add-payload -f "${grubelf}" -n ${target_grubelf_cbfs_path} -c lzma tmpgrubcfg=$(mktemp -t grub.cfg.XXXXXXXXXX) tmpgrubtestcfg=$(mktemp -t grubtest.cfg.XXXXXXXXXX) if [ "${grub_scan_disk}" = "ahci" ]; then sed 's/set\ grub_scan_disk=\"both\"/set\ grub_scan_disk=\"ahci\"/' "${grubcfg}" > "${tmpgrubcfg}" sed 's/set\ grub_scan_disk=\"both\"/set\ grub_scan_disk=\"ahci\"/' "${grubtestcfg}" > "${tmpgrubtestcfg}" elif [ "${grub_scan_disk}" = "ata" ]; then sed 's/set\ grub_scan_disk=\"both\"/set\ grub_scan_disk=\"ata\"/' "${grubcfg}" > "${tmpgrubcfg}" sed 's/set\ grub_scan_disk=\"both\"/set\ grub_scan_disk=\"ata\"/' "${grubtestcfg}" > "${tmpgrubtestcfg}" else cp "${grubcfg}" "${tmpgrubcfg}" cp "${grubtestcfg}" "${tmpgrubtestcfg}" fi "${cbfstool_path}" "${tmprom}" add -f "${tmpgrubcfg}" -n grub.cfg -t raw "${cbfstool_path}" "${tmprom}" add -f "${tmpgrubtestcfg}" -n grubtest.cfg -t raw rm -f "${tmpgrubcfg}" "${tmpgrubtestcfg}" backgroundfile="background1280x800.png" if [ "${board}" = "x60" ] || [ "${board}" = "t60_intelgpu" ]; then # TODO: don't hardcode this check. do it in board.cfg per board backgroundfile="background1024x768.png" fi backgroundfile="resources/grub/background/${backgroundfile}" "${cbfstool_path}" "${tmprom}" add -f ${backgroundfile} -n background.png -t raw printf "%s\n" "${tmprom}" } # Make separate ROM images with GRUB payload, for each supported keymap mkRomsWithGrub() { tmprompath="${1}" displaymode="${2}" firstpayloadname="${3}" # allow values: grub, seabios, seabios_withgrub if [ "${payload_grub_withseabios}" = "y" ] && [ "${firstpayloadname}" = "grub" ]; then mv "$(make_seabios_rom "${tmprompath}" "seabios.elf" "${seabios_opromloadonly}" "${cbfstool}")" "${tmprompath}" elif [ "${payload_seabios_withgrub}" ] && [ "${firstpayloadname}" != "grub" ]; then mv "$(make_seabios_rom "${tmprompath}" "fallback/payload" "${seabios_opromloadonly}" "${cbfstool}")" "${tmprompath}" fi for keymapfile in resources/grub/keymap/*; do if [ ! -f "${keymapfile}" ]; then continue fi keymap="${keymapfile##*/}" keymap="${keymap%.gkb}" grub_path_in_cbfs="fallback/payload" if [ "${firstpayloadname}" != "grub" ]; then grub_path_in_cbfs="img/grub2" fi tmpgrubrom="$(make_grubrom_from_keymap "${keymap}" "${tmprompath}" "${cbfstool}" "${grub_path_in_cbfs}")" # Having too much image types complicates things too much for # users. So we even if we can build 2 type of SeaBIOS images, # one with GRUB and one without GRUB, we only want to show a # single image to the user. This also requires to tell users # that they may or may not have GRUB as secondary payload, but # we already have this situation with memtest86+ that may or # may not be in the image depending on the flash size. if [ "${firstpayloadname}" = "seabios_withgrub" ] ; then firstpayloadname="seabios" fi newrompath="${romdir}/${firstpayloadname}_${board}_${displaymode}_${keymap}.rom" moverom "${tmpgrubrom}" "${newrompath}" "${romtype}" rm -f "${tmpgrubrom}" done } # Main ROM building function. This calls all other functions mkRoms() { cbcfgpath="${1}" displaymode="${2}" if [ ! -f "${cbcfgpath}" ]; then printf "'%s' does not exist. Skipping build for %s %s %s\n" \ "${cbcfgpath}" "${board}" "${displaymode}" return 0 fi mkCoreboot "${cbdir}" "${cbcfgpath}" if [ "${displaymode}" = "txtmode" ] && [ "${payload_memtest}" = "y" ]; then "${cbfstool}" "${corebootrom}" add-payload -f memtest86plus/memtest -n img/memtest -c lzma fi if [ "${payload_seabios}" = "y" ]; then if [ "${payload_seabios_withgrub}" = "n" ]; then tmpseabiosrom="$(make_seabios_rom "${corebootrom}" "fallback/payload" "${seabios_opromloadonly}" "${cbfstool}")" newrompath="${romdir}/seabios_${board}_${displaymode}.rom" moverom "${tmpseabiosrom}" "${newrompath}" "${romtype}" rm -f "${tmpseabiosrom}" else tmprom=$(mktemp -t coreboot_rom.XXXXXXXXXX) cp "${corebootrom}" "${tmprom}" mkRomsWithGrub "${tmprom}" "${displaymode}" "seabios_withgrub" rm -f "${tmprom}" fi fi if [ "${payload_grub}" = "y" ]; then mkRomsWithGrub "${corebootrom}" "${displaymode}" "grub" fi } for displaymode in corebootfb txtmode; do cbcfgpath="resources/coreboot/${board}/config/${displaymode}" mkRoms "${cbcfgpath}" "${displaymode}" done ( cd "${cbdir}" make distclean )