123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637 |
- #!/usr/bin/env bash
- # use TERM to exit on error
- trap "exit 1" TERM
- export TOP_PID=$$
- # coreboot repo
- COREBOOT_REPO_PURISM="https://source.puri.sm/coreboot/coreboot.git"
- COREBOOT_REPO_TAG="4.14-Purism-1"
- # dependencies
- DEPS_BASE=(dmidecode wget sha256sum gzip)
- DEPS_FLASH=(git build-essential libpci-dev libudev-dev zlib1g-dev)
- DEPS_FLASH_FEDORA=(git gcc g++ make xz bzip2 pciutils-devel)
- DEPS_COREBOOT=(gnat m4 bison flex libncurses5-dev python python2)
- DEPS_COREBOOT_FEDORA=(gcc-gnat bison flex ncurses-devel zlib-devel python python2 patch)
- DEPS_GRUB2=(autoconf automake autopoint libfreetype-dev fonts-unifont)
- DEPS_GRUB2_FEDORA=(autoconf automake gettext-devel freetype-devel unifont-fonts)
- #local dirs
- LOCAL_TOOLS_PATH="./tools"
- LOCAL_TEMP_DIR="$(mktemp -d)"
- # locally compiled coreboot utils
- CBFSTOOL_CB="./coreboot/util/cbfstool/cbfstool"
- IFDTOOL_CB="./coreboot/util/ifdtool/ifdtool"
- BACKUP_DIR="./backups"
- # functions, functions, functions
- check_root() {
- if [[ "$EUID" != 0 ]]; then
- die "This script must be run as root; re-run prefixed with sudo"
- fi
- }
- print_info () {
-
- echo "################################"
- echo "Librem 14 coreboot+Grub2 builder "
- echo "################################"
- }
- initial_setup () {
- # get model, firmware version, serial # via dmidecode
- vendor=$(dmidecode -s bios-vendor)
- model=$(dmidecode -s system-product-name | grep -i Librem)
- CURRENT_FW_VER=$(dmidecode -s bios-version)
- CURRENT_FW_DATE=$(dmidecode -s bios-release-date)
- DMIDECODE_SERIAL_NUMBER=$(dmidecode -t 1 | grep "Serial Number" | cut -d' ' -f 3-)
- # set CURRENT_FW_TYPE type
- if [ "$vendor" = "coreboot" ]; then
- case ${CURRENT_FW_VER^^} in
- *"PUREBOOT"* )
- CURRENT_FW_TYPE="HEADS";;
- *"HEADS"* )
- CURRENT_FW_TYPE="HEADS";;
- *"GRUB2"* )
- CURRENT_FW_TYPE="GRUB2";;
- * )
- CURRENT_FW_TYPE="STANDARD";;
- esac
- else
- CURRENT_FW_TYPE="UNKNOWN"
- fi
- FW_UPDATE=""
- case $CURRENT_FW_TYPE in
- "HEADS" )
- FW_TYPE_STRING="PureBoot (coreboot+Heads)"
- CURR_FW_NUM=`echo ${CURRENT_FW_VER} | sed -e "s/^PureBoot-beta-//" -e "s/^PureBoot-Release-//"`
- UPD_FW_NUM=`echo ${COREBOOT_HEADS_VERSION} | sed -e "s/^PureBoot-beta-//" -e "s/^PureBoot-Release-//"`
- [[ "$UPD_FW_NUM" > "$CURR_FW_NUM" ]] \
- && FW_UPDATE=`echo ${COREBOOT_HEADS_VERSION} | tr '-' ' '`
- CURRENT_FW_VER=`echo ${CURRENT_FW_VER#PureBoot-} | tr '-' ' '`
- CURRENT_FW_DATE=""
- ;;
- "STANDARD" )
- FW_TYPE_STRING="Standard (coreboot+SeaBIOS)"
- [[ "$COREBOOT_SEABIOS_VERSION" > "$CURRENT_FW_VER" ]] \
- && FW_UPDATE="$COREBOOT_SEABIOS_VERSION";
- CURRENT_FW_DATE="(${CURRENT_FW_DATE})"
- ;;
- "GRUB2" )
- FW_TYPE_STRING="Grub2 (coreboot+Grub2)"
- [[ "$COREBOOT_GRUB2_VERSION" > "$CURRENT_FW_VER" ]] \
- && FW_UPDATE="$COREBOOT_GRUB2_VERSION";
- CURRENT_FW_DATE="(${CURRENT_FW_DATE})"
- ;;
- * )
- FW_TYPE_STRING="Unknown" ;;
- esac
- # set LIBREM_MODEL, MODEL_INDEX
- # strip leading 'librem' + space/underscore
- model=`echo ${model,,} | sed -e "s/^librem//" -e "s/^ //" -e "s/^_//"`
- case ${model} in
- "14")
- LIBREM_MODEL="14";
- PLATFORM="CML";
- MODEL_INDEX="9";;
- *)
- LIBREM_MODEL="unknown";
- PLATFORM="UNK";
- MODEL_INDEX="0";;
- esac
- }
- cleanup() {
- # ensure all files owned by calling/non-root user
- chown -R -f $(logname). .
- }
- die () {
- local msg=$1
- if [ ! -z "$msg" ]; then
- echo ""
- echo -e "$msg"
- echo ""
- fi
- kill -s TERM $TOP_PID
- cleanup
- exit 1
- }
- get_flashrom () {
- # check if flashrom exists on system
- echo ""
- echo "Checking for usable version of flashrom"
- FLASHROM_CMD=$(which flashrom)
- if [[ ! -z "$FLASHROM_CMD" && "$PLATFORM" != "CML" ]]; then
- #check version
- version=$($FLASHROM_CMD -R | awk -F" " '{print $2;exit}' | grep "1.")
- if [[ "$version" > "v1.1" ]]; then
- # verify build supports logging (some PureOS/Debian versions broken)
- if $FLASHROM_CMD -L -o /tmp/flashrom.log >/dev/null 2>&1 ; then
- #use system flashrom
- echo "Found built-in flashrom version $version"
- return
- fi
- fi
- fi
- # no system flashrom, not version 1.2+, or PLATFORM == CML:
- # build from upstream flashrom source
- # check deps first
- check_dependencies "flashrom"
- if [ ! -d ${LOCAL_TOOLS_PATH}/flashrom ]; then
- (
- mkdir -p ${LOCAL_TOOLS_PATH}
- cd ${LOCAL_TOOLS_PATH}
- echo -n "Cloning flashrom git repo..."
- git clone --single-branch --branch master https://review.coreboot.org/flashrom.git >/dev/null
- [ $? -ne 0 ] && die "Error cloning flashrom repo from git"
- echo "done"
- )
- fi
- (
- echo -n "Building flashrom from source..."
- cd ${LOCAL_TOOLS_PATH}/flashrom
- git fetch origin >/dev/null 2>&1
- if ! git show c64486b >/dev/null 2>&1; then
- cd ..
- rm -rf ./flashrom
- git clone --single-branch --branch master https://review.coreboot.org/flashrom.git >/dev/null
- [ $? -ne 0 ] && die "Error cloning flashrom repo from git"
- cd flashrom
- fi
- git reset --hard c64486b >/dev/null 2>&1
- [ $? -ne 0 ] && die "Error checking out flashrom via git"
- make CONFIG_NOTHING=yes CONFIG_DUMMY=yes CONFIG_INTERNAL=yes WARNERROR=no
- [ $? -ne 0 ] && die "Error building flashrom (missing build dependency libpci-dev?)"
- echo "done"
- )
- FLASHROM_CMD="${LOCAL_TOOLS_PATH}/flashrom/flashrom"
- }
- get_serial () {
- if [ "$PLATFORM" = "BDW" ]; then
- #no serial to set, clear file if exists
- rm -f ${LOCAL_TEMP_DIR}/serial_number.txt >/dev/null
- return;
- fi
- #prompt user for serial # selection/entry
- echo ""
- echo "Set the device serial number:"
- echo ""
- if [ "$DMIDECODE_SERIAL_NUMBER" != "" ]; then
- DMIDECODE_SERIAL_OPT=1
- MANUAL_SERIAL_OPT=2
- NO_SERIAL_OPT=3
- echo "${DMIDECODE_SERIAL_OPT} - Extracted from your local system (${DMIDECODE_SERIAL_NUMBER})"
- else
- DMIDECODE_SERIAL_OPT=0
- MANUAL_SERIAL_OPT=1
- NO_SERIAL_OPT=2
- fi
- echo "${MANUAL_SERIAL_OPT} - Enter serial number manually"
- echo "${NO_SERIAL_OPT} - Do not set a serial number"
- echo ""
- serial=0
- [ ${MODEL_INDEX} = 0 ] && default=2 || default=1
- while [ "$serial" == "0" ]; do
- read -r -p "Enter your choice (default: $default): " serial
- [ "$serial" = "" ] && serial=$default
- case $serial in
- ${DMIDECODE_SERIAL_OPT} )
- if [ "$DMIDECODE_SERIAL_OPT" = 1 ]; then
- echo -n "$DMIDECODE_SERIAL_NUMBER" > ${LOCAL_TEMP_DIR}/serial_number.txt
- else
- echo "Invalid option"
- serial=0
- fi
- ;;
- ${MANUAL_SERIAL_OPT} )
- read -r -p "Enter the machine's serial number : " serial_number
- echo -n "$serial_number" > ${LOCAL_TEMP_DIR}/serial_number.txt
- ;;
- ${NO_SERIAL_OPT} )
- echo -n "" > ${LOCAL_TEMP_DIR}/serial_number.txt
- ;;
- * )
- echo "Invalid option"
- serial=0
- ;;
- esac
- done
- echo ""
- }
- flashrom_progress() {
- local current=0
- local total_bytes=0
- local percent=0
- local IN=''
- local spin='-\|/'
- local spin_idx=0
- local progressbar=''
- local progressbar2=''
- local status='init'
- local prev_word=''
- local prev_prev_word=''
- progressbar2=$(for ((i=0; i < 49; i++)) do echo -ne ' ' ; done)
- echo "Initializing internal Flash Programmer"
- while true ; do
- prev_prev_word=$prev_word
- prev_word=$IN
- read -r -d' ' IN || break
- if [ "$total_bytes" != "0" ]; then
- current=$(echo "$IN" | grep -E -o '0x[0-9a-f]+-0x[0-9a-f]+:.*' | grep -E -o "0x[0-9a-f]+" | tail -n 1)
- if [ "${current}" != "" ]; then
- percent=$((100 * (current + 1) / total_bytes))
- progressbar=$(for ((i=0; i < $((percent / 2)); i++)) do echo -ne '#' ; done)
- progressbar2=$(for ((i=0; i < $((49 - percent / 2)); i++)) do echo -ne ' ' ; done)
- fi
- else
- if [ "$prev_prev_word" == "Reading" ] && [ "$IN" == "bytes" ]; then
- total_bytes=$prev_word
- echo "Total flash size : $total_bytes bytes"
- fi
- fi
- if [ "$percent" -eq 100 ]; then
- spin_idx=4
- else
- spin_idx=$(( (spin_idx+1) %4 ))
- fi
- if [ "$status" == "init" ]; then
- if [ "$IN" == "contents..." ]; then
- status="reading"
- echo "Reading old flash contents. Please wait..."
- fi
- fi
- if [ "$status" == "reading" ]; then
- if echo "${IN}" | grep "done." > /dev/null ; then
- status="writing"
- fi
- fi
- if [ "$status" == "writing" ]; then
- echo -ne "Flashing: [${progressbar}${spin:$spin_idx:1}${progressbar2}] (${percent}%)\\r"
- if echo "$IN" | grep "Verifying" > /dev/null ; then
- status="verifying"
- echo ""
- echo "Verifying flash contents. Please wait..."
- fi
- if echo "$IN" | grep "identical" > /dev/null ; then
- status="done"
- echo ""
- echo "The flash contents are identical to the image being flashed."
- fi
- fi
- if [ "$status" == "verifying" ]; then
- if echo "${IN}" | grep "VERIFIED." > /dev/null ; then
- status="done"
- echo "The flash contents were verified and the image was flashed correctly."
- fi
- fi
- done
- echo ""
- if [ "$status" == "done" ]; then
- return 0
- else
- echo 'Error flashing coreboot -- see timestampped flashrom log in current directory for more info'
- echo ""
- return 1
- fi
- }
- set_serial_number () {
- # pass in full path of file in which to inject serial
- [[ -z "$1" || ! -f "$1" ]] && die "Error: a valid firmware filename is required"
- # check if we have a serial # to add to cbfs
- if [ -f ${LOCAL_TEMP_DIR}/serial_number.txt ]; then
- # get cbfstool if needed
- get_cbfstool
- # inject serial into coreboot image
- echo "Injecting serial number into firmware image"
- $CBFSTOOL_BIN "$1" remove -n serial_number -r COREBOOT >/dev/null 2>&1
- $CBFSTOOL_BIN "$1" add -n serial_number -t raw -f resources/serial_number.txt -r COREBOOT >/dev/null 2>&1
- [ $? -ne 0 ] && die "Error adding serial number to file ${1}"
- fi
- }
- backup_firmware () {
- # assume FLASHROM_CMD exists/is set already, LOCAL_TEMP_DIR set up
- mkdir -p ${BACKUP_DIR}
- DATE_FIX=$(date '+%Y%m%d-%H%M%S')
- $FLASHROM_CMD -p internal:ich_spi_mode=hwseq -r ${BACKUP_DIR}/backup-${DATE_FIX}.bin -o ${BACKUP_DIR}/backup-$(date '+%Y%m%d-%H%M%S').log >/dev/null 2>&1
- if [ $? -ne 0 ]; then
- die "Error reading current firmware; see flashrom log for more info."
- fi
- }
- flash_firmware_internal () {
- # assume FLASHROM_CMD exists/is set already
- # pass in full path of file to be flashed
- [[ -z "$1" || ! -f "$1" ]] && die "Error: a valid filename to be flashed is required"
- $FLASHROM_CMD -p internal:ich_spi_mode=hwseq -w "$1" -V -o "./flashrom-$(date '+%Y%m%d-%H%M%S').log" 2>&1 | flashrom_progress
- return $?
- }
- update_serial_number () {
- # ensure using SeaBIOS
- if [[ "${CURRENT_FW_TYPE}" != "STANDARD" ]]; then
- echo "This feature is only valid for use with the standard/SeaBIOS firmware"
- echo ""
- return
- fi
- # show serial menu / create serial.txt
- get_serial
- # check for / get flashrom
- get_flashrom
- # read current firmware
- echo ""
- if [ ! -f ${LOCAL_TEMP_DIR}/${CURRENT_FW_BIN} ]; then
- echo "Reading current firmware..."
- read_current_firmware
- else
- echo "Current firmware already read from flash"
- fi
-
- # inject into current firmware
- set_serial_number "${LOCAL_TEMP_DIR}/${CURRENT_FW_BIN}"
- # prompt to update
- echo ""
- flash=0
- while [ "$flash" != "y" ] && [ "$flash" != "n" ]; do
- read -r -p "Do you want to update the serial number now (Y/n) ? " flash
- [[ "$flash" == "N" ]] && flash="n"
- [[ "$flash" = "" || "$flash" == "Y" ]] && flash="y"
- done
- if [ "$flash" == "y" ]; then
- echo ""
- echo "coreboot flashing in progress. Do NOT interrupt this process."
- echo ""
- flash_firmware_internal "${LOCAL_TEMP_DIR}/${CURRENT_FW_BIN}"
- if [ $? -eq 0 ]; then
- echo "Serial number successfully updated; change will take effect on next boot"
- echo ""
- fi
- fi
- }
- update_crossgcc_toolchain() {
- # assume called from coreboot root dir
- local CURRENT_TOOLCHAIN_VERSION=0
- local GCC_FILE='util/crossgcc/xgcc/bin/i386-elf-gcc'
- local TARGET_TOOLCHAIN_VERSION="$(git log -n 1 --pretty=%h util/crossgcc)"
- if [ -f "${GCC_FILE}" ]; then
- CURRENT_TOOLCHAIN_VERSION=$(${GCC_FILE} --version | grep -m 1 'coreboot toolchain' \
- | cut -f2 -d'v' | cut -f2 -d'_' | cut -f1 -d')')
- fi
- if [ "${CURRENT_TOOLCHAIN_VERSION}" != "${TARGET_TOOLCHAIN_VERSION}" ]; then
- echo "coreboot toolchain version changed from ${CURRENT_TOOLCHAIN_VERSION} to ${TARGET_TOOLCHAIN_VERSION}"
- echo "Cleaning crossgcc compiler before rebuilding it"
- make crossgcc-clean
- retries=0
- until [ "$retries" -ge 5 ]
- do
- make crossgcc-i386 CPUS=$(nproc) && break
- retries=$((retries+1))
- done
- [ $? -ne 0 ] && die "Error building coreboot toolchain" || true
- fi
- }
- build_coreboot() {
- # extract / set device serial # to be injected later
- get_serial
- echo ""
- echo "Checking out/updating coreboot repo..."
- echo ""
- # check dir, clone if needed
- if [ ! -d coreboot ]; then
- echo ""
- git clone --branch ${COREBOOT_REPO_TAG} ${COREBOOT_REPO_PURISM} coreboot
- [ $? -ne 0 ] && die "Error cloning coreboot git repo"
- (
- cd coreboot
- # It can fail if you don't have a git global user.name/user.email setup
- make gitconfig 2>/dev/null || true
- # init/update submodules
- git submodule update --init --force --checkout >/dev/null 2>&1
- [ $? -ne 0 ] && die "Error checking out/updating coreboot submodules"
- )
- fi
- # check out correct branch
- (
- cd coreboot
- git fetch 2>/dev/null
- [ $? -ne 0 ] && die "Error fetching coreboot git repo"
- git fetch --tags 2>/dev/null
- [ $? -ne 0 ] && die "Error fetching coreboot git repo"
- git checkout --detach ${COREBOOT_REPO_TAG} 2>/dev/null
- [ $? -ne 0 ] && die "Error checking out coreboot git repo"
- # ensure submodules sane
- if [[ "`git diff 3rdparty`" != "" ]]; then
- git submodule update --force --checkout >/dev/null 2>&1
- [[ "`git diff 3rdparty`" != "" ]] && \
- die "submodules have been modified; build would not be reproducible"
- fi
- #build cbfstool and ifdtool
- (
- cd util/cbfstool
- make
- [ $? -ne 0 ] && die "Error building cbfstool"
- )
- (
- cd util/ifdtool
- make
- [ $? -ne 0 ] && die "Error building ifdtool"
- )
- )
- # build coreboot
- (
- # set pwd
- cd coreboot
- # let user know this will take a few
- echo -e "\n\n"
- echo "Ready to build coreboot - this will take some time depending on your connection"
- echo "speed and CPU/RAM, esp if the toolchain needs to be built."
- echo ""
- # check/build toolchain
- update_crossgcc_toolchain || die
- # get git version
- GIT_VERSION=$(git describe --tags --dirty)
- # do a clean build
- make distclean
- # copy config
- cp ../resources/config .config
- # copt grub config
- cp ../resources/grub.cfg grub.cfg
- echo "CONFIG_LOCALVERSION=\"${GIT_VERSION}\"" >> .config
- make olddefconfig >/dev/null
- # build coreboot and payload(s)
- make
- [ $? -ne 0 ] && die "Error building coreboot"
- # copy to root dir
- #cp build/coreboot.rom ../${COREBOOT_IMAGE}
- )
- # calculate hash of BIOS region before injecting bootorder/serial
- ${IFDTOOL_CB} -x ${COREBOOT_IMAGE}
- # set serial
- set_serial_number ${COREBOOT_IMAGE}
- # print CBFS contents
- ${CBFSTOOL_CB} ${COREBOOT_IMAGE} print
- echo ""
- echo ""
- echo "Finished building coreboot for Librem ${LIBREM_MODEL^}"
- echo ""
- # prompt to flash
- echo ""
- flash=0
- while [ "$flash" != "y" ] && [ "$flash" != "n" ]; do
- read -r -p "Do you want to flash the coreboot update now (y/N) ? " flash
- if [ "$flash" = "" ] || [ "$flash" == "N" ]; then
- flash="n"
- fi
- if [ "$flash" == "Y" ]; then
- flash="y"
- fi
- done
- if [ "$flash" == "y" ]; then
- # check for / get flashrom
- get_flashrom
- echo ""
- echo "coreboot flashing in progress. Do NOT interrupt this process."
- echo ""
- flash_firmware_internal ${COREBOOT_IMAGE}
- if [ $? -eq 0 ]; then
- echo ""
- echo "You must reboot for the coreboot update to take effect."
- echo ""
- read -r -p "Reboot now? (y/N) ? " rb
- if [ "$rb" = "Y" ] || [ "$rb" == "y" ]; then
- cleanup
- reboot
- fi
- fi
- else
- echo ""
- fi
- }
- check_dependencies() {
- local dep_type=$1
- local DEPS
- local use_dnf=false
- [[ `which dnf 2>/dev/null` ]] && use_dnf=true
- case $dep_type in
- "base")
- DEPS=${DEPS_BASE[@]} ;;
- "flashrom")
- if [ $use_dnf = true ]; then
- DEPS=(${DEPS_BASE[@]} ${DEPS_FLASH_FEDORA[@]}) ;
- else
- DEPS=(${DEPS_BASE[@]} ${DEPS_FLASH[@]}) ;
- fi
- ;;
- "coreboot")
- if [ $use_dnf = true ]; then
- DEPS=(${DEPS_BASE[@]} ${DEPS_FLASH_FEDORA[@]} ${DEPS_GRUB2_FEDORA[@]}) ;
- else
- DEPS=(${DEPS_BASE[@]} ${DEPS_FLASH[@]} ${DEPS_COREBOOT[@]} ${DEPS_GRUB2[@]}) ;
- fi
- ;;
- *)
- ;;
- esac
- local pkg needed=()
- for pkg in "${DEPS[@]}"; do
- if [[ ! `which ${pkg} 2>/dev/null` \
- && ! `dnf list installed 2>/dev/null | grep "^${pkg}"` \
- && ! `apt list --installed 2>/dev/null | grep "^${pkg}"` \
- && ! -f "/usr/share/doc/${pkg}/copyright" ]]; then
- needed+=("${pkg}")
- fi
- done
- if [[ "${#needed[@]}" -gt 0 ]]; then
- echo ""
- echo "One or more required dependencies are missing: ${needed[@]}"
- echo "The script will now attempt to install them for you"
- echo ""
- if [ $use_dnf = true ]; then
- if ! sudo dnf -y install "${needed[@]}" ; then
- die "Some required dependencies could not be installed automatically"
- fi
- elif which apt >/dev/null ; then
- sudo apt-get update >/dev/null 2>&1
- if ! sudo apt-get -y install "${needed[@]}" ; then
- die "Some required dependencies could not be installed automatically"
- fi
- else
- echo "The script was unable to install the required dependencies automatically"
- echo ""
- die "Please ensure all required dependencies are installed and re-run this script"
- fi
- fi
- }
- # Start of main script
- # let's do stuff
- check_root
- check_dependencies "base"
- check_dependencies "flashrom"
- check_dependencies "coreboot"
- initial_setup
- print_info
- get_flashrom
- build_coreboot
|