From 63a0bf10dc7e354eaa6f2f7da7d9c2c72c092bd0 Mon Sep 17 00:00:00 2001 From: Yogesh Lal Date: Wed, 1 Apr 2026 17:07:59 +0530 Subject: [PATCH 1/5] boot: add EFI boot method support Add end-to-end support for BOOT_METHOD=efi so EFI-based boot flows along with existing fastboot method. Changes included: - README: document efi as a supported BOOT_METHOD. - data validation: allow efi in allowed_boot_methods. - templates: add templates/boot/efi.jinja2 with a complete EFI deploy/boot flow. Signed-off-by: Yogesh Lal --- README.md | 2 +- data_validation/validate_data.py | 4 +- templates/boot/efi.jinja2 | 84 ++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 templates/boot/efi.jinja2 diff --git a/README.md b/README.md index 0402e6a..7798452 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ cd job_render # Usage - Set environment variables (BOOT_METHOD, TARGET, TARGET_DTB) in your shell before running the script using the export command. +Supported `BOOT_METHOD` values: `fastboot`, `u-boot`, `efi`. Example: ``` export BOOT_METHOD="fastboot" @@ -33,4 +34,3 @@ export TARGET_DTB="qcs6490-rb3gen2" # License job_render is licensed under the [*BSD-3-clause-clear License*](https://spdx.org/licenses/BSD-3-Clause-Clear.html). See [*LICENSE*](https://github.com/qualcomm-linux/job_render/blob/main/LICENSE) for the full license text. - diff --git a/data_validation/validate_data.py b/data_validation/validate_data.py index 1c183b1..2e2b742 100644 --- a/data_validation/validate_data.py +++ b/data_validation/validate_data.py @@ -4,7 +4,7 @@ import sys # Allowed boot methods -allowed_boot_methods = ['fastboot', 'u-boot'] +allowed_boot_methods = ['fastboot', 'u-boot', 'efi'] class Validator: @@ -57,4 +57,4 @@ def perform_validations_and_proceed(self): sys.exit(1) except Exception as e: print(f"An unexpected error occurred: {e}") - sys.exit(1) \ No newline at end of file + sys.exit(1) diff --git a/templates/boot/efi.jinja2 b/templates/boot/efi.jinja2 new file mode 100644 index 0000000..e57522d --- /dev/null +++ b/templates/boot/efi.jinja2 @@ -0,0 +1,84 @@ +{%- set flash_image_url = flash_image_url | default(platform_config.flash_image, true) -%} +{%- if flash_image_name is not defined and flash_image_url -%} +{%- set flash_image_name = flash_image_url.split('/')[-1] -%} +{%- endif -%} + +- deploy: + images: + image: +{% if flash_image_headers %} + headers: +{% for key, value in flash_image_headers.items() %} + {{ key }}: '{{ value }}' +{% endfor %} +{% endif %} + url: '{{ flash_image_url }}' + dtb: + url: '{{ node.artifacts.dtb }}' + kernel: + url: '{{ node.artifacts.kernel }}' + ramdisk: + {% if boot_commands == "ramdisk" %} + url: '{{ ramdiskroot }}/rootfs.cpio.gz' + {% else %} + url: 'https://storage.kernelci.org/images/rootfs/debian/bookworm-kselftest/20250724.0/{{ brarch }}/initrd.cpio.gz' + {% endif %} + compression: gz + format: cpio.newc + overlays: + lava: true +{% filter indent(width=10) %} +{% block testoverlays %}{% endblock %} +{% endfilter %} +{% set dtb = device_dtb.split('/')[-1] %} +{% if boot_commands == "ramdisk" %} +{% set ramdisk_name = "rootfs.cpio.gz" %} +{% else %} +{% set ramdisk_name = "initrd.cpio.gz" %} +{% endif %} + postprocess: + docker: + image: ghcr.io/nandini-matam/kmake-image:v5 + steps: +{%- if node.data.kernel_type.endswith('.gz') %} + - gunzip Image.gz +{%- endif %} + - generate_boot_bins.sh efi --ramdisk {{ ramdisk_name }} --systemd-boot /artifacts/systemd/usr/lib/systemd/boot/efi/systemd-bootaa64.efi --stub /artifacts/systemd/usr/lib/systemd/boot/efi/linuxaa64.efi.stub --linux Image --cmdline "console=ttyMSM0,115200n8 copy-modules rootdelay=10 root=PARTLABEL=rootfs rw rootwait qcom_geni_serial.con_enabled=1" --output images + - generate_boot_bins.sh dtb --input {{ dtb }} --output images + - export IMAGE_PATH=$PWD + - cp overlay*.tar.gz overlay.tar.gz + - echo "OVERLAY=overlay.tar.gz" >> $IMAGE_PATH/flash.settings + - echo "OVERLAY_PATH=/" >> $IMAGE_PATH/flash.settings + - echo "ROOTFS_IMAGE=rootfs.img" >> $IMAGE_PATH/flash.settings + - echo "EFI=images/efi.bin" >> $IMAGE_PATH/flash.settings + - echo "DTB=images/dtb.bin" >> $IMAGE_PATH/flash.settings + - echo "DEVICE_TYPE={{ platform_config.name }}" >> $IMAGE_PATH/flash.settings + - echo "PORT={{ flash_port | default('0', true) }}" >> $IMAGE_PATH/flash.settings + - cat $IMAGE_PATH/flash.settings + timeout: + minutes: 60 + to: downloads + +- deploy: + to: flasher + images: + image: + url: 'downloads://{{ flash_image_name }}' + settings: + url: 'downloads://flash.settings' + overlay: + url: 'downloads://overlay.tar.gz' + timeout: + minutes: 15 +- boot: + prompts: + - '/ #' + failure_retry: 3 + timeout: + minutes: 10 + timeouts: + bootloader-commands: + minutes: 5 + auto-login-action: + minutes: 5 + method: minimal From 9a838afe8ed14cbbd8b0f4f71a435c2f89bf24bb Mon Sep 17 00:00:00 2001 From: Yogesh Lal Date: Wed, 1 Apr 2026 17:08:58 +0530 Subject: [PATCH 2/5] job_render: require FLASH_IMAGE for EFI boot method EFI flash inputs through the job rendering path so EFI jobs can be rendered with the required flashing metadata. Signed-off-by: Yogesh Lal --- README.md | 1 + data/cloudData.json | 3 ++- data_validation/validate_data.py | 3 +++ lava_Job_definition_generator.py | 9 ++++++--- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7798452..52dbef8 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ cd job_render - Set environment variables (BOOT_METHOD, TARGET, TARGET_DTB) in your shell before running the script using the export command. Supported `BOOT_METHOD` values: `fastboot`, `u-boot`, `efi`. +When using `BOOT_METHOD=efi`, set `FLASH_IMAGE` (required) and optionally `FLASH_PORT`. Example: ``` export BOOT_METHOD="fastboot" diff --git a/data/cloudData.json b/data/cloudData.json index 3be6016..4144b13 100644 --- a/data/cloudData.json +++ b/data/cloudData.json @@ -17,7 +17,8 @@ "modules": "", "kselftest_tar_gz": "https://files.kernelci.org/kbuild-gcc-12-arm64-681a3b1cea9ae9de13450f99/kselftest.tar.gz", "ramdisk": "", - "firmware": "" + "firmware": "", + "flash_image": "" }, "data": { "kernel_revision": { diff --git a/data_validation/validate_data.py b/data_validation/validate_data.py index 2e2b742..73c89de 100644 --- a/data_validation/validate_data.py +++ b/data_validation/validate_data.py @@ -23,6 +23,9 @@ def validate_platform_config(self): # Check if boot_method is valid if self.platform_config['boot_method'] not in allowed_boot_methods: return False, f"Invalid boot_method: {self.platform_config['boot_method']}. Allowed values are {allowed_boot_methods},use export command to set environment variables" + + if self.platform_config['boot_method'] == 'efi' and not self.platform_config.get('flash_image'): + return False, "BOOT_METHOD 'efi' requires FLASH_IMAGE to be set" return True, "Valid platform_config" diff --git a/lava_Job_definition_generator.py b/lava_Job_definition_generator.py index fe514a8..f42594d 100644 --- a/lava_Job_definition_generator.py +++ b/lava_Job_definition_generator.py @@ -17,6 +17,8 @@ boot_method = os.environ.get("BOOT_METHOD") name = os.environ.get("TARGET") target_dtb = os.environ.get("TARGET_DTB") +flash_image = os.environ.get("FLASH_IMAGE") +flash_port = os.environ.get("FLASH_PORT", "0") brarch = 'arm64' @@ -35,7 +37,8 @@ platform_config = { 'boot_method': boot_method, - 'name': name + 'name': name, + 'flash_image': flash_image } # Example test method, # → picks the value from the environment if it exists @@ -129,7 +132,7 @@ class ConflictError(Exception): ### Render the template with dynamic data node_data = data_handler.get_fetched_data() -job_definition = template_handler.render_template(template, node=data_handler.get_fetched_data(), platform_config=platform_config, test_method=test_method, tests_count=data_handler.get_count_of_tests(),device_dtb = node_data['artifacts']['dtb'],brarch=brarch) +job_definition = template_handler.render_template(template, node=data_handler.get_fetched_data(), platform_config=platform_config, test_method=test_method, tests_count=data_handler.get_count_of_tests(),device_dtb = node_data['artifacts']['dtb'],brarch=brarch,flash_port=flash_port) # Parse the rendered YAML and Save the rendered job definition -template_handler.save_rendered_template(job_definition, os.path.join('renders','lava_job_definition.yaml')) \ No newline at end of file +template_handler.save_rendered_template(job_definition, os.path.join('renders','lava_job_definition.yaml')) From aa6dcfafd50739409291fef2903081c1d1fe2d88 Mon Sep 17 00:00:00 2001 From: Yogesh Lal Date: Wed, 1 Apr 2026 17:09:21 +0530 Subject: [PATCH 3/5] lava_Job_definition_generator: Fix renders directory check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generator did not ensures renders directory exists and on a clean workspace, attempts to write rendered files could fail with a “No such file or directory” error. This change ensure renders directory exists before saving. Signed-off-by: Yogesh Lal --- lava_Job_definition_generator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lava_Job_definition_generator.py b/lava_Job_definition_generator.py index f42594d..208ea84 100644 --- a/lava_Job_definition_generator.py +++ b/lava_Job_definition_generator.py @@ -26,6 +26,10 @@ if not os.path.exists('logs'): os.makedirs('logs') +# Create renders directory if it doesn't exist +if not os.path.exists('renders'): + os.makedirs('renders') + # Generate a unique log file name based on the current timestamp log_filename = datetime.now().strftime('logs/log_%Y%m%d_%H%M%S.log') From 3f690b1a6ce5d9e5f14d9fc3508ac3310563ef22 Mon Sep 17 00:00:00 2001 From: Yogesh Lal Date: Mon, 6 Apr 2026 12:29:39 +0530 Subject: [PATCH 4/5] Add meta-qcom flag for flasher auto_login Update templates/boot/flasher.jinja2 to emit the auto_login block only when meta_qcom is enabled, keeping flasher jobs without the login prompt by default. Also document the new flag in README usage notes for BOOT_METHOD=flasher. Signed-off-by: Yogesh Lal --- Handlers/argParseHandler.py | 6 ++++ README.md | 5 +-- lava_Job_definition_generator.py | 3 +- templates/boot/flasher.jinja2 | 52 ++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 templates/boot/flasher.jinja2 diff --git a/Handlers/argParseHandler.py b/Handlers/argParseHandler.py index 142e14a..aa8be04 100644 --- a/Handlers/argParseHandler.py +++ b/Handlers/argParseHandler.py @@ -20,6 +20,7 @@ def __init__(self, test_data): parser.add_argument('--buildurl', type=str, help='custom build json url') parser.add_argument('--localjson', type=str, help='local json parsing') parser.add_argument('--template', type=str, help='For parsing local path or web url for template') + parser.add_argument('--meta-qcom', action='store_true', help='Enable meta-qcom specific auto-login for flasher boot method') logging.debug('Added optional arguments') if self.test_data is not None: @@ -63,3 +64,8 @@ def get_template_path(self): template_path = self.argument.template logging.debug(f'Template path: {template_path}') return template_path + + def is_meta_qcom_enabled(self): + meta_qcom_enabled = self.argument.meta_qcom + logging.debug(f'Meta-qcom enabled: {meta_qcom_enabled}') + return meta_qcom_enabled diff --git a/README.md b/README.md index 52dbef8..01b7c79 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,9 @@ cd job_render # Usage - Set environment variables (BOOT_METHOD, TARGET, TARGET_DTB) in your shell before running the script using the export command. -Supported `BOOT_METHOD` values: `fastboot`, `u-boot`, `efi`. -When using `BOOT_METHOD=efi`, set `FLASH_IMAGE` (required) and optionally `FLASH_PORT`. +Supported `BOOT_METHOD` values: `fastboot`, `u-boot`, `efi`, `flasher`. +When using `BOOT_METHOD=efi` or `BOOT_METHOD=flasher`, set `FLASH_IMAGE` (required) and optionally `FLASH_PORT`. +For `BOOT_METHOD=flasher`, pass `--meta-qcom` to enable the `auto_login` block (login prompt `login:`, user `root`). Example: ``` export BOOT_METHOD="fastboot" diff --git a/lava_Job_definition_generator.py b/lava_Job_definition_generator.py index 208ea84..dbcb434 100644 --- a/lava_Job_definition_generator.py +++ b/lava_Job_definition_generator.py @@ -74,6 +74,7 @@ is_buildurl_provided = arg_parse_handler.get_buildurl() local_json_path = arg_parse_handler.get_local_json_path() template_path = arg_parse_handler.get_template_path() +meta_qcom_enabled = arg_parse_handler.is_meta_qcom_enabled() ### Error Handling for more than one arguments for json passing (options: build_url, node_id, localjson) class ConflictError(Exception): @@ -136,7 +137,7 @@ class ConflictError(Exception): ### Render the template with dynamic data node_data = data_handler.get_fetched_data() -job_definition = template_handler.render_template(template, node=data_handler.get_fetched_data(), platform_config=platform_config, test_method=test_method, tests_count=data_handler.get_count_of_tests(),device_dtb = node_data['artifacts']['dtb'],brarch=brarch,flash_port=flash_port) +job_definition = template_handler.render_template(template, node=data_handler.get_fetched_data(), platform_config=platform_config, test_method=test_method, tests_count=data_handler.get_count_of_tests(), device_dtb=node_data['artifacts']['dtb'], brarch=brarch, flash_port=flash_port, meta_qcom=meta_qcom_enabled) # Parse the rendered YAML and Save the rendered job definition template_handler.save_rendered_template(job_definition, os.path.join('renders','lava_job_definition.yaml')) diff --git a/templates/boot/flasher.jinja2 b/templates/boot/flasher.jinja2 new file mode 100644 index 0000000..5a957ed --- /dev/null +++ b/templates/boot/flasher.jinja2 @@ -0,0 +1,52 @@ +{%- set flash_image_url = flash_image_url | default(platform_config.flash_image, true) -%} +{%- set flash_image_auth = flash_image_auth | default('LAVA_BASIC_AUTH', true) -%} +{%- if flash_image_name is not defined and flash_image_url -%} +{%- set flash_image_path = flash_image_url.split('?')[0] -%} +{%- set flash_image_name = flash_image_path.split('/')[-1] -%} +{%- endif -%} + +- deploy: + images: + image: + headers: + Authorization: '{{ flash_image_auth }}' + url: '{{ flash_image_url }}' + postprocess: + docker: + image: ghcr.io/foundriesio/lava-lmp-sign:main + steps: + - export IMAGE_PATH=$PWD + - echo "DEVICE_TYPE={{ platform_config.name }}" >> $IMAGE_PATH/flash.settings + - echo "PORT={{ flash_port | default('0', true) }}" >> $IMAGE_PATH/flash.settings + - cat $IMAGE_PATH/flash.settings + timeout: + minutes: 20 + to: downloads + namespace: kernelciflasher + +- deploy: + images: + image: + url: 'downloads://{{ flash_image_name }}' + settings: + url: 'downloads://flash.settings' + timeout: + minutes: 30 + to: flasher + namespace: kernelciflasher + +- boot: + namespace: target +{% if meta_qcom | default(false, true) %} + auto_login: + login_prompt: 'login:' + username: root + password_prompt: Password + password: oelinux123 +{% endif %} + prompts: + - root@rb3gen2-core-kit + - root@(.*):[/~]# + timeout: + minutes: 20 + method: minimal From 2123fd04515f91067e0b566ba9c3898d914761d8 Mon Sep 17 00:00:00 2001 From: Salendarsingh Gaud Date: Mon, 6 Apr 2026 21:17:26 +0530 Subject: [PATCH 5/5] flasher: Update flasher.jinja2 to handle qcom-next jobs Update flasher.jinja2 to handle flatbuild url passed by the qcom-next jobs. --- data_validation/validate_data.py | 4 +-- templates/boot/flasher.jinja2 | 45 ++++++++++++++++---------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/data_validation/validate_data.py b/data_validation/validate_data.py index 73c89de..16ecacb 100644 --- a/data_validation/validate_data.py +++ b/data_validation/validate_data.py @@ -4,7 +4,7 @@ import sys # Allowed boot methods -allowed_boot_methods = ['fastboot', 'u-boot', 'efi'] +allowed_boot_methods = ['fastboot', 'u-boot', 'efi', 'flasher'] class Validator: @@ -26,7 +26,7 @@ def validate_platform_config(self): if self.platform_config['boot_method'] == 'efi' and not self.platform_config.get('flash_image'): return False, "BOOT_METHOD 'efi' requires FLASH_IMAGE to be set" - + return True, "Valid platform_config" diff --git a/templates/boot/flasher.jinja2 b/templates/boot/flasher.jinja2 index 5a957ed..c347026 100644 --- a/templates/boot/flasher.jinja2 +++ b/templates/boot/flasher.jinja2 @@ -1,42 +1,37 @@ -{%- set flash_image_url = flash_image_url | default(platform_config.flash_image, true) -%} -{%- set flash_image_auth = flash_image_auth | default('LAVA_BASIC_AUTH', true) -%} -{%- if flash_image_name is not defined and flash_image_url -%} -{%- set flash_image_path = flash_image_url.split('?')[0] -%} -{%- set flash_image_name = flash_image_path.split('/')[-1] -%} -{%- endif -%} - - deploy: images: image: - headers: - Authorization: '{{ flash_image_auth }}' - url: '{{ flash_image_url }}' + url: '{{ node.artifacts.flash_image }}' postprocess: docker: - image: ghcr.io/foundriesio/lava-lmp-sign:main + image: ghcr.io/mwasilew/docker-mkbootimage:master steps: - export IMAGE_PATH=$PWD + - cp overlay*.tar.gz overlay.tar.gz + - echo "OVERLAY=overlay.tar.gz" >> $IMAGE_PATH/flash.settings + - echo "OVERLAY_PATH=/" >> $IMAGE_PATH/flash.settings + - echo "ROOTFS_IMAGE=rootfs.img" >> $IMAGE_PATH/flash.settings - echo "DEVICE_TYPE={{ platform_config.name }}" >> $IMAGE_PATH/flash.settings - - echo "PORT={{ flash_port | default('0', true) }}" >> $IMAGE_PATH/flash.settings + - echo "PORT=0" >> $IMAGE_PATH/flash.settings - cat $IMAGE_PATH/flash.settings timeout: - minutes: 20 + minutes: 30 to: downloads - namespace: kernelciflasher - +{% set flash_image_url = node.artifacts.flash_image %} +{% set flash_image_path = flash_image_url.split('?')[0] %} +{% set flash_image_name = flash_image_path.split('/')[-1] %} - deploy: images: image: url: 'downloads://{{ flash_image_name }}' settings: - url: 'downloads://flash.settings' + url: downloads://flash.settings + overlay: + url: downloads://overlay.tar.gz timeout: - minutes: 30 + minutes: 10 to: flasher - namespace: kernelciflasher - - boot: - namespace: target {% if meta_qcom | default(false, true) %} auto_login: login_prompt: 'login:' @@ -45,8 +40,14 @@ password: oelinux123 {% endif %} prompts: - - root@rb3gen2-core-kit + - root@{{ platform_config.name }} - root@(.*):[/~]# + failure_retry: 3 timeout: - minutes: 20 + minutes: 10 + timeouts: + bootloader-commands: + minutes: 3 + auto-login-action: + minutes: 10 method: minimal