diff --git a/.github/workflows/jetson-agx-orin-devkit-64gb.yml b/.github/workflows/jetson-agx-orin-devkit-64gb.yml index 2aca3ca2..8b959025 100644 --- a/.github/workflows/jetson-agx-orin-devkit-64gb.yml +++ b/.github/workflows/jetson-agx-orin-devkit-64gb.yml @@ -9,10 +9,10 @@ on: - master # ESR branches glob pattern #- 20[0-9][0-9].[0-1]?[1470].x - # pull_request_target: - # branches: - # - main - # - master + pull_request_target: + branches: + - main + - master push: tags: # Semver tags glob pattern (includes ESR in format v20YY.MM.PATCH) @@ -31,11 +31,17 @@ on: type: string default: balena-staging.com +permissions: + id-token: write # This is required for requesting the JWT #https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#requesting-the-access-token + actions: read # We are fetching workflow run results of a merge commit when workflow is triggered by new tag, to see if tests pass + pull-requests: write # Read is required to fetch the PR that merged, in order to get the test results. Write is required to create PR comments for workflow approvals. + packages: read + contents: read jobs: yocto: name: Yocto - uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@ded533f1b8c8ff983dbcff2d219851130f8bfc4d # v1.27.2 + uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@master # Prevent duplicate workflow executions for pull_request (PR) and pull_request_target (PRT) events. # Both PR and PRT will be triggered for the same pull request, whether it is internal or from a fork. # This condition will prevent the workflow from running twice for the same pull request while diff --git a/.github/workflows/jetson-agx-orin-devkit.yml b/.github/workflows/jetson-agx-orin-devkit.yml index a2185138..aedd7c50 100644 --- a/.github/workflows/jetson-agx-orin-devkit.yml +++ b/.github/workflows/jetson-agx-orin-devkit.yml @@ -9,10 +9,10 @@ on: - master # ESR branches glob pattern #- 20[0-9][0-9].[0-1]?[1470].x - # pull_request_target: - # branches: - # - main - # - master + pull_request_target: + branches: + - main + - master push: tags: # Semver tags glob pattern (includes ESR in format v20YY.MM.PATCH) @@ -31,11 +31,17 @@ on: type: string default: balena-staging.com +permissions: + id-token: write # This is required for requesting the JWT #https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#requesting-the-access-token + actions: read # We are fetching workflow run results of a merge commit when workflow is triggered by new tag, to see if tests pass + pull-requests: write # Read is required to fetch the PR that merged, in order to get the test results. Write is required to create PR comments for workflow approvals. + packages: read + contents: read jobs: yocto: name: Yocto - uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@ded533f1b8c8ff983dbcff2d219851130f8bfc4d # v1.27.2 + uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@master # Prevent duplicate workflow executions for pull_request (PR) and pull_request_target (PRT) events. # Both PR and PRT will be triggered for the same pull request, whether it is internal or from a fork. # This condition will prevent the workflow from running twice for the same pull request while diff --git a/.github/workflows/jetson-orin-nano-devkit-nvme.yml b/.github/workflows/jetson-orin-nano-devkit-nvme.yml index ca77b7b0..269796cb 100644 --- a/.github/workflows/jetson-orin-nano-devkit-nvme.yml +++ b/.github/workflows/jetson-orin-nano-devkit-nvme.yml @@ -9,10 +9,10 @@ on: - master # ESR branches glob pattern #- 20[0-9][0-9].[0-1]?[1470].x - # pull_request_target: - # branches: - # - main - # - master + pull_request_target: + branches: + - main + - master push: tags: # Semver tags glob pattern (includes ESR in format v20YY.MM.PATCH) @@ -31,11 +31,17 @@ on: type: string default: balena-staging.com +permissions: + id-token: write # This is required for requesting the JWT #https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#requesting-the-access-token + actions: read # We are fetching workflow run results of a merge commit when workflow is triggered by new tag, to see if tests pass + pull-requests: write # Read is required to fetch the PR that merged, in order to get the test results. Write is required to create PR comments for workflow approvals. + packages: read + contents: read jobs: yocto: name: Yocto - uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@ded533f1b8c8ff983dbcff2d219851130f8bfc4d # v1.27.2 + uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@master # Prevent duplicate workflow executions for pull_request (PR) and pull_request_target (PRT) events. # Both PR and PRT will be triggered for the same pull request, whether it is internal or from a fork. # This condition will prevent the workflow from running twice for the same pull request while diff --git a/.github/workflows/jetson-orin-nano-seeed-j3010.yml b/.github/workflows/jetson-orin-nano-seeed-j3010.yml index a0a99858..946d201a 100644 --- a/.github/workflows/jetson-orin-nano-seeed-j3010.yml +++ b/.github/workflows/jetson-orin-nano-seeed-j3010.yml @@ -9,10 +9,10 @@ on: - master # ESR branches glob pattern #- 20[0-9][0-9].[0-1]?[1470].x - # pull_request_target: - # branches: - # - main - # - master + pull_request_target: + branches: + - main + - master push: tags: # Semver tags glob pattern (includes ESR in format v20YY.MM.PATCH) @@ -31,11 +31,17 @@ on: type: string default: balena-staging.com +permissions: + id-token: write # This is required for requesting the JWT #https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#requesting-the-access-token + actions: read # We are fetching workflow run results of a merge commit when workflow is triggered by new tag, to see if tests pass + pull-requests: write # Read is required to fetch the PR that merged, in order to get the test results. Write is required to create PR comments for workflow approvals. + packages: read + contents: read jobs: yocto: name: Yocto - uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@ded533f1b8c8ff983dbcff2d219851130f8bfc4d # v1.27.2 + uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@master # Prevent duplicate workflow executions for pull_request (PR) and pull_request_target (PRT) events. # Both PR and PRT will be triggered for the same pull request, whether it is internal or from a fork. # This condition will prevent the workflow from running twice for the same pull request while diff --git a/.github/workflows/jetson-orin-nx-seeed-j4012.yml b/.github/workflows/jetson-orin-nx-seeed-j4012.yml index 604ccc1e..08896692 100644 --- a/.github/workflows/jetson-orin-nx-seeed-j4012.yml +++ b/.github/workflows/jetson-orin-nx-seeed-j4012.yml @@ -9,10 +9,10 @@ on: - master # ESR branches glob pattern #- 20[0-9][0-9].[0-1]?[1470].x - # pull_request_target: - # branches: - # - main - # - master + pull_request_target: + branches: + - main + - master push: tags: # Semver tags glob pattern (includes ESR in format v20YY.MM.PATCH) @@ -31,11 +31,17 @@ on: type: string default: balena-staging.com +permissions: + id-token: write # This is required for requesting the JWT #https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#requesting-the-access-token + actions: read # We are fetching workflow run results of a merge commit when workflow is triggered by new tag, to see if tests pass + pull-requests: write # Read is required to fetch the PR that merged, in order to get the test results. Write is required to create PR comments for workflow approvals. + packages: read + contents: read jobs: yocto: name: Yocto - uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@ded533f1b8c8ff983dbcff2d219851130f8bfc4d # v1.27.2 + uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@master # Prevent duplicate workflow executions for pull_request (PR) and pull_request_target (PRT) events. # Both PR and PRT will be triggered for the same pull request, whether it is internal or from a fork. # This condition will prevent the workflow from running twice for the same pull request while diff --git a/.github/workflows/jetson-orin-nx-xavier-nx-devkit.yml b/.github/workflows/jetson-orin-nx-xavier-nx-devkit.yml index 83b248e7..59a8c947 100644 --- a/.github/workflows/jetson-orin-nx-xavier-nx-devkit.yml +++ b/.github/workflows/jetson-orin-nx-xavier-nx-devkit.yml @@ -9,10 +9,10 @@ on: - master # ESR branches glob pattern #- 20[0-9][0-9].[0-1]?[1470].x - # pull_request_target: - # branches: - # - main - # - master + pull_request_target: + branches: + - main + - master push: tags: # Semver tags glob pattern (includes ESR in format v20YY.MM.PATCH) @@ -31,11 +31,17 @@ on: type: string default: balena-staging.com +permissions: + id-token: write # This is required for requesting the JWT #https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#requesting-the-access-token + actions: read # We are fetching workflow run results of a merge commit when workflow is triggered by new tag, to see if tests pass + pull-requests: write # Read is required to fetch the PR that merged, in order to get the test results. Write is required to create PR comments for workflow approvals. + packages: read + contents: read jobs: yocto: name: Yocto - uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@ded533f1b8c8ff983dbcff2d219851130f8bfc4d # v1.27.2 + uses: balena-os/balena-yocto-scripts/.github/workflows/yocto-build-deploy.yml@master # Prevent duplicate workflow executions for pull_request (PR) and pull_request_target (PRT) events. # Both PR and PRT will be triggered for the same pull request, whether it is internal or from a fork. # This condition will prevent the workflow from running twice for the same pull request while diff --git a/balena-yocto-scripts b/balena-yocto-scripts index ded533f1..3eebec1f 160000 --- a/balena-yocto-scripts +++ b/balena-yocto-scripts @@ -1 +1 @@ -Subproject commit ded533f1b8c8ff983dbcff2d219851130f8bfc4d +Subproject commit 3eebec1f898038f5bbee0fcccb62b7178db2ef47 diff --git a/contracts b/contracts index 1d499fe7..01757392 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 1d499fe7d33791e1ac6dccfe031e178b7d520bb4 +Subproject commit 01757392e1088465b996fb9e92e1b6ab8a45d3b1 diff --git a/layers/meta-balena b/layers/meta-balena index 90608388..229a6ad1 160000 --- a/layers/meta-balena +++ b/layers/meta-balena @@ -1 +1 @@ -Subproject commit 9060838858c8461f42f88c344c8f63ab50c7707b +Subproject commit 229a6ad1da498c4ce8aaa17501ddad3e99806f08 diff --git a/layers/meta-balena-jetson/recipes-bsp/tegra-binaries/files/TEGRA_BL_3701_64.Cap.gz b/layers/meta-balena-jetson/recipes-bsp/tegra-binaries/files/TEGRA_BL_3701_64.Cap.gz index 2313f51e..bc851c28 100644 Binary files a/layers/meta-balena-jetson/recipes-bsp/tegra-binaries/files/TEGRA_BL_3701_64.Cap.gz and b/layers/meta-balena-jetson/recipes-bsp/tegra-binaries/files/TEGRA_BL_3701_64.Cap.gz differ diff --git a/layers/meta-balena-jetson/recipes-bsp/tegra-binaries/files/boot0_agx_orin_devkit_64.img.gz b/layers/meta-balena-jetson/recipes-bsp/tegra-binaries/files/boot0_agx_orin_devkit_64.img.gz index 322540f8..f93bb08f 100644 Binary files a/layers/meta-balena-jetson/recipes-bsp/tegra-binaries/files/boot0_agx_orin_devkit_64.img.gz and b/layers/meta-balena-jetson/recipes-bsp/tegra-binaries/files/boot0_agx_orin_devkit_64.img.gz differ diff --git a/layers/meta-balena-jetson/recipes-bsp/uefi/edk2-container_1.0.bb b/layers/meta-balena-jetson/recipes-bsp/uefi/edk2-container_1.0.bb index f2efa6b2..a0783e39 100644 --- a/layers/meta-balena-jetson/recipes-bsp/uefi/edk2-container_1.0.bb +++ b/layers/meta-balena-jetson/recipes-bsp/uefi/edk2-container_1.0.bb @@ -29,6 +29,7 @@ SRC_URI = " \ file://0001-AGX-Orin-64GB-Integrate-with-balenaOS-on-L4T-36.3_patch.txt \ file://0001-edk2-nvidia-Remove-pva-fw-from-required-list_patch.txt \ file://0001-StandaloneMmOptee-Don-t-assert-if-var-store-integrit_patch.txt \ + file://0001-TegraPlatformBootManager-TegraPlatformBootManagerDxe_patch.txt \ " inherit deploy diff --git a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-AGX-Orin-64GB-Integrate-with-balenaOS-on-L4T-36.3_patch.txt b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-AGX-Orin-64GB-Integrate-with-balenaOS-on-L4T-36.3_patch.txt index 72bafd75..37acc80b 100644 --- a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-AGX-Orin-64GB-Integrate-with-balenaOS-on-L4T-36.3_patch.txt +++ b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-AGX-Orin-64GB-Integrate-with-balenaOS-on-L4T-36.3_patch.txt @@ -1,26 +1,23 @@ -From 74a9cf2fd5badeb72f368ddc6629f5c3a434b4a0 Mon Sep 17 00:00:00 2001 +From 6ebf0ad4cfe96f56c74f8a8869eca51555a2342d Mon Sep 17 00:00:00 2001 From: Alexandru Costache -Date: Mon, 5 Aug 2024 17:04:58 +0300 -Subject: [PATCH] AGX Orin 64GB: Integrate with balenaOS on L4T 36.3 +Date: Sun, 6 Oct 2024 12:30:26 +0300 +Subject: [PATCH] With this patch, the Nvidia bootloader parses + resinos_uEnv.txt, extra_uEnv.txt and bootcount.env in order to load the + extlinux.conf and the selected dtb from the current active rootfs. It is also + able to boot a flasher image if the firmware in the QSPI is based on Jetpack + 6. -With this patch, the Nvidia bootloader parses resinos_uEnv.txt, -extra_uEnv.txt and bootcount.env in order to load the -extlinux.conf and the selected dtb from the current active -rootfs. - -Also, switch boot order to eMMC, NVME, USB for -the AGX Orin 64GB devkit +We also now leave the boot order as it comes by default Upstream-status: Inappropriate [configuration] Signed-off-by: Alexandru Costache --- - .../Application/L4TLauncher/L4TLauncher.c | 584 +++++++++++++++++- - .../Application/L4TLauncher/L4TLauncher.h | 15 +- - .../Tegra/DeviceTree/L4TConfiguration.dts | 2 +- - 3 files changed, 580 insertions(+), 21 deletions(-) + .../Application/L4TLauncher/L4TLauncher.c | 652 +++++++++++++++++- + .../Application/L4TLauncher/L4TLauncher.h | 18 + + 2 files changed, 651 insertions(+), 19 deletions(-) diff --git a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c -index ab86f3e0..570a1a20 100644 +index ab86f3e0..0e2a796e 100644 --- a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c +++ b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c @@ -1125,6 +1125,147 @@ CheckCommandString ( @@ -95,7 +92,7 @@ index ab86f3e0..570a1a20 100644 + + // Set default dtb as fail-safe + *CustomFdtName = AllocateZeroPool(MAX_DTB_PATH_LEN); -+ UnicodeSPrint (*CustomFdtName, MAX_DTB_PATH_LEN, L"boot\\%s\0", DEFAULT_ORIN_NANO_DTB_NAME); ++ UnicodeSPrint (*CustomFdtName, MAX_DTB_PATH_LEN, L"boot\\%s\0", DEFAULT_AGX_64_DTB_NAME); + + Status = FindPartitionInfo (DeviceHandle, BALENA_BOOT_PARTITION, BootChain, NULL, &BootPartHandle); + if (EFI_ERROR (Status)) { @@ -144,12 +141,12 @@ index ab86f3e0..570a1a20 100644 + if (NULL != DtbName) { + DtbName += StrLen(L"custom_fdt_file="); + UnicodeSPrint(*CustomFdtName, MAX_DTB_PATH_LEN, L"boot\\%s\0", DtbName); -+ Print(L"Selected dtb name is %s\n", *CustomFdtName); ++ Print(L"Selected dtb name is %s:\n", *CustomFdtName); + Status = CheckDtbExists(DeviceHandle, BootChain, BalenaOSRootFs, *CustomFdtName); + + if (EFI_ERROR(Status)) { + ErrorPrint(L"Selected dtb not found! Using the default DTB for this device type\n"); -+ UnicodeSPrint(*CustomFdtName, MAX_DTB_PATH_LEN, L"boot\\%s\0", DEFAULT_ORIN_NANO_DTB_NAME); ++ UnicodeSPrint(*CustomFdtName, MAX_DTB_PATH_LEN, L"boot\\%s\0", DEFAULT_AGX_64_DTB_NAME); + return Status; + } + } @@ -183,11 +180,12 @@ index ab86f3e0..570a1a20 100644 ) { EFI_STATUS Status; -@@ -1154,8 +1297,11 @@ ProcessExtLinuxConfig ( +@@ -1154,8 +1297,12 @@ ProcessExtLinuxConfig ( CHAR16 *Timeout = NULL; CHAR16 *CbootArg = NULL; CHAR16 *PostCbootArg = NULL; + CONST CHAR16 *RootL = L"root=LABEL="; ++ CONST CHAR16 *flasherArg = L"flasher"; BOOLEAN Ascii; UINTN Index; + CHAR16 *UpdatedCmdline = NULL; @@ -195,7 +193,7 @@ index ab86f3e0..570a1a20 100644 ZeroMem (BootConfig, sizeof (EXTLINUX_BOOT_CONFIG)); -@@ -1163,12 +1309,10 @@ ProcessExtLinuxConfig ( +@@ -1163,12 +1310,10 @@ ProcessExtLinuxConfig ( return EFI_INVALID_PARAMETER; } @@ -211,7 +209,7 @@ index ab86f3e0..570a1a20 100644 Status = OpenAndReadFileToBuffer ( *RootFsHandle, EXTLINUX_CONF_PATH, -@@ -1240,6 +1384,23 @@ ProcessExtLinuxConfig ( +@@ -1240,6 +1385,23 @@ ProcessExtLinuxConfig ( Status = CheckCommandString (CleanLine, EXTLINUX_KEY_FDT, &BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].DtbPath); if (!EFI_ERROR (Status)) { @@ -235,7 +233,7 @@ index ab86f3e0..570a1a20 100644 continue; } -@@ -1249,6 +1410,19 @@ ProcessExtLinuxConfig ( +@@ -1249,6 +1411,24 @@ ProcessExtLinuxConfig ( } Status = CheckCommandString (CleanLine, EXTLINUX_KEY_APPEND, &BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs); @@ -247,7 +245,12 @@ index ab86f3e0..570a1a20 100644 + } + + /* Append root label to cmdline that has been read from the extlinux.conf read from the active rootfs */ -+ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + argsSize, L"%s %s%s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs); ++ if (0 == StrnCmp(BalenaOSRootFs, BALENA_FLASHER_ROOT_PARTITION, StrLen(BalenaOSRootFs))) { ++ Print(L"%a: Flasher image detected, appending flasher arg...\r\n", __FUNCTION__); ++ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + StrSize(flasherArg) +argsSize, L"%s %s%s %s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs, flasherArg); ++ } else { ++ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + argsSize, L"%s %s%s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs); ++ } + if (NULL != BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs) { + FreePool(BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs); + } @@ -255,7 +258,7 @@ index ab86f3e0..570a1a20 100644 if (!EFI_ERROR (Status)) { CbootArg = StrStr (BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, EXTLINUX_CBOOT_ARG); if (CbootArg != NULL) { -@@ -1470,12 +1644,12 @@ ExtLinuxBoot ( +@@ -1470,12 +1650,12 @@ ExtLinuxBoot ( // Reload fdt if needed Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, &AcpiBase); @@ -270,7 +273,7 @@ index ab86f3e0..570a1a20 100644 Status = OpenAndReadFileToBuffer ( DeviceHandle, BootOption->DtbPath, -@@ -1690,6 +1864,301 @@ Exit: +@@ -1690,6 +1870,299 @@ Exit: return Status; } @@ -555,11 +558,9 @@ index ab86f3e0..570a1a20 100644 + FreePool (FileLine); + FileLine = NULL; + } -+ + if (FileHandle != NULL) { + FileHandleClose (FileHandle); + } -+ + return Status; + } + } @@ -572,7 +573,7 @@ index ab86f3e0..570a1a20 100644 /** Process the boot mode selection from command line and variables -@@ -1718,7 +2187,7 @@ ProcessBootParams ( +@@ -1718,7 +2191,7 @@ ProcessBootParams ( } BootParams->BootChain = 0; @@ -581,7 +582,7 @@ index ab86f3e0..570a1a20 100644 DataSize = sizeof (BootParams->BootMode); Status = gRT->GetVariable (L4T_BOOTMODE_VARIABLE_NAME, &gNVIDIAPublicVariableGuid, NULL, &DataSize, &BootParams->BootMode); if (EFI_ERROR (Status) || (BootParams->BootMode > NVIDIA_L4T_BOOTMODE_RECOVERY)) { -@@ -1729,17 +2198,22 @@ ProcessBootParams ( +@@ -1729,17 +2202,22 @@ ProcessBootParams ( Status = gRT->GetVariable (BOOT_FW_VARIABLE_NAME, &gNVIDIAPublicVariableGuid, NULL, &DataSize, &BootChain); // If variable does not exist, is >4 bytes or has a value larger than 1, boot partition A if (!EFI_ERROR (Status) && (BootChain <= 1)) { @@ -606,7 +607,7 @@ index ab86f3e0..570a1a20 100644 if (LoadedImage->LoadOptionsSize) { CurrentBootOption = StrStr (LoadedImage->LoadOptions, BOOTMODE_DIRECT_STRING); if (CurrentBootOption != NULL) { -@@ -1769,7 +2243,9 @@ ProcessBootParams ( +@@ -1769,7 +2247,9 @@ ProcessBootParams ( if (EFI_ERROR (Status)) { ErrorPrint (L"Failed to read boot chain override: %r\r\n", Status); } else if (StringValue <= 1) { @@ -616,7 +617,7 @@ index ab86f3e0..570a1a20 100644 } else { ErrorPrint (L"Boot chain override value out of range, ignoring\r\n"); } -@@ -1777,15 +2253,17 @@ ProcessBootParams ( +@@ -1777,15 +2257,17 @@ ProcessBootParams ( } // Find valid Rootfs Chain. If not, select recovery kernel @@ -638,7 +639,57 @@ index ab86f3e0..570a1a20 100644 } return EFI_SUCCESS; -@@ -2434,7 +2912,14 @@ L4TLauncher ( +@@ -2404,6 +2886,49 @@ GetDeviceHandleForFvBoot ( + return LoadedHandle; + } + ++STATIC ++EFI_STATUS ++EFIAPI ++CheckForFlasherImage ( ++ IN EFI_HANDLE DeviceHandle, ++ IN UINT32 BootChain ++) ++{ ++ EFI_STATUS Status; ++ EFI_HANDLE BootPartHandle; ++ EFI_DEVICE_PATH *FullDevicePath; ++ EFI_DEVICE_PATH *TmpFullDevicePath; ++ EFI_FILE_HANDLE FileHandle = NULL; ++ ++ Status = FindPartitionInfo (DeviceHandle, BALENA_FLASHER_BOOT_PARTITION, BootChain, NULL, &BootPartHandle); ++ ++ if (EFI_ERROR (Status)) { ++ ErrorPrint (L"%a: Could not find partition Balena flasher boot partition\r\n", __FUNCTION__); ++ return Status; ++ } ++ ++ FullDevicePath = FileDevicePath (BootPartHandle, BALENA_BOOT_FLASHER_FILE_PATH); ++ if (NULL == FullDevicePath) { ++ ErrorPrint (L"%a: Failed to create path for balena-image-flasher\r\n", __FUNCTION__); ++ return EFI_OUT_OF_RESOURCES; ++ } ++ ++ TmpFullDevicePath = FullDevicePath; ++ Status = EfiOpenFileByDevicePath (&TmpFullDevicePath, &FileHandle, EFI_FILE_MODE_READ, 0); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint (L"%a: Failed to open balena-image-flasher: %r\r\n", __FUNCTION__, Status); ++ return Status; ++ } ++ ++ ErrorPrint (L"%a: balena-image-flasher file found: %r\r\n", __FUNCTION__, Status); ++ ++ if (FileHandle != NULL) { ++ FileHandleClose (FileHandle); ++ } ++ ++ return EFI_SUCCESS; ++} ++ + /** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including +@@ -2434,7 +2959,15 @@ L4TLauncher ( EXTLINUX_BOOT_CONFIG ExtLinuxConfig; UINTN ExtLinuxBootOption; UINTN Index; @@ -651,10 +702,11 @@ index ab86f3e0..570a1a20 100644 + BOOLEAN RunningUpdatedBootSlot = FALSE; + BOOLEAN BootLimitReached = FALSE; + BOOLEAN BootSlotProcessed = FALSE; ++ BOOLEAN IsBalenaImageFlasher = FALSE; Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage); if (EFI_ERROR (Status)) { ErrorPrint (L"%a: Unable to locate loaded image: %r\r\n", __FUNCTION__, Status); -@@ -2498,12 +2983,73 @@ L4TLauncher ( +@@ -2498,12 +3031,93 @@ L4TLauncher ( } while (FALSE); } @@ -666,30 +718,38 @@ index ab86f3e0..570a1a20 100644 + Print (L"%a: Attempting Direct Boot\r\n", __FUNCTION__); do { - Status = ProcessExtLinuxConfig (DeviceHandle, BootParams.BootChain, &ExtLinuxConfig, &RootFsDeviceHandle); -+ Status = ProcessResinOSuEnv (DeviceHandle, BootParams.BootChain, &RootFsIndex, &UpgradeAvailable); ++ Status = CheckForFlasherImage (DeviceHandle, BootParams.BootChain); + if (EFI_ERROR (Status)) { -+ ErrorPrint(L"resinOS_uEnv.txt does not exist\n"); -+ } ++ Print(L"balena-image-flasher flag not present.\r\n"); ++ ++ Status = ProcessResinOSuEnv (DeviceHandle, BootParams.BootChain, &RootFsIndex, &UpgradeAvailable); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint(L"resinOS_uEnv.txt does not exist\r\n"); ++ } + -+ Print(L"Current resin_root_part=%d - upgrade_available=%d\r\n", RootFsIndex, UpgradeAvailable); -+ if (1 == UpgradeAvailable) { ++ Print(L"Current resin_root_part=%d - upgrade_available=%d\r\n", RootFsIndex, UpgradeAvailable); ++ if (1 == UpgradeAvailable) { + Status = ProcessBootCountValue (DeviceHandle, BootParams.BootChain, &BootCountValue); + if (EFI_ERROR (Status)) { -+ ErrorPrint(L"%a: Error while processing bootcount value\r\n", __FUNCTION__); ++ ErrorPrint(L"%a: Error while processing bootcount value\r\n", __FUNCTION__); + } + Print (L"%a: BootCountValue is %d\r\n", __FUNCTION__, BootCountValue); -+ } ++ } + -+ Status = ProcessBootSlotFile(DeviceHandle, BootParams.BootChain, &UpdatedBootSlot, &RunningUpdatedBootSlot); -+ if (EFI_ERROR (Status)) { -+ ErrorPrint(L"%a: Error while processing boot slot value\r\n", __FUNCTION__); ++ Status = ProcessBootSlotFile(DeviceHandle, BootParams.BootChain, &UpdatedBootSlot, &RunningUpdatedBootSlot); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint(L"%a: Error while processing boot slot value\r\n", __FUNCTION__); ++ } else { ++ ErrorPrint(L"%a: Updated (target) boot slot: %u - Current running boot slot: %u\r\n", __FUNCTION__, UpdatedBootSlot, BootParams.BootChain); ++ BootSlotProcessed = TRUE; ++ } + } else { -+ ErrorPrint(L"%a: Updated (target) boot slot: %u - Current running boot slot: %u\r\n", __FUNCTION__, UpdatedBootSlot, BootParams.BootChain); -+ BootSlotProcessed = TRUE; ++ ErrorPrint(L"Found balena-image-flasher\n"); ++ IsBalenaImageFlasher = TRUE; + } + + /* If boot limit is reached while upgrade_available=1, switch partitions */ -+ if (BALENA_OS_BC_LIM <= BootCountValue) { ++ if (BALENA_OS_BC_LIM <= BootCountValue && !IsBalenaImageFlasher) { + if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { + RootFsIndex = BALENA_ROOTFS_INDEX_A; + } else { @@ -705,25 +765,37 @@ index ab86f3e0..570a1a20 100644 + ResetCold(); + } else { + ErrorPrint(L"%a: Running the fallback (pre-update) boot slot %u because boot limit was reached. Updated boot slot booted previously was %u\r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); -+ /* This allows for the L4T 35.5.0 capsule to be applied during rollback-altboot */ ++ + ValidateActiveBootChain(); + ErrorPrint(L"%a: Running the fallback (pre-update) boot slot %u because boot limit was reached, and marked current one as active. Updated boot slot booted previously was %u\r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); + } -+ } else if (1 == UpgradeAvailable && BootSlotProcessed && !RunningUpdatedBootSlot) { ++ } else if (1 == UpgradeAvailable && BootSlotProcessed && !RunningUpdatedBootSlot && !IsBalenaImageFlasher) { + ErrorPrint(L"%a: Capsule update failed or was interrupted, because current boot slot is %u when it should be %u. Forcing altboot mode so the system can recover by booting the compatible rootfs. \r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); + if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { -+ RootFsIndex = BALENA_ROOTFS_INDEX_A; ++ RootFsIndex = BALENA_ROOTFS_INDEX_A; + } else { -+ RootFsIndex = BALENA_ROOTFS_INDEX_B; ++ RootFsIndex = BALENA_ROOTFS_INDEX_B; + } ++ } else if (IsBalenaImageFlasher) { ++ RootFsIndex = BALENA_FLASHER_ROOTFS_INDEX; + } + -+ if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { -+ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_B; -+ } else { -+ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; ++ switch (RootFsIndex) { ++ case BALENA_ROOTFS_INDEX_A: ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; ++ break; ++ case BALENA_ROOTFS_INDEX_B: ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_B; ++ break; ++ case BALENA_FLASHER_ROOTFS_INDEX: ++ BalenaOSRootFs = BALENA_FLASHER_ROOT_PARTITION; ++ break; ++ default: ++ ErrorPrint(L"%a: Unexpected root filesystem index %d! Will default to rootA.\r\n", __FUNCTION__, RootFsIndex); ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; + } + ++ + Status = ProcessExtLinuxConfig (DeviceHandle, BootParams.BootChain, BalenaOSRootFs, &ExtLinuxConfig, &RootFsDeviceHandle, RunningUpdatedBootSlot); if (EFI_ERROR (Status)) { - ErrorPrint (L"%a: Unable to process extlinux config: %r\r\n", __FUNCTION__, Status); @@ -732,13 +804,17 @@ index ab86f3e0..570a1a20 100644 break; } diff --git a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h -index fc46a70d..c06010a7 100644 +index fc46a70d..e6528b29 100644 --- a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h +++ b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h -@@ -32,11 +32,22 @@ +@@ -32,11 +32,27 @@ #define BOOT_OS_VARIABLE_NAME L"BootChainOsCurrent" #define ROOTFS_BASE_NAME L"APP" ++#define BALENA_FLASHER_ROOT_PARTITION L"flash-rootA" ++#define BALENA_FLASHER_BOOT_PARTITION L"flash-boot" ++#define BALENA_BOOT_FLASHER_FILE_PATH L"balena-image-flasher" ++#define BALENA_FLASHER_ROOTFS_INDEX 2 +#define BALENA_ROOTFS_BASE_NAME_A L"resin-rootA" +#define BALENA_ROOTFS_BASE_NAME_B L"resin-rootB" +#define BALENA_BOOT_PARTITION L"resin-boot" @@ -754,12 +830,12 @@ index fc46a70d..c06010a7 100644 #define BOOTIMG_DTB_BASE_NAME L"kernel-dtb" #define RECOVERY_BASE_NAME L"recovery" #define RECOVERY_DTB_BASE_NAME L"recovery-dtb" -- -+#define DEFAULT_ORIN_NANO_DTB_NAME L"tegra234-p3737-0000+p3701-0005-nv.dtb" + ++#define DEFAULT_AGX_64_DTB_NAME L"tegra234-p3737-0000+p3701-0005-nv.dtb" #define EXTLINUX_KEY_TIMEOUT L"TIMEOUT" #define EXTLINUX_KEY_DEFAULT L"DEFAULT" #define EXTLINUX_KEY_MENU_TITLE L"MENU TITLE" -@@ -48,8 +59,10 @@ +@@ -48,8 +64,10 @@ #define EXTLINUX_KEY_APPEND L"APPEND" #define EXTLINUX_KEY_OVERLAYS L"OVERLAYS" @@ -770,19 +846,6 @@ index fc46a70d..c06010a7 100644 #define MAX_EXTLINUX_OPTIONS 10 typedef struct { -diff --git a/Silicon/NVIDIA/Tegra/DeviceTree/L4TConfiguration.dts b/Silicon/NVIDIA/Tegra/DeviceTree/L4TConfiguration.dts -index 0750c809..d5b8f6bd 100644 ---- a/Silicon/NVIDIA/Tegra/DeviceTree/L4TConfiguration.dts -+++ b/Silicon/NVIDIA/Tegra/DeviceTree/L4TConfiguration.dts -@@ -55,7 +55,7 @@ - - gNVIDIATokenSpaceGuid { - DefaultBootPriority { -- data = "usb,nvme,emmc,sd,ufs"; -+ data = "emmc,nvme,usb,sd,ufs"; - locked; - }; - }; -- 2.34.1 diff --git a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Orin-NX-16GB-Integrate-with-balenaOS-on-L4T-36.3_patch.txt b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Orin-NX-16GB-Integrate-with-balenaOS-on-L4T-36.3_patch.txt index 5f3a9482..07c3941d 100644 --- a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Orin-NX-16GB-Integrate-with-balenaOS-on-L4T-36.3_patch.txt +++ b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Orin-NX-16GB-Integrate-with-balenaOS-on-L4T-36.3_patch.txt @@ -1,22 +1,23 @@ -From 3f2be20463b1873a0f6c3bbef69729d59486745d Mon Sep 17 00:00:00 2001 +From c4a648c008471ac692b56cac3f5e09d0e265e1a4 Mon Sep 17 00:00:00 2001 From: Alexandru Costache -Date: Tue, 16 Jul 2024 09:25:40 +0000 +Date: Sun, 6 Oct 2024 13:38:26 +0300 Subject: [PATCH] Orin NX 16GB: Integrate with balenaOS on L4T 36.3 With this patch, the Nvidia bootloader parses resinos_uEnv.txt, extra_uEnv.txt and bootcount.env in order to load the extlinux.conf and the selected dtb from the current active -rootfs. +rootfs. It is also able to boot a flasher image if +the firmware in the QSPI is based on Jetpack 6. Upstream-Status: Inappropriate [configuration] Signed-off-by: Alexandru Costache --- - .../Application/L4TLauncher/L4TLauncher.c | 584 +++++++++++++++++- - .../Application/L4TLauncher/L4TLauncher.h | 15 +- - 2 files changed, 579 insertions(+), 20 deletions(-) + .../Application/L4TLauncher/L4TLauncher.c | 653 +++++++++++++++++- + .../Application/L4TLauncher/L4TLauncher.h | 18 + + 2 files changed, 652 insertions(+), 19 deletions(-) diff --git a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c -index ab86f3e0..2c2f5d5a 100644 +index ab86f3e0..d41394a1 100644 --- a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c +++ b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c @@ -1125,6 +1125,147 @@ CheckCommandString ( @@ -179,11 +180,12 @@ index ab86f3e0..2c2f5d5a 100644 ) { EFI_STATUS Status; -@@ -1154,8 +1297,11 @@ ProcessExtLinuxConfig ( +@@ -1154,8 +1297,12 @@ ProcessExtLinuxConfig ( CHAR16 *Timeout = NULL; CHAR16 *CbootArg = NULL; CHAR16 *PostCbootArg = NULL; + CONST CHAR16 *RootL = L"root=LABEL="; ++ CONST CHAR16 *flasherArg = L"flasher"; BOOLEAN Ascii; UINTN Index; + CHAR16 *UpdatedCmdline = NULL; @@ -191,7 +193,7 @@ index ab86f3e0..2c2f5d5a 100644 ZeroMem (BootConfig, sizeof (EXTLINUX_BOOT_CONFIG)); -@@ -1163,12 +1309,10 @@ ProcessExtLinuxConfig ( +@@ -1163,12 +1310,10 @@ ProcessExtLinuxConfig ( return EFI_INVALID_PARAMETER; } @@ -207,7 +209,7 @@ index ab86f3e0..2c2f5d5a 100644 Status = OpenAndReadFileToBuffer ( *RootFsHandle, EXTLINUX_CONF_PATH, -@@ -1240,6 +1384,23 @@ ProcessExtLinuxConfig ( +@@ -1240,6 +1385,23 @@ ProcessExtLinuxConfig ( Status = CheckCommandString (CleanLine, EXTLINUX_KEY_FDT, &BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].DtbPath); if (!EFI_ERROR (Status)) { @@ -231,7 +233,7 @@ index ab86f3e0..2c2f5d5a 100644 continue; } -@@ -1249,6 +1410,19 @@ ProcessExtLinuxConfig ( +@@ -1249,6 +1411,24 @@ ProcessExtLinuxConfig ( } Status = CheckCommandString (CleanLine, EXTLINUX_KEY_APPEND, &BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs); @@ -243,7 +245,12 @@ index ab86f3e0..2c2f5d5a 100644 + } + + /* Append root label to cmdline that has been read from the extlinux.conf read from the active rootfs */ -+ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + argsSize, L"%s %s%s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs); ++ if (0 == StrnCmp(BalenaOSRootFs, BALENA_FLASHER_ROOT_PARTITION, StrLen(BalenaOSRootFs))) { ++ Print(L"%a: Flasher image detected, appending flasher arg...\r\n", __FUNCTION__); ++ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + StrSize(flasherArg) +argsSize, L"%s %s%s %s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs, flasherArg); ++ } else { ++ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + argsSize, L"%s %s%s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs); ++ } + if (NULL != BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs) { + FreePool(BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs); + } @@ -251,7 +258,7 @@ index ab86f3e0..2c2f5d5a 100644 if (!EFI_ERROR (Status)) { CbootArg = StrStr (BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, EXTLINUX_CBOOT_ARG); if (CbootArg != NULL) { -@@ -1470,12 +1644,12 @@ ExtLinuxBoot ( +@@ -1470,12 +1650,12 @@ ExtLinuxBoot ( // Reload fdt if needed Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, &AcpiBase); @@ -266,7 +273,7 @@ index ab86f3e0..2c2f5d5a 100644 Status = OpenAndReadFileToBuffer ( DeviceHandle, BootOption->DtbPath, -@@ -1690,6 +1864,301 @@ Exit: +@@ -1690,6 +1870,299 @@ Exit: return Status; } @@ -551,11 +558,9 @@ index ab86f3e0..2c2f5d5a 100644 + FreePool (FileLine); + FileLine = NULL; + } -+ + if (FileHandle != NULL) { + FileHandleClose (FileHandle); + } -+ + return Status; + } + } @@ -568,7 +573,7 @@ index ab86f3e0..2c2f5d5a 100644 /** Process the boot mode selection from command line and variables -@@ -1718,7 +2187,7 @@ ProcessBootParams ( +@@ -1718,7 +2191,7 @@ ProcessBootParams ( } BootParams->BootChain = 0; @@ -577,7 +582,7 @@ index ab86f3e0..2c2f5d5a 100644 DataSize = sizeof (BootParams->BootMode); Status = gRT->GetVariable (L4T_BOOTMODE_VARIABLE_NAME, &gNVIDIAPublicVariableGuid, NULL, &DataSize, &BootParams->BootMode); if (EFI_ERROR (Status) || (BootParams->BootMode > NVIDIA_L4T_BOOTMODE_RECOVERY)) { -@@ -1729,17 +2198,22 @@ ProcessBootParams ( +@@ -1729,17 +2202,22 @@ ProcessBootParams ( Status = gRT->GetVariable (BOOT_FW_VARIABLE_NAME, &gNVIDIAPublicVariableGuid, NULL, &DataSize, &BootChain); // If variable does not exist, is >4 bytes or has a value larger than 1, boot partition A if (!EFI_ERROR (Status) && (BootChain <= 1)) { @@ -602,7 +607,7 @@ index ab86f3e0..2c2f5d5a 100644 if (LoadedImage->LoadOptionsSize) { CurrentBootOption = StrStr (LoadedImage->LoadOptions, BOOTMODE_DIRECT_STRING); if (CurrentBootOption != NULL) { -@@ -1769,7 +2243,9 @@ ProcessBootParams ( +@@ -1769,7 +2247,9 @@ ProcessBootParams ( if (EFI_ERROR (Status)) { ErrorPrint (L"Failed to read boot chain override: %r\r\n", Status); } else if (StringValue <= 1) { @@ -612,7 +617,7 @@ index ab86f3e0..2c2f5d5a 100644 } else { ErrorPrint (L"Boot chain override value out of range, ignoring\r\n"); } -@@ -1777,15 +2253,17 @@ ProcessBootParams ( +@@ -1777,15 +2257,17 @@ ProcessBootParams ( } // Find valid Rootfs Chain. If not, select recovery kernel @@ -634,7 +639,58 @@ index ab86f3e0..2c2f5d5a 100644 } return EFI_SUCCESS; -@@ -2434,7 +2912,14 @@ L4TLauncher ( +@@ -2404,6 +2886,50 @@ GetDeviceHandleForFvBoot ( + return LoadedHandle; + } + ++STATIC ++EFI_STATUS ++EFIAPI ++CheckForFlasherImage ( ++ IN EFI_HANDLE DeviceHandle, ++ IN UINT32 BootChain ++) ++{ ++ EFI_STATUS Status; ++ EFI_HANDLE BootPartHandle; ++ EFI_DEVICE_PATH *FullDevicePath; ++ EFI_DEVICE_PATH *TmpFullDevicePath; ++ EFI_FILE_HANDLE FileHandle = NULL; ++ ++ Status = FindPartitionInfo (DeviceHandle, BALENA_FLASHER_BOOT_PARTITION, BootChain, NULL, &BootPartHandle); ++ ++ if (EFI_ERROR (Status)) { ++ ErrorPrint (L"%a: Could not find partition Balena flasher boot partition\r\n", __FUNCTION__); ++ return Status; ++ } ++ ++ FullDevicePath = FileDevicePath (BootPartHandle, BALENA_BOOT_FLASHER_FILE_PATH); ++ if (NULL == FullDevicePath) { ++ ErrorPrint (L"%a: Failed to create path for balena-image-flasher\r\n", __FUNCTION__); ++ return EFI_OUT_OF_RESOURCES; ++ } ++ ++ TmpFullDevicePath = FullDevicePath; ++ Status = EfiOpenFileByDevicePath (&TmpFullDevicePath, &FileHandle, EFI_FILE_MODE_READ, 0); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint (L"%a: Failed to open balena-image-flasher: %r\r\n", __FUNCTION__, Status); ++ return Status; ++ } ++ ++ ErrorPrint (L"%a: balena-image-flasher file found: %r\r\n", __FUNCTION__, Status); ++ ++ if (FileHandle != NULL) { ++ FileHandleClose (FileHandle); ++ } ++ ++ return EFI_SUCCESS; ++ ++} ++ + /** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including +@@ -2434,7 +2960,15 @@ L4TLauncher ( EXTLINUX_BOOT_CONFIG ExtLinuxConfig; UINTN ExtLinuxBootOption; UINTN Index; @@ -647,10 +703,11 @@ index ab86f3e0..2c2f5d5a 100644 + BOOLEAN RunningUpdatedBootSlot = FALSE; + BOOLEAN BootLimitReached = FALSE; + BOOLEAN BootSlotProcessed = FALSE; ++ BOOLEAN IsBalenaImageFlasher = FALSE; Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage); if (EFI_ERROR (Status)) { ErrorPrint (L"%a: Unable to locate loaded image: %r\r\n", __FUNCTION__, Status); -@@ -2498,12 +2983,73 @@ L4TLauncher ( +@@ -2498,12 +3032,93 @@ L4TLauncher ( } while (FALSE); } @@ -662,30 +719,38 @@ index ab86f3e0..2c2f5d5a 100644 + Print (L"%a: Attempting Direct Boot\r\n", __FUNCTION__); do { - Status = ProcessExtLinuxConfig (DeviceHandle, BootParams.BootChain, &ExtLinuxConfig, &RootFsDeviceHandle); -+ Status = ProcessResinOSuEnv (DeviceHandle, BootParams.BootChain, &RootFsIndex, &UpgradeAvailable); ++ Status = CheckForFlasherImage (DeviceHandle, BootParams.BootChain); + if (EFI_ERROR (Status)) { -+ ErrorPrint(L"resinOS_uEnv.txt does not exist\n"); -+ } ++ Print(L"balena-image-flasher flag not present.\r\n"); ++ ++ Status = ProcessResinOSuEnv (DeviceHandle, BootParams.BootChain, &RootFsIndex, &UpgradeAvailable); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint(L"resinOS_uEnv.txt does not exist\r\n"); ++ } + -+ Print(L"Current resin_root_part=%d - upgrade_available=%d\r\n", RootFsIndex, UpgradeAvailable); -+ if (1 == UpgradeAvailable) { ++ Print(L"Current resin_root_part=%d - upgrade_available=%d\r\n", RootFsIndex, UpgradeAvailable); ++ if (1 == UpgradeAvailable) { + Status = ProcessBootCountValue (DeviceHandle, BootParams.BootChain, &BootCountValue); + if (EFI_ERROR (Status)) { -+ ErrorPrint(L"%a: Error while processing bootcount value\r\n", __FUNCTION__); ++ ErrorPrint(L"%a: Error while processing bootcount value\r\n", __FUNCTION__); + } + Print (L"%a: BootCountValue is %d\r\n", __FUNCTION__, BootCountValue); -+ } ++ } + -+ Status = ProcessBootSlotFile(DeviceHandle, BootParams.BootChain, &UpdatedBootSlot, &RunningUpdatedBootSlot); -+ if (EFI_ERROR (Status)) { -+ ErrorPrint(L"%a: Error while processing boot slot value\r\n", __FUNCTION__); ++ Status = ProcessBootSlotFile(DeviceHandle, BootParams.BootChain, &UpdatedBootSlot, &RunningUpdatedBootSlot); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint(L"%a: Error while processing boot slot value\r\n", __FUNCTION__); ++ } else { ++ ErrorPrint(L"%a: Updated (target) boot slot: %u - Current running boot slot: %u\r\n", __FUNCTION__, UpdatedBootSlot, BootParams.BootChain); ++ BootSlotProcessed = TRUE; ++ } + } else { -+ ErrorPrint(L"%a: Updated (target) boot slot: %u - Current running boot slot: %u\r\n", __FUNCTION__, UpdatedBootSlot, BootParams.BootChain); -+ BootSlotProcessed = TRUE; ++ ErrorPrint(L"Found balena-image-flasher\n"); ++ IsBalenaImageFlasher = TRUE; + } + + /* If boot limit is reached while upgrade_available=1, switch partitions */ -+ if (BALENA_OS_BC_LIM <= BootCountValue) { ++ if (BALENA_OS_BC_LIM <= BootCountValue && !IsBalenaImageFlasher) { + if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { + RootFsIndex = BALENA_ROOTFS_INDEX_A; + } else { @@ -701,25 +766,37 @@ index ab86f3e0..2c2f5d5a 100644 + ResetCold(); + } else { + ErrorPrint(L"%a: Running the fallback (pre-update) boot slot %u because boot limit was reached. Updated boot slot booted previously was %u\r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); -+ /* This allows for the L4T 35.5.0 capsule to be applied during rollback-altboot */ ++ + ValidateActiveBootChain(); + ErrorPrint(L"%a: Running the fallback (pre-update) boot slot %u because boot limit was reached, and marked current one as active. Updated boot slot booted previously was %u\r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); + } -+ } else if (1 == UpgradeAvailable && BootSlotProcessed && !RunningUpdatedBootSlot) { ++ } else if (1 == UpgradeAvailable && BootSlotProcessed && !RunningUpdatedBootSlot && !IsBalenaImageFlasher) { + ErrorPrint(L"%a: Capsule update failed or was interrupted, because current boot slot is %u when it should be %u. Forcing altboot mode so the system can recover by booting the compatible rootfs. \r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); + if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { -+ RootFsIndex = BALENA_ROOTFS_INDEX_A; ++ RootFsIndex = BALENA_ROOTFS_INDEX_A; + } else { -+ RootFsIndex = BALENA_ROOTFS_INDEX_B; ++ RootFsIndex = BALENA_ROOTFS_INDEX_B; + } ++ } else if (IsBalenaImageFlasher) { ++ RootFsIndex = BALENA_FLASHER_ROOTFS_INDEX; + } + -+ if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { -+ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_B; -+ } else { -+ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; ++ switch (RootFsIndex) { ++ case BALENA_ROOTFS_INDEX_A: ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; ++ break; ++ case BALENA_ROOTFS_INDEX_B: ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_B; ++ break; ++ case BALENA_FLASHER_ROOTFS_INDEX: ++ BalenaOSRootFs = BALENA_FLASHER_ROOT_PARTITION; ++ break; ++ default: ++ ErrorPrint(L"%a: Unexpected root filesystem index %d! Will default to rootA.\r\n", __FUNCTION__, RootFsIndex); ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; + } + ++ + Status = ProcessExtLinuxConfig (DeviceHandle, BootParams.BootChain, BalenaOSRootFs, &ExtLinuxConfig, &RootFsDeviceHandle, RunningUpdatedBootSlot); if (EFI_ERROR (Status)) { - ErrorPrint (L"%a: Unable to process extlinux config: %r\r\n", __FUNCTION__, Status); @@ -728,13 +805,17 @@ index ab86f3e0..2c2f5d5a 100644 break; } diff --git a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h -index fc46a70d..d9f16d8c 100644 +index fc46a70d..56400a4d 100644 --- a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h +++ b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h -@@ -32,11 +32,22 @@ +@@ -32,11 +32,27 @@ #define BOOT_OS_VARIABLE_NAME L"BootChainOsCurrent" #define ROOTFS_BASE_NAME L"APP" ++#define BALENA_FLASHER_ROOT_PARTITION L"flash-rootA" ++#define BALENA_FLASHER_BOOT_PARTITION L"flash-boot" ++#define BALENA_BOOT_FLASHER_FILE_PATH L"balena-image-flasher" ++#define BALENA_FLASHER_ROOTFS_INDEX 2 +#define BALENA_ROOTFS_BASE_NAME_A L"resin-rootA" +#define BALENA_ROOTFS_BASE_NAME_B L"resin-rootB" +#define BALENA_BOOT_PARTITION L"resin-boot" @@ -750,12 +831,12 @@ index fc46a70d..d9f16d8c 100644 #define BOOTIMG_DTB_BASE_NAME L"kernel-dtb" #define RECOVERY_BASE_NAME L"recovery" #define RECOVERY_DTB_BASE_NAME L"recovery-dtb" -- + +#define DEFAULT_ORIN_NX_DVK_DTB_NAME L"tegra234-p3768-0000+p3767-0000-nv.dtb" #define EXTLINUX_KEY_TIMEOUT L"TIMEOUT" #define EXTLINUX_KEY_DEFAULT L"DEFAULT" #define EXTLINUX_KEY_MENU_TITLE L"MENU TITLE" -@@ -48,8 +59,10 @@ +@@ -48,8 +64,10 @@ #define EXTLINUX_KEY_APPEND L"APPEND" #define EXTLINUX_KEY_OVERLAYS L"OVERLAYS" diff --git a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Orin-Nano-Integrate-with-balenaOS-on-L4T-36.3_patch.txt b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Orin-Nano-Integrate-with-balenaOS-on-L4T-36.3_patch.txt index d2f4d80f..ffa51b5e 100644 --- a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Orin-Nano-Integrate-with-balenaOS-on-L4T-36.3_patch.txt +++ b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Orin-Nano-Integrate-with-balenaOS-on-L4T-36.3_patch.txt @@ -1,22 +1,23 @@ -From 9e06dbf1557acaae9eae46010e0ebde182a31571 Mon Sep 17 00:00:00 2001 +From 0eda3dbaf1f0e9a92794fc1669ff74a3d83d6a29 Mon Sep 17 00:00:00 2001 From: Alexandru Costache -Date: Tue, 16 Jul 2024 09:16:00 +0000 +Date: Sun, 6 Oct 2024 13:34:53 +0300 Subject: [PATCH] Orin Nano: Integrate with balenaOS on L4T 36.3 With this patch, the Nvidia bootloader parses resinos_uEnv.txt, extra_uEnv.txt and bootcount.env in order to load the extlinux.conf and the selected dtb from the current active -rootfs. +rootfs. It is also able to boot a flasher image if +the firmware in the QSPI is based on Jetpack 6. Upstream-Status: Inappropriate [configuration] Signed-off-by: Alexandru Costache --- - .../Application/L4TLauncher/L4TLauncher.c | 583 +++++++++++++++++- - .../Application/L4TLauncher/L4TLauncher.h | 15 +- - 2 files changed, 578 insertions(+), 20 deletions(-) + .../Application/L4TLauncher/L4TLauncher.c | 653 +++++++++++++++++- + .../Application/L4TLauncher/L4TLauncher.h | 18 + + 2 files changed, 652 insertions(+), 19 deletions(-) diff --git a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c -index ab86f3e0..87d32558 100644 +index ab86f3e0..ec5954fd 100644 --- a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c +++ b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c @@ -1125,6 +1125,147 @@ CheckCommandString ( @@ -179,11 +180,12 @@ index ab86f3e0..87d32558 100644 ) { EFI_STATUS Status; -@@ -1154,8 +1297,11 @@ ProcessExtLinuxConfig ( +@@ -1154,8 +1297,12 @@ ProcessExtLinuxConfig ( CHAR16 *Timeout = NULL; CHAR16 *CbootArg = NULL; CHAR16 *PostCbootArg = NULL; + CONST CHAR16 *RootL = L"root=LABEL="; ++ CONST CHAR16 *flasherArg = L"flasher"; BOOLEAN Ascii; UINTN Index; + CHAR16 *UpdatedCmdline = NULL; @@ -191,7 +193,7 @@ index ab86f3e0..87d32558 100644 ZeroMem (BootConfig, sizeof (EXTLINUX_BOOT_CONFIG)); -@@ -1163,12 +1309,10 @@ ProcessExtLinuxConfig ( +@@ -1163,12 +1310,10 @@ ProcessExtLinuxConfig ( return EFI_INVALID_PARAMETER; } @@ -207,7 +209,7 @@ index ab86f3e0..87d32558 100644 Status = OpenAndReadFileToBuffer ( *RootFsHandle, EXTLINUX_CONF_PATH, -@@ -1240,6 +1384,23 @@ ProcessExtLinuxConfig ( +@@ -1240,6 +1385,23 @@ ProcessExtLinuxConfig ( Status = CheckCommandString (CleanLine, EXTLINUX_KEY_FDT, &BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].DtbPath); if (!EFI_ERROR (Status)) { @@ -231,7 +233,7 @@ index ab86f3e0..87d32558 100644 continue; } -@@ -1249,6 +1410,19 @@ ProcessExtLinuxConfig ( +@@ -1249,6 +1411,24 @@ ProcessExtLinuxConfig ( } Status = CheckCommandString (CleanLine, EXTLINUX_KEY_APPEND, &BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs); @@ -243,7 +245,12 @@ index ab86f3e0..87d32558 100644 + } + + /* Append root label to cmdline that has been read from the extlinux.conf read from the active rootfs */ -+ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + argsSize, L"%s %s%s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs); ++ if (0 == StrnCmp(BalenaOSRootFs, BALENA_FLASHER_ROOT_PARTITION, StrLen(BalenaOSRootFs))) { ++ Print(L"%a: Flasher image detected, appending flasher arg...\r\n", __FUNCTION__); ++ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + StrSize(flasherArg) +argsSize, L"%s %s%s %s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs, flasherArg); ++ } else { ++ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + argsSize, L"%s %s%s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs); ++ } + if (NULL != BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs) { + FreePool(BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs); + } @@ -251,7 +258,7 @@ index ab86f3e0..87d32558 100644 if (!EFI_ERROR (Status)) { CbootArg = StrStr (BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, EXTLINUX_CBOOT_ARG); if (CbootArg != NULL) { -@@ -1470,12 +1644,12 @@ ExtLinuxBoot ( +@@ -1470,12 +1650,12 @@ ExtLinuxBoot ( // Reload fdt if needed Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, &AcpiBase); @@ -266,7 +273,7 @@ index ab86f3e0..87d32558 100644 Status = OpenAndReadFileToBuffer ( DeviceHandle, BootOption->DtbPath, -@@ -1690,6 +1864,299 @@ Exit: +@@ -1690,6 +1870,299 @@ Exit: return Status; } @@ -566,7 +573,7 @@ index ab86f3e0..87d32558 100644 /** Process the boot mode selection from command line and variables -@@ -1718,7 +2185,7 @@ ProcessBootParams ( +@@ -1718,7 +2191,7 @@ ProcessBootParams ( } BootParams->BootChain = 0; @@ -575,7 +582,7 @@ index ab86f3e0..87d32558 100644 DataSize = sizeof (BootParams->BootMode); Status = gRT->GetVariable (L4T_BOOTMODE_VARIABLE_NAME, &gNVIDIAPublicVariableGuid, NULL, &DataSize, &BootParams->BootMode); if (EFI_ERROR (Status) || (BootParams->BootMode > NVIDIA_L4T_BOOTMODE_RECOVERY)) { -@@ -1729,17 +2196,22 @@ ProcessBootParams ( +@@ -1729,17 +2202,22 @@ ProcessBootParams ( Status = gRT->GetVariable (BOOT_FW_VARIABLE_NAME, &gNVIDIAPublicVariableGuid, NULL, &DataSize, &BootChain); // If variable does not exist, is >4 bytes or has a value larger than 1, boot partition A if (!EFI_ERROR (Status) && (BootChain <= 1)) { @@ -600,7 +607,7 @@ index ab86f3e0..87d32558 100644 if (LoadedImage->LoadOptionsSize) { CurrentBootOption = StrStr (LoadedImage->LoadOptions, BOOTMODE_DIRECT_STRING); if (CurrentBootOption != NULL) { -@@ -1769,7 +2241,9 @@ ProcessBootParams ( +@@ -1769,7 +2247,9 @@ ProcessBootParams ( if (EFI_ERROR (Status)) { ErrorPrint (L"Failed to read boot chain override: %r\r\n", Status); } else if (StringValue <= 1) { @@ -610,7 +617,7 @@ index ab86f3e0..87d32558 100644 } else { ErrorPrint (L"Boot chain override value out of range, ignoring\r\n"); } -@@ -1777,15 +2251,17 @@ ProcessBootParams ( +@@ -1777,15 +2257,17 @@ ProcessBootParams ( } // Find valid Rootfs Chain. If not, select recovery kernel @@ -632,7 +639,58 @@ index ab86f3e0..87d32558 100644 } return EFI_SUCCESS; -@@ -2434,7 +2910,14 @@ L4TLauncher ( +@@ -2404,6 +2886,50 @@ GetDeviceHandleForFvBoot ( + return LoadedHandle; + } + ++STATIC ++EFI_STATUS ++EFIAPI ++CheckForFlasherImage ( ++ IN EFI_HANDLE DeviceHandle, ++ IN UINT32 BootChain ++) ++{ ++ EFI_STATUS Status; ++ EFI_HANDLE BootPartHandle; ++ EFI_DEVICE_PATH *FullDevicePath; ++ EFI_DEVICE_PATH *TmpFullDevicePath; ++ EFI_FILE_HANDLE FileHandle = NULL; ++ ++ Status = FindPartitionInfo (DeviceHandle, BALENA_FLASHER_BOOT_PARTITION, BootChain, NULL, &BootPartHandle); ++ ++ if (EFI_ERROR (Status)) { ++ ErrorPrint (L"%a: Could not find partition Balena flasher boot partition\r\n", __FUNCTION__); ++ return Status; ++ } ++ ++ FullDevicePath = FileDevicePath (BootPartHandle, BALENA_BOOT_FLASHER_FILE_PATH); ++ if (NULL == FullDevicePath) { ++ ErrorPrint (L"%a: Failed to create path for balena-image-flasher\r\n", __FUNCTION__); ++ return EFI_OUT_OF_RESOURCES; ++ } ++ ++ TmpFullDevicePath = FullDevicePath; ++ Status = EfiOpenFileByDevicePath (&TmpFullDevicePath, &FileHandle, EFI_FILE_MODE_READ, 0); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint (L"%a: Failed to open balena-image-flasher: %r\r\n", __FUNCTION__, Status); ++ return Status; ++ } ++ ++ ErrorPrint (L"%a: balena-image-flasher file found: %r\r\n", __FUNCTION__, Status); ++ ++ if (FileHandle != NULL) { ++ FileHandleClose (FileHandle); ++ } ++ ++ return EFI_SUCCESS; ++ ++} ++ + /** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including +@@ -2434,7 +2960,15 @@ L4TLauncher ( EXTLINUX_BOOT_CONFIG ExtLinuxConfig; UINTN ExtLinuxBootOption; UINTN Index; @@ -645,10 +703,11 @@ index ab86f3e0..87d32558 100644 + BOOLEAN RunningUpdatedBootSlot = FALSE; + BOOLEAN BootLimitReached = FALSE; + BOOLEAN BootSlotProcessed = FALSE; ++ BOOLEAN IsBalenaImageFlasher = FALSE; Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage); if (EFI_ERROR (Status)) { ErrorPrint (L"%a: Unable to locate loaded image: %r\r\n", __FUNCTION__, Status); -@@ -2498,12 +2981,74 @@ L4TLauncher ( +@@ -2498,12 +3032,93 @@ L4TLauncher ( } while (FALSE); } @@ -660,30 +719,38 @@ index ab86f3e0..87d32558 100644 + Print (L"%a: Attempting Direct Boot\r\n", __FUNCTION__); do { - Status = ProcessExtLinuxConfig (DeviceHandle, BootParams.BootChain, &ExtLinuxConfig, &RootFsDeviceHandle); -+ Status = ProcessResinOSuEnv (DeviceHandle, BootParams.BootChain, &RootFsIndex, &UpgradeAvailable); ++ Status = CheckForFlasherImage (DeviceHandle, BootParams.BootChain); + if (EFI_ERROR (Status)) { -+ ErrorPrint(L"resinOS_uEnv.txt does not exist\n"); -+ } ++ Print(L"balena-image-flasher flag not present.\r\n"); ++ ++ Status = ProcessResinOSuEnv (DeviceHandle, BootParams.BootChain, &RootFsIndex, &UpgradeAvailable); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint(L"resinOS_uEnv.txt does not exist\r\n"); ++ } + -+ Print(L"Current resin_root_part=%d - upgrade_available=%d\r\n", RootFsIndex, UpgradeAvailable); -+ if (1 == UpgradeAvailable) { ++ Print(L"Current resin_root_part=%d - upgrade_available=%d\r\n", RootFsIndex, UpgradeAvailable); ++ if (1 == UpgradeAvailable) { + Status = ProcessBootCountValue (DeviceHandle, BootParams.BootChain, &BootCountValue); + if (EFI_ERROR (Status)) { -+ ErrorPrint(L"%a: Error while processing bootcount value\r\n", __FUNCTION__); ++ ErrorPrint(L"%a: Error while processing bootcount value\r\n", __FUNCTION__); + } + Print (L"%a: BootCountValue is %d\r\n", __FUNCTION__, BootCountValue); -+ } ++ } + -+ Status = ProcessBootSlotFile(DeviceHandle, BootParams.BootChain, &UpdatedBootSlot, &RunningUpdatedBootSlot); -+ if (EFI_ERROR (Status)) { -+ ErrorPrint(L"%a: Error while processing boot slot value\r\n", __FUNCTION__); ++ Status = ProcessBootSlotFile(DeviceHandle, BootParams.BootChain, &UpdatedBootSlot, &RunningUpdatedBootSlot); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint(L"%a: Error while processing boot slot value\r\n", __FUNCTION__); ++ } else { ++ ErrorPrint(L"%a: Updated (target) boot slot: %u - Current running boot slot: %u\r\n", __FUNCTION__, UpdatedBootSlot, BootParams.BootChain); ++ BootSlotProcessed = TRUE; ++ } + } else { -+ ErrorPrint(L"%a: Updated (target) boot slot: %u - Current running boot slot: %u\r\n", __FUNCTION__, UpdatedBootSlot, BootParams.BootChain); -+ BootSlotProcessed = TRUE; ++ ErrorPrint(L"Found balena-image-flasher\n"); ++ IsBalenaImageFlasher = TRUE; + } + + /* If boot limit is reached while upgrade_available=1, switch partitions */ -+ if (BALENA_OS_BC_LIM <= BootCountValue) { ++ if (BALENA_OS_BC_LIM <= BootCountValue && !IsBalenaImageFlasher) { + if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { + RootFsIndex = BALENA_ROOTFS_INDEX_A; + } else { @@ -703,22 +770,33 @@ index ab86f3e0..87d32558 100644 + ValidateActiveBootChain(); + ErrorPrint(L"%a: Running the fallback (pre-update) boot slot %u because boot limit was reached, and marked current one as active. Updated boot slot booted previously was %u\r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); + } -+ } else if (1 == UpgradeAvailable && BootSlotProcessed && !RunningUpdatedBootSlot) { ++ } else if (1 == UpgradeAvailable && BootSlotProcessed && !RunningUpdatedBootSlot && !IsBalenaImageFlasher) { + ErrorPrint(L"%a: Capsule update failed or was interrupted, because current boot slot is %u when it should be %u. Forcing altboot mode so the system can recover by booting the compatible rootfs. \r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); + if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { -+ RootFsIndex = BALENA_ROOTFS_INDEX_A; ++ RootFsIndex = BALENA_ROOTFS_INDEX_A; + } else { -+ RootFsIndex = BALENA_ROOTFS_INDEX_B; ++ RootFsIndex = BALENA_ROOTFS_INDEX_B; + } ++ } else if (IsBalenaImageFlasher) { ++ RootFsIndex = BALENA_FLASHER_ROOTFS_INDEX; + } + -+ -+ if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { -+ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_B; -+ } else { -+ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; ++ switch (RootFsIndex) { ++ case BALENA_ROOTFS_INDEX_A: ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; ++ break; ++ case BALENA_ROOTFS_INDEX_B: ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_B; ++ break; ++ case BALENA_FLASHER_ROOTFS_INDEX: ++ BalenaOSRootFs = BALENA_FLASHER_ROOT_PARTITION; ++ break; ++ default: ++ ErrorPrint(L"%a: Unexpected root filesystem index %d! Will default to rootA.\r\n", __FUNCTION__, RootFsIndex); ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; + } + ++ + Status = ProcessExtLinuxConfig (DeviceHandle, BootParams.BootChain, BalenaOSRootFs, &ExtLinuxConfig, &RootFsDeviceHandle, RunningUpdatedBootSlot); if (EFI_ERROR (Status)) { - ErrorPrint (L"%a: Unable to process extlinux config: %r\r\n", __FUNCTION__, Status); @@ -727,13 +805,17 @@ index ab86f3e0..87d32558 100644 break; } diff --git a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h -index fc46a70d..42d21165 100644 +index fc46a70d..cc07b84b 100644 --- a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h +++ b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h -@@ -32,11 +32,22 @@ +@@ -32,11 +32,27 @@ #define BOOT_OS_VARIABLE_NAME L"BootChainOsCurrent" #define ROOTFS_BASE_NAME L"APP" ++#define BALENA_FLASHER_ROOT_PARTITION L"flash-rootA" ++#define BALENA_FLASHER_BOOT_PARTITION L"flash-boot" ++#define BALENA_BOOT_FLASHER_FILE_PATH L"balena-image-flasher" ++#define BALENA_FLASHER_ROOTFS_INDEX 2 +#define BALENA_ROOTFS_BASE_NAME_A L"resin-rootA" +#define BALENA_ROOTFS_BASE_NAME_B L"resin-rootB" +#define BALENA_BOOT_PARTITION L"resin-boot" @@ -749,12 +831,12 @@ index fc46a70d..42d21165 100644 #define BOOTIMG_DTB_BASE_NAME L"kernel-dtb" #define RECOVERY_BASE_NAME L"recovery" #define RECOVERY_DTB_BASE_NAME L"recovery-dtb" -- + +#define DEFAULT_ORIN_NANO_DTB_NAME L"tegra234-p3768-0000+p3767-0005-nv.dtb" #define EXTLINUX_KEY_TIMEOUT L"TIMEOUT" #define EXTLINUX_KEY_DEFAULT L"DEFAULT" #define EXTLINUX_KEY_MENU_TITLE L"MENU TITLE" -@@ -48,8 +59,10 @@ +@@ -48,8 +64,10 @@ #define EXTLINUX_KEY_APPEND L"APPEND" #define EXTLINUX_KEY_OVERLAYS L"OVERLAYS" diff --git a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Seeed-J3010-Integrate-with-balenaOS-on-L4T-36.3_patch.txt b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Seeed-J3010-Integrate-with-balenaOS-on-L4T-36.3_patch.txt index de6d0862..0a11706f 100644 --- a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Seeed-J3010-Integrate-with-balenaOS-on-L4T-36.3_patch.txt +++ b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Seeed-J3010-Integrate-with-balenaOS-on-L4T-36.3_patch.txt @@ -1,22 +1,23 @@ -From 4fa2c09eac1dd66044758a65f394427b158068b4 Mon Sep 17 00:00:00 2001 +From 57ff121b8e035fb4969dd71217e9c87b22353366 Mon Sep 17 00:00:00 2001 From: Alexandru Costache -Date: Tue, 16 Jul 2024 09:57:47 +0000 +Date: Sun, 6 Oct 2024 13:41:20 +0300 Subject: [PATCH] Seeed J3010: Integrate with balenaOS on L4T 36.3 With this patch, the Nvidia bootloader parses resinos_uEnv.txt, extra_uEnv.txt and bootcount.env in order to load the extlinux.conf and the selected dtb from the current active -rootfs. +rootfs. It is also able to boot a flasher image if +the firmware in the QSPI is based on Jetpack 6. Upstream-Status: Inappropriate [configuration] Signed-off-by: Alexandru Costache --- - .../Application/L4TLauncher/L4TLauncher.c | 584 +++++++++++++++++- - .../Application/L4TLauncher/L4TLauncher.h | 15 +- - 2 files changed, 579 insertions(+), 20 deletions(-) + .../Application/L4TLauncher/L4TLauncher.c | 653 +++++++++++++++++- + .../Application/L4TLauncher/L4TLauncher.h | 18 + + 2 files changed, 652 insertions(+), 19 deletions(-) diff --git a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c -index ab86f3e0..570a1a20 100644 +index ab86f3e0..ec5954fd 100644 --- a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c +++ b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c @@ -1125,6 +1125,147 @@ CheckCommandString ( @@ -140,7 +141,7 @@ index ab86f3e0..570a1a20 100644 + if (NULL != DtbName) { + DtbName += StrLen(L"custom_fdt_file="); + UnicodeSPrint(*CustomFdtName, MAX_DTB_PATH_LEN, L"boot\\%s\0", DtbName); -+ Print(L"Selected dtb name is %s\n", *CustomFdtName); ++ Print(L"Selected dtb name is %s:\n", *CustomFdtName); + Status = CheckDtbExists(DeviceHandle, BootChain, BalenaOSRootFs, *CustomFdtName); + + if (EFI_ERROR(Status)) { @@ -179,11 +180,12 @@ index ab86f3e0..570a1a20 100644 ) { EFI_STATUS Status; -@@ -1154,8 +1297,11 @@ ProcessExtLinuxConfig ( +@@ -1154,8 +1297,12 @@ ProcessExtLinuxConfig ( CHAR16 *Timeout = NULL; CHAR16 *CbootArg = NULL; CHAR16 *PostCbootArg = NULL; + CONST CHAR16 *RootL = L"root=LABEL="; ++ CONST CHAR16 *flasherArg = L"flasher"; BOOLEAN Ascii; UINTN Index; + CHAR16 *UpdatedCmdline = NULL; @@ -191,7 +193,7 @@ index ab86f3e0..570a1a20 100644 ZeroMem (BootConfig, sizeof (EXTLINUX_BOOT_CONFIG)); -@@ -1163,12 +1309,10 @@ ProcessExtLinuxConfig ( +@@ -1163,12 +1310,10 @@ ProcessExtLinuxConfig ( return EFI_INVALID_PARAMETER; } @@ -207,7 +209,7 @@ index ab86f3e0..570a1a20 100644 Status = OpenAndReadFileToBuffer ( *RootFsHandle, EXTLINUX_CONF_PATH, -@@ -1240,6 +1384,23 @@ ProcessExtLinuxConfig ( +@@ -1240,6 +1385,23 @@ ProcessExtLinuxConfig ( Status = CheckCommandString (CleanLine, EXTLINUX_KEY_FDT, &BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].DtbPath); if (!EFI_ERROR (Status)) { @@ -231,7 +233,7 @@ index ab86f3e0..570a1a20 100644 continue; } -@@ -1249,6 +1410,19 @@ ProcessExtLinuxConfig ( +@@ -1249,6 +1411,24 @@ ProcessExtLinuxConfig ( } Status = CheckCommandString (CleanLine, EXTLINUX_KEY_APPEND, &BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs); @@ -243,7 +245,12 @@ index ab86f3e0..570a1a20 100644 + } + + /* Append root label to cmdline that has been read from the extlinux.conf read from the active rootfs */ -+ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + argsSize, L"%s %s%s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs); ++ if (0 == StrnCmp(BalenaOSRootFs, BALENA_FLASHER_ROOT_PARTITION, StrLen(BalenaOSRootFs))) { ++ Print(L"%a: Flasher image detected, appending flasher arg...\r\n", __FUNCTION__); ++ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + StrSize(flasherArg) +argsSize, L"%s %s%s %s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs, flasherArg); ++ } else { ++ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + argsSize, L"%s %s%s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs); ++ } + if (NULL != BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs) { + FreePool(BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs); + } @@ -251,7 +258,7 @@ index ab86f3e0..570a1a20 100644 if (!EFI_ERROR (Status)) { CbootArg = StrStr (BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, EXTLINUX_CBOOT_ARG); if (CbootArg != NULL) { -@@ -1470,12 +1644,12 @@ ExtLinuxBoot ( +@@ -1470,12 +1650,12 @@ ExtLinuxBoot ( // Reload fdt if needed Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, &AcpiBase); @@ -266,7 +273,7 @@ index ab86f3e0..570a1a20 100644 Status = OpenAndReadFileToBuffer ( DeviceHandle, BootOption->DtbPath, -@@ -1690,6 +1864,301 @@ Exit: +@@ -1690,6 +1870,299 @@ Exit: return Status; } @@ -551,11 +558,9 @@ index ab86f3e0..570a1a20 100644 + FreePool (FileLine); + FileLine = NULL; + } -+ + if (FileHandle != NULL) { + FileHandleClose (FileHandle); + } -+ + return Status; + } + } @@ -568,7 +573,7 @@ index ab86f3e0..570a1a20 100644 /** Process the boot mode selection from command line and variables -@@ -1718,7 +2187,7 @@ ProcessBootParams ( +@@ -1718,7 +2191,7 @@ ProcessBootParams ( } BootParams->BootChain = 0; @@ -577,7 +582,7 @@ index ab86f3e0..570a1a20 100644 DataSize = sizeof (BootParams->BootMode); Status = gRT->GetVariable (L4T_BOOTMODE_VARIABLE_NAME, &gNVIDIAPublicVariableGuid, NULL, &DataSize, &BootParams->BootMode); if (EFI_ERROR (Status) || (BootParams->BootMode > NVIDIA_L4T_BOOTMODE_RECOVERY)) { -@@ -1729,17 +2198,22 @@ ProcessBootParams ( +@@ -1729,17 +2202,22 @@ ProcessBootParams ( Status = gRT->GetVariable (BOOT_FW_VARIABLE_NAME, &gNVIDIAPublicVariableGuid, NULL, &DataSize, &BootChain); // If variable does not exist, is >4 bytes or has a value larger than 1, boot partition A if (!EFI_ERROR (Status) && (BootChain <= 1)) { @@ -602,7 +607,7 @@ index ab86f3e0..570a1a20 100644 if (LoadedImage->LoadOptionsSize) { CurrentBootOption = StrStr (LoadedImage->LoadOptions, BOOTMODE_DIRECT_STRING); if (CurrentBootOption != NULL) { -@@ -1769,7 +2243,9 @@ ProcessBootParams ( +@@ -1769,7 +2247,9 @@ ProcessBootParams ( if (EFI_ERROR (Status)) { ErrorPrint (L"Failed to read boot chain override: %r\r\n", Status); } else if (StringValue <= 1) { @@ -612,7 +617,7 @@ index ab86f3e0..570a1a20 100644 } else { ErrorPrint (L"Boot chain override value out of range, ignoring\r\n"); } -@@ -1777,15 +2253,17 @@ ProcessBootParams ( +@@ -1777,15 +2257,17 @@ ProcessBootParams ( } // Find valid Rootfs Chain. If not, select recovery kernel @@ -634,7 +639,58 @@ index ab86f3e0..570a1a20 100644 } return EFI_SUCCESS; -@@ -2434,7 +2912,14 @@ L4TLauncher ( +@@ -2404,6 +2886,50 @@ GetDeviceHandleForFvBoot ( + return LoadedHandle; + } + ++STATIC ++EFI_STATUS ++EFIAPI ++CheckForFlasherImage ( ++ IN EFI_HANDLE DeviceHandle, ++ IN UINT32 BootChain ++) ++{ ++ EFI_STATUS Status; ++ EFI_HANDLE BootPartHandle; ++ EFI_DEVICE_PATH *FullDevicePath; ++ EFI_DEVICE_PATH *TmpFullDevicePath; ++ EFI_FILE_HANDLE FileHandle = NULL; ++ ++ Status = FindPartitionInfo (DeviceHandle, BALENA_FLASHER_BOOT_PARTITION, BootChain, NULL, &BootPartHandle); ++ ++ if (EFI_ERROR (Status)) { ++ ErrorPrint (L"%a: Could not find partition Balena flasher boot partition\r\n", __FUNCTION__); ++ return Status; ++ } ++ ++ FullDevicePath = FileDevicePath (BootPartHandle, BALENA_BOOT_FLASHER_FILE_PATH); ++ if (NULL == FullDevicePath) { ++ ErrorPrint (L"%a: Failed to create path for balena-image-flasher\r\n", __FUNCTION__); ++ return EFI_OUT_OF_RESOURCES; ++ } ++ ++ TmpFullDevicePath = FullDevicePath; ++ Status = EfiOpenFileByDevicePath (&TmpFullDevicePath, &FileHandle, EFI_FILE_MODE_READ, 0); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint (L"%a: Failed to open balena-image-flasher: %r\r\n", __FUNCTION__, Status); ++ return Status; ++ } ++ ++ ErrorPrint (L"%a: balena-image-flasher file found: %r\r\n", __FUNCTION__, Status); ++ ++ if (FileHandle != NULL) { ++ FileHandleClose (FileHandle); ++ } ++ ++ return EFI_SUCCESS; ++ ++} ++ + /** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including +@@ -2434,7 +2960,15 @@ L4TLauncher ( EXTLINUX_BOOT_CONFIG ExtLinuxConfig; UINTN ExtLinuxBootOption; UINTN Index; @@ -647,10 +703,11 @@ index ab86f3e0..570a1a20 100644 + BOOLEAN RunningUpdatedBootSlot = FALSE; + BOOLEAN BootLimitReached = FALSE; + BOOLEAN BootSlotProcessed = FALSE; ++ BOOLEAN IsBalenaImageFlasher = FALSE; Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage); if (EFI_ERROR (Status)) { ErrorPrint (L"%a: Unable to locate loaded image: %r\r\n", __FUNCTION__, Status); -@@ -2498,12 +2983,73 @@ L4TLauncher ( +@@ -2498,12 +3032,93 @@ L4TLauncher ( } while (FALSE); } @@ -662,30 +719,38 @@ index ab86f3e0..570a1a20 100644 + Print (L"%a: Attempting Direct Boot\r\n", __FUNCTION__); do { - Status = ProcessExtLinuxConfig (DeviceHandle, BootParams.BootChain, &ExtLinuxConfig, &RootFsDeviceHandle); -+ Status = ProcessResinOSuEnv (DeviceHandle, BootParams.BootChain, &RootFsIndex, &UpgradeAvailable); ++ Status = CheckForFlasherImage (DeviceHandle, BootParams.BootChain); + if (EFI_ERROR (Status)) { -+ ErrorPrint(L"resinOS_uEnv.txt does not exist\n"); -+ } ++ Print(L"balena-image-flasher flag not present.\r\n"); ++ ++ Status = ProcessResinOSuEnv (DeviceHandle, BootParams.BootChain, &RootFsIndex, &UpgradeAvailable); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint(L"resinOS_uEnv.txt does not exist\r\n"); ++ } + -+ Print(L"Current resin_root_part=%d - upgrade_available=%d\r\n", RootFsIndex, UpgradeAvailable); -+ if (1 == UpgradeAvailable) { ++ Print(L"Current resin_root_part=%d - upgrade_available=%d\r\n", RootFsIndex, UpgradeAvailable); ++ if (1 == UpgradeAvailable) { + Status = ProcessBootCountValue (DeviceHandle, BootParams.BootChain, &BootCountValue); + if (EFI_ERROR (Status)) { -+ ErrorPrint(L"%a: Error while processing bootcount value\r\n", __FUNCTION__); ++ ErrorPrint(L"%a: Error while processing bootcount value\r\n", __FUNCTION__); + } + Print (L"%a: BootCountValue is %d\r\n", __FUNCTION__, BootCountValue); -+ } ++ } + -+ Status = ProcessBootSlotFile(DeviceHandle, BootParams.BootChain, &UpdatedBootSlot, &RunningUpdatedBootSlot); -+ if (EFI_ERROR (Status)) { -+ ErrorPrint(L"%a: Error while processing boot slot value\r\n", __FUNCTION__); ++ Status = ProcessBootSlotFile(DeviceHandle, BootParams.BootChain, &UpdatedBootSlot, &RunningUpdatedBootSlot); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint(L"%a: Error while processing boot slot value\r\n", __FUNCTION__); ++ } else { ++ ErrorPrint(L"%a: Updated (target) boot slot: %u - Current running boot slot: %u\r\n", __FUNCTION__, UpdatedBootSlot, BootParams.BootChain); ++ BootSlotProcessed = TRUE; ++ } + } else { -+ ErrorPrint(L"%a: Updated (target) boot slot: %u - Current running boot slot: %u\r\n", __FUNCTION__, UpdatedBootSlot, BootParams.BootChain); -+ BootSlotProcessed = TRUE; ++ ErrorPrint(L"Found balena-image-flasher\n"); ++ IsBalenaImageFlasher = TRUE; + } + + /* If boot limit is reached while upgrade_available=1, switch partitions */ -+ if (BALENA_OS_BC_LIM <= BootCountValue) { ++ if (BALENA_OS_BC_LIM <= BootCountValue && !IsBalenaImageFlasher) { + if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { + RootFsIndex = BALENA_ROOTFS_INDEX_A; + } else { @@ -701,25 +766,37 @@ index ab86f3e0..570a1a20 100644 + ResetCold(); + } else { + ErrorPrint(L"%a: Running the fallback (pre-update) boot slot %u because boot limit was reached. Updated boot slot booted previously was %u\r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); -+ /* This allows for the L4T 35.5.0 capsule to be applied during rollback-altboot */ ++ + ValidateActiveBootChain(); + ErrorPrint(L"%a: Running the fallback (pre-update) boot slot %u because boot limit was reached, and marked current one as active. Updated boot slot booted previously was %u\r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); + } -+ } else if (1 == UpgradeAvailable && BootSlotProcessed && !RunningUpdatedBootSlot) { ++ } else if (1 == UpgradeAvailable && BootSlotProcessed && !RunningUpdatedBootSlot && !IsBalenaImageFlasher) { + ErrorPrint(L"%a: Capsule update failed or was interrupted, because current boot slot is %u when it should be %u. Forcing altboot mode so the system can recover by booting the compatible rootfs. \r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); + if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { -+ RootFsIndex = BALENA_ROOTFS_INDEX_A; ++ RootFsIndex = BALENA_ROOTFS_INDEX_A; + } else { -+ RootFsIndex = BALENA_ROOTFS_INDEX_B; ++ RootFsIndex = BALENA_ROOTFS_INDEX_B; + } ++ } else if (IsBalenaImageFlasher) { ++ RootFsIndex = BALENA_FLASHER_ROOTFS_INDEX; + } + -+ if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { -+ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_B; -+ } else { -+ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; ++ switch (RootFsIndex) { ++ case BALENA_ROOTFS_INDEX_A: ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; ++ break; ++ case BALENA_ROOTFS_INDEX_B: ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_B; ++ break; ++ case BALENA_FLASHER_ROOTFS_INDEX: ++ BalenaOSRootFs = BALENA_FLASHER_ROOT_PARTITION; ++ break; ++ default: ++ ErrorPrint(L"%a: Unexpected root filesystem index %d! Will default to rootA.\r\n", __FUNCTION__, RootFsIndex); ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; + } + ++ + Status = ProcessExtLinuxConfig (DeviceHandle, BootParams.BootChain, BalenaOSRootFs, &ExtLinuxConfig, &RootFsDeviceHandle, RunningUpdatedBootSlot); if (EFI_ERROR (Status)) { - ErrorPrint (L"%a: Unable to process extlinux config: %r\r\n", __FUNCTION__, Status); @@ -728,13 +805,17 @@ index ab86f3e0..570a1a20 100644 break; } diff --git a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h -index fc46a70d..33ebdbec 100644 +index fc46a70d..a2e0223b 100644 --- a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h +++ b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h -@@ -32,11 +32,22 @@ +@@ -32,11 +32,27 @@ #define BOOT_OS_VARIABLE_NAME L"BootChainOsCurrent" #define ROOTFS_BASE_NAME L"APP" ++#define BALENA_FLASHER_ROOT_PARTITION L"flash-rootA" ++#define BALENA_FLASHER_BOOT_PARTITION L"flash-boot" ++#define BALENA_BOOT_FLASHER_FILE_PATH L"balena-image-flasher" ++#define BALENA_FLASHER_ROOTFS_INDEX 2 +#define BALENA_ROOTFS_BASE_NAME_A L"resin-rootA" +#define BALENA_ROOTFS_BASE_NAME_B L"resin-rootB" +#define BALENA_BOOT_PARTITION L"resin-boot" @@ -750,12 +831,12 @@ index fc46a70d..33ebdbec 100644 #define BOOTIMG_DTB_BASE_NAME L"kernel-dtb" #define RECOVERY_BASE_NAME L"recovery" #define RECOVERY_DTB_BASE_NAME L"recovery-dtb" -- + +#define DEFAULT_ORIN_NANO_DTB_NAME L"tegra234-p3768-0000+p3767-0004-nv.dtb" #define EXTLINUX_KEY_TIMEOUT L"TIMEOUT" #define EXTLINUX_KEY_DEFAULT L"DEFAULT" #define EXTLINUX_KEY_MENU_TITLE L"MENU TITLE" -@@ -48,8 +59,10 @@ +@@ -48,8 +64,10 @@ #define EXTLINUX_KEY_APPEND L"APPEND" #define EXTLINUX_KEY_OVERLAYS L"OVERLAYS" diff --git a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Seeed-J4012-Integrate-with-balenaOS-on-L4T-36.3_patch.txt b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Seeed-J4012-Integrate-with-balenaOS-on-L4T-36.3_patch.txt index 100b8dd3..168eb744 100644 --- a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Seeed-J4012-Integrate-with-balenaOS-on-L4T-36.3_patch.txt +++ b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-Seeed-J4012-Integrate-with-balenaOS-on-L4T-36.3_patch.txt @@ -1,22 +1,23 @@ -From 5127308a2b0be7c742fb80303e87e70e3a8abe75 Mon Sep 17 00:00:00 2001 +From 2911643402daa6144845df081e5b7c52200d63fb Mon Sep 17 00:00:00 2001 From: Alexandru Costache -Date: Tue, 16 Jul 2024 09:48:42 +0000 +Date: Sun, 6 Oct 2024 13:43:30 +0300 Subject: [PATCH] Seeed J4012: Integrate with balenaOS on L4T 36.3 With this patch, the Nvidia bootloader parses resinos_uEnv.txt, extra_uEnv.txt and bootcount.env in order to load the extlinux.conf and the selected dtb from the current active -rootfs. +rootfs. It is also able to boot a flasher image if +the firmware in the QSPI is based on Jetpack 6. Upstream-Status: Inappropriate [configuration] Signed-off-by: Alexandru Costache --- - .../Application/L4TLauncher/L4TLauncher.c | 583 +++++++++++++++++- - .../Application/L4TLauncher/L4TLauncher.h | 15 +- - 2 files changed, 578 insertions(+), 20 deletions(-) + .../Application/L4TLauncher/L4TLauncher.c | 653 +++++++++++++++++- + .../Application/L4TLauncher/L4TLauncher.h | 18 + + 2 files changed, 652 insertions(+), 19 deletions(-) diff --git a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c -index ab86f3e0..a5a18895 100644 +index ab86f3e0..b2f37a29 100644 --- a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c +++ b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.c @@ -1125,6 +1125,147 @@ CheckCommandString ( @@ -179,11 +180,12 @@ index ab86f3e0..a5a18895 100644 ) { EFI_STATUS Status; -@@ -1154,8 +1297,11 @@ ProcessExtLinuxConfig ( +@@ -1154,8 +1297,12 @@ ProcessExtLinuxConfig ( CHAR16 *Timeout = NULL; CHAR16 *CbootArg = NULL; CHAR16 *PostCbootArg = NULL; + CONST CHAR16 *RootL = L"root=LABEL="; ++ CONST CHAR16 *flasherArg = L"flasher"; BOOLEAN Ascii; UINTN Index; + CHAR16 *UpdatedCmdline = NULL; @@ -191,7 +193,7 @@ index ab86f3e0..a5a18895 100644 ZeroMem (BootConfig, sizeof (EXTLINUX_BOOT_CONFIG)); -@@ -1163,12 +1309,10 @@ ProcessExtLinuxConfig ( +@@ -1163,12 +1310,10 @@ ProcessExtLinuxConfig ( return EFI_INVALID_PARAMETER; } @@ -207,7 +209,7 @@ index ab86f3e0..a5a18895 100644 Status = OpenAndReadFileToBuffer ( *RootFsHandle, EXTLINUX_CONF_PATH, -@@ -1240,6 +1384,23 @@ ProcessExtLinuxConfig ( +@@ -1240,6 +1385,23 @@ ProcessExtLinuxConfig ( Status = CheckCommandString (CleanLine, EXTLINUX_KEY_FDT, &BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].DtbPath); if (!EFI_ERROR (Status)) { @@ -231,7 +233,7 @@ index ab86f3e0..a5a18895 100644 continue; } -@@ -1249,6 +1410,19 @@ ProcessExtLinuxConfig ( +@@ -1249,6 +1411,24 @@ ProcessExtLinuxConfig ( } Status = CheckCommandString (CleanLine, EXTLINUX_KEY_APPEND, &BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs); @@ -243,7 +245,12 @@ index ab86f3e0..a5a18895 100644 + } + + /* Append root label to cmdline that has been read from the extlinux.conf read from the active rootfs */ -+ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + argsSize, L"%s %s%s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs); ++ if (0 == StrnCmp(BalenaOSRootFs, BALENA_FLASHER_ROOT_PARTITION, StrLen(BalenaOSRootFs))) { ++ Print(L"%a: Flasher image detected, appending flasher arg...\r\n", __FUNCTION__); ++ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + StrSize(flasherArg) +argsSize, L"%s %s%s %s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs, flasherArg); ++ } else { ++ UnicodeSPrint (UpdatedCmdline, StrSize(RootL) + StrSize(BalenaOSRootFs) + argsSize, L"%s %s%s\0", BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, RootL, BalenaOSRootFs); ++ } + if (NULL != BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs) { + FreePool(BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs); + } @@ -251,7 +258,7 @@ index ab86f3e0..a5a18895 100644 if (!EFI_ERROR (Status)) { CbootArg = StrStr (BootConfig->BootOptions[BootConfig->NumberOfBootOptions-1].BootArgs, EXTLINUX_CBOOT_ARG); if (CbootArg != NULL) { -@@ -1470,12 +1644,12 @@ ExtLinuxBoot ( +@@ -1470,12 +1650,12 @@ ExtLinuxBoot ( // Reload fdt if needed Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, &AcpiBase); @@ -266,7 +273,7 @@ index ab86f3e0..a5a18895 100644 Status = OpenAndReadFileToBuffer ( DeviceHandle, BootOption->DtbPath, -@@ -1690,6 +1864,301 @@ Exit: +@@ -1690,6 +1870,299 @@ Exit: return Status; } @@ -551,11 +558,9 @@ index ab86f3e0..a5a18895 100644 + FreePool (FileLine); + FileLine = NULL; + } -+ + if (FileHandle != NULL) { -+ FileHandleClose (FileHandle); ++ FileHandleClose (FileHandle); + } -+ + return Status; + } + } @@ -568,7 +573,7 @@ index ab86f3e0..a5a18895 100644 /** Process the boot mode selection from command line and variables -@@ -1718,7 +2187,7 @@ ProcessBootParams ( +@@ -1718,7 +2191,7 @@ ProcessBootParams ( } BootParams->BootChain = 0; @@ -577,7 +582,7 @@ index ab86f3e0..a5a18895 100644 DataSize = sizeof (BootParams->BootMode); Status = gRT->GetVariable (L4T_BOOTMODE_VARIABLE_NAME, &gNVIDIAPublicVariableGuid, NULL, &DataSize, &BootParams->BootMode); if (EFI_ERROR (Status) || (BootParams->BootMode > NVIDIA_L4T_BOOTMODE_RECOVERY)) { -@@ -1729,17 +2198,22 @@ ProcessBootParams ( +@@ -1729,17 +2202,22 @@ ProcessBootParams ( Status = gRT->GetVariable (BOOT_FW_VARIABLE_NAME, &gNVIDIAPublicVariableGuid, NULL, &DataSize, &BootChain); // If variable does not exist, is >4 bytes or has a value larger than 1, boot partition A if (!EFI_ERROR (Status) && (BootChain <= 1)) { @@ -602,7 +607,7 @@ index ab86f3e0..a5a18895 100644 if (LoadedImage->LoadOptionsSize) { CurrentBootOption = StrStr (LoadedImage->LoadOptions, BOOTMODE_DIRECT_STRING); if (CurrentBootOption != NULL) { -@@ -1769,7 +2243,9 @@ ProcessBootParams ( +@@ -1769,7 +2247,9 @@ ProcessBootParams ( if (EFI_ERROR (Status)) { ErrorPrint (L"Failed to read boot chain override: %r\r\n", Status); } else if (StringValue <= 1) { @@ -612,7 +617,7 @@ index ab86f3e0..a5a18895 100644 } else { ErrorPrint (L"Boot chain override value out of range, ignoring\r\n"); } -@@ -1777,15 +2253,17 @@ ProcessBootParams ( +@@ -1777,15 +2257,17 @@ ProcessBootParams ( } // Find valid Rootfs Chain. If not, select recovery kernel @@ -634,7 +639,58 @@ index ab86f3e0..a5a18895 100644 } return EFI_SUCCESS; -@@ -2434,7 +2912,14 @@ L4TLauncher ( +@@ -2404,6 +2886,50 @@ GetDeviceHandleForFvBoot ( + return LoadedHandle; + } + ++STATIC ++EFI_STATUS ++EFIAPI ++CheckForFlasherImage ( ++ IN EFI_HANDLE DeviceHandle, ++ IN UINT32 BootChain ++) ++{ ++ EFI_STATUS Status; ++ EFI_HANDLE BootPartHandle; ++ EFI_DEVICE_PATH *FullDevicePath; ++ EFI_DEVICE_PATH *TmpFullDevicePath; ++ EFI_FILE_HANDLE FileHandle = NULL; ++ ++ Status = FindPartitionInfo (DeviceHandle, BALENA_FLASHER_BOOT_PARTITION, BootChain, NULL, &BootPartHandle); ++ ++ if (EFI_ERROR (Status)) { ++ ErrorPrint (L"%a: Could not find partition Balena flasher boot partition\r\n", __FUNCTION__); ++ return Status; ++ } ++ ++ FullDevicePath = FileDevicePath (BootPartHandle, BALENA_BOOT_FLASHER_FILE_PATH); ++ if (NULL == FullDevicePath) { ++ ErrorPrint (L"%a: Failed to create path for balena-image-flasher\r\n", __FUNCTION__); ++ return EFI_OUT_OF_RESOURCES; ++ } ++ ++ TmpFullDevicePath = FullDevicePath; ++ Status = EfiOpenFileByDevicePath (&TmpFullDevicePath, &FileHandle, EFI_FILE_MODE_READ, 0); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint (L"%a: Failed to open balena-image-flasher: %r\r\n", __FUNCTION__, Status); ++ return Status; ++ } ++ ++ ErrorPrint (L"%a: balena-image-flasher file found: %r\r\n", __FUNCTION__, Status); ++ ++ if (FileHandle != NULL) { ++ FileHandleClose (FileHandle); ++ } ++ ++ return EFI_SUCCESS; ++ ++} ++ + /** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including +@@ -2434,7 +2960,15 @@ L4TLauncher ( EXTLINUX_BOOT_CONFIG ExtLinuxConfig; UINTN ExtLinuxBootOption; UINTN Index; @@ -647,10 +703,11 @@ index ab86f3e0..a5a18895 100644 + BOOLEAN RunningUpdatedBootSlot = FALSE; + BOOLEAN BootLimitReached = FALSE; + BOOLEAN BootSlotProcessed = FALSE; ++ BOOLEAN IsBalenaImageFlasher = FALSE; Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage); if (EFI_ERROR (Status)) { ErrorPrint (L"%a: Unable to locate loaded image: %r\r\n", __FUNCTION__, Status); -@@ -2498,12 +2983,72 @@ L4TLauncher ( +@@ -2498,12 +3032,93 @@ L4TLauncher ( } while (FALSE); } @@ -662,30 +719,38 @@ index ab86f3e0..a5a18895 100644 + Print (L"%a: Attempting Direct Boot\r\n", __FUNCTION__); do { - Status = ProcessExtLinuxConfig (DeviceHandle, BootParams.BootChain, &ExtLinuxConfig, &RootFsDeviceHandle); -+ Status = ProcessResinOSuEnv (DeviceHandle, BootParams.BootChain, &RootFsIndex, &UpgradeAvailable); ++ Status = CheckForFlasherImage (DeviceHandle, BootParams.BootChain); + if (EFI_ERROR (Status)) { -+ ErrorPrint(L"resinOS_uEnv.txt does not exist\n"); -+ } ++ Print(L"balena-image-flasher flag not present.\r\n"); ++ ++ Status = ProcessResinOSuEnv (DeviceHandle, BootParams.BootChain, &RootFsIndex, &UpgradeAvailable); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint(L"resinOS_uEnv.txt does not exist\r\n"); ++ } + -+ Print(L"Current resin_root_part=%d - upgrade_available=%d\r\n", RootFsIndex, UpgradeAvailable); -+ if (1 == UpgradeAvailable) { ++ Print(L"Current resin_root_part=%d - upgrade_available=%d\r\n", RootFsIndex, UpgradeAvailable); ++ if (1 == UpgradeAvailable) { + Status = ProcessBootCountValue (DeviceHandle, BootParams.BootChain, &BootCountValue); + if (EFI_ERROR (Status)) { -+ ErrorPrint(L"%a: Error while processing bootcount value\r\n", __FUNCTION__); ++ ErrorPrint(L"%a: Error while processing bootcount value\r\n", __FUNCTION__); + } + Print (L"%a: BootCountValue is %d\r\n", __FUNCTION__, BootCountValue); -+ } ++ } + -+ Status = ProcessBootSlotFile(DeviceHandle, BootParams.BootChain, &UpdatedBootSlot, &RunningUpdatedBootSlot); -+ if (EFI_ERROR (Status)) { -+ ErrorPrint(L"%a: Error while processing boot slot value\r\n", __FUNCTION__); ++ Status = ProcessBootSlotFile(DeviceHandle, BootParams.BootChain, &UpdatedBootSlot, &RunningUpdatedBootSlot); ++ if (EFI_ERROR (Status)) { ++ ErrorPrint(L"%a: Error while processing boot slot value\r\n", __FUNCTION__); ++ } else { ++ ErrorPrint(L"%a: Updated (target) boot slot: %u - Current running boot slot: %u\r\n", __FUNCTION__, UpdatedBootSlot, BootParams.BootChain); ++ BootSlotProcessed = TRUE; ++ } + } else { -+ ErrorPrint(L"%a: Updated (target) boot slot: %u - Current running boot slot: %u\r\n", __FUNCTION__, UpdatedBootSlot, BootParams.BootChain); -+ BootSlotProcessed = TRUE; ++ ErrorPrint(L"Found balena-image-flasher\n"); ++ IsBalenaImageFlasher = TRUE; + } + + /* If boot limit is reached while upgrade_available=1, switch partitions */ -+ if (BALENA_OS_BC_LIM <= BootCountValue) { ++ if (BALENA_OS_BC_LIM <= BootCountValue && !IsBalenaImageFlasher) { + if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { + RootFsIndex = BALENA_ROOTFS_INDEX_A; + } else { @@ -701,24 +766,37 @@ index ab86f3e0..a5a18895 100644 + ResetCold(); + } else { + ErrorPrint(L"%a: Running the fallback (pre-update) boot slot %u because boot limit was reached. Updated boot slot booted previously was %u\r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); ++ + ValidateActiveBootChain(); + ErrorPrint(L"%a: Running the fallback (pre-update) boot slot %u because boot limit was reached, and marked current one as active. Updated boot slot booted previously was %u\r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); + } -+ } else if (1 == UpgradeAvailable && BootSlotProcessed && !RunningUpdatedBootSlot) { ++ } else if (1 == UpgradeAvailable && BootSlotProcessed && !RunningUpdatedBootSlot && !IsBalenaImageFlasher) { + ErrorPrint(L"%a: Capsule update failed or was interrupted, because current boot slot is %u when it should be %u. Forcing altboot mode so the system can recover by booting the compatible rootfs. \r\n", __FUNCTION__, BootParams.BootChain, UpdatedBootSlot); + if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { -+ RootFsIndex = BALENA_ROOTFS_INDEX_A; ++ RootFsIndex = BALENA_ROOTFS_INDEX_A; + } else { -+ RootFsIndex = BALENA_ROOTFS_INDEX_B; ++ RootFsIndex = BALENA_ROOTFS_INDEX_B; + } ++ } else if (IsBalenaImageFlasher) { ++ RootFsIndex = BALENA_FLASHER_ROOTFS_INDEX; + } + -+ if (BALENA_ROOTFS_INDEX_B == RootFsIndex) { -+ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_B; -+ } else { -+ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; ++ switch (RootFsIndex) { ++ case BALENA_ROOTFS_INDEX_A: ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; ++ break; ++ case BALENA_ROOTFS_INDEX_B: ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_B; ++ break; ++ case BALENA_FLASHER_ROOTFS_INDEX: ++ BalenaOSRootFs = BALENA_FLASHER_ROOT_PARTITION; ++ break; ++ default: ++ ErrorPrint(L"%a: Unexpected root filesystem index %d! Will default to rootA.\r\n", __FUNCTION__, RootFsIndex); ++ BalenaOSRootFs = BALENA_ROOTFS_BASE_NAME_A; + } + ++ + Status = ProcessExtLinuxConfig (DeviceHandle, BootParams.BootChain, BalenaOSRootFs, &ExtLinuxConfig, &RootFsDeviceHandle, RunningUpdatedBootSlot); if (EFI_ERROR (Status)) { - ErrorPrint (L"%a: Unable to process extlinux config: %r\r\n", __FUNCTION__, Status); @@ -727,13 +805,17 @@ index ab86f3e0..a5a18895 100644 break; } diff --git a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h -index fc46a70d..7ce172ef 100644 +index fc46a70d..c2fcec3e 100644 --- a/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h +++ b/Silicon/NVIDIA/Application/L4TLauncher/L4TLauncher.h -@@ -32,11 +32,22 @@ +@@ -32,11 +32,27 @@ #define BOOT_OS_VARIABLE_NAME L"BootChainOsCurrent" #define ROOTFS_BASE_NAME L"APP" ++#define BALENA_FLASHER_ROOT_PARTITION L"flash-rootA" ++#define BALENA_FLASHER_BOOT_PARTITION L"flash-boot" ++#define BALENA_BOOT_FLASHER_FILE_PATH L"balena-image-flasher" ++#define BALENA_FLASHER_ROOTFS_INDEX 2 +#define BALENA_ROOTFS_BASE_NAME_A L"resin-rootA" +#define BALENA_ROOTFS_BASE_NAME_B L"resin-rootB" +#define BALENA_BOOT_PARTITION L"resin-boot" @@ -749,12 +831,12 @@ index fc46a70d..7ce172ef 100644 #define BOOTIMG_DTB_BASE_NAME L"kernel-dtb" #define RECOVERY_BASE_NAME L"recovery" #define RECOVERY_DTB_BASE_NAME L"recovery-dtb" -- + +#define DEFAULT_ORIN_NX_DVK_DTB_NAME L"tegra234-p3768-0000+p3767-0000-nv.dtb" #define EXTLINUX_KEY_TIMEOUT L"TIMEOUT" #define EXTLINUX_KEY_DEFAULT L"DEFAULT" #define EXTLINUX_KEY_MENU_TITLE L"MENU TITLE" -@@ -48,8 +59,10 @@ +@@ -48,8 +64,10 @@ #define EXTLINUX_KEY_APPEND L"APPEND" #define EXTLINUX_KEY_OVERLAYS L"OVERLAYS" diff --git a/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-TegraPlatformBootManager-TegraPlatformBootManagerDxe_patch.txt b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-TegraPlatformBootManager-TegraPlatformBootManagerDxe_patch.txt new file mode 100644 index 00000000..7f559339 --- /dev/null +++ b/layers/meta-balena-jetson/recipes-bsp/uefi/files/0001-TegraPlatformBootManager-TegraPlatformBootManagerDxe_patch.txt @@ -0,0 +1,39 @@ +From 42a758bc63a58e29e6ce622e65d3c4d4301f1bfc Mon Sep 17 00:00:00 2001 +From: Alexandru Costache +Date: Thu, 28 Nov 2024 12:15:53 +0200 +Subject: [PATCH] TegraPlatformBootManager/TegraPlatformBootManagerDxe: Force + new device hierarchy + +--- + .../TegraPlatformBootManagerDxe.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/Silicon/NVIDIA/Drivers/TegraPlatformBootManager/TegraPlatformBootManagerDxe.c b/Silicon/NVIDIA/Drivers/TegraPlatformBootManager/TegraPlatformBootManagerDxe.c +index c164689f..e961a92a 100644 +--- a/Silicon/NVIDIA/Drivers/TegraPlatformBootManager/TegraPlatformBootManagerDxe.c ++++ b/Silicon/NVIDIA/Drivers/TegraPlatformBootManager/TegraPlatformBootManagerDxe.c +@@ -903,6 +903,7 @@ RefreshNvBootOptions ( + UINTN NvBootOptionsCount; + UINTN Index; + INTN Match; ++ BOOLEAN PcdNewDeviceHierarchyOverride = FALSE; + + if ((BootOptions == NULL) || + (BootOptionsCount == 0)) +@@ -910,7 +911,12 @@ RefreshNvBootOptions ( + return EFI_INVALID_PARAMETER; + } + +- if (PcdGet8 (PcdNewDeviceHierarchy)) { ++ if (!PcdGet8 (PcdNewDeviceHierarchy)) { ++ DEBUG ((DEBUG_ERROR, "%a: This device has been flashed with PcdNewDeviceHierarchy = 0. Overriding to allow flasher images to be booted directly.\n", __FUNCTION__)); ++ PcdNewDeviceHierarchyOverride = TRUE; ++ } ++ DEBUG ((DEBUG_ERROR, "%a: >>>>> This firmware is updated\n", __FUNCTION__)); ++ if (PcdGet8 (PcdNewDeviceHierarchy) || TRUE == PcdNewDeviceHierarchyOverride) { + NvBootOptions = NULL; + NvBootOptions = EfiBootManagerGetLoadOptions ( + &NvBootOptionsCount, +-- +2.34.1 + diff --git a/layers/meta-balena-jetson/recipes-bsp/uefi/files/Dockerfile b/layers/meta-balena-jetson/recipes-bsp/uefi/files/Dockerfile index ab68f66d..3c449255 100644 --- a/layers/meta-balena-jetson/recipes-bsp/uefi/files/Dockerfile +++ b/layers/meta-balena-jetson/recipes-bsp/uefi/files/Dockerfile @@ -25,6 +25,7 @@ COPY ./0001-Seeed-J4012-Integrate-with-balenaOS-on-L4T-36.3_patch.txt /build/nvi COPY ./0001-Seeed-J3010-Integrate-with-balenaOS-on-L4T-36.3_patch.txt /build/nvidia-uefi/edk2-nvidia/0001-Seeed-J3010-Integrate-with-balenaOS-on-L4T-36.3.patch COPY ./0001-AGX-Orin-64GB-Integrate-with-balenaOS-on-L4T-36.3_patch.txt /build/nvidia-uefi/edk2-nvidia/0001-AGX-Orin-64GB-Integrate-with-balenaOS-on-L4T-36.3.patch COPY ./0001-edk2-nvidia-Remove-pva-fw-from-required-list_patch.txt /build/nvidia-uefi/edk2-nvidia/0001-edk2-nvidia-Remove-pva-fw-from-required-list.patch +COPY ./0001-TegraPlatformBootManager-TegraPlatformBootManagerDxe_patch.txt build/nvidia-uefi/edk2-nvidia/0001-TegraPlatformBootManager-TegraPlatformBootManagerDxe.patch # See https://forums.developer.nvidia.com/t/jetson-orin-nano-ota-35-5-0-36-3-failed-to-boot-after-ota-upgrade-from-35-5-0-to-36-3/296205/16 COPY ./0001-StandaloneMmOptee-Don-t-assert-if-var-store-integrit_patch.txt /build/nvidia-uefi/edk2-nvidia/0001-StandaloneMmOptee-Don-t-assert-if-var-store-integrit.patch diff --git a/layers/meta-balena-jetson/recipes-bsp/uefi/files/build.sh b/layers/meta-balena-jetson/recipes-bsp/uefi/files/build.sh index 97f167c4..260a903a 100644 --- a/layers/meta-balena-jetson/recipes-bsp/uefi/files/build.sh +++ b/layers/meta-balena-jetson/recipes-bsp/uefi/files/build.sh @@ -4,6 +4,8 @@ # from github with balenaOS specific changes # and then builds the UEFI firmware and bootloader +set -e + declare -A device_specific_patches device_specific_patches["jetson-agx-orin-devkit"]="0001-AGX-Orin-32GB-Integrate-with-balenaOS-on-L4T-36.3.patch" @@ -18,6 +20,11 @@ edk2_nvidia_patches=( "0001-edk2-nvidia-Add-changes-for-balenaOS-integration.pat "0001-edk2-nvidia-Remove-pva-fw-from-required-list.patch" \ "0001-StandaloneMmOptee-Don-t-assert-if-var-store-integrit.patch" ) +if [[ "${DEVICE_TYPE}" == "jetson-agx-orin-devkit-64gb" ]]; then + echo "Applying edk2-nvidia jetson-agx-orin-devkit-64gb NewDeviceHierarchy override patch" + edk2_nvidia_patches+=("0001-TegraPlatformBootManager-TegraPlatformBootManagerDxe.patch") +fi + cd /build/nvidia-uefi/edk2 && \ git reset --hard HEAD && \ git apply ${edk2_patch} && \ diff --git a/layers/meta-balena-jetson/recipes-core/images/balena-image-flasher.bbappend b/layers/meta-balena-jetson/recipes-core/images/balena-image-flasher.bbappend index 13d55f66..dfa4b8d7 100644 --- a/layers/meta-balena-jetson/recipes-core/images/balena-image-flasher.bbappend +++ b/layers/meta-balena-jetson/recipes-core/images/balena-image-flasher.bbappend @@ -3,3 +3,8 @@ include balena-image.inc IMAGE_INSTALL:remove:jetson-orin-nx-xavier-nx-devkit="l4t-launcher-extlinux" IMAGE_INSTALL:append = "efitools-utils efibootmgr" +BALENA_BOOT_PARTITION_FILES:append = " \ + bootfiles/EFI/BOOT/BOOTAA64.efi:/EFI/BOOT/BOOTAA64.efi \ + extra_uEnv.txt:/extra_uEnv.txt \ +" + diff --git a/layers/meta-balena-jetson/recipes-core/images/balena-image-initramfs.bbappend b/layers/meta-balena-jetson/recipes-core/images/balena-image-initramfs.bbappend index c2828895..748ccd2c 100644 --- a/layers/meta-balena-jetson/recipes-core/images/balena-image-initramfs.bbappend +++ b/layers/meta-balena-jetson/recipes-core/images/balena-image-initramfs.bbappend @@ -8,10 +8,21 @@ PACKAGE_INSTALL:append = " kernel-module-pcie-tegra194 " PACKAGE_INSTALL:append = " nv-kernel-module-pcie-tegra-vf " PACKAGE_INSTALL:append = " kernel-module-nvme " PACKAGE_INSTALL:append = " kernel-module-phy-tegra194-p2u" -PACKAGE_INSTALL:append:jetson-orin-nx-seeed-j4012 = " initramfs-module-nvme-wait" +# The three packages below are needed if flashing is +# performed in the initramfs, for example in the case +# where a NVME is flashed directly with the flasher image +# and booted. +# NOTE: We assume only the NVME will be previsioned in +# this case, because the default firmware doesn't +# allow access to the QSPI, but install the qspi driver +# in case it is accessible +PACKAGE_INSTALL:append = " mtd-utils fatrw gptfdisk kernel-module-spi-tegra210-quad" +PACKAGE_INSTALL:append = " mtd-utils fatrw gptfdisk kernel-module-spi-tegra210-quad jetson-qspi-manager setup-nv-boot-control" # Below modules are needed for the AGX Orin 64GB to # detect USB keys PACKAGE_INSTALL:append:jetson-agx-orin-devkit-64gb = " kernel-module-typec-ucsi" PACKAGE_INSTALL:append:jetson-agx-orin-devkit-64gb = " kernel-module-typec" PACKAGE_INSTALL:append:jetson-agx-orin-devkit-64gb = " kernel-module-ucsi-ccg" + +IMAGE_ROOTFS_MAXSIZE="40960" diff --git a/layers/meta-balena-jetson/recipes-core/images/balena-image.inc b/layers/meta-balena-jetson/recipes-core/images/balena-image.inc index 9d8c0d24..ab9b3444 100644 --- a/layers/meta-balena-jetson/recipes-core/images/balena-image.inc +++ b/layers/meta-balena-jetson/recipes-core/images/balena-image.inc @@ -25,6 +25,7 @@ IMAGE_INSTALL:append = " \ l4t-launcher-extlinux \ dtc \ pciutils \ + jetson-qspi-manager \ nvidia-kernel-oot-devicetrees nvidia-kernel-oot-display nvidia-kernel-oot-cameras nvidia-kernel-oot-bluetooth nvidia-kernel-oot-wifi \ nvidia-kernel-oot-canbus nvidia-kernel-oot-virtualization nvidia-kernel-oot-base nvidia-drm-loadconf \ " diff --git a/layers/meta-balena-jetson/recipes-core/initrdscripts/files/copy_jetson_artifacts.patch b/layers/meta-balena-jetson/recipes-core/initrdscripts/files/copy_jetson_artifacts.patch new file mode 100644 index 00000000..2670c345 --- /dev/null +++ b/layers/meta-balena-jetson/recipes-core/initrdscripts/files/copy_jetson_artifacts.patch @@ -0,0 +1,41 @@ +Copy jetson boot artifacts during migration, so that the +QSPI can be flashed directly or trough an UEFI capsule application +on the next boot. + +Signed-off-by: Alexandru Costache + +Index: 1.0-r4/migrate +=================================================================== +--- 1.0-r4.orig/migrate ++++ 1.0-r4/migrate +@@ -99,6 +99,8 @@ migrate_enabled() { + + # Main module function + migrate_run() { ++ jetson_orin_capsule=$(find "${ROOTFS_DIR}" -xdev -type f -name "TEGRA_BL_*Cap*") ++ jetson_orin_boot_artifact=$(find "${ROOTFS_DIR}" -xdev -type f -name "boot0.img.gz") + # Find the raw image in the rootfs partition + image=$(find "${ROOTFS_DIR}" -xdev -type f -name "${BALENA_IMAGE}") + kernel_images=$(find "${ROOTFS_DIR}" -xdev -type f -name "@@KERNEL_IMAGETYPE@@*") +@@ -125,6 +127,21 @@ migrate_run() { + # shellcheck disable=SC2086 + cp -a ${kernel_images} "/tmp" + ++ # If this is a Jetson Orin device, the flasher image contains ++ # a compressed boot blob as well as a UEFI capsule. ++ # If the QSPI is accessible, it will be written directly, ++ # otherwise a capsule update will be triggered on the next boot ++ # after provisioning ++ if [ -f "${jetson_orin_capsule}" ]; then ++ cp "${jetson_orin_capsule}" "/tmp/" ++ info "Copied ${jetson_orin_capsule}" ++ fi ++ ++ if [ -f "${jetson_orin_boot_artifact}" ]; then ++ cp "${jetson_orin_boot_artifact}" "/tmp/" ++ info "Copied ${jetson_orin_boot_artifact}" ++ fi ++ + # Need to source this again to set CONFIG_PATH correctly + unset CONFIG_PATH + # shellcheck disable=SC1091 diff --git a/layers/meta-balena-jetson/recipes-core/initrdscripts/files/nvmewait b/layers/meta-balena-jetson/recipes-core/initrdscripts/files/nvmewait deleted file mode 100644 index a20ea773..00000000 --- a/layers/meta-balena-jetson/recipes-core/initrdscripts/files/nvmewait +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - -# shellcheck disable=SC1091 -. /usr/libexec/os-helpers-fs -. /usr/libexec/os-helpers-logging - -nvmewait_enabled() { - return 0 -} - -nvmewait_run() { - C=0 - delay=1 - - # Using -b makes the initramfs crash - while [ ! -e /dev/nvme0n1p1 ]; - do - - if [ "$C" == 10 ]; then - echo "Timeout while waiting for nvme. Fsck may not run on all partitions" - break - fi - - debug "Attempt ${C}: Waiting ${delay} second for nvme to initialize..." - sleep ${delay} - - C=$(( $C + 1 )) - done -} diff --git a/layers/meta-balena-jetson/recipes-core/initrdscripts/initramfs-framework_%.bbappend b/layers/meta-balena-jetson/recipes-core/initrdscripts/initramfs-framework_%.bbappend index 593a3652..cd923a3d 100644 --- a/layers/meta-balena-jetson/recipes-core/initrdscripts/initramfs-framework_%.bbappend +++ b/layers/meta-balena-jetson/recipes-core/initrdscripts/initramfs-framework_%.bbappend @@ -1,25 +1,5 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/files:" - -SRC_URI:append:jetson-orin-nx-seeed-j4012 = " \ - file://nvmewait \ -" - -PACKAGES:append:jetson-orin-nx-seeed-j4012 = " \ - initramfs-module-nvme-wait \ +SRC_URI:append = " \ + file://copy_jetson_artifacts.patch \ " - -do_install:append:jetson-orin-nx-seeed-j4012() { - install -m 0755 ${WORKDIR}/nvmewait ${D}/init.d/02-nvmewait -} - -# Run this script after 01-udev -# to ensure nvme partition symlinks -# are populated in /dev/disk/by-label -# prior to running fsck. This because -# the Nvidia OOT drivers take a bit -# longer to initialize on the J4012 -# and fsck may not run on all partitions -SUMMARY:initramfs-module-nvme-wait = "Wait for nvme devices to initialize after pcie and nvme oot drivers are loaded" -RDEPENDS:initramfs-module-nvme-wait = "${PN}-base" -FILES:initramfs-module-nvme-wait = "/init.d/02-nvmewait" diff --git a/layers/meta-balena-jetson/recipes-kernel/linux/linux-jammy-nvidia-tegra_5.15.bbappend b/layers/meta-balena-jetson/recipes-kernel/linux/linux-jammy-nvidia-tegra_5.15.bbappend index 7adb5fb8..b0db1397 100644 --- a/layers/meta-balena-jetson/recipes-kernel/linux/linux-jammy-nvidia-tegra_5.15.bbappend +++ b/layers/meta-balena-jetson/recipes-kernel/linux/linux-jammy-nvidia-tegra_5.15.bbappend @@ -154,7 +154,7 @@ BALENA_CONFIGS[lan743x] = " \ L4TVER=" l4tver=${L4T_VERSION}" -KERNEL_ARGS = " firmware_class.path=/etc/firmware fbcon=map:0 " +KERNEL_ARGS = " firmware_class.path=/etc/firmware fbcon=map:0 rootdelay=1 roottimeout=60" KERNEL_ARGS += "${@bb.utils.contains('DISTRO_FEATURES','osdev-image',' mminit_loglevel=4 console=tty0 console=ttyTCU0,115200 ',' console=null quiet splash vt.global_cursor_default=0 consoleblank=0',d)} l4tver=${L4T_VERSION} " generate_extlinux_conf() { diff --git a/layers/meta-balena-jetson/recipes-kernel/nvidia-kernel-oot/nvidia-kernel-oot/enable_80211d.patch b/layers/meta-balena-jetson/recipes-kernel/nvidia-kernel-oot/nvidia-kernel-oot/enable_80211d.patch new file mode 100644 index 00000000..81cdbce9 --- /dev/null +++ b/layers/meta-balena-jetson/recipes-kernel/nvidia-kernel-oot/nvidia-kernel-oot/enable_80211d.patch @@ -0,0 +1,18 @@ +Enable CONFIG_80211D for rtl8822ce + +Upstream-Status: Inappropriate[configuration] +Signed-off-by: Alexandru Costache + +Index: nvidia-kernel-oot/nvidia-oot/drivers/net/wireless/realtek/rtl8822ce/Makefile +=================================================================== +--- nvidia-kernel-oot.orig/nvidia-oot/drivers/net/wireless/realtek/rtl8822ce/Makefile ++++ nvidia-kernel-oot/nvidia-oot/drivers/net/wireless/realtek/rtl8822ce/Makefile +@@ -75,7 +75,7 @@ CONFIG_TXPWR_LIMIT_EN = auto + CONFIG_RTW_CHPLAN = 0xFFFF + CONFIG_RTW_ADAPTIVITY_EN = disable + CONFIG_RTW_ADAPTIVITY_MODE = normal +-CONFIG_80211D = n ++CONFIG_80211D = y + CONFIG_SIGNAL_SCALE_MAPPING = n + CONFIG_80211W = y + CONFIG_REDUCE_TX_CPU_LOADING = n diff --git a/layers/meta-balena-jetson/recipes-kernel/nvidia-kernel-oot/nvidia-kernel-oot_36.3.0.bbappend b/layers/meta-balena-jetson/recipes-kernel/nvidia-kernel-oot/nvidia-kernel-oot_36.3.0.bbappend new file mode 100644 index 00000000..59b92ae5 --- /dev/null +++ b/layers/meta-balena-jetson/recipes-kernel/nvidia-kernel-oot/nvidia-kernel-oot_36.3.0.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS:append := ":${THISDIR}/${PN}" + +SRC_URI += " \ + file://enable_80211d.patch \ +" diff --git a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-agx-orin-devkit b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-agx-orin-devkit index 7f6830e6..890c1d14 100644 --- a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-agx-orin-devkit +++ b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-agx-orin-devkit @@ -12,7 +12,7 @@ source /usr/bin/uefi_common.func DURING_UPDATE=${DURING_UPDATE:-0} bootloader_blob="/opt/tegra-binaries/TEGRA_BL_3701.Cap.gz" bootloader_device="/dev/mtdblock0" -internal_storage="/dev/mmcblk0" +internal_storage="/dev/$(findmnt --noheadings --canonicalize --output SOURCE "/mnt/sysroot/active/" | xargs lsblk -no pkname)" use_capsule=1 efivars_dir="/sys/firmware/efi/efivars/" # See https://github.com/OE4T/meta-tegra/blob/master/recipes-bsp/tools/setup-nv-boot-control/setup-nv-boot-control.sh.in diff --git a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-agx-orin-devkit-64gb b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-agx-orin-devkit-64gb index f67cb472..6e34c7e7 100644 --- a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-agx-orin-devkit-64gb +++ b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-agx-orin-devkit-64gb @@ -11,7 +11,7 @@ source /usr/bin/uefi_common.func DURING_UPDATE=${DURING_UPDATE:-0} bootloader_device="/dev/mtdblock0" -internal_storage="/dev/nvme0n1" +internal_storage="/dev/$(findmnt --noheadings --canonicalize --output SOURCE "/mnt/sysroot/active/" | xargs lsblk -no pkname)" use_capsule=1 bootloader_blob="/opt/tegra-binaries/TEGRA_BL_3701_64.Cap.gz" efivars_dir="/sys/firmware/efi/efivars/" diff --git a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nano-devkit-nvme b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nano-devkit-nvme index 280ce073..dc9e135b 100644 --- a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nano-devkit-nvme +++ b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nano-devkit-nvme @@ -20,7 +20,7 @@ device_type="jetson-orin-nano-devkit" DURING_UPDATE=${DURING_UPDATE:-0} bootloader_device="/dev/mtdblock0" -internal_storage="/dev/nvme0n1" +internal_storage="/dev/$(findmnt --noheadings --canonicalize --output SOURCE "/mnt/sysroot/active/" | xargs lsblk -no pkname)" use_capsule=1 bootloader_blob="/opt/tegra-binaries/TEGRA_BL_Orin_Nano.Cap.gz" boardspec=$(tegra-boardspec 2>/dev/null) diff --git a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nano-seeed-j3010 b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nano-seeed-j3010 index 006b206d..38f1107e 100644 --- a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nano-seeed-j3010 +++ b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nano-seeed-j3010 @@ -11,7 +11,7 @@ source /usr/bin/uefi_common.func DURING_UPDATE=${DURING_UPDATE:-0} bootloader_device="/dev/mtdblock0" -internal_storage="/dev/nvme0n1" +internal_storage="/dev/$(findmnt --noheadings --canonicalize --output SOURCE "/mnt/sysroot/active/" | xargs lsblk -no pkname)" use_capsule=1 bootloader_blob="/opt/tegra-binaries/TEGRA_BL_Seeed_j3010.Cap.gz" efivars_dir="/sys/firmware/efi/efivars/" diff --git a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nx-seeed-j4012 b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nx-seeed-j4012 index 6dfb4b74..375fbb60 100644 --- a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nx-seeed-j4012 +++ b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nx-seeed-j4012 @@ -11,7 +11,7 @@ source /usr/bin/uefi_common.func DURING_UPDATE=${DURING_UPDATE:-0} bootloader_device="/dev/mtdblock0" -internal_storage="/dev/nvme0n1" +internal_storage="/dev/$(findmnt --noheadings --canonicalize --output SOURCE "/mnt/sysroot/active/" | xargs lsblk -no pkname)" use_capsule=1 bootloader_blob="/opt/tegra-binaries/TEGRA_BL_Seeed_j4012.Cap.gz" efivars_dir="/sys/firmware/efi/efivars/" diff --git a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nx-xavier-nx-devkit b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nx-xavier-nx-devkit index 21c61e3c..5c9ff1f4 100644 --- a/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nx-xavier-nx-devkit +++ b/layers/meta-balena-jetson/recipes-support/hostapp-update-hooks/files/99-resin-bootfiles-orin-nx-xavier-nx-devkit @@ -11,7 +11,7 @@ source /usr/bin/uefi_common.func DURING_UPDATE=${DURING_UPDATE:-0} bootloader_device="/dev/mtdblock0" -internal_storage="/dev/nvme0n1" +internal_storage="/dev/$(findmnt --noheadings --canonicalize --output SOURCE "/mnt/sysroot/active/" | xargs lsblk -no pkname)" use_capsule=1 bootloader_blob="/opt/tegra-binaries/TEGRA_BL_Orin_NX.Cap.gz" efivars_dir="/sys/firmware/efi/efivars/" diff --git a/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager.bb b/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager.bb new file mode 100644 index 00000000..ccf43795 --- /dev/null +++ b/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager.bb @@ -0,0 +1,48 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +DESCRIPTION = "Jetson Orin QSPI manager service" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${BALENA_COREBASE}/COPYING.Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10" + +SRC_URI = " \ + file://jetson-qspi-manager \ + file://jetson-qspi-helpers \ + file://jetson-qspi-manager.service \ + " + +S = "${WORKDIR}" + +RDEPENDS:${PN} += " bash " + +inherit allarch systemd + +COMPAT_SPEC_NAME="jetson-orin-nano-devkit" +COMPAT_SPEC_NAME:jetson-agx-orin-devkit="jetson-agx-orin-devkit" +COMPAT_SPEC_NAME:jetson-agx-orin-devkit-64gb="jetson-agx-orin-devkit" + +SYSTEMD_SERVICE:${PN} = " \ + jetson-qspi-manager.service \ + " + +do_patch[noexec] = "1" +do_compile[noexec] = "1" +do_build[noexec] = "1" + +do_install() { + install -d ${D}${bindir}/ + install -d ${D}/${libexecdir}/ + install -m 0755 ${WORKDIR}/jetson-qspi-manager ${D}${bindir}/ + install -m 0644 ${WORKDIR}/jetson-qspi-helpers ${D}/${libexecdir}/ + + sed -i -e 's,@ORIN_DEVICE_TYPE@,${COMPAT_SPEC_NAME},g' ${D}/${libexecdir}/jetson-qspi-helpers + + if ${@bb.utils.contains('DISTRO_FEATURES','systemd','true','false',d)}; then + install -d ${D}${systemd_unitdir}/system/ + install -d ${D}${sysconfdir}/systemd/system/multi-user.target.wants/ + install -m 0644 ${WORKDIR}/jetson-qspi-manager.service ${D}${systemd_unitdir}/system/ + + sed -i -e 's,@BASE_BINDIR@,${base_bindir},g' \ + -e 's,@BINDIR@,${bindir},g' \ + ${D}${systemd_unitdir}/system/*.service + fi +} diff --git a/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager/jetson-qspi-helpers b/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager/jetson-qspi-helpers new file mode 100644 index 00000000..963b9836 --- /dev/null +++ b/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager/jetson-qspi-helpers @@ -0,0 +1,192 @@ +#!/bin/bash + +set -e + +. /usr/libexec/os-helpers-logging +. /usr/libexec/os-helpers-fs + +source /usr/bin/uefi_common.func + +MAX_CAPSULE_UPDATE_RETRIES=3 +RETRY_COUNT_FILE="jetson-qspi-retry-count" +CAPSULE_TARGET_PATH="/EFI/UpdateCapsule/TEGRA_BL.Cap" +BOOT_MOUNTPOINT="/mnt/boot/" +UEFI_CAPSULE_TARGET_MOUNTPOINT="$BOOT_MOUNTPOINT" +force_capsule_update="n" + +efivars_dir="/sys/firmware/efi/efivars/" +# See https://github.com/OE4T/meta-tegra/blob/master/recipes-bsp/tools/setup-nv-boot-control/setup-nv-boot-control.sh.in +platform_spec_efivar="${efivars_dir}TegraPlatformSpec-781e084c-a330-417c-b678-38e696380cb9" +platform_compat_spec_efivar="${efivars_dir}TegraPlatformCompatSpec-781e084c-a330-417c-b678-38e696380cb9" +os_indications_efivar="${efivars_dir}OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c" +tmp_file="/tmp/platformspecfile.bin" + +device_type="@ORIN_DEVICE_TYPE@" +boardspec=$(tegra-boardspec 2>/dev/null) +TegraPlatformSpec="${boardspec}-${device_type}-" +compatspec=$(echo "$boardspec" | gen_compat_spec) +TegraPlatformCompatSpec="${compatspec}-${device_type}-" + +qspi_accessible() { + if [ -e /dev/mtd0 ]; then + return 0 + else + return 1 + fi +} + +check_qspi_accessible() { + if qspi_accessible ; then + info "QSPI is accessible" + exit 0 + else + info "QSPI is inaccessible" + exit 1 + fi +} + +capsule_update_prepared() { + if [ -e ${os_indications_efivar} ] && [ -e ${CAPSULE_TARGET_PATH} ]; then + return 0 + else + return 1 + fi +} + +write_jetson_update_efivars() { + if [ -d $efivars_dir ]; then + # If the file already exists, writing to it will fail + # causing the entire hook to fail + if [ ! -e ${platform_spec_efivar} ]; then + printf "\x07\x00\x00\x00" > ${tmp_file} + printf "%s" "${TegraPlatformSpec}" >> ${tmp_file} + tmp_file_size=$(stat -c%s ${tmp_file}) + dd if=${tmp_file} of=${platform_spec_efivar} bs=${tmp_file_size} + fi + + if [ ! -e ${platform_compat_spec_efivar} ]; then + printf "\x07\x00\x00\x00" > ${tmp_file} + printf "%s" "${TegraPlatformCompatSpec}" >> ${tmp_file} + tmp_file_size=$(stat -c%s ${tmp_file}) + dd if=${tmp_file} of=${platform_compat_spec_efivar} bs=${tmp_file_size} + #info "PlatformCompatSpec variable created" + else + #info "PlatformCompatSpec variable already exists" + if [[ ${kernel_l4t} == "36.3" ]]; then + #info "PlatformCompatSpec variable already exists and we are running in L4T 36.3, it needs to be re-written." + chattr -i ${platform_compat_spec_efivar} + printf "\x07\x00\x00\x00" > ${tmp_file} + printf "%s" "${TegraPlatformCompatSpec}" >> ${tmp_file} + tmp_file_size=$(stat -c%s ${tmp_file}) + dd if=${tmp_file} of=${platform_compat_spec_efivar} bs=${tmp_file_size} + fi + fi + + printf "%b" '\x07\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00' > ${tmp_file} + dd if=${tmp_file} of=${os_indications_efivar}; bs=12; + else + fail "sysfs not accessible!" + return 1 + fi + + return 0 +} + +# $1 - target path for unpacking UEFI capsule +prepare_capsule() { + uefi_capsule=$(find / -xdev -type f -name "TEGRA_BL*.Cap.gz") + + # Unzip capsule to the boot partition + #info "Will extract UEFI Capsule..." + mkdir -p ${1}/EFI/UpdateCapsule/ + gunzip -k -f -c ${uefi_capsule} | dd of=${1}/${CAPSULE_TARGET_PATH} + sync + #info "Extracted UEFI Capsule." + write_jetson_update_efivars +} + +# $1 - number of retries +# $2 - path to where $RETRY_COUNT_FILE should be stored +write_retries_file() { + tmpfile=$(mktemp) + echo "jetson_capsule_retries=$1" > $tmpfile + mv $tmpfile ${2}/${RETRY_COUNT_FILE} && sync + info "Attempt $jetson_capsule_retries: The QSPI will be updated on the next boot" + info "Please do not power off or reset the device during the next boot, until the device shows up online." +} + +# $1 - directory which stores the retries file +remove_retries_file() { + if [ ! -z "${1}" ]; then + retry_count_file_path="${1}/$RETRY_COUNT_FILE" + else + retry_count_file_path="${BOOT_MOUNTPOINT}/${RETRY_COUNT_FILE}" + fi + + if [ -f "${retry_count_file_path}" ]; then + #info "Retry count file found, will be removed" + rm $retry_count_file_path + fi +} + +# $1 - boot partition mountpoint - if unspecified, defaults to /mnt/boot/ +# $2 - UEFI capsule target mountpoint - if unspecified, defaults to /mnt/boot/ +# $3 - force capsule update application, even if the QSPI is accessible. This is +# useful when provisioning a device which has been provisioned with balenaOS +# previously, and we want to update the QSPI as well. +try_capsule_update() { + if [ ! -z "${1}" ]; then + BOOT_MOUNTPOINT=${1} + fi + + if [ ! -z "${2}" ]; then + UEFI_CAPSULE_TARGET_MOUNTPOINT=${2} + fi + + # We can only force the capsule update once if + # the QSPI is already accessible, because we don't + # know if it's accessible due to a succesfull past capsule application + # or if it's because the QSPI was raw flashed like that but with + # a different balenaOS build. + if [ ! -z "${3}" ]; then + force_capsule_update="y" + fi + + if qspi_accessible; then + if [[ "y" == "$force_capsule_update" ]]; then + info "QSPI is accessible, and forced update was requested" + else + info "OK: QSPI is accessible, no forced update requested" + remove_retries_file $BOOT_MOUNTPOINT + exit 0 + fi + fi + + if capsule_update_prepared; then + info "UEFI capsule already triggered externally" + remove_retries_file $BOOT_MOUNTPOINT + + exit 0 + else + if [ -f ${BOOT_MOUNTPOINT}/${RETRY_COUNT_FILE} ]; then + source ${BOOT_MOUNTPOINT}/${RETRY_COUNT_FILE} + if [ ! -z $jetson_capsule_retries ]; then + if [ $jetson_capsule_retries -le 3 ]; then + prepare_capsule $UEFI_CAPSULE_TARGET_MOUNTPOINT + jetson_capsule_retries=$(( $jetson_capsule_retries + 1 )) + write_retries_file $jetson_capsule_retries $BOOT_MOUNTPOINT + elif [ $jetson_capsule_retries -gt 3 ]; then + warn "Reached max number of capsule update retries!" + warn "Please re-flash your QSPI, or issue 'jetson-qspi-manager -r -u'" + exit 1 + fi + else + warn "Jetson QSPI retry count file empty or corrupt!" + exit 1 + fi + else + prepare_capsule $UEFI_CAPSULE_TARGET_MOUNTPOINT + write_retries_file 1 $BOOT_MOUNTPOINT + fi + fi +} diff --git a/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager/jetson-qspi-manager b/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager/jetson-qspi-manager new file mode 100755 index 00000000..c39d3f4d --- /dev/null +++ b/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager/jetson-qspi-manager @@ -0,0 +1,57 @@ +#!/bin/bash + +set -e + +. /usr/libexec/jetson-qspi-helpers + +help () { + cat << EOF +Script for QSPI management on Jetson Orin Devices +jetson-qspi-manager [options] + +Options: + -h, --help + Display this help and exit. + + -r, --reset + Remove the retries count file, and thus reset the number of capsule updates + + -u, --prepare-update + Prepare the UEFI capsule update to be applied on the next boot + + -c, --check + Check if the QSPI is accessible +EOF +} + +# Parse arguments +if [ "$#" -eq "0" ]; then + help + exit 1 +else + while [ "$#" -gt "0" ]; do + key=$1 + case $key in + -h|--help) + help + exit 0 + ;; + -c|--check) + check_qspi_accessible + shift + ;; + -u|--prepare-update) + try_capsule_update + shift + ;; + -r|--reset) + remove_retries_file + shift + ;; + *) + echo "[WARNING] $0 : Argument '$1' unknown." + exit 1 + ;; + esac + done +fi diff --git a/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager/jetson-qspi-manager.service b/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager/jetson-qspi-manager.service new file mode 100644 index 00000000..c7464a0a --- /dev/null +++ b/layers/meta-balena-jetson/recipes-support/jetson-qspi-manager/jetson-qspi-manager/jetson-qspi-manager.service @@ -0,0 +1,14 @@ +[Unit] +Description=Jetson Orin QSPI management service +DefaultDependencies=no +After=resin-boot.service +Before=umount.target +Conflicts=umount.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@BASE_BINDIR@/sh -c '@BINDIR@/jetson-qspi-manager -u' + +[Install] +WantedBy=multi-user.target diff --git a/layers/meta-balena-jetson/recipes-support/resin-init/resin-init-flasher-board/resin-init-flasher-board b/layers/meta-balena-jetson/recipes-support/resin-init/resin-init-flasher-board/resin-init-flasher-board index c7ae5fad..dba4ca4c 100755 --- a/layers/meta-balena-jetson/recipes-support/resin-init/resin-init-flasher-board/resin-init-flasher-board +++ b/layers/meta-balena-jetson/recipes-support/resin-init/resin-init-flasher-board/resin-init-flasher-board @@ -3,12 +3,14 @@ set -e . /usr/libexec/os-helpers-logging +. /usr/libexec/os-helpers-fs + +source /usr/libexec/jetson-qspi-helpers -MMC_DEV="mmcblk0" -NVME_DEV="nvme0n1" MTD_DEV="mtd0" FLASHER_CONF_FILE=/etc/resin-init-flasher.conf BOOT_BLOB="boot0.img" +INTERNAL_BOOT_MP="/tmp/internal_boot" if [ -f ${FLASHER_CONF_FILE} ]; then source ${FLASHER_CONF_FILE} @@ -16,25 +18,22 @@ else fail "No configuration for resin-init-flasher found." fi -mtd_debug erase /dev/${MTD_DEV} 0x0 0x4000000 -gunzip -k -c "/opt/tegra-binaries/${BOOT_BLOB}.gz" | dd of=/tmp/${BOOT_BLOB} bs=1M -mtd_debug write /dev/${MTD_DEV} 0x0 0x4000000 /tmp/${BOOT_BLOB} -sync - -# Relocate secondary gpt after writing image to the nvme with dd -sed -e 's/\s*\([\+0-9a-zA-Z]*\).*/\1/' << EOF | fdisk /dev/${INTERNAL_DEVICE_KERNEL} - v - w -EOF - -# All flasher images provision the internal storage (eMMC or NVME) and QSPI, -# so let's ensure the desired target is booted. The pre-set UEFI boot order -# in the boot blob for AGX Orin 64GB is eMMC followed by NVME, so we'll clear the -# eMMC to prevent it from being used. -if [[ "${INTERNAL_DEVICE_KERNEL}" = "${MMC_DEV}" ]]; then - log "Target internal device is the eMMC. Will not erase NVME or any other boot media." -elif [[ -b /dev/${MMC_DEV} ]]; then - log "Target internal device is ${INTERNAL_DEVICE_KERNEL}. Erasing eMMC partition table to prevent booting from it. Default boot order is dictated by UEFI variables." - sgdisk -Z /dev/${MMC_DEV} +if grep -q "jf_rcm_boot=1" /proc/cmdline ; then + # Find path to the compressed boot blob image + # that gets written to the QSPI + BOOT_BLOB_PATH=$(find / -xdev -type f -name "${BOOT_BLOB}.gz") + if [ ! -f "${BOOT_BLOB_PATH}" ]; then + fail "Boot blob not found in rootfs" + else + log "Found boot blob in ${BOOT_BLOB_PATH}" + fi + + log "QSPI is accessible, it will be updated" + mtd_debug erase /dev/${MTD_DEV} 0x0 0x4000000 + gunzip -k -f -c "${BOOT_BLOB_PATH}" | dd of=/tmp/${BOOT_BLOB} bs=1M + mtd_debug write /dev/${MTD_DEV} 0x0 0x4000000 /tmp/${BOOT_BLOB} + sync +else + log "QSPI is inaccessible in the hostOS update hook, will attempt update trough capsule on next boot" + try_capsule_update $INTERNAL_BOOT_MP $INTERNAL_BOOT_MP y fi -