Message ID | 20240228165356.3993592-1-stefano.babic@swupdate.org |
---|---|
State | Accepted |
Headers | show |
Series | [V2,1/2] handler: eMMC internal register | expand |
Hi Stefano, thanks for the changes. I tested again. Code does what it's supposed to, great! Best regards, Michael Stefano Babic schrieb am Mittwoch, 28. Februar 2024 um 17:54:03 UTC+1: > This adds support for managing hardware functions in eMMC device. First > version of the handler supports toggeling of the hardware boot > partitions. > > Signed-off-by: Stefano Babic <stefan...@swupdate.org> > --- > > Changes since V1: > - check for returned active partition to exclude user Boot Area > - Add trace to log which becomes the next boot device > > corelib/Makefile | 3 +- > corelib/emmc.h | 140 ++++++++++++++++++++++++++++++++++++ > corelib/emmc_utils.c | 110 ++++++++++++++++++++++++++++ > handlers/Config.in | 11 +++ > handlers/Makefile | 1 + > handlers/emmc_csd_handler.c | 75 +++++++++++++++++++ > include/util.h | 4 ++ > 7 files changed, 343 insertions(+), 1 deletion(-) > create mode 100644 corelib/emmc.h > create mode 100644 corelib/emmc_utils.c > create mode 100644 handlers/emmc_csd_handler.c > > diff --git a/corelib/Makefile b/corelib/Makefile > index 7e706d87..5917e379 100644 > --- a/corelib/Makefile > +++ b/corelib/Makefile > @@ -2,7 +2,8 @@ > # > # SPDX-License-Identifier: GPL-2.0-only > > -lib-y += multipart_parser.o \ > +lib-y += emmc_utils.o \ > + multipart_parser.o \ > parsing_library_libjson.o \ > server_utils.o > lib-$(CONFIG_DOWNLOAD) += downloader.o > diff --git a/corelib/emmc.h b/corelib/emmc.h > new file mode 100644 > index 00000000..35f8a304 > --- /dev/null > +++ b/corelib/emmc.h > @@ -0,0 +1,140 @@ > +/* > + * (C) Copyright 2024 > + * Stefano Babic, stefan...@swupdate.org > + * > + * SPDX-License-Identifier: GPL-2.0-only > + */ > + > +#pragma once > + > +/* From kernel linux/mmc/mmc.h */ > +#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ > +#define MMC_SWITCH 6 /* ac [31:0] See below R1b */ > +#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ > +#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ > +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ > + > +/* From kernel linux/mmc/core.h */ > +#define MMC_RSP_NONE 0 /* no response */ > +#define MMC_RSP_PRESENT (1 << 0) > +#define MMC_RSP_136 (1 << 1) /* 136 bit response */ > +#define MMC_RSP_CRC (1 << 2) /* expect valid crc */ > +#define MMC_RSP_BUSY (1 << 3) /* card may send busy */ > +#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */ > + > +#define MMC_CMD_AC (0 << 5) > +#define MMC_CMD_ADTC (1 << 5) > +#define MMC_CMD_BC (2 << 5) > + > +#define MMC_RSP_SPI_S1 (1 << 7) /* one status byte */ > +#define MMC_RSP_SPI_BUSY (1 << 10) /* card may send busy */ > + > +#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1) > +#define MMC_RSP_SPI_R1B (MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY) > + > +#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) > +#define MMC_RSP_R1B > (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) > + > +/* > + * EXT_CSD fields > + */ > +#define EXT_CSD_S_CMD_SET 504 > +#define EXT_CSD_HPI_FEATURE 503 > +#define EXT_CSD_BKOPS_SUPPORT 502 /* RO */ > +#define EXT_CSD_SUPPORTED_MODES 493 /* RO */ > +#define EXT_CSD_FFU_FEATURES 492 /* RO */ > +#define EXT_CSD_FFU_ARG_3 490 /* RO */ > +#define EXT_CSD_FFU_ARG_2 489 /* RO */ > +#define EXT_CSD_FFU_ARG_1 488 /* RO */ > +#define EXT_CSD_FFU_ARG_0 487 /* RO */ > +#define EXT_CSD_CMDQ_DEPTH 307 /* RO */ > +#define EXT_CSD_CMDQ_SUPPORT 308 /* RO */ > +#define EXT_CSD_NUM_OF_FW_SEC_PROG_3 305 /* RO */ > +#define EXT_CSD_NUM_OF_FW_SEC_PROG_2 304 /* RO */ > +#define EXT_CSD_NUM_OF_FW_SEC_PROG_1 303 /* RO */ > +#define EXT_CSD_NUM_OF_FW_SEC_PROG_0 302 /* RO */ > +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B 269 /* RO */ > +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A 268 /* RO */ > +#define EXT_CSD_PRE_EOL_INFO 267 /* RO */ > +#define EXT_CSD_FIRMWARE_VERSION 254 /* RO */ > +#define EXT_CSD_CACHE_SIZE_3 252 > +#define EXT_CSD_CACHE_SIZE_2 251 > +#define EXT_CSD_CACHE_SIZE_1 250 > +#define EXT_CSD_CACHE_SIZE_0 249 > +#define EXT_CSD_SEC_FEATURE_SUPPORT 231 > +#define EXT_CSD_BOOT_INFO 228 /* R/W */ > +#define EXT_CSD_BOOT_MULT 226 /* RO */ > +#define EXT_CSD_HC_ERASE_GRP_SIZE 224 > +#define EXT_CSD_HC_WP_GRP_SIZE 221 > +#define EXT_CSD_SEC_COUNT_3 215 > +#define EXT_CSD_SEC_COUNT_2 214 > +#define EXT_CSD_SEC_COUNT_1 213 > +#define EXT_CSD_SEC_COUNT_0 212 > +#define EXT_CSD_PART_SWITCH_TIME 199 > +#define EXT_CSD_REV 192 > +#define EXT_CSD_BOOT_CFG 179 > +#define EXT_CSD_PART_CONFIG 179 > +#define EXT_CSD_BOOT_BUS_CONDITIONS 177 > +#define EXT_CSD_ERASE_GROUP_DEF 175 > +#define EXT_CSD_BOOT_WP_STATUS 174 > +#define EXT_CSD_BOOT_WP 173 > +#define EXT_CSD_USER_WP 171 > +#define EXT_CSD_FW_CONFIG 169 /* R/W */ > +#define EXT_CSD_WR_REL_SET 167 > +#define EXT_CSD_WR_REL_PARAM 166 > +#define EXT_CSD_SANITIZE_START 165 > +#define EXT_CSD_BKOPS_EN 163 /* R/W */ > +#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ > +#define EXT_CSD_PARTITIONING_SUPPORT 160 /* RO */ > +#define EXT_CSD_MAX_ENH_SIZE_MULT_2 159 > +#define EXT_CSD_MAX_ENH_SIZE_MULT_1 158 > +#define EXT_CSD_MAX_ENH_SIZE_MULT_0 157 > +#define EXT_CSD_PARTITIONS_ATTRIBUTE 156 /* R/W */ > +#define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */ > +#define EXT_CSD_GP_SIZE_MULT_4_2 154 > +#define EXT_CSD_GP_SIZE_MULT_4_1 153 > +#define EXT_CSD_GP_SIZE_MULT_4_0 152 > +#define EXT_CSD_GP_SIZE_MULT_3_2 151 > +#define EXT_CSD_GP_SIZE_MULT_3_1 150 > +#define EXT_CSD_GP_SIZE_MULT_3_0 149 > +#define EXT_CSD_GP_SIZE_MULT_2_2 148 > +#define EXT_CSD_GP_SIZE_MULT_2_1 147 > +#define EXT_CSD_GP_SIZE_MULT_2_0 146 > +#define EXT_CSD_GP_SIZE_MULT_1_2 145 > +#define EXT_CSD_GP_SIZE_MULT_1_1 144 > +#define EXT_CSD_GP_SIZE_MULT_1_0 143 > +#define EXT_CSD_ENH_SIZE_MULT_2 142 > +#define EXT_CSD_ENH_SIZE_MULT_1 141 > +#define EXT_CSD_ENH_SIZE_MULT_0 140 > +#define EXT_CSD_ENH_START_ADDR_3 139 > +#define EXT_CSD_ENH_START_ADDR_2 138 > +#define EXT_CSD_ENH_START_ADDR_1 137 > +#define EXT_CSD_ENH_START_ADDR_0 136 > +#define EXT_CSD_NATIVE_SECTOR_SIZE 63 /* R */ > +#define EXT_CSD_USE_NATIVE_SECTOR 62 /* R/W */ > +#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ > +#define EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_1 53 > +#define EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 52 > +#define EXT_CSD_CACHE_CTRL 33 > +#define EXT_CSD_MODE_CONFIG 30 > +#define EXT_CSD_MODE_OPERATION_CODES 29 /* W */ > +#define EXT_CSD_FFU_STATUS 26 /* R */ > +#define EXT_CSD_SECURE_REMOVAL_TYPE 16 /* R/W */ > +#define EXT_CSD_CMDQ_MODE_EN 15 /* R/W */ > + > + > +/* > + * EXT_CSD field definitions > + */ > +#define EXT_CSD_CONFIG_SECRM_TYPE (0x30) > +#define EXT_CSD_SUPPORTED_SECRM_TYPE (0x0f) > +#define EXT_CSD_FFU_INSTALL (0x01) > +#define EXT_CSD_FFU_MODE (0x01) > +#define EXT_CSD_NORMAL_MODE (0x00) > +#define EXT_CSD_FFU (1<<0) > +#define EXT_CSD_UPDATE_DISABLE (1<<0) > +#define EXT_CSD_HPI_SUPP (1<<0) > +#define EXT_CSD_HPI_IMPL (1<<1) > +#define EXT_CSD_CMD_SET_NORMAL (1<<0) > + > + > diff --git a/corelib/emmc_utils.c b/corelib/emmc_utils.c > new file mode 100644 > index 00000000..f12c7867 > --- /dev/null > +++ b/corelib/emmc_utils.c > @@ -0,0 +1,110 @@ > +/* > + * (C) Copyright 2024 > + * Stefano Babic, stefan...@swupdate.org > + * > + * SPDX-License-Identifier: GPL-2.0-only > + */ > +#include <stdio.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <stdlib.h> > +#include <stdbool.h> > +#include <stddef.h> > +#include <stdint.h> > +#include <errno.h> > +#include <linux/version.h> > +#include <sys/ioctl.h> > +#include <linux/major.h> > +#include <linux/mmc/ioctl.h> > +#include "emmc.h" > +#include "util.h" > + > +/* > + * Code taken from mmc-utils, mmc_cmds.c > + */ > +static int emmc_read_extcsd(int fd, __u8 *ext_csd) > +{ > + int ret = 0; > + struct mmc_ioc_cmd idata; > + memset(&idata, 0, sizeof(idata)); > + memset(ext_csd, 0, sizeof(__u8) * 512); > + idata.write_flag = 0; > + idata.opcode = MMC_SEND_EXT_CSD; > + idata.arg = 0; > + idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; > + idata.blksz = 512; > + idata.blocks = 1; > + mmc_ioc_cmd_set_data(idata, ext_csd); > + > + ret = ioctl(fd, MMC_IOC_CMD, &idata); > + if (ret) > + ERROR("eMMC ioctl return error %d", ret); > + > + return ret; > +} > + > +static void fill_switch_cmd(struct mmc_ioc_cmd *cmd, __u8 index, __u8 > value) > +{ > + cmd->opcode = MMC_SWITCH; > + cmd->write_flag = 1; > + cmd->arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | > + (value << 8) | EXT_CSD_CMD_SET_NORMAL; > + cmd->flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; > +} > + > +static int emmc_write_extcsd_value(int fd, __u8 index, __u8 value, > unsigned int timeout_ms) > +{ > + int ret = 0; > + struct mmc_ioc_cmd idata = {}; > + > + fill_switch_cmd(&idata, index, value); > + > + /* Kernel will set cmd_timeout_ms if 0 is set */ > + idata.cmd_timeout_ms = timeout_ms; > + > + ret = ioctl(fd, MMC_IOC_CMD, &idata); > + if (ret) > + ERROR("eMMC ioctl return error %d", ret); > + > + return ret; > +} /* end of imported code */ > + > +int emmc_get_active_bootpart(int fd) > +{ > + int ret; > + uint8_t extcsd[512]; > + int active; > + > + ret = emmc_read_extcsd(fd, extcsd); > + > + if (ret) > + return -1; > + > + /* > + * Return partition number starting from 0 > + * This corresponds to mmcblkXboot0 and mmcblkXboot1 > + */ > + active = ((extcsd[EXT_CSD_PART_CONFIG] & 0x38) >> 3) - 1; > + > + return active; > +} > + > +int emmc_write_bootpart(int fd, int bootpart) > +{ > + uint8_t value; > + int ret; > + uint8_t extcsd[512]; > + > + /* > + * Do not clear BOOT_ACK > + */ > + ret = emmc_read_extcsd(fd, extcsd); > + value = extcsd[EXT_CSD_PART_CONFIG] & (1 << 6); > + > + bootpart = ((bootpart + 1) & 0x3) << 3; > + value |= bootpart; > + > + ret = emmc_write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value, 0); > + > + return ret; > +} > diff --git a/handlers/Config.in b/handlers/Config.in > index b01d033d..ca473646 100644 > --- a/handlers/Config.in > +++ b/handlers/Config.in > @@ -140,6 +140,17 @@ config EMBEDDED_LUA_HANDLER_SOURCE > Path to the Lua handler source code file to be > embedded into the SWUpdate binary. > > + > +config EMMC_HANDLER > + bool "eMMC handler" > + default n > + help > + This handler allows to switch the boot partitions via > + the eMMC internal CSD register. One common use case is to > + upgrade the bootloader, using the two hardware partitions > + with a dual-copy concept. This guarantees that the upgrade > + is power-cut safe. > + > config RAW > bool "raw" > default n > diff --git a/handlers/Makefile b/handlers/Makefile > index 24fd487c..12d4aeb8 100644 > --- a/handlers/Makefile > +++ b/handlers/Makefile > @@ -14,6 +14,7 @@ obj-$(CONFIG_BTRFS_FILESYSTEM) += btrfs_handler.o > obj-$(CONFIG_COPY) += copy_handler.o > obj-$(CONFIG_CFI) += flash_handler.o > obj-$(CONFIG_DELTA) += delta_handler.o delta_downloader.o zchunk_range.o > +obj-$(CONFIG_EMMC_HANDLER) += emmc_csd_handler.o > obj-$(CONFIG_DISKFORMAT_HANDLER) += diskformat_handler.o > obj-$(CONFIG_DISKPART) += diskpart_handler.o > obj-$(CONFIG_UNIQUEUUID) += uniqueuuid_handler.o > diff --git a/handlers/emmc_csd_handler.c b/handlers/emmc_csd_handler.c > new file mode 100644 > index 00000000..27bd0257 > --- /dev/null > +++ b/handlers/emmc_csd_handler.c > @@ -0,0 +1,75 @@ > +/* > + * (C) Copyright 2024 > + * Stefano Babic, stefan...@swupdate.org > + * > + * SPDX-License-Identifier: GPL-2.0-only > + */ > +/* > + * This handler manages the CSD register accoriding to eMMC > + * specifications. Base for this handler are the mmcutils, > + * see: > + * https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git > + */ > + > +#include <stdio.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <stdlib.h> > +#include <stdbool.h> > +#include <stddef.h> > +#include <errno.h> > +#include <linux/version.h> > +#include <sys/ioctl.h> > +#include <linux/mmc/ioctl.h> > +#include "swupdate_image.h" > +#include "handler.h" > +#include "util.h" > + > +void emmc_csd_handler(void); > + > +static int emmc_boot_toggle(struct img_type *img, void *data) > +{ > + int active, ret; > + struct script_handler_data *script_data = data; > + if (script_data->scriptfn == PREINSTALL) > + return 0; > + > + /* Open the device (partition) */ > + int fdin = open(img->device, O_RDONLY); > + if (fdin < 0) { > + ERROR("Failed to open %s: %s", img->device, strerror(errno)); > + return -ENODEV; > + } > + > + active = emmc_get_active_bootpart(fdin); > + if (active < 0) { > + ERROR("Current HW boot partition cannot be retrieved"); > + close(fdin); > + return -1; > + } > + > + /* > + * If User Partition is activated, does nothing > + * and report this to the user. > + */ > + if (active > 1) { > + WARN("Boot device set to User area, no changes !"); > + ret = 0; > + } else { > + active = (active == 0) ? 1 : 0; > + TRACE("Setting Boot to HW Partition %d", active); > + ret = emmc_write_bootpart(fdin, active); > + if (ret) > + ERROR("Failure writing CSD register"); > + } > + > + close(fdin); > + return ret; > +} > + > +__attribute__((constructor)) > +void emmc_csd_handler(void) > +{ > + register_handler("emmc_boot_toggle", emmc_boot_toggle, > + SCRIPT_HANDLER | NO_DATA_HANDLER, NULL); > +} > diff --git a/include/util.h b/include/util.h > index 490014e7..e1350633 100644 > --- a/include/util.h > +++ b/include/util.h > @@ -272,3 +272,7 @@ int swupdate_umount(const char *dir); > > /* Date / Time utilities */ > char *swupdate_time_iso8601(struct timeval *tv); > + > +/* eMMC functions */ > +int emmc_write_bootpart(int fd, int bootpart); > +int emmc_get_active_bootpart(int fd); > -- > 2.34.1 > >
diff --git a/corelib/Makefile b/corelib/Makefile index 7e706d87..5917e379 100644 --- a/corelib/Makefile +++ b/corelib/Makefile @@ -2,7 +2,8 @@ # # SPDX-License-Identifier: GPL-2.0-only -lib-y += multipart_parser.o \ +lib-y += emmc_utils.o \ + multipart_parser.o \ parsing_library_libjson.o \ server_utils.o lib-$(CONFIG_DOWNLOAD) += downloader.o diff --git a/corelib/emmc.h b/corelib/emmc.h new file mode 100644 index 00000000..35f8a304 --- /dev/null +++ b/corelib/emmc.h @@ -0,0 +1,140 @@ +/* + * (C) Copyright 2024 + * Stefano Babic, stefano.babic@swupdate.org + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#pragma once + +/* From kernel linux/mmc/mmc.h */ +#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ +#define MMC_SWITCH 6 /* ac [31:0] See below R1b */ +#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ +#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ + +/* From kernel linux/mmc/core.h */ +#define MMC_RSP_NONE 0 /* no response */ +#define MMC_RSP_PRESENT (1 << 0) +#define MMC_RSP_136 (1 << 1) /* 136 bit response */ +#define MMC_RSP_CRC (1 << 2) /* expect valid crc */ +#define MMC_RSP_BUSY (1 << 3) /* card may send busy */ +#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */ + +#define MMC_CMD_AC (0 << 5) +#define MMC_CMD_ADTC (1 << 5) +#define MMC_CMD_BC (2 << 5) + +#define MMC_RSP_SPI_S1 (1 << 7) /* one status byte */ +#define MMC_RSP_SPI_BUSY (1 << 10) /* card may send busy */ + +#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1) +#define MMC_RSP_SPI_R1B (MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY) + +#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) + +/* + * EXT_CSD fields + */ +#define EXT_CSD_S_CMD_SET 504 +#define EXT_CSD_HPI_FEATURE 503 +#define EXT_CSD_BKOPS_SUPPORT 502 /* RO */ +#define EXT_CSD_SUPPORTED_MODES 493 /* RO */ +#define EXT_CSD_FFU_FEATURES 492 /* RO */ +#define EXT_CSD_FFU_ARG_3 490 /* RO */ +#define EXT_CSD_FFU_ARG_2 489 /* RO */ +#define EXT_CSD_FFU_ARG_1 488 /* RO */ +#define EXT_CSD_FFU_ARG_0 487 /* RO */ +#define EXT_CSD_CMDQ_DEPTH 307 /* RO */ +#define EXT_CSD_CMDQ_SUPPORT 308 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_3 305 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_2 304 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_1 303 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_0 302 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B 269 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A 268 /* RO */ +#define EXT_CSD_PRE_EOL_INFO 267 /* RO */ +#define EXT_CSD_FIRMWARE_VERSION 254 /* RO */ +#define EXT_CSD_CACHE_SIZE_3 252 +#define EXT_CSD_CACHE_SIZE_2 251 +#define EXT_CSD_CACHE_SIZE_1 250 +#define EXT_CSD_CACHE_SIZE_0 249 +#define EXT_CSD_SEC_FEATURE_SUPPORT 231 +#define EXT_CSD_BOOT_INFO 228 /* R/W */ +#define EXT_CSD_BOOT_MULT 226 /* RO */ +#define EXT_CSD_HC_ERASE_GRP_SIZE 224 +#define EXT_CSD_HC_WP_GRP_SIZE 221 +#define EXT_CSD_SEC_COUNT_3 215 +#define EXT_CSD_SEC_COUNT_2 214 +#define EXT_CSD_SEC_COUNT_1 213 +#define EXT_CSD_SEC_COUNT_0 212 +#define EXT_CSD_PART_SWITCH_TIME 199 +#define EXT_CSD_REV 192 +#define EXT_CSD_BOOT_CFG 179 +#define EXT_CSD_PART_CONFIG 179 +#define EXT_CSD_BOOT_BUS_CONDITIONS 177 +#define EXT_CSD_ERASE_GROUP_DEF 175 +#define EXT_CSD_BOOT_WP_STATUS 174 +#define EXT_CSD_BOOT_WP 173 +#define EXT_CSD_USER_WP 171 +#define EXT_CSD_FW_CONFIG 169 /* R/W */ +#define EXT_CSD_WR_REL_SET 167 +#define EXT_CSD_WR_REL_PARAM 166 +#define EXT_CSD_SANITIZE_START 165 +#define EXT_CSD_BKOPS_EN 163 /* R/W */ +#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ +#define EXT_CSD_PARTITIONING_SUPPORT 160 /* RO */ +#define EXT_CSD_MAX_ENH_SIZE_MULT_2 159 +#define EXT_CSD_MAX_ENH_SIZE_MULT_1 158 +#define EXT_CSD_MAX_ENH_SIZE_MULT_0 157 +#define EXT_CSD_PARTITIONS_ATTRIBUTE 156 /* R/W */ +#define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */ +#define EXT_CSD_GP_SIZE_MULT_4_2 154 +#define EXT_CSD_GP_SIZE_MULT_4_1 153 +#define EXT_CSD_GP_SIZE_MULT_4_0 152 +#define EXT_CSD_GP_SIZE_MULT_3_2 151 +#define EXT_CSD_GP_SIZE_MULT_3_1 150 +#define EXT_CSD_GP_SIZE_MULT_3_0 149 +#define EXT_CSD_GP_SIZE_MULT_2_2 148 +#define EXT_CSD_GP_SIZE_MULT_2_1 147 +#define EXT_CSD_GP_SIZE_MULT_2_0 146 +#define EXT_CSD_GP_SIZE_MULT_1_2 145 +#define EXT_CSD_GP_SIZE_MULT_1_1 144 +#define EXT_CSD_GP_SIZE_MULT_1_0 143 +#define EXT_CSD_ENH_SIZE_MULT_2 142 +#define EXT_CSD_ENH_SIZE_MULT_1 141 +#define EXT_CSD_ENH_SIZE_MULT_0 140 +#define EXT_CSD_ENH_START_ADDR_3 139 +#define EXT_CSD_ENH_START_ADDR_2 138 +#define EXT_CSD_ENH_START_ADDR_1 137 +#define EXT_CSD_ENH_START_ADDR_0 136 +#define EXT_CSD_NATIVE_SECTOR_SIZE 63 /* R */ +#define EXT_CSD_USE_NATIVE_SECTOR 62 /* R/W */ +#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ +#define EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_1 53 +#define EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 52 +#define EXT_CSD_CACHE_CTRL 33 +#define EXT_CSD_MODE_CONFIG 30 +#define EXT_CSD_MODE_OPERATION_CODES 29 /* W */ +#define EXT_CSD_FFU_STATUS 26 /* R */ +#define EXT_CSD_SECURE_REMOVAL_TYPE 16 /* R/W */ +#define EXT_CSD_CMDQ_MODE_EN 15 /* R/W */ + + +/* + * EXT_CSD field definitions + */ +#define EXT_CSD_CONFIG_SECRM_TYPE (0x30) +#define EXT_CSD_SUPPORTED_SECRM_TYPE (0x0f) +#define EXT_CSD_FFU_INSTALL (0x01) +#define EXT_CSD_FFU_MODE (0x01) +#define EXT_CSD_NORMAL_MODE (0x00) +#define EXT_CSD_FFU (1<<0) +#define EXT_CSD_UPDATE_DISABLE (1<<0) +#define EXT_CSD_HPI_SUPP (1<<0) +#define EXT_CSD_HPI_IMPL (1<<1) +#define EXT_CSD_CMD_SET_NORMAL (1<<0) + + diff --git a/corelib/emmc_utils.c b/corelib/emmc_utils.c new file mode 100644 index 00000000..f12c7867 --- /dev/null +++ b/corelib/emmc_utils.c @@ -0,0 +1,110 @@ +/* + * (C) Copyright 2024 + * Stefano Babic, stefano.babic@swupdate.org + * + * SPDX-License-Identifier: GPL-2.0-only + */ +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <errno.h> +#include <linux/version.h> +#include <sys/ioctl.h> +#include <linux/major.h> +#include <linux/mmc/ioctl.h> +#include "emmc.h" +#include "util.h" + +/* + * Code taken from mmc-utils, mmc_cmds.c + */ +static int emmc_read_extcsd(int fd, __u8 *ext_csd) +{ + int ret = 0; + struct mmc_ioc_cmd idata; + memset(&idata, 0, sizeof(idata)); + memset(ext_csd, 0, sizeof(__u8) * 512); + idata.write_flag = 0; + idata.opcode = MMC_SEND_EXT_CSD; + idata.arg = 0; + idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + idata.blksz = 512; + idata.blocks = 1; + mmc_ioc_cmd_set_data(idata, ext_csd); + + ret = ioctl(fd, MMC_IOC_CMD, &idata); + if (ret) + ERROR("eMMC ioctl return error %d", ret); + + return ret; +} + +static void fill_switch_cmd(struct mmc_ioc_cmd *cmd, __u8 index, __u8 value) +{ + cmd->opcode = MMC_SWITCH; + cmd->write_flag = 1; + cmd->arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | + (value << 8) | EXT_CSD_CMD_SET_NORMAL; + cmd->flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; +} + +static int emmc_write_extcsd_value(int fd, __u8 index, __u8 value, unsigned int timeout_ms) +{ + int ret = 0; + struct mmc_ioc_cmd idata = {}; + + fill_switch_cmd(&idata, index, value); + + /* Kernel will set cmd_timeout_ms if 0 is set */ + idata.cmd_timeout_ms = timeout_ms; + + ret = ioctl(fd, MMC_IOC_CMD, &idata); + if (ret) + ERROR("eMMC ioctl return error %d", ret); + + return ret; +} /* end of imported code */ + +int emmc_get_active_bootpart(int fd) +{ + int ret; + uint8_t extcsd[512]; + int active; + + ret = emmc_read_extcsd(fd, extcsd); + + if (ret) + return -1; + + /* + * Return partition number starting from 0 + * This corresponds to mmcblkXboot0 and mmcblkXboot1 + */ + active = ((extcsd[EXT_CSD_PART_CONFIG] & 0x38) >> 3) - 1; + + return active; +} + +int emmc_write_bootpart(int fd, int bootpart) +{ + uint8_t value; + int ret; + uint8_t extcsd[512]; + + /* + * Do not clear BOOT_ACK + */ + ret = emmc_read_extcsd(fd, extcsd); + value = extcsd[EXT_CSD_PART_CONFIG] & (1 << 6); + + bootpart = ((bootpart + 1) & 0x3) << 3; + value |= bootpart; + + ret = emmc_write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value, 0); + + return ret; +} diff --git a/handlers/Config.in b/handlers/Config.in index b01d033d..ca473646 100644 --- a/handlers/Config.in +++ b/handlers/Config.in @@ -140,6 +140,17 @@ config EMBEDDED_LUA_HANDLER_SOURCE Path to the Lua handler source code file to be embedded into the SWUpdate binary. + +config EMMC_HANDLER + bool "eMMC handler" + default n + help + This handler allows to switch the boot partitions via + the eMMC internal CSD register. One common use case is to + upgrade the bootloader, using the two hardware partitions + with a dual-copy concept. This guarantees that the upgrade + is power-cut safe. + config RAW bool "raw" default n diff --git a/handlers/Makefile b/handlers/Makefile index 24fd487c..12d4aeb8 100644 --- a/handlers/Makefile +++ b/handlers/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_BTRFS_FILESYSTEM) += btrfs_handler.o obj-$(CONFIG_COPY) += copy_handler.o obj-$(CONFIG_CFI) += flash_handler.o obj-$(CONFIG_DELTA) += delta_handler.o delta_downloader.o zchunk_range.o +obj-$(CONFIG_EMMC_HANDLER) += emmc_csd_handler.o obj-$(CONFIG_DISKFORMAT_HANDLER) += diskformat_handler.o obj-$(CONFIG_DISKPART) += diskpart_handler.o obj-$(CONFIG_UNIQUEUUID) += uniqueuuid_handler.o diff --git a/handlers/emmc_csd_handler.c b/handlers/emmc_csd_handler.c new file mode 100644 index 00000000..27bd0257 --- /dev/null +++ b/handlers/emmc_csd_handler.c @@ -0,0 +1,75 @@ +/* + * (C) Copyright 2024 + * Stefano Babic, stefano.babic@swupdate.org + * + * SPDX-License-Identifier: GPL-2.0-only + */ +/* + * This handler manages the CSD register accoriding to eMMC + * specifications. Base for this handler are the mmcutils, + * see: + * https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git + */ + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stddef.h> +#include <errno.h> +#include <linux/version.h> +#include <sys/ioctl.h> +#include <linux/mmc/ioctl.h> +#include "swupdate_image.h" +#include "handler.h" +#include "util.h" + +void emmc_csd_handler(void); + +static int emmc_boot_toggle(struct img_type *img, void *data) +{ + int active, ret; + struct script_handler_data *script_data = data; + if (script_data->scriptfn == PREINSTALL) + return 0; + + /* Open the device (partition) */ + int fdin = open(img->device, O_RDONLY); + if (fdin < 0) { + ERROR("Failed to open %s: %s", img->device, strerror(errno)); + return -ENODEV; + } + + active = emmc_get_active_bootpart(fdin); + if (active < 0) { + ERROR("Current HW boot partition cannot be retrieved"); + close(fdin); + return -1; + } + + /* + * If User Partition is activated, does nothing + * and report this to the user. + */ + if (active > 1) { + WARN("Boot device set to User area, no changes !"); + ret = 0; + } else { + active = (active == 0) ? 1 : 0; + TRACE("Setting Boot to HW Partition %d", active); + ret = emmc_write_bootpart(fdin, active); + if (ret) + ERROR("Failure writing CSD register"); + } + + close(fdin); + return ret; +} + +__attribute__((constructor)) +void emmc_csd_handler(void) +{ + register_handler("emmc_boot_toggle", emmc_boot_toggle, + SCRIPT_HANDLER | NO_DATA_HANDLER, NULL); +} diff --git a/include/util.h b/include/util.h index 490014e7..e1350633 100644 --- a/include/util.h +++ b/include/util.h @@ -272,3 +272,7 @@ int swupdate_umount(const char *dir); /* Date / Time utilities */ char *swupdate_time_iso8601(struct timeval *tv); + +/* eMMC functions */ +int emmc_write_bootpart(int fd, int bootpart); +int emmc_get_active_bootpart(int fd);
This adds support for managing hardware functions in eMMC device. First version of the handler supports toggeling of the hardware boot partitions. Signed-off-by: Stefano Babic <stefano.babic@swupdate.org> --- Changes since V1: - check for returned active partition to exclude user Boot Area - Add trace to log which becomes the next boot device corelib/Makefile | 3 +- corelib/emmc.h | 140 ++++++++++++++++++++++++++++++++++++ corelib/emmc_utils.c | 110 ++++++++++++++++++++++++++++ handlers/Config.in | 11 +++ handlers/Makefile | 1 + handlers/emmc_csd_handler.c | 75 +++++++++++++++++++ include/util.h | 4 ++ 7 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 corelib/emmc.h create mode 100644 corelib/emmc_utils.c create mode 100644 handlers/emmc_csd_handler.c -- 2.34.1