#!/usr/bin/env sh # SPDX-License-Identifier: GPL-3.0-only # SPDX-FileCopyrightText: 2022 Caleb La Grange # SPDX-FileCopyrightText: 2022 Ferass El Hafidi # SPDX-FileCopyrightText: 2023 Leah Rowe . "include/err.sh" . "include/defconfig.sh" . "include/blobutil.sh" main() { [ $# -gt 0 ] || \ err "No argument given" board="${1}" boarddir="${cbcfgsdir}/${board}" check_defconfig "${boarddir}" || exit 0 detect_firmware && exit 0 scan_sources_config build_dependencies download_blobs } detect_firmware() { set -- "${boarddir}/config/"* . "${1}" 2>/dev/null for c in CONFIG_HAVE_MRC CONFIG_HAVE_ME_BIN CONFIG_KBC1126_FIRMWARE \ CONFIG_VGA_BIOS_FILE CONFIG_INCLUDE_SMSC_SCH5545_EC_FW; do eval "[ -z \"\${${c}}\" ] || return 1" done printf "Blobs not needed for: %s\n" "${board}" 1>&2 } scan_sources_config() { _b=${board%%_*mb} # shorthand to avoid duplicating config per rom size awkstr=" /\{.*${_b}.*}{/ {flag=1;next} /\}/{flag=0} flag { print }" while read -r line ; do set ${line} 1>/dev/null 2>/dev/null eval "${1}=\"${2}\"" done << EOF $(eval "awk '${awkstr}' config/blobs/sources") EOF } build_dependencies() { [ -d ${cbdir} ] || \ ./update project trees coreboot ${cbdir##*/} || \ err "build_dependencies: can't fetch ${cbdir}" for d in uefitool biosutilities bios_extract me_cleaner; do [ -d "${d}" ] && continue ./update project repo "${d}" || \ err "build_dependencies: can't fetch ${d}" done [ -f uefitool/uefiextract ] || \ ./handle make file -b uefitool || \ err "build_dependencies: can't build uefitool" if [ ! -f "${cbdir}/util/kbc1126/kbc1126_ec_dump" ]; then make -C "${cbdir}/util/kbc1126" || \ err "build_dependencies: can't build kbc1126_ec_dump" fi } download_blobs() { [ -z "${CONFIG_HAVE_ME_BIN}" ] || \ download_blob_intel_me || err "download_blobs ${board}: !me" [ -z "${CONFIG_INCLUDE_SMSC_SCH5545_EC_FW}" ] || \ download_sch5545ec || err "download_blobs ${board}: !sch5545" [ -z "${CONFIG_KBC1126_FIRMWARE}" ] || \ download_ec || err "download_blobs ${board}: kbc1126" [ -z "${CONFIG_VGA_BIOS_FILE}" ] || \ download_e6400vga || err "download_blobs ${board}: !e6400vga" if [ ! -z "${CONFIG_HAVE_MRC}" ]; then ./update blobs mrc || err "download_blobs ${board}: !mrc" fi } download_blob_intel_me() { printf "Downloading neutered ME for board: %s\n" "${board}" fetch_update "${DL_url}" "${DL_url_bkup}" "${DL_hash}" || return 1 extract_blob_intel_me || return 1 } extract_blob_intel_me() { printf "Extracting neutered ME for %s\n" "${board}" _me_destination=${CONFIG_ME_BIN_PATH#../../} mk_blobdir "${_me_destination}" "extract_blob_intel_me" || return 0 mk_appdir "extract_blob_intel_me" bruteforce_extract_blob_intel_me "$(pwd)/${_me_destination}" \ "$(pwd)/${appdir}" || \ err "extract_blob_intel_me: could not extract Intel ME firmware" [ -f "${_me_destination}" ] || \ err "extract_blob_intel_me, ${board}: me.bin missing" printf "Truncated and cleaned me output to: %s\n" "${_me_destination}" } # cursed, carcinogenic code. TODO rewrite it better bruteforce_extract_blob_intel_me() { _me_destination="${1}" cdir="${2}" # must be an absolute path, not relative [ -f "${_me_destination}" ] && return 0 sdir="$(mktemp -d)" mkdir -p "${sdir}" || return 1 ( printf "Entering %s\n" "${cdir}" cd "${cdir}" || \ err "bruteforce_extract_blob_intel_me: can't cd \"${cdir}\"" for i in *; do if [ -f "${_me_destination}" ]; then # me.bin found, so avoid needless further traversal break elif [ -L "${i}" ]; then # symlinks are a security risk, in this context continue elif [ -f "${i}" ]; then "${mecleaner}" -r -t -O "${sdir}/vendorfile" \ -M "${_me_destination}" "${i}" \ && break # (we found me.bin) "${mecleaner}" -r -t -O "${_me_destination}" "${i}" \ && break # (we found me.bin) "${me7updateparser}" -O "${_me_destination}" "${i}" \ && break # (we found me.bin) _7ztest="${_7ztest}a" extract_archive "${i}" "${_7ztest}" || continue bruteforce_extract_blob_intel_me "${_me_destination}" \ "${cdir}/${_7ztest}" elif [ -d "$i" ]; then bruteforce_extract_blob_intel_me "${_me_destination}" \ "${cdir}/${i}" else printf "SKIPPING: %s\n" "${i}" continue fi cdir="${1}" cd "${cdir}" # audit note: we already checked this (see above) done ) rm -Rf "${sdir}" || \ err "bruteforce_extract_blob_intel_me: can't rm -Rf \"${sdir}\"" } download_ec() { printf "Downloading KBC1126 EC firmware for HP laptop\n" fetch_update "${EC_url}" "${EC_url_bkup}" "${EC_hash}" || return 1 extract_blob_kbc1126_ec || return 1 } extract_blob_kbc1126_ec() { printf "Extracting KBC1126 EC firmware for board: %s\n" ${board} _ec_destination=${CONFIG_KBC1126_FW1#../../} mk_blobdir "${_ec_destination}" "extract_blob_kbc1126_ec" || return 0 mk_appdir "extract_blob_kbc1126_ec" ( cd "${appdir}/" || \ err "extract_blob_kbc1126_ec: !cd \"${appdir}/\"" mv Rompaq/68*.BIN ec.bin || : if [ ! -f ec.bin ]; then unar -D ROM.CAB Rom.bin || \ unar -D Rom.CAB Rom.bin || \ unar -D 68*.CAB Rom.bin || \ err "extract_blob_kbc1126_ec: can't extract ec.bin" mv Rom.bin ec.bin || \ err "extract_blob_kbc1126_ec: *didn't* extract ec.bin" fi [ -f ec.bin ] || \ err "extract_blob_kbc1126_ec: ${board}: can't extract ec.bin" "${kbc1126_ec_dump}" ec.bin || \ err "extract_blob_kbc1126_ec: ${board}: can't extract ecfw1/2.bin" ) ec_ex="y" for i in 1 2; do [ -f "${appdir}/ec.bin.fw${i}" ] || ec_ex="n" done [ "${ec_ex}" = "y" ] || \ err "extract_blob_kbc1126_ec: ${board}: didn't extract ecfw1/2.bin" cp "${appdir}/"ec.bin.fw* "${_ec_destination%/*}/" || \ err "extract_blob_kbc1126_ec: cant mv ecfw1/2 ${_ec_destination%/*}" } download_e6400vga() { printf "Downloading Nvidia VGA ROM for Dell Latitude E6400\n" fetch_update "${E6400_VGA_DL_url}" "${E6400_VGA_DL_url_bkup}" \ "${E6400_VGA_DL_hash}" || return 1 extract_e6400vga || return 1 } extract_e6400vga() { printf "Extracting Nvidia VGA ROM for ${board}\n" _vga_destination=${CONFIG_VGA_BIOS_FILE#../../} mk_blobdir "${_vga_destination}" "extract_e6400vga" || return 0 mk_appdir "extract_e6400vga" [ "${E6400_VGA_offset}" = "" ] && \ err "extract_e6400vga: E6400 VGA offset not defined" [ "${E6400_VGA_romname}" = "" ] && \ err "extract_e6400vga: E6400 VGA ROM name not defined" tail -c +${E6400_VGA_offset} "${dl_path}" | \ gunzip >"${appdir}/bios.bin" || : ( cd "${appdir}" || err "extract_e6400vga: can't cd ${appdir}" [ -f "bios.bin" ] || err "extract_e6400vga: can't extract bios.bin" "${e6400_unpack}" bios.bin || printf "TODO: fix dell extract util\n" [ -f "${E6400_VGA_romname}" ] || \ err "extract_e6400vga: can't extract vga rom from bios.bin" ) cp "${appdir}"/"${E6400_VGA_romname}" "${_vga_destination}" || \ err "extract_e6400vga: can't copy vga rom to ${_vga_destination}" printf "E6400 Nvidia ROM saved to: %s\n" "${_vga_destination}" } download_sch5545ec() { printf "Downloading SMSC SCH5545 Environment Controller firmware\n" fetch_update "${SCH5545EC_DL_url}" "${SCH5545EC_DL_url_bkup}" \ "${SCH5545EC_DL_hash}" || return 1 extract_sch5545ec || return 1 } # TODO: this code is cancer. hardcoded is bad, and stupid. # TODO: make it *scan* (based on signature, in each file) extract_sch5545ec() { printf "Extracting SCH5545 Environment Controller firmware for '%s'\n" \ ${board} rm -Rf "${dl_path}_extracted" || err "!rm ${dl_path}_extracted" _sch5545ec_destination=${CONFIG_SMSC_SCH5545_EC_FW_FILE#../../} mk_blobdir "${_sch5545ec_destination}" "extract_sch5545ec" || return 0 mk_appdir "extract_sch5545ec" # full system ROM (UEFI), to extract with UEFIExtract: _bios="${dl_path}_extracted/Firmware" _bios="${_bios}/1 ${dlsum} -- 1 System BIOS vA.28.bin" # this is the SCH5545 firmware, inside of the extracted UEFI ROM: _sch5545ec_fw="${_bios}.dump/4 7A9354D9-0468-444A-81CE-0BF617D890DF" _sch5545ec_fw="${_sch5545ec_fw}/54 D386BEB8-4B54-4E69-94F5-06091F67E0D3" _sch5545ec_fw="${_sch5545ec_fw}/0 Raw section/body.bin" # <-- this! # this makes the file defined by _sch5545ec_fw available to copy "${uefiextract}" "${_bios}" || \ err "extract_sch5545ec: cannot extract from uefi image" cp "${_sch5545ec_fw}" "${_sch5545ec_destination}" || \ err "extract_sch5545ec: cannot copy sch5545ec firmware file" } fetch_update() { printf "Fetching vendor update for board: %s\n" "${board}" dl="${1}" dl_bkup="${2}" dlsum="${3}" dl_path="${blobdir}/cache/${dlsum}" mkdir -p "${blobdir}/cache" || err "fetch_update: !mkdir ${blobdir}/cache" dl_fail="y" vendor_checksum "${dlsum}" && dl_fail="n" for x in "${dl}" "${dl_bkup}"; do [ "${dl_fail}" = "n" ] && break [ -z "${x}" ] && continue rm -f "${dl_path}" || \ err "fetch_update: !rm -f ${dl_path}" wget --tries 3 -U "${agent}" "${x}" -O "${dl_path}" || continue vendor_checksum "${dlsum}" && dl_fail="n" done if [ "${dl_fail}" = "y" ]; then printf "ERROR: invalid vendor updates for: %s\n" "${board}" 1>&2 err "fetch_update ${dlsum}: matched vendor update unavailable" fi } vendor_checksum() { if [ ! -f "${dl_path}" ]; then printf "Vendor update not found on disk for: %s\n" "${board}" \ 1>&2 return 1 elif [ "$(sha512sum ${dl_path} | awk '{print $1}')" != "${1}" ]; then printf "Bad checksum on vendor update for: %s\n" "${board}" 1>&2 return 1 fi } mk_blobdir() { [ -f "${1}" ] && \ printf "${2} firmware already downloaded\n" 1>&2 && return 1 if [ ! -d "${1%/*}" ]; then mkdir -p "${1%/*}" || err "${2}: mkdir ${1%/*}" fi } mk_appdir() { [ ! -d "${appdir}" ] || rm -Rf "${appdir}" || \ err "${1}: can't remove ${appdir}" mkdir -p "${appdir}/" || err "${1}: !mkdir ${appdir}" extract_archive "${dl_path}" "${appdir}" || \ [ "${1}" = "extract_e6400vga" ] || err "${1}: can't extract update" } extract_archive() { innoextract "${1}" -d "${2}" || 7z x "${1}" -o"${2}" || \ unar "${1}" -o "${2}" || \ python "${pfs_extract}" "${1}" -e || return 1 } main $@