[nelson] 14.20230831.4.72 u-boot source
GitOrigin-RevId: 83c221b24156bcacd9f748212ea6f2df82d15607
Change-Id: I5379517a151ff3114a44f6970ee61c516c50ea56
Reviewed-on: https://turquoise-internal-review.googlesource.com/c/third_party/u-boot/+/801610
Reviewed-by: David Pursell <dpursell@google.com>
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index cfaaa92..fc89a15 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -670,6 +670,82 @@
return getvar_response_buffer;
}
+static const char *get_emmc_wear_eol(const char *arg)
+{
+ struct mmc *mmc = find_mmc_device(CONFIG_FASTBOOT_FLASH_MMC_DEV);
+ if (!mmc || !mmc->ext_csd) {
+ return "no eMMC info";
+ }
+
+ const uint8_t value = mmc->ext_csd[EXT_CSD_PRE_EOL_INFO];
+
+ // Convert raw value into something human-readable.
+ // See eMMC spec v5.1 section 7.4.24.
+ const char *readable;
+ switch (value) {
+ case 0x00:
+ readable = "undefined";
+ break;
+ case 0x01:
+ readable = "normal";
+ break;
+ case 0x02:
+ readable = "warning";
+ break;
+ case 0x03:
+ readable = "urgent";
+ break;
+ default:
+ readable = "unknown";
+ break;
+ }
+
+ snprintf(getvar_response_buffer, GETVAR_RESPONSE_BUFFER_LEN, "%s (%d)",
+ readable, value);
+ return getvar_response_buffer;
+}
+
+static const char *s_emmc_wear_level_list[] = { "A", "B", NULL };
+static const char *get_emmc_wear_level(const char *arg)
+{
+ if (!arg) {
+ return NULL;
+ }
+
+ struct mmc *mmc = find_mmc_device(CONFIG_FASTBOOT_FLASH_MMC_DEV);
+ if (!mmc || !mmc->ext_csd) {
+ return "no eMMC info";
+ }
+
+ uint value = 0;
+ if (strcmp(arg, "A") == 0) {
+ value = mmc->dev_lifetime_est_typ_a;
+ } else if (strcmp(arg, "B") == 0) {
+ value = mmc->dev_lifetime_est_typ_b;
+ } else {
+ return NULL;
+ }
+
+ // Convert raw value into something human-readable.
+ // See eMMC spec v5.1 sections 7.4.22 and 7.4.23.
+ char readable[16];
+ if (value == 0x00) {
+ strncpy(readable, "undefined", sizeof(readable));
+ } else if (value <= 0x0A) {
+ uint low = value * 10;
+ uint high = low + 10;
+ snprintf(readable, sizeof(readable), "%d%%-%d%%", low, high);
+ } else if (value == 0x0B) {
+ strncpy(readable, "EOL", sizeof(readable));
+ } else {
+ strncpy(readable, "unknown", sizeof(readable));
+ }
+
+ snprintf(getvar_response_buffer, GETVAR_RESPONSE_BUFFER_LEN, "%s (%d)",
+ readable, value);
+ return getvar_response_buffer;
+}
+
static const char *get_bootloader_board(const char *arg)
{
const char *s = "unknown";
@@ -1023,6 +1099,15 @@
.func = get_emmc_vendor,
},
{
+ .name = "emmc-wear-eol",
+ .func = get_emmc_wear_eol,
+ },
+ {
+ .name = "emmc-wear-level",
+ .func = get_emmc_wear_level,
+ .default_args = s_emmc_wear_level_list,
+ },
+ {
.name = "hw-revision",
.func = get_hw_revision,
},
@@ -1506,14 +1591,26 @@
return;
}
- char *part_name = req->buf;
+ char buf[FASTBOOT_RESPONSE_LEN];
+ char *command = req->buf;
// skip past "oem stage-partition"
- if (!(strsep(&part_name, " ") && strsep(&part_name, " ") &&
- part_name)) {
+ if (!(strsep(&command, " ") && strsep(&command, " ") && command)) {
fb_fail("No partition given");
return;
}
+ const char *part_name = command;
+
+ // Optional offset and size params. Mostly useful if a partition is too
+ // big to fit in the fastboot buffer at once.
+ uint64_t offset = 0;
+ uint64_t size = 0;
+ if (strsep(&command, " ")) {
+ offset = simple_strtoul(command, NULL, 0);
+ if (strsep(&command, " ")) {
+ size = simple_strtoul(command, NULL, 0);
+ }
+ }
zircon_partition *part = zircon_get_partition(part_name);
@@ -1524,21 +1621,49 @@
return;
};
- if (part->size > CONFIG_FASTBOOT_BUF_SIZE || part->size > U32_MAX) {
- FB_ERR("Partition too large\n");
- fb_fail("Partition too large");
+ if (offset + size < offset) {
+ fb_fail("(Offset + size) overflow");
zircon_free_partition(part);
return;
}
- if (part->read(part, 0, (void *)CONFIG_FASTBOOT_BUF_ADDR, part->size)) {
- FB_ERR("Unable to read partition: %s\n", part_name);
+ if (offset >= part->size) {
+ snprintf(buf, sizeof(buf),
+ "Offset exceeds partition size (%llu)", part->size);
+ fb_fail(buf);
+ zircon_free_partition(part);
+ return;
+ }
+
+ // If size isn't given or overflows, default to the rest of the partition.
+ // Clamping overflow is important for usability, since callers may not
+ // know the partition size. This way they can just request fixed-size chunks
+ // until they receive a smaller piece back then they know they've reached
+ // the end.
+ if (size == 0 || offset + size > part->size) {
+ size = part->size - offset;
+ }
+
+ // Trying to request more than we can fit in the buffer is an error.
+ // If we also clamped here, it would be impossible to distinguish an
+ // end-of-partition short read from a too-large read.
+ if (size > CONFIG_FASTBOOT_BUF_SIZE || size > U32_MAX) {
+ snprintf(buf, sizeof(buf),
+ "Read too large (max 0x%llX), use offset/size params",
+ min((uint64_t)CONFIG_FASTBOOT_BUF_SIZE,
+ (uint64_t)U32_MAX));
+ fb_fail(buf);
+ zircon_free_partition(part);
+ return;
+ }
+
+ if (part->read(part, offset, (void *)CONFIG_FASTBOOT_BUF_ADDR, size)) {
fb_fail("Unable to read partition");
zircon_free_partition(part);
return;
}
- upload_size = part->size;
+ upload_size = size;
zircon_free_partition(part);
fb_okay(NULL);
}
diff --git a/include/mmc.h b/include/mmc.h
index 2772e63..a146656 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -237,6 +237,7 @@
#define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */
#define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */
#define EXT_CSD_BOOT_MULT 226 /* RO */
+#define EXT_CSD_PRE_EOL_INFO 267 /* RO */
#define EXT_CSD_DEV_LIFETIME_EST_TYP_A 268 /* RO */
#define EXT_CSD_DEV_LIFETIME_EST_TYP_B 269 /* RO */
#define EXT_CSD_BKOPS_SUPPORT 502 /* RO */