blob: bf75a9a1b24c714792bae3712b83b96353b1df8f [file] [log] [blame]
Caleb Connolly2bdb2522024-10-12 15:57:19 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Capsule update support for Qualcomm boards.
4 *
5 * Copyright (c) 2024 Linaro Ltd.
6 * Author: Caleb Connolly <caleb.connolly@linaro.org>
7 */
8
9#define pr_fmt(fmt) "QCOM-FMP: " fmt
10
11#include <dm/device.h>
12#include <dm/uclass.h>
13#include <efi.h>
14#include <efi_loader.h>
15#include <malloc.h>
16#include <scsi.h>
17#include <part.h>
18#include <linux/err.h>
19
20#include "qcom-priv.h"
21
22/*
23 * NOTE: for now this implementation only supports the rb3gen2. Supporting other
24 * boards that boot in different ways (e.g. chainloaded from ABL) will require
25 * additional complexity to properly create the dfu string and fw_images array.
26 */
27
28/*
29 * To handle different variants like chainloaded U-Boot here we'll need to
30 * build the fw_images array dynamically at runtime. It looks like
31 * mach-rockchip is a good example for how to do this.
32 * Detecting which image types a board uses is TBD, hence for now we only
33 * support the one new board that runs U-Boot as its primary bootloader.
34 */
35struct efi_fw_image fw_images[] = {
36 {
37 /* U-Boot flashed to the uefi_X partition (e.g. rb3gen2) */
38 .fw_name = u"UBOOT_UEFI_PARTITION",
39 .image_index = 1,
40 },
41};
42
43struct efi_capsule_update_info update_info = {
44 /* Filled in by configure_dfu_string() */
45 .dfu_string = NULL,
46 .num_images = ARRAY_SIZE(fw_images),
47 .images = fw_images,
48};
49
50/* LSB first */
51struct part_slot_status {
52 u16: 2;
53 u16 active : 1;
54 u16: 3;
55 u16 successful : 1;
56 u16 unbootable : 1;
57 u16 tries_remaining : 4;
58};
59
60static int find_boot_partition(const char *partname, struct blk_desc *blk_dev, char *name)
61{
62 int ret;
63 int partnum;
64 struct disk_partition info;
65 struct part_slot_status *slot_status;
66
67 for (partnum = 1;; partnum++) {
68 ret = part_get_info(blk_dev, partnum, &info);
69 if (ret)
70 return ret;
71
72 slot_status = (struct part_slot_status *)&info.type_flags;
73 log_io("%16s: Active: %1d, Successful: %1d, Unbootable: %1d, Tries left: %1d\n",
74 info.name, slot_status->active,
75 slot_status->successful, slot_status->unbootable,
76 slot_status->tries_remaining);
77 /*
78 * FIXME: eventually we'll want to find the active/inactive variant of the partition
79 * but on the rb3gen2 these values might all be 0
80 */
81 if (!strncmp(info.name, partname, strlen(partname))) {
82 log_debug("Found active %s partition: '%s'!\n", partname, info.name);
83 strlcpy(name, info.name, sizeof(info.name));
84 return partnum;
85 }
86 }
87
88 return -1;
89}
90
91/**
92 * qcom_configure_capsule_updates() - Configure the DFU string for capsule updates
93 *
94 * U-Boot is flashed to the boot partition on Qualcomm boards. In most cases there
95 * are two boot partitions, boot_a and boot_b. As we don't currently support doing
96 * full A/B updates, we only support updating the currently active boot partition.
97 *
98 * So we need to find the current slot suffix and the associated boot partition.
99 * We do this by looking for the boot partition that has the 'active' flag set
100 * in the GPT partition vendor attribute bits.
101 */
102void qcom_configure_capsule_updates(void)
103{
104 struct blk_desc *desc;
105 int ret = 0, partnum = -1, devnum;
106 static char dfu_string[32] = { 0 };
107 char name[32]; /* GPT partition name */
108 char *partname = "uefi_a";
109 struct udevice *dev = NULL;
110
111 if (IS_ENABLED(CONFIG_SCSI)) {
112 /* Scan for SCSI devices */
113 ret = scsi_scan(false);
114 if (ret) {
115 debug("Failed to scan SCSI devices: %d\n", ret);
116 return;
117 }
118 }
119
120 uclass_foreach_dev_probe(UCLASS_BLK, dev) {
121 if (device_get_uclass_id(dev) != UCLASS_BLK)
122 continue;
123
124 desc = dev_get_uclass_plat(dev);
125 if (!desc || desc->part_type == PART_TYPE_UNKNOWN)
126 continue;
127 devnum = desc->devnum;
128 partnum = find_boot_partition(partname, desc,
129 name);
130 if (partnum >= 0)
131 break;
132 }
133
134 if (partnum < 0) {
135 log_err("Failed to find boot partition\n");
136 return;
137 }
138
139 switch (desc->uclass_id) {
140 case UCLASS_SCSI:
141 snprintf(dfu_string, 32, "scsi %d=u-boot.bin part %d", devnum, partnum);
142 break;
143 case UCLASS_MMC:
144 snprintf(dfu_string, 32, "mmc 0=u-boot.bin part %d %d", devnum, partnum);
145 break;
146 default:
147 debug("Unsupported storage uclass: %d\n", desc->uclass_id);
148 return;
149 }
150 log_debug("boot partition is %s, DFU string: '%s'\n", name, dfu_string);
151
152 update_info.dfu_string = dfu_string;
153}