mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-20 13:50:11 +01:00
ci/bare-metal: Remove remnants of old bare-metal setups
With the rest of the Qualcomm devices moving to LAVA, we can remove the original (!) bare-metal infrastructure, leaving only the Igalia RPi devices still using bare-metal. When those are converted to b2c, we can remove the rest of bare-metal. Signed-off-by: Daniel Stone <daniels@collabora.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/35148>
This commit is contained in:
parent
38caef8fa2
commit
086d7cb8fd
17 changed files with 11 additions and 1392 deletions
|
|
@ -1,122 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# shellcheck disable=SC1091 # The relative paths in this file only become valid at runtime.
|
|
||||||
# shellcheck disable=SC2034
|
|
||||||
# shellcheck disable=SC2086 # we want word splitting
|
|
||||||
|
|
||||||
# Boot script for Chrome OS devices attached to a servo debug connector, using
|
|
||||||
# NFS and TFTP to boot.
|
|
||||||
|
|
||||||
# We're run from the root of the repo, make a helper var for our paths
|
|
||||||
BM=$CI_PROJECT_DIR/install/bare-metal
|
|
||||||
CI_COMMON=$CI_PROJECT_DIR/install/common
|
|
||||||
CI_INSTALL=$CI_PROJECT_DIR/install
|
|
||||||
|
|
||||||
# Runner config checks
|
|
||||||
if [ -z "$BM_SERIAL" ]; then
|
|
||||||
echo "Must set BM_SERIAL in your gitlab-runner config.toml [[runners]] environment"
|
|
||||||
echo "This is the CPU serial device."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$BM_SERIAL_EC" ]; then
|
|
||||||
echo "Must set BM_SERIAL in your gitlab-runner config.toml [[runners]] environment"
|
|
||||||
echo "This is the EC serial device for controlling board power"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -d /nfs ]; then
|
|
||||||
echo "NFS rootfs directory needs to be mounted at /nfs by the gitlab runner"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -d /tftp ]; then
|
|
||||||
echo "TFTP directory for this board needs to be mounted at /tftp by the gitlab runner"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# job config checks
|
|
||||||
if [ -z "$BM_KERNEL" ]; then
|
|
||||||
echo "Must set BM_KERNEL to your board's kernel FIT image"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$BM_ROOTFS" ]; then
|
|
||||||
echo "Must set BM_ROOTFS to your board's rootfs directory in the job's variables"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$BM_CMDLINE" ]; then
|
|
||||||
echo "Must set BM_CMDLINE to your board's kernel command line arguments"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
. "${SCRIPTS_DIR}/setup-test-env.sh"
|
|
||||||
|
|
||||||
section_start prepare_rootfs "Preparing rootfs components"
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
# Clear out any previous run's artifacts.
|
|
||||||
rm -rf results/
|
|
||||||
mkdir -p results
|
|
||||||
|
|
||||||
# Create the rootfs in the NFS directory. rm to make sure it's in a pristine
|
|
||||||
# state, since it's volume-mounted on the host.
|
|
||||||
rsync -a --delete $BM_ROOTFS/ /nfs/
|
|
||||||
mkdir -p /nfs/results
|
|
||||||
. $BM/rootfs-setup.sh /nfs
|
|
||||||
|
|
||||||
# Put the kernel/dtb image and the boot command line in the tftp directory for
|
|
||||||
# the board to find. For normal Mesa development, we build the kernel and
|
|
||||||
# store it in the docker container that this script is running in.
|
|
||||||
#
|
|
||||||
# However, container builds are expensive, so when you're hacking on the
|
|
||||||
# kernel, it's nice to be able to skip the half hour container build and plus
|
|
||||||
# moving that container to the runner. So, if BM_KERNEL is a URL, fetch it
|
|
||||||
# instead of looking in the container. Note that the kernel build should be
|
|
||||||
# the output of:
|
|
||||||
#
|
|
||||||
# make Image.lzma
|
|
||||||
#
|
|
||||||
# mkimage \
|
|
||||||
# -A arm64 \
|
|
||||||
# -f auto \
|
|
||||||
# -C lzma \
|
|
||||||
# -d arch/arm64/boot/Image.lzma \
|
|
||||||
# -b arch/arm64/boot/dts/qcom/sdm845-cheza-r3.dtb \
|
|
||||||
# cheza-image.img
|
|
||||||
|
|
||||||
rm -rf /tftp/*
|
|
||||||
if echo "$BM_KERNEL" | grep -q http; then
|
|
||||||
curl -L --retry 4 -f --retry-all-errors --retry-delay 60 \
|
|
||||||
"$BM_KERNEL" -o /tftp/vmlinuz
|
|
||||||
else
|
|
||||||
cp /baremetal-files/"$BM_KERNEL" /tftp/vmlinuz
|
|
||||||
fi
|
|
||||||
echo "$BM_CMDLINE" > /tftp/cmdline
|
|
||||||
|
|
||||||
set +e
|
|
||||||
STRUCTURED_LOG_FILE=results/job_detail.json
|
|
||||||
python3 $CI_INSTALL/custom_logger.py ${STRUCTURED_LOG_FILE} --update dut_job_type "${DEVICE_TYPE}"
|
|
||||||
python3 $CI_INSTALL/custom_logger.py ${STRUCTURED_LOG_FILE} --update farm "${FARM}"
|
|
||||||
python3 $CI_INSTALL/custom_logger.py ${STRUCTURED_LOG_FILE} --create-dut-job dut_name "${CI_RUNNER_DESCRIPTION}"
|
|
||||||
python3 $CI_INSTALL/custom_logger.py ${STRUCTURED_LOG_FILE} --update-dut-time submit "${CI_JOB_STARTED_AT}"
|
|
||||||
section_end prepare_rootfs
|
|
||||||
|
|
||||||
python3 $BM/cros_servo_run.py \
|
|
||||||
--cpu $BM_SERIAL \
|
|
||||||
--ec $BM_SERIAL_EC \
|
|
||||||
--test-timeout ${TEST_PHASE_TIMEOUT_MINUTES:-20}
|
|
||||||
ret=$?
|
|
||||||
|
|
||||||
section_start dut_cleanup "Cleaning up after job"
|
|
||||||
python3 $CI_INSTALL/custom_logger.py ${STRUCTURED_LOG_FILE} --close-dut-job
|
|
||||||
python3 $CI_INSTALL/custom_logger.py ${STRUCTURED_LOG_FILE} --close
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Bring artifacts back from the NFS dir to the build dir where gitlab-runner
|
|
||||||
# will look for them.
|
|
||||||
cp -Rp /nfs/results/. results/
|
|
||||||
section_end dut_cleanup
|
|
||||||
|
|
||||||
exit $ret
|
|
||||||
|
|
@ -1,206 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
#
|
|
||||||
# Copyright © 2020 Google LLC
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import datetime
|
|
||||||
import math
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from custom_logger import CustomLogger
|
|
||||||
from serial_buffer import SerialBuffer
|
|
||||||
|
|
||||||
ANSI_ESCAPE="\x1b[0K"
|
|
||||||
ANSI_COLOUR="\x1b[0;36m"
|
|
||||||
ANSI_RESET="\x1b[0m"
|
|
||||||
SECTION_START="start"
|
|
||||||
SECTION_END="end"
|
|
||||||
|
|
||||||
class CrosServoRun:
|
|
||||||
def __init__(self, cpu, ec, test_timeout, logger):
|
|
||||||
self.cpu_ser = SerialBuffer(
|
|
||||||
cpu, "results/serial.txt", ": ")
|
|
||||||
# Merge the EC serial into the cpu_ser's line stream so that we can
|
|
||||||
# effectively poll on both at the same time and not have to worry about
|
|
||||||
self.ec_ser = SerialBuffer(
|
|
||||||
ec, "results/serial-ec.txt", " EC: ", line_queue=self.cpu_ser.line_queue)
|
|
||||||
self.test_timeout = test_timeout
|
|
||||||
self.logger = logger
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.ec_ser.close()
|
|
||||||
self.cpu_ser.close()
|
|
||||||
|
|
||||||
def ec_write(self, s):
|
|
||||||
print("EC> %s" % s)
|
|
||||||
self.ec_ser.serial.write(s.encode())
|
|
||||||
|
|
||||||
def cpu_write(self, s):
|
|
||||||
print("> %s" % s)
|
|
||||||
self.cpu_ser.serial.write(s.encode())
|
|
||||||
|
|
||||||
def print_error(self, message):
|
|
||||||
RED = '\033[0;31m'
|
|
||||||
NO_COLOR = '\033[0m'
|
|
||||||
print(RED + message + NO_COLOR)
|
|
||||||
self.logger.update_status_fail(message)
|
|
||||||
|
|
||||||
def get_rel_timestamp(self):
|
|
||||||
now = datetime.datetime.now(tz=datetime.UTC)
|
|
||||||
then_env = os.getenv("CI_JOB_STARTED_AT")
|
|
||||||
if not then_env:
|
|
||||||
return ""
|
|
||||||
delta = now - datetime.datetime.fromisoformat(then_env)
|
|
||||||
return f"[{math.floor(delta.seconds / 60):02}:{(delta.seconds % 60):02}]"
|
|
||||||
|
|
||||||
def get_cur_timestamp(self):
|
|
||||||
return str(int(datetime.datetime.timestamp(datetime.datetime.now())))
|
|
||||||
|
|
||||||
def print_gitlab_section(self, action, name, description, collapse=True):
|
|
||||||
assert action in [SECTION_START, SECTION_END]
|
|
||||||
out = ANSI_ESCAPE + "section_" + action + ":"
|
|
||||||
out += self.get_cur_timestamp() + ":"
|
|
||||||
out += name
|
|
||||||
if action == "start" and collapse:
|
|
||||||
out += "[collapsed=true]"
|
|
||||||
out += "\r" + ANSI_ESCAPE + ANSI_COLOUR
|
|
||||||
out += self.get_rel_timestamp() + " " + description + ANSI_RESET
|
|
||||||
print(out)
|
|
||||||
|
|
||||||
def boot_section(self, action):
|
|
||||||
self.print_gitlab_section(action, "dut_boot", "Booting hardware device", True)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
# Flush any partial commands in the EC's prompt, then ask for a reboot.
|
|
||||||
self.ec_write("\n")
|
|
||||||
self.ec_write("reboot\n")
|
|
||||||
|
|
||||||
bootloader_done = False
|
|
||||||
self.logger.create_job_phase("boot")
|
|
||||||
self.boot_section(SECTION_START)
|
|
||||||
tftp_failures = 0
|
|
||||||
# This is emitted right when the bootloader pauses to check for input.
|
|
||||||
# Emit a ^N character to request network boot, because we don't have a
|
|
||||||
# direct-to-netboot firmware on cheza.
|
|
||||||
for line in self.cpu_ser.lines(timeout=120, phase="bootloader"):
|
|
||||||
if re.search("load_archive: loading locale_en.bin", line):
|
|
||||||
self.cpu_write("\016")
|
|
||||||
bootloader_done = True
|
|
||||||
break
|
|
||||||
|
|
||||||
# The Cheza firmware seems to occasionally get stuck looping in
|
|
||||||
# this error state during TFTP booting, possibly based on amount of
|
|
||||||
# network traffic around it, but it'll usually recover after a
|
|
||||||
# reboot. Currently mostly visible on google-freedreno-cheza-14.
|
|
||||||
if re.search("R8152: Bulk read error 0xffffffbf", line):
|
|
||||||
tftp_failures += 1
|
|
||||||
if tftp_failures >= 10:
|
|
||||||
self.print_error(
|
|
||||||
"Detected intermittent tftp failure, restarting run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# If the board has a netboot firmware and we made it to booting the
|
|
||||||
# kernel, proceed to processing of the test run.
|
|
||||||
if re.search("Booting Linux", line):
|
|
||||||
bootloader_done = True
|
|
||||||
break
|
|
||||||
|
|
||||||
# The Cheza boards have issues with failing to bring up power to
|
|
||||||
# the system sometimes, possibly dependent on ambient temperature
|
|
||||||
# in the farm.
|
|
||||||
if re.search("POWER_GOOD not seen in time", line):
|
|
||||||
self.print_error(
|
|
||||||
"Detected intermittent poweron failure, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if not bootloader_done:
|
|
||||||
self.print_error("Failed to make it through bootloader, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
self.logger.create_job_phase("test")
|
|
||||||
for line in self.cpu_ser.lines(timeout=self.test_timeout, phase="test"):
|
|
||||||
if re.search("---. end Kernel panic", line):
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# There are very infrequent bus errors during power management transitions
|
|
||||||
# on cheza, which we don't expect to be the case on future boards.
|
|
||||||
if re.search("Kernel panic - not syncing: Asynchronous SError Interrupt", line):
|
|
||||||
self.print_error(
|
|
||||||
"Detected cheza power management bus error, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# If the network device dies, it's probably not graphics's fault, just try again.
|
|
||||||
if re.search("NETDEV WATCHDOG", line):
|
|
||||||
self.print_error(
|
|
||||||
"Detected network device failure, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# These HFI response errors started appearing with the introduction
|
|
||||||
# of piglit runs. CosmicPenguin says:
|
|
||||||
#
|
|
||||||
# "message ID 106 isn't a thing, so likely what happened is that we
|
|
||||||
# got confused when parsing the HFI queue. If it happened on only
|
|
||||||
# one run, then memory corruption could be a possible clue"
|
|
||||||
#
|
|
||||||
# Given that it seems to trigger randomly near a GPU fault and then
|
|
||||||
# break many tests after that, just restart the whole run.
|
|
||||||
if re.search("a6xx_hfi_send_msg.*Unexpected message id .* on the response queue", line):
|
|
||||||
self.print_error(
|
|
||||||
"Detected cheza power management bus error, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if re.search("coreboot.*bootblock starting", line):
|
|
||||||
self.print_error(
|
|
||||||
"Detected spontaneous reboot, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if re.search("arm-smmu 5040000.iommu: TLB sync timed out -- SMMU may be deadlocked", line):
|
|
||||||
self.print_error("Detected cheza MMU fail, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
result = re.search(r"hwci: mesa: (\S*), exit_code: (\d+)", line)
|
|
||||||
if result:
|
|
||||||
status = result.group(1)
|
|
||||||
exit_code = int(result.group(2))
|
|
||||||
|
|
||||||
if status == "pass":
|
|
||||||
self.logger.update_dut_job("status", "pass")
|
|
||||||
else:
|
|
||||||
self.logger.update_status_fail("test fail")
|
|
||||||
|
|
||||||
self.logger.update_dut_job("exit_code", exit_code)
|
|
||||||
return exit_code
|
|
||||||
|
|
||||||
self.print_error(
|
|
||||||
"Reached the end of the CPU serial log without finding a result")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument('--cpu', type=str,
|
|
||||||
help='CPU Serial device', required=True)
|
|
||||||
parser.add_argument(
|
|
||||||
'--ec', type=str, help='EC Serial device', required=True)
|
|
||||||
parser.add_argument(
|
|
||||||
'--test-timeout', type=int, help='Test phase timeout (minutes)', required=True)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
logger = CustomLogger("results/job_detail.json")
|
|
||||||
logger.update_dut_time("start", None)
|
|
||||||
servo = CrosServoRun(args.cpu, args.ec, args.test_timeout * 60, logger)
|
|
||||||
retval = servo.run()
|
|
||||||
|
|
||||||
# power down the CPU on the device
|
|
||||||
servo.ec_write("power off\n")
|
|
||||||
logger.update_dut_time("end", None)
|
|
||||||
servo.close()
|
|
||||||
|
|
||||||
sys.exit(retval)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
relay=$1
|
|
||||||
|
|
||||||
if [ -z "$relay" ]; then
|
|
||||||
echo "Must supply a relay arg"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
"$CI_PROJECT_DIR"/install/bare-metal/eth008-power-relay.py "$ETH_HOST" "$ETH_PORT" off "$relay"
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import socket
|
|
||||||
|
|
||||||
host = sys.argv[1]
|
|
||||||
port = sys.argv[2]
|
|
||||||
mode = sys.argv[3]
|
|
||||||
relay = sys.argv[4]
|
|
||||||
msg = None
|
|
||||||
|
|
||||||
if mode == "on":
|
|
||||||
msg = b'\x20'
|
|
||||||
else:
|
|
||||||
msg = b'\x21'
|
|
||||||
|
|
||||||
msg += int(relay).to_bytes(1, 'big')
|
|
||||||
msg += b'\x00'
|
|
||||||
|
|
||||||
c = socket.create_connection((host, int(port)))
|
|
||||||
c.sendall(msg)
|
|
||||||
|
|
||||||
data = c.recv(1)
|
|
||||||
c.close()
|
|
||||||
|
|
||||||
if data[0] == b'\x01':
|
|
||||||
print('Command failed')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
relay=$1
|
|
||||||
|
|
||||||
if [ -z "$relay" ]; then
|
|
||||||
echo "Must supply a relay arg"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
"$CI_PROJECT_DIR"/install/bare-metal/eth008-power-relay.py "$ETH_HOST" "$ETH_PORT" off "$relay"
|
|
||||||
sleep 5
|
|
||||||
"$CI_PROJECT_DIR"/install/bare-metal/eth008-power-relay.py "$ETH_HOST" "$ETH_PORT" on "$relay"
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
STRINGS=$(mktemp)
|
|
||||||
ERRORS=$(mktemp)
|
|
||||||
|
|
||||||
trap 'rm $STRINGS; rm $ERRORS;' EXIT
|
|
||||||
|
|
||||||
FILE=$1
|
|
||||||
shift 1
|
|
||||||
|
|
||||||
while getopts "f:e:" opt; do
|
|
||||||
case $opt in
|
|
||||||
f) echo "$OPTARG" >> "$STRINGS";;
|
|
||||||
e) echo "$OPTARG" >> "$STRINGS" ; echo "$OPTARG" >> "$ERRORS";;
|
|
||||||
*) exit
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
shift $((OPTIND -1))
|
|
||||||
|
|
||||||
echo "Waiting for $FILE to say one of following strings"
|
|
||||||
cat "$STRINGS"
|
|
||||||
|
|
||||||
while ! grep -E -wf "$STRINGS" "$FILE"; do
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
|
|
||||||
if grep -E -wf "$ERRORS" "$FILE"; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# shellcheck disable=SC1091 # The relative paths in this file only become valid at runtime.
|
|
||||||
# shellcheck disable=SC2034
|
|
||||||
# shellcheck disable=SC2086 # we want word splitting
|
|
||||||
|
|
||||||
. "$SCRIPTS_DIR"/setup-test-env.sh
|
|
||||||
|
|
||||||
BM=$CI_PROJECT_DIR/install/bare-metal
|
|
||||||
CI_COMMON=$CI_PROJECT_DIR/install/common
|
|
||||||
|
|
||||||
if [ -z "$BM_SERIAL" ] && [ -z "$BM_SERIAL_SCRIPT" ]; then
|
|
||||||
echo "Must set BM_SERIAL OR BM_SERIAL_SCRIPT in your gitlab-runner config.toml [[runners]] environment"
|
|
||||||
echo "BM_SERIAL:"
|
|
||||||
echo " This is the serial device to talk to for waiting for fastboot to be ready and logging from the kernel."
|
|
||||||
echo "BM_SERIAL_SCRIPT:"
|
|
||||||
echo " This is a shell script to talk to for waiting for fastboot to be ready and logging from the kernel."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$BM_POWERUP" ]; then
|
|
||||||
echo "Must set BM_POWERUP in your gitlab-runner config.toml [[runners]] environment"
|
|
||||||
echo "This is a shell script that should reset the device and begin its boot sequence"
|
|
||||||
echo "such that it pauses at fastboot."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$BM_POWERDOWN" ]; then
|
|
||||||
echo "Must set BM_POWERDOWN in your gitlab-runner config.toml [[runners]] environment"
|
|
||||||
echo "This is a shell script that should power off the device."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$BM_FASTBOOT_SERIAL" ]; then
|
|
||||||
echo "Must set BM_FASTBOOT_SERIAL in your gitlab-runner config.toml [[runners]] environment"
|
|
||||||
echo "This must be the a stable-across-resets fastboot serial number."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$BM_KERNEL" ]; then
|
|
||||||
echo "Must set BM_KERNEL to your board's kernel vmlinuz or Image.gz in the job's variables:"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$BM_DTB" ]; then
|
|
||||||
echo "Must set BM_DTB to your board's DTB file in the job's variables:"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$BM_ROOTFS" ]; then
|
|
||||||
echo "Must set BM_ROOTFS to your board's rootfs directory in the job's variables:"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if echo $BM_CMDLINE | grep -q "root=/dev/nfs"; then
|
|
||||||
BM_FASTBOOT_NFSROOT=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
section_start prepare_rootfs "Preparing rootfs components"
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
# Clear out any previous run's artifacts.
|
|
||||||
rm -rf results/
|
|
||||||
mkdir -p results/
|
|
||||||
|
|
||||||
if [ -n "$BM_FASTBOOT_NFSROOT" ]; then
|
|
||||||
# Create the rootfs in the NFS directory. rm to make sure it's in a pristine
|
|
||||||
# state, since it's volume-mounted on the host.
|
|
||||||
rsync -a --delete $BM_ROOTFS/ /nfs/
|
|
||||||
mkdir -p /nfs/results
|
|
||||||
. $BM/rootfs-setup.sh /nfs
|
|
||||||
|
|
||||||
# Root on NFS, no need for an inintramfs.
|
|
||||||
rm -f rootfs.cpio.gz
|
|
||||||
touch rootfs.cpio
|
|
||||||
gzip rootfs.cpio
|
|
||||||
else
|
|
||||||
# Create the rootfs in a temp dir
|
|
||||||
rsync -a --delete $BM_ROOTFS/ rootfs/
|
|
||||||
. $BM/rootfs-setup.sh rootfs
|
|
||||||
|
|
||||||
# Finally, pack it up into a cpio rootfs. Skip the vulkan CTS since none of
|
|
||||||
# these devices use it and it would take up space in the initrd.
|
|
||||||
|
|
||||||
EXCLUDE_FILTER="deqp|arb_gpu_shader5|arb_gpu_shader_fp64|arb_gpu_shader_int64|glsl-4.[0123456]0|arb_tessellation_shader"
|
|
||||||
|
|
||||||
pushd rootfs
|
|
||||||
find -H . | \
|
|
||||||
grep -E -v "external/(openglcts|vulkancts|amber|glslang|spirv-tools)" |
|
|
||||||
grep -E -v "traces-db|apitrace|renderdoc" | \
|
|
||||||
grep -E -v $EXCLUDE_FILTER | \
|
|
||||||
cpio -H newc -o | \
|
|
||||||
xz --check=crc32 -T4 - > $CI_PROJECT_DIR/rootfs.cpio.gz
|
|
||||||
popd
|
|
||||||
fi
|
|
||||||
|
|
||||||
if echo "$BM_KERNEL $BM_DTB" | grep -q http; then
|
|
||||||
curl -L --retry 4 -f --retry-all-errors --retry-delay 60 \
|
|
||||||
"$BM_KERNEL" -o kernel
|
|
||||||
# FIXME: modules should be supplied too
|
|
||||||
curl -L --retry 4 -f --retry-all-errors --retry-delay 60 \
|
|
||||||
"$BM_DTB" -o dtb
|
|
||||||
|
|
||||||
cat kernel dtb > Image.gz-dtb
|
|
||||||
else
|
|
||||||
cat /baremetal-files/"$BM_KERNEL" /baremetal-files/"$BM_DTB".dtb > Image.gz-dtb
|
|
||||||
cp /baremetal-files/"$BM_DTB".dtb dtb
|
|
||||||
fi
|
|
||||||
|
|
||||||
export PATH=$BM:$PATH
|
|
||||||
|
|
||||||
mkdir -p artifacts
|
|
||||||
mkbootimg.py \
|
|
||||||
--kernel Image.gz-dtb \
|
|
||||||
--ramdisk rootfs.cpio.gz \
|
|
||||||
--dtb dtb \
|
|
||||||
--cmdline "$BM_CMDLINE" \
|
|
||||||
$BM_MKBOOT_PARAMS \
|
|
||||||
--header_version 2 \
|
|
||||||
-o artifacts/fastboot.img
|
|
||||||
|
|
||||||
rm Image.gz-dtb dtb
|
|
||||||
|
|
||||||
# Start background command for talking to serial if we have one.
|
|
||||||
if [ -n "$BM_SERIAL_SCRIPT" ]; then
|
|
||||||
$BM_SERIAL_SCRIPT > results/serial-output.txt &
|
|
||||||
|
|
||||||
while [ ! -e results/serial-output.txt ]; do
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
section_end prepare_rootfs
|
|
||||||
|
|
||||||
set +e
|
|
||||||
$BM/fastboot_run.py \
|
|
||||||
--dev="$BM_SERIAL" \
|
|
||||||
--test-timeout ${TEST_PHASE_TIMEOUT_MINUTES:-20} \
|
|
||||||
--fbserial="$BM_FASTBOOT_SERIAL" \
|
|
||||||
--powerup="$BM_POWERUP" \
|
|
||||||
--powerdown="$BM_POWERDOWN"
|
|
||||||
ret=$?
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ -n "$BM_FASTBOOT_NFSROOT" ]; then
|
|
||||||
# Bring artifacts back from the NFS dir to the build dir where gitlab-runner
|
|
||||||
# will look for them.
|
|
||||||
cp -Rp /nfs/results/. results/
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit $ret
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
#
|
|
||||||
# Copyright © 2020 Google LLC
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
# copy of this software and associated documentation files (the "Software"),
|
|
||||||
# to deal in the Software without restriction, including without limitation
|
|
||||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
# and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
# Software is furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice (including the next
|
|
||||||
# paragraph) shall be included in all copies or substantial portions of the
|
|
||||||
# Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
# IN THE SOFTWARE.
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import subprocess
|
|
||||||
import re
|
|
||||||
from serial_buffer import SerialBuffer
|
|
||||||
import sys
|
|
||||||
import threading
|
|
||||||
|
|
||||||
|
|
||||||
class FastbootRun:
|
|
||||||
def __init__(self, args, test_timeout):
|
|
||||||
self.powerup = args.powerup
|
|
||||||
self.ser = SerialBuffer(
|
|
||||||
args.dev, "results/serial-output.txt", "R SERIAL> ")
|
|
||||||
self.fastboot = "fastboot boot -s {ser} artifacts/fastboot.img".format(
|
|
||||||
ser=args.fbserial)
|
|
||||||
self.test_timeout = test_timeout
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.ser.close()
|
|
||||||
|
|
||||||
def print_error(self, message):
|
|
||||||
RED = '\033[0;31m'
|
|
||||||
NO_COLOR = '\033[0m'
|
|
||||||
print(RED + message + NO_COLOR)
|
|
||||||
|
|
||||||
def logged_system(self, cmd, timeout=60):
|
|
||||||
print("Running '{}'".format(cmd))
|
|
||||||
try:
|
|
||||||
return subprocess.call(cmd, shell=True, timeout=timeout)
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
self.print_error("timeout, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
if ret := self.logged_system(self.powerup):
|
|
||||||
return ret
|
|
||||||
|
|
||||||
fastboot_ready = False
|
|
||||||
for line in self.ser.lines(timeout=2 * 60, phase="bootloader"):
|
|
||||||
if re.search("[Ff]astboot: [Pp]rocessing commands", line) or \
|
|
||||||
re.search("Listening for fastboot command on", line):
|
|
||||||
fastboot_ready = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if re.search("data abort", line):
|
|
||||||
self.print_error(
|
|
||||||
"Detected crash during boot, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if not fastboot_ready:
|
|
||||||
self.print_error(
|
|
||||||
"Failed to get to fastboot prompt, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if ret := self.logged_system(self.fastboot):
|
|
||||||
return ret
|
|
||||||
|
|
||||||
print_more_lines = -1
|
|
||||||
for line in self.ser.lines(timeout=self.test_timeout, phase="test"):
|
|
||||||
if print_more_lines == 0:
|
|
||||||
return 1
|
|
||||||
if print_more_lines > 0:
|
|
||||||
print_more_lines -= 1
|
|
||||||
|
|
||||||
if re.search("---. end Kernel panic", line):
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# The db820c boards intermittently reboot. Just restart the run
|
|
||||||
# when if we see a reboot after we got past fastboot.
|
|
||||||
if re.search("PON REASON", line):
|
|
||||||
self.print_error(
|
|
||||||
"Detected spontaneous reboot, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# db820c sometimes wedges around iommu fault recovery
|
|
||||||
if re.search("watchdog: BUG: soft lockup - CPU.* stuck", line):
|
|
||||||
self.print_error(
|
|
||||||
"Detected kernel soft lockup, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# If the network device dies, it's probably not graphics's fault, just try again.
|
|
||||||
if re.search("NETDEV WATCHDOG", line):
|
|
||||||
self.print_error(
|
|
||||||
"Detected network device failure, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# A3xx recovery doesn't quite work. Sometimes the GPU will get
|
|
||||||
# wedged and recovery will fail (because power can't be reset?)
|
|
||||||
# This assumes that the jobs are sufficiently well-tested that GPU
|
|
||||||
# hangs aren't always triggered, so just try again. But print some
|
|
||||||
# more lines first so that we get better information on the cause
|
|
||||||
# of the hang. Once a hang happens, it's pretty chatty.
|
|
||||||
if "[drm:adreno_recover] *ERROR* gpu hw init failed: -22" in line:
|
|
||||||
self.print_error(
|
|
||||||
"Detected GPU hang, abandoning run.")
|
|
||||||
if print_more_lines == -1:
|
|
||||||
print_more_lines = 30
|
|
||||||
|
|
||||||
result = re.search(r"hwci: mesa: (\S*), exit_code: (\d+)", line)
|
|
||||||
if result:
|
|
||||||
status = result.group(1)
|
|
||||||
exit_code = int(result.group(2))
|
|
||||||
|
|
||||||
return exit_code
|
|
||||||
|
|
||||||
self.print_error(
|
|
||||||
"Reached the end of the CPU serial log without finding a result, abandoning run.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument(
|
|
||||||
'--dev', type=str, help='Serial device (otherwise reading from serial-output.txt)')
|
|
||||||
parser.add_argument('--powerup', type=str,
|
|
||||||
help='shell command for rebooting', required=True)
|
|
||||||
parser.add_argument('--powerdown', type=str,
|
|
||||||
help='shell command for powering off', required=True)
|
|
||||||
parser.add_argument('--fbserial', type=str,
|
|
||||||
help='fastboot serial number of the board', required=True)
|
|
||||||
parser.add_argument('--test-timeout', type=int,
|
|
||||||
help='Test phase timeout (minutes)', required=True)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
fastboot = FastbootRun(args, args.test_timeout * 60)
|
|
||||||
|
|
||||||
retval = fastboot.run()
|
|
||||||
fastboot.close()
|
|
||||||
|
|
||||||
fastboot.logged_system(args.powerdown)
|
|
||||||
|
|
||||||
sys.exit(retval)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
relay=$1
|
|
||||||
|
|
||||||
if [ -z "$relay" ]; then
|
|
||||||
echo "Must supply a relay arg"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
"$CI_PROJECT_DIR"/install/bare-metal/google-power-relay.py off "$relay"
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import serial
|
|
||||||
|
|
||||||
mode = sys.argv[1]
|
|
||||||
relay = sys.argv[2]
|
|
||||||
|
|
||||||
# our relays are "off" means "board is powered".
|
|
||||||
mode_swap = {
|
|
||||||
"on": "off",
|
|
||||||
"off": "on",
|
|
||||||
}
|
|
||||||
mode = mode_swap[mode]
|
|
||||||
|
|
||||||
ser = serial.Serial('/dev/ttyACM0', 115200, timeout=2)
|
|
||||||
command = "relay {} {}\n\r".format(mode, relay)
|
|
||||||
ser.write(command.encode())
|
|
||||||
ser.close()
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
relay=$1
|
|
||||||
|
|
||||||
if [ -z "$relay" ]; then
|
|
||||||
echo "Must supply a relay arg"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
"$CI_PROJECT_DIR"/install/bare-metal/google-power-relay.py off "$relay"
|
|
||||||
sleep 5
|
|
||||||
"$CI_PROJECT_DIR"/install/bare-metal/google-power-relay.py on "$relay"
|
|
||||||
|
|
@ -1,569 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
#
|
|
||||||
# Copyright 2015, The Android Open Source Project
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
"""Creates the boot image."""
|
|
||||||
from argparse import (ArgumentParser, ArgumentTypeError,
|
|
||||||
FileType, RawDescriptionHelpFormatter)
|
|
||||||
from hashlib import sha1
|
|
||||||
from os import fstat
|
|
||||||
from struct import pack
|
|
||||||
import array
|
|
||||||
import collections
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import tempfile
|
|
||||||
# Constant and structure definition is in
|
|
||||||
# system/tools/mkbootimg/include/bootimg/bootimg.h
|
|
||||||
BOOT_MAGIC = 'ANDROID!'
|
|
||||||
BOOT_MAGIC_SIZE = 8
|
|
||||||
BOOT_NAME_SIZE = 16
|
|
||||||
BOOT_ARGS_SIZE = 512
|
|
||||||
BOOT_EXTRA_ARGS_SIZE = 1024
|
|
||||||
BOOT_IMAGE_HEADER_V1_SIZE = 1648
|
|
||||||
BOOT_IMAGE_HEADER_V2_SIZE = 1660
|
|
||||||
BOOT_IMAGE_HEADER_V3_SIZE = 1580
|
|
||||||
BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096
|
|
||||||
BOOT_IMAGE_HEADER_V4_SIZE = 1584
|
|
||||||
BOOT_IMAGE_V4_SIGNATURE_SIZE = 4096
|
|
||||||
VENDOR_BOOT_MAGIC = 'VNDRBOOT'
|
|
||||||
VENDOR_BOOT_MAGIC_SIZE = 8
|
|
||||||
VENDOR_BOOT_NAME_SIZE = BOOT_NAME_SIZE
|
|
||||||
VENDOR_BOOT_ARGS_SIZE = 2048
|
|
||||||
VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112
|
|
||||||
VENDOR_BOOT_IMAGE_HEADER_V4_SIZE = 2128
|
|
||||||
VENDOR_RAMDISK_TYPE_NONE = 0
|
|
||||||
VENDOR_RAMDISK_TYPE_PLATFORM = 1
|
|
||||||
VENDOR_RAMDISK_TYPE_RECOVERY = 2
|
|
||||||
VENDOR_RAMDISK_TYPE_DLKM = 3
|
|
||||||
VENDOR_RAMDISK_NAME_SIZE = 32
|
|
||||||
VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16
|
|
||||||
VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE = 108
|
|
||||||
# Names with special meaning, mustn't be specified in --ramdisk_name.
|
|
||||||
VENDOR_RAMDISK_NAME_BLOCKLIST = {b'default'}
|
|
||||||
PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT = '--vendor_ramdisk_fragment'
|
|
||||||
def filesize(f):
|
|
||||||
if f is None:
|
|
||||||
return 0
|
|
||||||
try:
|
|
||||||
return fstat(f.fileno()).st_size
|
|
||||||
except OSError:
|
|
||||||
return 0
|
|
||||||
def update_sha(sha, f):
|
|
||||||
if f:
|
|
||||||
sha.update(f.read())
|
|
||||||
f.seek(0)
|
|
||||||
sha.update(pack('I', filesize(f)))
|
|
||||||
else:
|
|
||||||
sha.update(pack('I', 0))
|
|
||||||
def pad_file(f, padding):
|
|
||||||
pad = (padding - (f.tell() & (padding - 1))) & (padding - 1)
|
|
||||||
f.write(pack(str(pad) + 'x'))
|
|
||||||
def get_number_of_pages(image_size, page_size):
|
|
||||||
"""calculates the number of pages required for the image"""
|
|
||||||
return (image_size + page_size - 1) // page_size
|
|
||||||
def get_recovery_dtbo_offset(args):
|
|
||||||
"""calculates the offset of recovery_dtbo image in the boot image"""
|
|
||||||
num_header_pages = 1 # header occupies a page
|
|
||||||
num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize)
|
|
||||||
num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk),
|
|
||||||
args.pagesize)
|
|
||||||
num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize)
|
|
||||||
dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages +
|
|
||||||
num_ramdisk_pages + num_second_pages)
|
|
||||||
return dtbo_offset
|
|
||||||
def write_header_v3_and_above(args):
|
|
||||||
if args.header_version > 3:
|
|
||||||
boot_header_size = BOOT_IMAGE_HEADER_V4_SIZE
|
|
||||||
else:
|
|
||||||
boot_header_size = BOOT_IMAGE_HEADER_V3_SIZE
|
|
||||||
args.output.write(pack(f'{BOOT_MAGIC_SIZE}s', BOOT_MAGIC.encode()))
|
|
||||||
# kernel size in bytes
|
|
||||||
args.output.write(pack('I', filesize(args.kernel)))
|
|
||||||
# ramdisk size in bytes
|
|
||||||
args.output.write(pack('I', filesize(args.ramdisk)))
|
|
||||||
# os version and patch level
|
|
||||||
args.output.write(pack('I', (args.os_version << 11) | args.os_patch_level))
|
|
||||||
args.output.write(pack('I', boot_header_size))
|
|
||||||
# reserved
|
|
||||||
args.output.write(pack('4I', 0, 0, 0, 0))
|
|
||||||
# version of boot image header
|
|
||||||
args.output.write(pack('I', args.header_version))
|
|
||||||
args.output.write(pack(f'{BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE}s',
|
|
||||||
args.cmdline))
|
|
||||||
if args.header_version >= 4:
|
|
||||||
# The signature used to verify boot image v4.
|
|
||||||
args.output.write(pack('I', BOOT_IMAGE_V4_SIGNATURE_SIZE))
|
|
||||||
pad_file(args.output, BOOT_IMAGE_HEADER_V3_PAGESIZE)
|
|
||||||
def write_vendor_boot_header(args):
|
|
||||||
if filesize(args.dtb) == 0:
|
|
||||||
raise ValueError('DTB image must not be empty.')
|
|
||||||
if args.header_version > 3:
|
|
||||||
vendor_ramdisk_size = args.vendor_ramdisk_total_size
|
|
||||||
vendor_boot_header_size = VENDOR_BOOT_IMAGE_HEADER_V4_SIZE
|
|
||||||
else:
|
|
||||||
vendor_ramdisk_size = filesize(args.vendor_ramdisk)
|
|
||||||
vendor_boot_header_size = VENDOR_BOOT_IMAGE_HEADER_V3_SIZE
|
|
||||||
args.vendor_boot.write(pack(f'{VENDOR_BOOT_MAGIC_SIZE}s',
|
|
||||||
VENDOR_BOOT_MAGIC.encode()))
|
|
||||||
# version of boot image header
|
|
||||||
args.vendor_boot.write(pack('I', args.header_version))
|
|
||||||
# flash page size
|
|
||||||
args.vendor_boot.write(pack('I', args.pagesize))
|
|
||||||
# kernel physical load address
|
|
||||||
args.vendor_boot.write(pack('I', args.base + args.kernel_offset))
|
|
||||||
# ramdisk physical load address
|
|
||||||
args.vendor_boot.write(pack('I', args.base + args.ramdisk_offset))
|
|
||||||
# ramdisk size in bytes
|
|
||||||
args.vendor_boot.write(pack('I', vendor_ramdisk_size))
|
|
||||||
args.vendor_boot.write(pack(f'{VENDOR_BOOT_ARGS_SIZE}s',
|
|
||||||
args.vendor_cmdline))
|
|
||||||
# kernel tags physical load address
|
|
||||||
args.vendor_boot.write(pack('I', args.base + args.tags_offset))
|
|
||||||
# asciiz product name
|
|
||||||
args.vendor_boot.write(pack(f'{VENDOR_BOOT_NAME_SIZE}s', args.board))
|
|
||||||
# header size in bytes
|
|
||||||
args.vendor_boot.write(pack('I', vendor_boot_header_size))
|
|
||||||
# dtb size in bytes
|
|
||||||
args.vendor_boot.write(pack('I', filesize(args.dtb)))
|
|
||||||
# dtb physical load address
|
|
||||||
args.vendor_boot.write(pack('Q', args.base + args.dtb_offset))
|
|
||||||
if args.header_version > 3:
|
|
||||||
vendor_ramdisk_table_size = (args.vendor_ramdisk_table_entry_num *
|
|
||||||
VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE)
|
|
||||||
# vendor ramdisk table size in bytes
|
|
||||||
args.vendor_boot.write(pack('I', vendor_ramdisk_table_size))
|
|
||||||
# number of vendor ramdisk table entries
|
|
||||||
args.vendor_boot.write(pack('I', args.vendor_ramdisk_table_entry_num))
|
|
||||||
# vendor ramdisk table entry size in bytes
|
|
||||||
args.vendor_boot.write(pack('I', VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE))
|
|
||||||
# bootconfig section size in bytes
|
|
||||||
args.vendor_boot.write(pack('I', filesize(args.vendor_bootconfig)))
|
|
||||||
pad_file(args.vendor_boot, args.pagesize)
|
|
||||||
def write_header(args):
|
|
||||||
if args.header_version > 4:
|
|
||||||
raise ValueError(
|
|
||||||
f'Boot header version {args.header_version} not supported')
|
|
||||||
if args.header_version in {3, 4}:
|
|
||||||
return write_header_v3_and_above(args)
|
|
||||||
ramdisk_load_address = ((args.base + args.ramdisk_offset)
|
|
||||||
if filesize(args.ramdisk) > 0 else 0)
|
|
||||||
second_load_address = ((args.base + args.second_offset)
|
|
||||||
if filesize(args.second) > 0 else 0)
|
|
||||||
args.output.write(pack(f'{BOOT_MAGIC_SIZE}s', BOOT_MAGIC.encode()))
|
|
||||||
# kernel size in bytes
|
|
||||||
args.output.write(pack('I', filesize(args.kernel)))
|
|
||||||
# kernel physical load address
|
|
||||||
args.output.write(pack('I', args.base + args.kernel_offset))
|
|
||||||
# ramdisk size in bytes
|
|
||||||
args.output.write(pack('I', filesize(args.ramdisk)))
|
|
||||||
# ramdisk physical load address
|
|
||||||
args.output.write(pack('I', ramdisk_load_address))
|
|
||||||
# second bootloader size in bytes
|
|
||||||
args.output.write(pack('I', filesize(args.second)))
|
|
||||||
# second bootloader physical load address
|
|
||||||
args.output.write(pack('I', second_load_address))
|
|
||||||
# kernel tags physical load address
|
|
||||||
args.output.write(pack('I', args.base + args.tags_offset))
|
|
||||||
# flash page size
|
|
||||||
args.output.write(pack('I', args.pagesize))
|
|
||||||
# version of boot image header
|
|
||||||
args.output.write(pack('I', args.header_version))
|
|
||||||
# os version and patch level
|
|
||||||
args.output.write(pack('I', (args.os_version << 11) | args.os_patch_level))
|
|
||||||
# asciiz product name
|
|
||||||
args.output.write(pack(f'{BOOT_NAME_SIZE}s', args.board))
|
|
||||||
args.output.write(pack(f'{BOOT_ARGS_SIZE}s', args.cmdline))
|
|
||||||
sha = sha1()
|
|
||||||
update_sha(sha, args.kernel)
|
|
||||||
update_sha(sha, args.ramdisk)
|
|
||||||
update_sha(sha, args.second)
|
|
||||||
if args.header_version > 0:
|
|
||||||
update_sha(sha, args.recovery_dtbo)
|
|
||||||
if args.header_version > 1:
|
|
||||||
update_sha(sha, args.dtb)
|
|
||||||
img_id = pack('32s', sha.digest())
|
|
||||||
args.output.write(img_id)
|
|
||||||
args.output.write(pack(f'{BOOT_EXTRA_ARGS_SIZE}s', args.extra_cmdline))
|
|
||||||
if args.header_version > 0:
|
|
||||||
if args.recovery_dtbo:
|
|
||||||
# recovery dtbo size in bytes
|
|
||||||
args.output.write(pack('I', filesize(args.recovery_dtbo)))
|
|
||||||
# recovert dtbo offset in the boot image
|
|
||||||
args.output.write(pack('Q', get_recovery_dtbo_offset(args)))
|
|
||||||
else:
|
|
||||||
# Set to zero if no recovery dtbo
|
|
||||||
args.output.write(pack('I', 0))
|
|
||||||
args.output.write(pack('Q', 0))
|
|
||||||
# Populate boot image header size for header versions 1 and 2.
|
|
||||||
if args.header_version == 1:
|
|
||||||
args.output.write(pack('I', BOOT_IMAGE_HEADER_V1_SIZE))
|
|
||||||
elif args.header_version == 2:
|
|
||||||
args.output.write(pack('I', BOOT_IMAGE_HEADER_V2_SIZE))
|
|
||||||
if args.header_version > 1:
|
|
||||||
if filesize(args.dtb) == 0:
|
|
||||||
raise ValueError('DTB image must not be empty.')
|
|
||||||
# dtb size in bytes
|
|
||||||
args.output.write(pack('I', filesize(args.dtb)))
|
|
||||||
# dtb physical load address
|
|
||||||
args.output.write(pack('Q', args.base + args.dtb_offset))
|
|
||||||
pad_file(args.output, args.pagesize)
|
|
||||||
return img_id
|
|
||||||
class AsciizBytes:
|
|
||||||
"""Parses a string and encodes it as an asciiz bytes object.
|
|
||||||
>>> AsciizBytes(bufsize=4)('foo')
|
|
||||||
b'foo\\x00'
|
|
||||||
>>> AsciizBytes(bufsize=4)('foob')
|
|
||||||
Traceback (most recent call last):
|
|
||||||
...
|
|
||||||
argparse.ArgumentTypeError: Encoded asciiz length exceeded: max 4, got 5
|
|
||||||
"""
|
|
||||||
def __init__(self, bufsize):
|
|
||||||
self.bufsize = bufsize
|
|
||||||
def __call__(self, arg):
|
|
||||||
arg_bytes = arg.encode() + b'\x00'
|
|
||||||
if len(arg_bytes) > self.bufsize:
|
|
||||||
raise ArgumentTypeError(
|
|
||||||
'Encoded asciiz length exceeded: '
|
|
||||||
f'max {self.bufsize}, got {len(arg_bytes)}')
|
|
||||||
return arg_bytes
|
|
||||||
class VendorRamdiskTableBuilder:
|
|
||||||
"""Vendor ramdisk table builder.
|
|
||||||
Attributes:
|
|
||||||
entries: A list of VendorRamdiskTableEntry namedtuple.
|
|
||||||
ramdisk_total_size: Total size in bytes of all ramdisks in the table.
|
|
||||||
"""
|
|
||||||
VendorRamdiskTableEntry = collections.namedtuple( # pylint: disable=invalid-name
|
|
||||||
'VendorRamdiskTableEntry',
|
|
||||||
['ramdisk_path', 'ramdisk_size', 'ramdisk_offset', 'ramdisk_type',
|
|
||||||
'ramdisk_name', 'board_id'])
|
|
||||||
def __init__(self):
|
|
||||||
self.entries = []
|
|
||||||
self.ramdisk_total_size = 0
|
|
||||||
self.ramdisk_names = set()
|
|
||||||
def add_entry(self, ramdisk_path, ramdisk_type, ramdisk_name, board_id):
|
|
||||||
# Strip any trailing null for simple comparison.
|
|
||||||
stripped_ramdisk_name = ramdisk_name.rstrip(b'\x00')
|
|
||||||
if stripped_ramdisk_name in VENDOR_RAMDISK_NAME_BLOCKLIST:
|
|
||||||
raise ValueError(
|
|
||||||
f'Banned vendor ramdisk name: {stripped_ramdisk_name}')
|
|
||||||
if stripped_ramdisk_name in self.ramdisk_names:
|
|
||||||
raise ValueError(
|
|
||||||
f'Duplicated vendor ramdisk name: {stripped_ramdisk_name}')
|
|
||||||
self.ramdisk_names.add(stripped_ramdisk_name)
|
|
||||||
if board_id is None:
|
|
||||||
board_id = array.array(
|
|
||||||
'I', [0] * VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE)
|
|
||||||
else:
|
|
||||||
board_id = array.array('I', board_id)
|
|
||||||
if len(board_id) != VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE:
|
|
||||||
raise ValueError('board_id size must be '
|
|
||||||
f'{VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}')
|
|
||||||
with open(ramdisk_path, 'rb') as f:
|
|
||||||
ramdisk_size = filesize(f)
|
|
||||||
self.entries.append(self.VendorRamdiskTableEntry(
|
|
||||||
ramdisk_path, ramdisk_size, self.ramdisk_total_size, ramdisk_type,
|
|
||||||
ramdisk_name, board_id))
|
|
||||||
self.ramdisk_total_size += ramdisk_size
|
|
||||||
def write_ramdisks_padded(self, fout, alignment):
|
|
||||||
for entry in self.entries:
|
|
||||||
with open(entry.ramdisk_path, 'rb') as f:
|
|
||||||
fout.write(f.read())
|
|
||||||
pad_file(fout, alignment)
|
|
||||||
def write_entries_padded(self, fout, alignment):
|
|
||||||
for entry in self.entries:
|
|
||||||
fout.write(pack('I', entry.ramdisk_size))
|
|
||||||
fout.write(pack('I', entry.ramdisk_offset))
|
|
||||||
fout.write(pack('I', entry.ramdisk_type))
|
|
||||||
fout.write(pack(f'{VENDOR_RAMDISK_NAME_SIZE}s',
|
|
||||||
entry.ramdisk_name))
|
|
||||||
fout.write(entry.board_id)
|
|
||||||
pad_file(fout, alignment)
|
|
||||||
def write_padded_file(f_out, f_in, padding):
|
|
||||||
if f_in is None:
|
|
||||||
return
|
|
||||||
f_out.write(f_in.read())
|
|
||||||
pad_file(f_out, padding)
|
|
||||||
def parse_int(x):
|
|
||||||
return int(x, 0)
|
|
||||||
def parse_os_version(x):
|
|
||||||
match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
|
|
||||||
if match:
|
|
||||||
a = int(match.group(1))
|
|
||||||
b = c = 0
|
|
||||||
if match.lastindex >= 2:
|
|
||||||
b = int(match.group(2))
|
|
||||||
if match.lastindex == 3:
|
|
||||||
c = int(match.group(3))
|
|
||||||
# 7 bits allocated for each field
|
|
||||||
assert a < 128
|
|
||||||
assert b < 128
|
|
||||||
assert c < 128
|
|
||||||
return (a << 14) | (b << 7) | c
|
|
||||||
return 0
|
|
||||||
def parse_os_patch_level(x):
|
|
||||||
match = re.search(r'^(\d{4})-(\d{2})(?:-(\d{2}))?', x)
|
|
||||||
if match:
|
|
||||||
y = int(match.group(1)) - 2000
|
|
||||||
m = int(match.group(2))
|
|
||||||
# 7 bits allocated for the year, 4 bits for the month
|
|
||||||
assert 0 <= y < 128
|
|
||||||
assert 0 < m <= 12
|
|
||||||
return (y << 4) | m
|
|
||||||
return 0
|
|
||||||
def parse_vendor_ramdisk_type(x):
|
|
||||||
type_dict = {
|
|
||||||
'none': VENDOR_RAMDISK_TYPE_NONE,
|
|
||||||
'platform': VENDOR_RAMDISK_TYPE_PLATFORM,
|
|
||||||
'recovery': VENDOR_RAMDISK_TYPE_RECOVERY,
|
|
||||||
'dlkm': VENDOR_RAMDISK_TYPE_DLKM,
|
|
||||||
}
|
|
||||||
if x.lower() in type_dict:
|
|
||||||
return type_dict[x.lower()]
|
|
||||||
return parse_int(x)
|
|
||||||
def get_vendor_boot_v4_usage():
|
|
||||||
return """vendor boot version 4 arguments:
|
|
||||||
--ramdisk_type {none,platform,recovery,dlkm}
|
|
||||||
specify the type of the ramdisk
|
|
||||||
--ramdisk_name NAME
|
|
||||||
specify the name of the ramdisk
|
|
||||||
--board_id{0..15} NUMBER
|
|
||||||
specify the value of the board_id vector, defaults to 0
|
|
||||||
--vendor_ramdisk_fragment VENDOR_RAMDISK_FILE
|
|
||||||
path to the vendor ramdisk file
|
|
||||||
These options can be specified multiple times, where each vendor ramdisk
|
|
||||||
option group ends with a --vendor_ramdisk_fragment option.
|
|
||||||
Each option group appends an additional ramdisk to the vendor boot image.
|
|
||||||
"""
|
|
||||||
def parse_vendor_ramdisk_args(args, args_list):
|
|
||||||
"""Parses vendor ramdisk specific arguments.
|
|
||||||
Args:
|
|
||||||
args: An argparse.Namespace object. Parsed results are stored into this
|
|
||||||
object.
|
|
||||||
args_list: A list of argument strings to be parsed.
|
|
||||||
Returns:
|
|
||||||
A list argument strings that are not parsed by this method.
|
|
||||||
"""
|
|
||||||
parser = ArgumentParser(add_help=False)
|
|
||||||
parser.add_argument('--ramdisk_type', type=parse_vendor_ramdisk_type,
|
|
||||||
default=VENDOR_RAMDISK_TYPE_NONE)
|
|
||||||
parser.add_argument('--ramdisk_name',
|
|
||||||
type=AsciizBytes(bufsize=VENDOR_RAMDISK_NAME_SIZE),
|
|
||||||
required=True)
|
|
||||||
for i in range(VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE):
|
|
||||||
parser.add_argument(f'--board_id{i}', type=parse_int, default=0)
|
|
||||||
parser.add_argument(PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT, required=True)
|
|
||||||
unknown_args = []
|
|
||||||
vendor_ramdisk_table_builder = VendorRamdiskTableBuilder()
|
|
||||||
if args.vendor_ramdisk is not None:
|
|
||||||
vendor_ramdisk_table_builder.add_entry(
|
|
||||||
args.vendor_ramdisk.name, VENDOR_RAMDISK_TYPE_PLATFORM, b'', None)
|
|
||||||
while PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT in args_list:
|
|
||||||
idx = args_list.index(PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT) + 2
|
|
||||||
vendor_ramdisk_args = args_list[:idx]
|
|
||||||
args_list = args_list[idx:]
|
|
||||||
ramdisk_args, extra_args = parser.parse_known_args(vendor_ramdisk_args)
|
|
||||||
ramdisk_args_dict = vars(ramdisk_args)
|
|
||||||
unknown_args.extend(extra_args)
|
|
||||||
ramdisk_path = ramdisk_args.vendor_ramdisk_fragment
|
|
||||||
ramdisk_type = ramdisk_args.ramdisk_type
|
|
||||||
ramdisk_name = ramdisk_args.ramdisk_name
|
|
||||||
board_id = [ramdisk_args_dict[f'board_id{i}']
|
|
||||||
for i in range(VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE)]
|
|
||||||
vendor_ramdisk_table_builder.add_entry(ramdisk_path, ramdisk_type,
|
|
||||||
ramdisk_name, board_id)
|
|
||||||
if len(args_list) > 0:
|
|
||||||
unknown_args.extend(args_list)
|
|
||||||
args.vendor_ramdisk_total_size = (vendor_ramdisk_table_builder
|
|
||||||
.ramdisk_total_size)
|
|
||||||
args.vendor_ramdisk_table_entry_num = len(vendor_ramdisk_table_builder
|
|
||||||
.entries)
|
|
||||||
args.vendor_ramdisk_table_builder = vendor_ramdisk_table_builder
|
|
||||||
return unknown_args
|
|
||||||
def parse_cmdline():
|
|
||||||
version_parser = ArgumentParser(add_help=False)
|
|
||||||
version_parser.add_argument('--header_version', type=parse_int, default=0)
|
|
||||||
if version_parser.parse_known_args()[0].header_version < 3:
|
|
||||||
# For boot header v0 to v2, the kernel commandline field is split into
|
|
||||||
# two fields, cmdline and extra_cmdline. Both fields are asciiz strings,
|
|
||||||
# so we minus one here to ensure the encoded string plus the
|
|
||||||
# null-terminator can fit in the buffer size.
|
|
||||||
cmdline_size = BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE - 1
|
|
||||||
else:
|
|
||||||
cmdline_size = BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE
|
|
||||||
parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter,
|
|
||||||
epilog=get_vendor_boot_v4_usage())
|
|
||||||
parser.add_argument('--kernel', type=FileType('rb'),
|
|
||||||
help='path to the kernel')
|
|
||||||
parser.add_argument('--ramdisk', type=FileType('rb'),
|
|
||||||
help='path to the ramdisk')
|
|
||||||
parser.add_argument('--second', type=FileType('rb'),
|
|
||||||
help='path to the second bootloader')
|
|
||||||
parser.add_argument('--dtb', type=FileType('rb'), help='path to the dtb')
|
|
||||||
dtbo_group = parser.add_mutually_exclusive_group()
|
|
||||||
dtbo_group.add_argument('--recovery_dtbo', type=FileType('rb'),
|
|
||||||
help='path to the recovery DTBO')
|
|
||||||
dtbo_group.add_argument('--recovery_acpio', type=FileType('rb'),
|
|
||||||
metavar='RECOVERY_ACPIO', dest='recovery_dtbo',
|
|
||||||
help='path to the recovery ACPIO')
|
|
||||||
parser.add_argument('--cmdline', type=AsciizBytes(bufsize=cmdline_size),
|
|
||||||
default='', help='kernel command line arguments')
|
|
||||||
parser.add_argument('--vendor_cmdline',
|
|
||||||
type=AsciizBytes(bufsize=VENDOR_BOOT_ARGS_SIZE),
|
|
||||||
default='',
|
|
||||||
help='vendor boot kernel command line arguments')
|
|
||||||
parser.add_argument('--base', type=parse_int, default=0x10000000,
|
|
||||||
help='base address')
|
|
||||||
parser.add_argument('--kernel_offset', type=parse_int, default=0x00008000,
|
|
||||||
help='kernel offset')
|
|
||||||
parser.add_argument('--ramdisk_offset', type=parse_int, default=0x01000000,
|
|
||||||
help='ramdisk offset')
|
|
||||||
parser.add_argument('--second_offset', type=parse_int, default=0x00f00000,
|
|
||||||
help='second bootloader offset')
|
|
||||||
parser.add_argument('--dtb_offset', type=parse_int, default=0x01f00000,
|
|
||||||
help='dtb offset')
|
|
||||||
parser.add_argument('--os_version', type=parse_os_version, default=0,
|
|
||||||
help='operating system version')
|
|
||||||
parser.add_argument('--os_patch_level', type=parse_os_patch_level,
|
|
||||||
default=0, help='operating system patch level')
|
|
||||||
parser.add_argument('--tags_offset', type=parse_int, default=0x00000100,
|
|
||||||
help='tags offset')
|
|
||||||
parser.add_argument('--board', type=AsciizBytes(bufsize=BOOT_NAME_SIZE),
|
|
||||||
default='', help='board name')
|
|
||||||
parser.add_argument('--pagesize', type=parse_int,
|
|
||||||
choices=[2**i for i in range(11, 15)], default=2048,
|
|
||||||
help='page size')
|
|
||||||
parser.add_argument('--id', action='store_true',
|
|
||||||
help='print the image ID on standard output')
|
|
||||||
parser.add_argument('--header_version', type=parse_int, default=0,
|
|
||||||
help='boot image header version')
|
|
||||||
parser.add_argument('-o', '--output', type=FileType('wb'),
|
|
||||||
help='output file name')
|
|
||||||
parser.add_argument('--gki_signing_algorithm',
|
|
||||||
help='GKI signing algorithm to use')
|
|
||||||
parser.add_argument('--gki_signing_key',
|
|
||||||
help='path to RSA private key file')
|
|
||||||
parser.add_argument('--gki_signing_signature_args',
|
|
||||||
help='other hash arguments passed to avbtool')
|
|
||||||
parser.add_argument('--gki_signing_avbtool_path',
|
|
||||||
help='path to avbtool for boot signature generation')
|
|
||||||
parser.add_argument('--vendor_boot', type=FileType('wb'),
|
|
||||||
help='vendor boot output file name')
|
|
||||||
parser.add_argument('--vendor_ramdisk', type=FileType('rb'),
|
|
||||||
help='path to the vendor ramdisk')
|
|
||||||
parser.add_argument('--vendor_bootconfig', type=FileType('rb'),
|
|
||||||
help='path to the vendor bootconfig file')
|
|
||||||
args, extra_args = parser.parse_known_args()
|
|
||||||
if args.vendor_boot is not None and args.header_version > 3:
|
|
||||||
extra_args = parse_vendor_ramdisk_args(args, extra_args)
|
|
||||||
if len(extra_args) > 0:
|
|
||||||
raise ValueError(f'Unrecognized arguments: {extra_args}')
|
|
||||||
if args.header_version < 3:
|
|
||||||
args.extra_cmdline = args.cmdline[BOOT_ARGS_SIZE-1:]
|
|
||||||
args.cmdline = args.cmdline[:BOOT_ARGS_SIZE-1] + b'\x00'
|
|
||||||
assert len(args.cmdline) <= BOOT_ARGS_SIZE
|
|
||||||
assert len(args.extra_cmdline) <= BOOT_EXTRA_ARGS_SIZE
|
|
||||||
return args
|
|
||||||
def add_boot_image_signature(args, pagesize):
|
|
||||||
"""Adds the boot image signature.
|
|
||||||
Note that the signature will only be verified in VTS to ensure a
|
|
||||||
generic boot.img is used. It will not be used by the device
|
|
||||||
bootloader at boot time. The bootloader should only verify
|
|
||||||
the boot vbmeta at the end of the boot partition (or in the top-level
|
|
||||||
vbmeta partition) via the Android Verified Boot process, when the
|
|
||||||
device boots.
|
|
||||||
"""
|
|
||||||
args.output.flush() # Flush the buffer for signature calculation.
|
|
||||||
# Appends zeros if the signing key is not specified.
|
|
||||||
if not args.gki_signing_key or not args.gki_signing_algorithm:
|
|
||||||
zeros = b'\x00' * BOOT_IMAGE_V4_SIGNATURE_SIZE
|
|
||||||
args.output.write(zeros)
|
|
||||||
pad_file(args.output, pagesize)
|
|
||||||
return
|
|
||||||
avbtool = 'avbtool' # Used from otatools.zip or Android build env.
|
|
||||||
# We need to specify the path of avbtool in build/core/Makefile.
|
|
||||||
# Because avbtool is not guaranteed to be in $PATH there.
|
|
||||||
if args.gki_signing_avbtool_path:
|
|
||||||
avbtool = args.gki_signing_avbtool_path
|
|
||||||
# Need to specify a value of --partition_size for avbtool to work.
|
|
||||||
# We use 64 MB below, but avbtool will not resize the boot image to
|
|
||||||
# this size because --do_not_append_vbmeta_image is also specified.
|
|
||||||
avbtool_cmd = [
|
|
||||||
avbtool, 'add_hash_footer',
|
|
||||||
'--partition_name', 'boot',
|
|
||||||
'--partition_size', str(64 * 1024 * 1024),
|
|
||||||
'--image', args.output.name,
|
|
||||||
'--algorithm', args.gki_signing_algorithm,
|
|
||||||
'--key', args.gki_signing_key,
|
|
||||||
'--salt', 'd00df00d'] # TODO: use a hash of kernel/ramdisk as the salt.
|
|
||||||
# Additional arguments passed to avbtool.
|
|
||||||
if args.gki_signing_signature_args:
|
|
||||||
avbtool_cmd += args.gki_signing_signature_args.split()
|
|
||||||
# Outputs the signed vbmeta to a separate file, then append to boot.img
|
|
||||||
# as the boot signature.
|
|
||||||
with tempfile.TemporaryDirectory() as temp_out_dir:
|
|
||||||
boot_signature_output = os.path.join(temp_out_dir, 'boot_signature')
|
|
||||||
avbtool_cmd += ['--do_not_append_vbmeta_image',
|
|
||||||
'--output_vbmeta_image', boot_signature_output]
|
|
||||||
subprocess.check_call(avbtool_cmd)
|
|
||||||
with open(boot_signature_output, 'rb') as boot_signature:
|
|
||||||
if filesize(boot_signature) > BOOT_IMAGE_V4_SIGNATURE_SIZE:
|
|
||||||
raise ValueError(
|
|
||||||
f'boot sigature size is > {BOOT_IMAGE_V4_SIGNATURE_SIZE}')
|
|
||||||
write_padded_file(args.output, boot_signature, pagesize)
|
|
||||||
def write_data(args, pagesize):
|
|
||||||
write_padded_file(args.output, args.kernel, pagesize)
|
|
||||||
write_padded_file(args.output, args.ramdisk, pagesize)
|
|
||||||
write_padded_file(args.output, args.second, pagesize)
|
|
||||||
if args.header_version > 0 and args.header_version < 3:
|
|
||||||
write_padded_file(args.output, args.recovery_dtbo, pagesize)
|
|
||||||
if args.header_version == 2:
|
|
||||||
write_padded_file(args.output, args.dtb, pagesize)
|
|
||||||
if args.header_version >= 4:
|
|
||||||
add_boot_image_signature(args, pagesize)
|
|
||||||
def write_vendor_boot_data(args):
|
|
||||||
if args.header_version > 3:
|
|
||||||
builder = args.vendor_ramdisk_table_builder
|
|
||||||
builder.write_ramdisks_padded(args.vendor_boot, args.pagesize)
|
|
||||||
write_padded_file(args.vendor_boot, args.dtb, args.pagesize)
|
|
||||||
builder.write_entries_padded(args.vendor_boot, args.pagesize)
|
|
||||||
write_padded_file(args.vendor_boot, args.vendor_bootconfig,
|
|
||||||
args.pagesize)
|
|
||||||
else:
|
|
||||||
write_padded_file(args.vendor_boot, args.vendor_ramdisk, args.pagesize)
|
|
||||||
write_padded_file(args.vendor_boot, args.dtb, args.pagesize)
|
|
||||||
def main():
|
|
||||||
args = parse_cmdline()
|
|
||||||
if args.vendor_boot is not None:
|
|
||||||
if args.header_version not in {3, 4}:
|
|
||||||
raise ValueError(
|
|
||||||
'--vendor_boot not compatible with given header version')
|
|
||||||
if args.header_version == 3 and args.vendor_ramdisk is None:
|
|
||||||
raise ValueError('--vendor_ramdisk missing or invalid')
|
|
||||||
write_vendor_boot_header(args)
|
|
||||||
write_vendor_boot_data(args)
|
|
||||||
if args.output is not None:
|
|
||||||
if args.second is not None and args.header_version > 2:
|
|
||||||
raise ValueError(
|
|
||||||
'--second not compatible with given header version')
|
|
||||||
img_id = write_header(args)
|
|
||||||
if args.header_version > 2:
|
|
||||||
write_data(args, BOOT_IMAGE_HEADER_V3_PAGESIZE)
|
|
||||||
else:
|
|
||||||
write_data(args, args.pagesize)
|
|
||||||
if args.id and img_id is not None:
|
|
||||||
print('0x' + ''.join(f'{octet:02x}' for octet in img_id))
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
@ -29,39 +29,5 @@ if [[ $arch == "arm64" ]]; then
|
||||||
-O "${KERNEL_IMAGE_BASE}"/arm64/Image
|
-O "${KERNEL_IMAGE_BASE}"/arm64/Image
|
||||||
curl -L --retry 4 -f --retry-all-errors --retry-delay 60 \
|
curl -L --retry 4 -f --retry-all-errors --retry-delay 60 \
|
||||||
-O "${KERNEL_IMAGE_BASE}"/arm64/Image.gz
|
-O "${KERNEL_IMAGE_BASE}"/arm64/Image.gz
|
||||||
curl -L --retry 4 -f --retry-all-errors --retry-delay 60 \
|
|
||||||
-O "${KERNEL_IMAGE_BASE}"/arm64/cheza-kernel
|
|
||||||
|
|
||||||
DEVICE_TREES=""
|
|
||||||
DEVICE_TREES="$DEVICE_TREES apq8016-sbc-usb-host.dtb"
|
|
||||||
DEVICE_TREES="$DEVICE_TREES apq8096-db820c.dtb"
|
|
||||||
|
|
||||||
for DTB in $DEVICE_TREES; do
|
|
||||||
curl -L --retry 4 -f --retry-all-errors --retry-delay 60 \
|
|
||||||
-O "${KERNEL_IMAGE_BASE}/arm64/$DTB"
|
|
||||||
done
|
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
# Download and extract the firmware for the Qualcomm DUTs
|
|
||||||
curl --location --fail --retry-connrefused --retry 3 --retry-delay 10 \
|
|
||||||
-O http://ftp.de.debian.org/debian/pool/non-free-firmware/f/firmware-nonfree/firmware-qcom-soc_20230210-5_all.deb
|
|
||||||
|
|
||||||
mkdir firmware/
|
|
||||||
dpkg-deb -x firmware-qcom-soc_20230210-5_all.deb firmware/
|
|
||||||
|
|
||||||
# Copy only the firmware files for the devices in CI
|
|
||||||
install -Dm644 firmware/lib/firmware/qcom/a300_pfp.fw /rootfs-arm64/lib/firmware/qcom/a300_pfp.fw
|
|
||||||
install -Dm644 firmware/lib/firmware/qcom/a300_pm4.fw /rootfs-arm64/lib/firmware/qcom/a300_pm4.fw
|
|
||||||
install -Dm644 firmware/lib/firmware/qcom/a530_pfp.fw /rootfs-arm64/lib/firmware/qcom/a530_pfp.fw
|
|
||||||
install -Dm644 firmware/lib/firmware/qcom/a530_pm4.fw /rootfs-arm64/lib/firmware/qcom/a530_pm4.fw
|
|
||||||
install -Dm644 firmware/lib/firmware/qcom/a530_zap.mdt /rootfs-arm64/lib/firmware/qcom/a530_zap.mdt
|
|
||||||
install -Dm644 firmware/lib/firmware/qcom/a530v3_gpmu.fw2 /rootfs-arm64/lib/firmware/qcom/a530v3_gpmu.fw2
|
|
||||||
install -Dm644 firmware/lib/firmware/qcom/apq8096/a530_zap.mbn /rootfs-arm64/lib/firmware/qcom/apq8096/a530_zap.mbn
|
|
||||||
install -Dm644 firmware/lib/firmware/qcom/a630_gmu.bin /rootfs-arm64/lib/firmware/qcom/a630_gmu.bin
|
|
||||||
install -Dm644 firmware/lib/firmware/qcom/a630_sqe.fw /rootfs-arm64/lib/firmware/qcom/a630_sqe.fw
|
|
||||||
install -Dm644 firmware/lib/firmware/qcom/sdm845/a630_zap.mbn /rootfs-arm64/lib/firmware/qcom/sdm845/a630_zap.mbn
|
|
||||||
|
|
||||||
rm firmware-qcom-soc_20230210-5_all.deb
|
|
||||||
rm -rf firmware/
|
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ set -o xtrace
|
||||||
DEPS=(
|
DEPS=(
|
||||||
cpio
|
cpio
|
||||||
curl
|
curl
|
||||||
fastboot
|
|
||||||
netcat-openbsd
|
netcat-openbsd
|
||||||
openssh-server
|
openssh-server
|
||||||
procps
|
procps
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
# anholt | baremetal/poe-powered | @anholt
|
# anholt | baremetal/poe-powered | @anholt
|
||||||
# austriancoder | ci-tron | @austriancoder
|
# austriancoder | ci-tron | @austriancoder
|
||||||
# collabora | lava | @daniels, @sergi
|
# collabora | lava | @daniels, @sergi
|
||||||
# google-freedreno | baremetal/{fastboot,cros-servo} | @robclark
|
# google-freedreno | none (moving to LAVA) | @daniels, @sergi
|
||||||
# igalia | baremetal/poe-powered | @jasuarez, @chema
|
# igalia | baremetal/poe-powered | @jasuarez, @chema
|
||||||
# lima | lava | @enunes
|
# lima | lava | @enunes
|
||||||
# microsoft | custom | @jenatali, @alatiera
|
# microsoft | custom | @jenatali, @alatiera
|
||||||
|
|
@ -211,7 +211,6 @@
|
||||||
when: never
|
when: never
|
||||||
- !reference [.valve-farm-rules, rules]
|
- !reference [.valve-farm-rules, rules]
|
||||||
|
|
||||||
|
|
||||||
.austriancoder-farm-rules:
|
.austriancoder-farm-rules:
|
||||||
rules:
|
rules:
|
||||||
- exists: [ .ci-farms-disabled/austriancoder ]
|
- exists: [ .ci-farms-disabled/austriancoder ]
|
||||||
|
|
@ -232,27 +231,6 @@
|
||||||
when: never
|
when: never
|
||||||
- !reference [.austriancoder-farm-rules, rules]
|
- !reference [.austriancoder-farm-rules, rules]
|
||||||
|
|
||||||
|
|
||||||
.google-freedreno-farm-rules:
|
|
||||||
rules:
|
|
||||||
- exists: [ .ci-farms-disabled/google-freedreno ]
|
|
||||||
when: never
|
|
||||||
- changes: [ .ci-farms-disabled/google-freedreno ]
|
|
||||||
if: '$CI_PIPELINE_SOURCE != "schedule"'
|
|
||||||
when: on_success
|
|
||||||
- changes: [ .ci-farms-disabled/* ]
|
|
||||||
if: '$CI_PIPELINE_SOURCE != "schedule"'
|
|
||||||
when: never
|
|
||||||
|
|
||||||
.google-freedreno-farm-manual-rules:
|
|
||||||
rules:
|
|
||||||
- exists: [ .ci-farms-disabled/google-freedreno ]
|
|
||||||
when: never
|
|
||||||
- changes: [ .ci-farms-disabled/google-freedreno ]
|
|
||||||
if: '$CI_PIPELINE_SOURCE != "schedule"'
|
|
||||||
when: never
|
|
||||||
- !reference [.google-freedreno-farm-rules, rules]
|
|
||||||
|
|
||||||
.vmware-farm-rules:
|
.vmware-farm-rules:
|
||||||
rules:
|
rules:
|
||||||
- exists: [ .ci-farms-disabled/vmware ]
|
- exists: [ .ci-farms-disabled/vmware ]
|
||||||
|
|
@ -315,6 +293,15 @@
|
||||||
- !reference [.pengutronix-farm-rules, rules]
|
- !reference [.pengutronix-farm-rules, rules]
|
||||||
|
|
||||||
|
|
||||||
|
# Temporary placeholder as the devices move across to LAVA.
|
||||||
|
.google-freedreno-farm-rules:
|
||||||
|
rules:
|
||||||
|
- when: never
|
||||||
|
|
||||||
|
.google-freedreno-farm-manual-rules:
|
||||||
|
rules:
|
||||||
|
- when: never
|
||||||
|
|
||||||
# Skip container & build jobs when disabling any farm, and run them if any
|
# Skip container & build jobs when disabling any farm, and run them if any
|
||||||
# farm gets re-enabled.
|
# farm gets re-enabled.
|
||||||
# Only apply these rules in MR context, because otherwise we get a false
|
# Only apply these rules in MR context, because otherwise we get a false
|
||||||
|
|
@ -359,10 +346,6 @@
|
||||||
changes: [ .ci-farms-disabled/austriancoder ]
|
changes: [ .ci-farms-disabled/austriancoder ]
|
||||||
exists: [ .ci-farms-disabled/austriancoder ]
|
exists: [ .ci-farms-disabled/austriancoder ]
|
||||||
when: never
|
when: never
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
||||||
changes: [ .ci-farms-disabled/google-freedreno ]
|
|
||||||
exists: [ .ci-farms-disabled/google-freedreno ]
|
|
||||||
when: never
|
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
changes: [ .ci-farms-disabled/ondracka ]
|
changes: [ .ci-farms-disabled/ondracka ]
|
||||||
exists: [ .ci-farms-disabled/ondracka ]
|
exists: [ .ci-farms-disabled/ondracka ]
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ include:
|
||||||
- .gitlab-ci/conditional-build-image-tags.yml
|
- .gitlab-ci/conditional-build-image-tags.yml
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
DEBIAN_BASE_TAG: "20250515-firmware"
|
DEBIAN_BASE_TAG: "20250526-barermetal"
|
||||||
|
|
||||||
DEBIAN_BUILD_TAG: "20250515-aarch64"
|
DEBIAN_BUILD_TAG: "20250515-aarch64"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue