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..f71dd1a1
--- /dev/null
+++ b/handlers/emmc_csd_handler.c
@@ -0,0 +1,67 @@
+/*
+ * (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;
+	}
+
+	active = (active == 0) ? 1 : 0;
+
+	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);
