#!/usr/bin/env bash # helper script: create ROM images for a given mainboard # # Copyright (C) 2020,2021 Leah Rowe # # 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 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 [ "${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 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 descriptors ich9m 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 descriptors ich9m fi dd if=descriptors/ich9m/ich9fdnogbe_${romsize}m.bin of=${newrompath} bs=1 count=4k conv=notrunc fi done if [ "${cuttype}" = "i945 laptop" ]; then dd if=${newrompath} of=top64k.bin bs=1 skip=$[$(stat -c %s ${newrompath}) - 0x10000] count=64k dd if=top64k.bin of=${newrompath} bs=1 seek=$[$(stat -c %s ${newrompath}) - 0x20000] count=64k conv=notrunc rm -f top64k.bin fi } # expected: configs must not specify a payload mkCoreboot() { cbdir="${1}" # e.g. coreboot/default cbcfgpath="${2}" # e.g. resources/coreboot/x200_8mb/config/libgfxinit_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 target_initmode="${4}" # e.g. libgfxinit cbfstool_path="${5}" if [ "${target_initmode}" = "normal" ]; then target_seabioself="payload/seabios/seabios_vgarom.elf" # if normal, etc/pci-optionrom-exec will be set to 2 else target_seabioself="payload/seabios/seabios_${target_initmode}.elf" # if libgfxinit, etc/pci-optionrom-exec will be set to 2 # if vgarom, etc/pci-optionrom-exec will be set to 0 fi 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 if [ "${target_initmode}" = "normal" ] || [ "${target_initmode}" = "libgfxinit" ]; then "${cbfstool}" "${tmprom}" add-int -i 2 -n etc/pci-optionrom-exec elif [ "${target_initmode}" = "vgarom" ]; then "${cbfstool}" "${tmprom}" add-int -i 0 -n etc/pci-optionrom-exec fi # for undefined modes, don't add this integer. rely on SeaBIOS defaults "${cbfstool}" "${tmprom}" add-int -i 0 -n etc/optionroms-checksum "${cbfstool}" "${tmprom}" add-int -i ${target_opromloadonly} -n etc/only-load-option-roms if [ "${target_initmode}" = "libgfxinit" ]; then "${cbfstool_path}" "${tmprom}" add -f "${target_seavgabios_rom}" -n vgaroms/seavgabios.bin -t raw fi 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 "${cbfstool_path}" "${tmprom}" add -f "${grubcfg}" -n grub.cfg -t raw "${cbfstool_path}" "${tmprom}" add -f "${grubtestcfg}" -n grubtest.cfg -t raw 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}" initmode="${2}" displaymode="${3}" firstpayloadname="${4}" # allow values: grub, seabios, seabios_withgrub, seabios_grubfirst if [ "${payload_grub_withseabios}" = "y" ] && [ "${firstpayloadname}" = "grub" ]; then mv "$(make_seabios_rom "${tmprompath}" "seabios.elf" "${seabios_opromloadonly}" "${initmode}" "${cbfstool}")" "${tmprompath}" elif [ "${payload_seabios_withgrub}" ] && [ "${firstpayloadname}" != "grub" ]; then mv "$(make_seabios_rom "${tmprompath}" "fallback/payload" "${seabios_opromloadonly}" "${initmode}" "${cbfstool}")" "${tmprompath}" if [ "${firstpayloadname}" = "seabios_grubfirst" ]; then tmpbootorder=$(mktemp -t coreboot_rom.XXXXXXXXXX) printf "/rom@img/grub2\n" > "${tmpbootorder}" "${cbfstool}" "${tmprompath}" add -f "${tmpbootorder}" -n bootorder -t raw rm -f "${tmpbootorder}" "${cbfstool}" "${tmprompath}" add-int -i 0 -n etc/show-boot-menu fi 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}")" if [ "${initmode}" = "normal" ]; then newrompath="${romdir}/${firstpayloadname}_${board}_${initmode}_${keymap}.rom" else newrompath="${romdir}/${firstpayloadname}_${board}_${initmode}_${displaymode}_${keymap}.rom" fi moverom "${tmpgrubrom}" "${newrompath}" "${romtype}" rm -f "${tmpgrubrom}" done } # Main ROM building function. This calls all other functions mkRoms() { cbcfgpath="${1}" displaymode="${2}" initmode="${3}" if [ ! -f "${cbcfgpath}" ]; then printf "'%s' does not exist. Skipping build for %s %s %s\n" \ "${cbcfgpath}" "${board}" "${displaymode}" "${initmode}" 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}" "${initmode}" "${cbfstool}")" if [ "${initmode}" = "normal" ]; then newrompath="${romdir}/seabios_${board}_${initmode}.rom" else newrompath="${romdir}/seabios_${board}_${initmode}_${displaymode}.rom" fi moverom "${tmpseabiosrom}" "${newrompath}" "${romtype}" rm -f "${tmpseabiosrom}" else tmprom=$(mktemp -t coreboot_rom.XXXXXXXXXX) cp "${corebootrom}" "${tmprom}" mkRomsWithGrub "${tmprom}" "${initmode}" "${displaymode}" "seabios_withgrub" cp "${corebootrom}" "${tmprom}" mkRomsWithGrub "${tmprom}" "${initmode}" "${displaymode}" "seabios_grubfirst" rm -f "${tmprom}" fi fi if [ "${payload_grub}" = "y" ]; then mkRomsWithGrub "${corebootrom}" "${initmode}" "${displaymode}" "grub" fi } initmode="libgfxinit" for displaymode in corebootfb txtmode; do cbcfgpath="resources/coreboot/${board}/config/${initmode}_${displaymode}" mkRoms "${cbcfgpath}" "${displaymode}" "${initmode}" done initmode="vgarom" for displaymode in vesafb txtmode; do cbcfgpath="resources/coreboot/${board}/config/${initmode}_${displaymode}" mkRoms "${cbcfgpath}" "${displaymode}" "${initmode}" done initmode="normal" displaymode="txtmode" cbcfgpath="resources/coreboot/${board}/config/${initmode}" mkRoms "${cbcfgpath}" "${displaymode}" "${initmode}" ( cd "${cbdir}" make distclean )