Skip to content

Link eMMC boot slots with parent slots - #1750

Open
dwntri wants to merge 9 commits into
rauc:masterfrom
dwntri:feat/emmc-boot-linked
Open

dwntri wants to merge 9 commits into
rauc:masterfrom
dwntri:feat/emmc-boot-linked

Conversation

@dwntri

@dwntri dwntri commented Jun 30, 2025 •

Copy link
Copy Markdown

This feature can be used if it is not possible to avoid incompatibilities between
the bootloader (and it's components) and the operating system
During each update RAUC will ensure that the active boot partition
matches the active unsuccessful slots.
This feature will need to be supported by the bootloader, as RAUC will not write the ext_csd registers itself as in the already existing eMMC boot method, but will rely on the bootloader to sort handle it.

Depends On #1752

@dwntri dwntri added the enhancement Adds new functionality or enhanced handling to RAUC label Jun 30, 2025
Comment thread src/update_handler.c Fixed
@codecov

codecov Bot commented Jun 30, 2025 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.53488% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.75%. Comparing base (d29c7bc) to head (5e7ab43).
⚠️ Report is 167 commits behind head on master.

Files with missing lines Patch % Lines
src/update_handler.c 80.39% 10 Missing ⚠️
src/emmc.c 69.23% 8 Missing ⚠️
src/config_file.c 87.87% 4 Missing ⚠️
test/utils.c 81.25% 3 Missing ⚠️
src/utils.c 85.71% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1750      +/-   ##
==========================================
+ Coverage   84.64%   84.75%   +0.10%     
==========================================
  Files          76       76              
  Lines       22752    23000     +248     
==========================================
+ Hits        19258    19493     +235     
- Misses       3494     3507      +13     
Flag Coverage Δ
service=false 80.93% <89.53%> (+0.15%) ⬆️
service=true 84.73% <89.53%> (+0.10%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dwntri
dwntri force-pushed the feat/emmc-boot-linked branch 8 times, most recently from 4c0aeaf to b85184b Compare July 7, 2025 06:44
@dwntri
dwntri marked this pull request as ready for review July 7, 2025 06:45
@dwntri
dwntri requested a review from ejoerns July 7, 2025 06:45
@dwntri
dwntri force-pushed the feat/emmc-boot-linked branch from b85184b to 5dbb004 Compare July 10, 2025 09:17
@dwntri
dwntri force-pushed the feat/emmc-boot-linked branch from 5dbb004 to 0f8fbc1 Compare July 29, 2025 09:25
@dwntri

dwntri commented Jul 29, 2025 •

Copy link
Copy Markdown
Author

Rebased onto current master, as the dependant #1752 is merged.
So this can be reviewed / merged now

@jluebbe
jluebbe requested review from jluebbe and removed request for ejoerns July 29, 2025 10:04
@jluebbe jluebbe assigned jluebbe and unassigned dwntri Jul 29, 2025
@dwntri
dwntri requested a review from ejoerns August 14, 2025 06:38
@dwntri
dwntri force-pushed the feat/emmc-boot-linked branch from 0f8fbc1 to 999d390 Compare August 14, 2025 06:47
@dwntri

dwntri commented Aug 14, 2025 •

Copy link
Copy Markdown
Author

Just rebased onto current master

@dwntri
dwntri force-pushed the feat/emmc-boot-linked branch from 999d390 to 5a0eeb2 Compare August 24, 2025 10:02
@dwntri

dwntri commented Aug 24, 2025

Copy link
Copy Markdown
Author

Got rid of unneeded function call and function parameter. And improved some code comments.
Just left the fixup commit for now to make changes more ovbious. Will squash this before merge

Comment thread src/update_handler.c Fixed
@dwntri
dwntri force-pushed the feat/emmc-boot-linked branch 3 times, most recently from 0d1b2ba to 3311795 Compare August 24, 2025 11:38
@dwntri dwntri removed the needs-rebase Must be rebased on current master (for testing or conflict resolution) label Oct 21, 2025
@dwntri

dwntri commented Oct 21, 2025

Copy link
Copy Markdown
Author

rebased and refactored to match latest changes in master branch

@ejoerns
ejoerns force-pushed the feat/emmc-boot-linked branch from 2a5394a to a351d18 Compare October 21, 2025 10:37
Comment thread test/config_file.c Outdated
@ejoerns

ejoerns commented Oct 21, 2025

Copy link
Copy Markdown
Member

Just made some minor adjustments to comments, commit messages, and a test case.

Lars Schmidt added 3 commits February 18, 2026 22:58
This function takes a partition of an eMMC and applies a regex to it
in order to find out the base dev. This makes it easier to find out
if two partitions are targeting the same eMMC.

Signed-off-by: Lars Schmidt <[email protected]>
In cases where the file descriptor is just opened to get
the size of the device, it is useful to have this encapsulated
to make usage easier and reduce code duplication.

Signed-off-by: Lars Schmidt <[email protected]>
Introduced the `r_slot_get_all_of_type()` function to retrieve all slots of a specified type.
This provides a convenient way to filter slots by type, and will make future implementations
easier.

Signed-off-by: Lars Schmidt <[email protected]>
@ejoerns
ejoerns force-pushed the feat/emmc-boot-linked branch from a351d18 to 44a63a9 Compare February 18, 2026 22:01
@ejoerns

ejoerns commented Feb 18, 2026

Copy link
Copy Markdown
Member

rebased onto master to resolve conflicts.

@ejoerns
ejoerns force-pushed the feat/emmc-boot-linked branch from 44a63a9 to 2dc9558 Compare February 18, 2026 22:08
Lars Schmidt added 6 commits February 18, 2026 23:51
Move toggling and checking of active partition to its own function .
The function takes the currently active boot partition from its parameters
and changes the active boot partition to the currently inactive boot partition.
It also validates if the activation was successful, in order to check for a known
ioctl ext_csd bug, see [1].

This encapsulates already existing code into a function and makes it easier to reuse
and avoids duplication in the future.

[1]: https://rauc.readthedocs.io/en/latest/advanced.html#update-bootloader-in-emmc-boot-partitions

Signed-off-by: Lars Schmidt <[email protected]>
Add validation function check_emmc_linked_slots for emmc-boot-linked
slots to ensure unique boot devices, required parent slots,
and same base device for linked pairs. This way a faulty configuration
gets detected early and the user is notified accordingly.

Signed-off-by: Lars Schmidt <[email protected]>
This adds an update handler for the 'emmc-boot-linked' slot type that
just copies the image to the respective emmc boot partition (without
touching the boot partition bit in extCSD).

The handler also handles migration from a different boot slot
configuration, like boot-emmc: If the target slot for the bootloader
update is the same as the currently used one, RAUC will backup the
current bootloader to the inactive boot partition first.

Signed-off-by: Lars Schmidt <[email protected]>
The emmc tests will get extended. As preparation the emmc tests
are moved to their own file. This separates the tests more clearly and
makes it easier to distinguish them from other tests. Especially the emmc
tests can not be executed in every environment as they need need
RAUC_TEST_EMMC in the os environment.

Signed-off-by: Lars Schmidt <[email protected]>
Add test coverage for emmc-boot-linked handler including migration,
error conditions, and multi-slot configurations.

Signed-off-by: Lars Schmidt <[email protected]>
Add description about the new slot type and describe how the migration
to this slot type is done. It adds the possibility to group single eMMC boot
slots with rootfs and other slots in order to provide a fallback
mechanism for systems on eMMC.

Signed-off-by: Lars Schmidt <[email protected]>
@ejoerns
ejoerns force-pushed the feat/emmc-boot-linked branch from 2dc9558 to 5e7ab43 Compare February 18, 2026 22:51
Comment thread src/emmc.c
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);

/* Pattern to find valid MMC/eMMC base device */
*base_device = r_regex_match_simple("(/dev/mmcblk[0-9]+)", device_path);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex should be anchored at the start:

Suggested change
*base_device = r_regex_match_simple("(/dev/mmcblk[0-9]+)", device_path);
*base_device = r_regex_match_simple("^(/dev/mmcblk[0-9]+)", device_path);

Comment thread docs/advanced.rst
itself (like the :ref:`default mechanism <sec-emmc-boot>` would)
but will leave that to the bootloader.
RAUC simply maintains the boot state, which the bootloader then uses
to activate the correct boot partition on the eMMC, perform a reset,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
to activate the correct boot partition on the eMMC, perform a reset,
to activate the correct boot partition on the eMMC, perform a reset,

Comment thread docs/advanced.rst
RAUC simply maintains the boot state, which the bootloader then uses
to activate the correct boot partition on the eMMC, perform a reset,
and start the OS with its matching bootloader.
This reduces the effort required to maintain bootloader and OS consistency.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This reduces the effort required to maintain bootloader and OS consistency.
This can reduce the effort required to maintain bootloader and OS compatibility,
as each OS version is only started by a specific bootloader version.
On the other hand, protecting against bootloader downgrades becomes more complex.

Comment thread docs/advanced.rst

When migrating to this mechanism, the currently used eMMC boot partition and system slot
may not yet match those which are linked.
This case is handled by RAUC in order to avoid inconsistent results and unbootable systems.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This case is handled by RAUC in order to avoid inconsistent results and unbootable systems.
In order to avoid inconsistent results and unbootable systems, this case is handled by RAUC.

Comment thread docs/advanced.rst
Comment on lines +1304 to +1305
During the update process, RAUC compares the bootloader update target with
the currently activated bootloader.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
During the update process, RAUC compares the bootloader update target with
the currently activated bootloader.
During the update process, RAUC checks if the bootloader update target is
the currently activated bootloader partition.

Comment thread src/update_handler.c
Comment on lines 2398 to +2399
}
static gboolean emmc_boot_linked_migration_helper(RaucImage *image, RaucSlot *dest_slot, GError **error)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add empty line. Also, the image parameter is unused.

Comment thread src/update_handler.c
Comment on lines +2437 to +2442
/* Check if the target slot is the currently active eMMC boot partition */
g_message("Active eMMC boot partition for %s: boot%d", real_dest, active_partition);
g_autofree gchar *boot_suffix = g_strdup_printf("boot%d", active_partition);
if (!g_str_has_suffix(dest_slot->device, boot_suffix)) {
return TRUE;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is wrong. This checks if the the target slot is not the active one and returns successfully in that case, as no copying is needed.

Comment thread test/conftest.py
Comment on lines +697 to +699
self.base_dev = os.environ["RAUC_TEST_EMMC"]
self.boot0 = f"{self.base_dev}boot0"
self.boot1 = f"{self.base_dev}boot1"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests could be simplified a lot by using Path objects, but that could be a follow-up PR.

Comment thread test/test_emmc.py
Comment on lines +271 to +274
out, err, exitcode = run(f"{system.prefix} write-slot bootloader.0 install-content/rootfs.img")

# Restore permissions
os.chmod(force_ro_path, 0o644)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure to restore permissions by using try:/finally:.

Comment thread include/emmc.h
Comment on lines +108 to +110
* This function sets the specified boot partition as active for the given
* eMMC device. It ensures that the specified partition is correctly set
* as the active boot partition, allowing the system to boot from it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is wrong. The function sets the other partition as active, which is leads to confusing usage of this function. Perhaps a better name would be r_emmc_activate_bootpart and passing the intended new state.

@jluebbe jluebbe assigned ejoerns and unassigned jluebbe Mar 16, 2026
@ejoerns

ejoerns commented Apr 24, 2026

Copy link
Copy Markdown
Member

Update of the PR with what I had in the queue from the last rework iteration.

range-diff

 1:  928040c5 !  1:  b7781fa2 src/emmc: add function to get base dev of partitioned eMMC
    @@ Commit message
         if two partitions are targeting the same eMMC.
     
         Signed-off-by: Lars Schmidt <[email protected]>
    +    Signed-off-by: Enrico Jörns <[email protected]>
     
      ## include/emmc.h ##
     @@ include/emmc.h: gboolean r_emmc_force_part_ro(const gchar *device, GError **error);
    @@ include/emmc.h: gboolean r_emmc_force_part_ro(const gchar *device, GError **erro
     + * Extracts the base device from the given eMMC boot partition.
     + *
     + * @param device_path eMMC boot partition /dev path (/dev/mmcblkXbootY)
    -+ * @param base_device return location for the output base device (/dev/mmcblkX)
     + * @param error return location for a GError, or NULL
     + *
    -+ * @return True if succeeded, False if failed
    ++ * @return base device if succeeded, NULL if failed
     + */
    -+gboolean r_emmc_extract_base_dev(const gchar *device_path, gchar **base_device, GError **error)
    ++gchar* r_emmc_extract_base_dev(const gchar *device_path, GError **error)
     +G_GNUC_WARN_UNUSED_RESULT;
     
      ## src/emmc.c ##
    @@ src/emmc.c: gboolean r_emmc_force_part_rw(const gchar *device, GError **error)
      	return ret;
      }
     +
    -+gboolean r_emmc_extract_base_dev(const gchar *device_path, gchar **base_device, GError **error)
    ++gchar* r_emmc_extract_base_dev(const gchar *device_path, GError **error)
     +{
    -+	g_return_val_if_fail(device_path != NULL, FALSE);
    -+	g_return_val_if_fail(base_device != NULL, FALSE);
    -+	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
    ++	g_return_val_if_fail(device_path != NULL, NULL);
    ++	g_return_val_if_fail(error == NULL || *error == NULL, NULL);
     +
     +	/* Pattern to find valid MMC/eMMC base device */
    -+	*base_device = r_regex_match_simple("(/dev/mmcblk[0-9]+)", device_path);
    ++	gchar *base_device = r_regex_match_simple("^(/dev/mmcblk[0-9]+)", device_path);
     +
    -+	if (*base_device == NULL) {
    ++	if (base_device == NULL) {
     +		g_set_error(error, R_EMMC_ERROR, R_EMMC_ERROR_FAILED,
     +				"Device path '%s' does not contain valid MMC device pattern", device_path);
    -+		return FALSE;
    ++		return NULL;
     +	}
     +
    -+	return TRUE;
    ++	return base_device;
     +}
 2:  78d7e90e =  2:  2569e863 src/utils: add function to get size from dev
 3:  63006b63 =  3:  8a9de392 src/slot: add function to get all slots of a type
 4:  e58556b0 !  4:  72dbb69e src/emmc: refactor toggle the active boot partition of an eMMC
    @@ Metadata
     Author: Lars Schmidt <[email protected]>
     
      ## Commit message ##
    -    src/emmc: refactor toggle the active boot partition of an eMMC
    +    src/emmc: refactor eMMC boot partition toggling
     
    -    Move toggling and checking of active partition to its own function .
    -    The function takes the currently active boot partition from its parameters
    -    and changes the active boot partition to the currently inactive boot partition.
    -    It also validates if the activation was successful, in order to check for a known
    -    ioctl ext_csd bug, see [1].
    +    Move boot partition activation and validation into a helper function.
     
         This encapsulates already existing code into a function and makes it easier to reuse
         and avoids duplication in the future.
     
    -    [1]: https://rauc.readthedocs.io/en/latest/advanced.html#update-bootloader-in-emmc-boot-partitions
    -
         Signed-off-by: Lars Schmidt <[email protected]>
     
      ## include/emmc.h ##
     @@ include/emmc.h: G_GNUC_WARN_UNUSED_RESULT;
       */
    - gboolean r_emmc_extract_base_dev(const gchar *device_path, gchar **base_device, GError **error)
    + gchar* r_emmc_extract_base_dev(const gchar *device_path, GError **error)
      G_GNUC_WARN_UNUSED_RESULT;
     +
     +/**
    -+ * Toggles the active eMMC boot partition.
    ++ * Active the specified eMMC boot partition.
     + *
     + * This function sets the specified boot partition as active for the given
     + * eMMC device. It ensures that the specified partition is correctly set
     + * as the active boot partition, allowing the system to boot from it.
     + *
     + * @param device eMMC device path
    -+ * @param active_partition the currently active partition index (0 or 1)
    ++ * @param activate_partition the partition index to activate (0 or 1)
     + * @param error return location for a GError, or NULL
     + *
     + * @return True if the operation succeeded, False if it failed
     + */
    -+gboolean r_emmc_toggle_active_bootpart(const gchar *device, gint active_partition, GError **error)
    ++gboolean r_emmc_activate_bootpart(const gchar *device, gint activate_partition, GError **error)
     +G_GNUC_WARN_UNUSED_RESULT;
     
      ## src/emmc.c ##
    -@@ src/emmc.c: gboolean r_emmc_extract_base_dev(const gchar *device_path, gchar **base_device,
    +@@ src/emmc.c: gchar* r_emmc_extract_base_dev(const gchar *device_path, GError **error)
      
    - 	return TRUE;
    + 	return base_device;
      }
     +
    -+gboolean r_emmc_toggle_active_bootpart(const gchar *device, gint active_partition, GError **error)
    ++gboolean r_emmc_activate_bootpart(const gchar *device, gint activate_partition, GError **error)
     +{
     +	GError *ierror = NULL;
     +
     +	g_return_val_if_fail(device != NULL, FALSE);
    -+	g_return_val_if_fail(active_partition < EMMC_BOOT_PARTITIONS, FALSE);
    ++	g_return_val_if_fail(activate_partition >= 0 && activate_partition < EMMC_BOOT_PARTITIONS, FALSE);
     +	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
     +
    -+	/* toggle active boot partition in ext_csd register; do this explicitly on
    -+	 * determined boot partition to force the kernel to switch to the partition;
    -+	 * for simplicity reasons: in case the user partition is active use
    -+	 * mmcblkXboot1, in case no partition is active use mmcblkXboot0
    -+	 */
    -+	g_debug("Toggling active eMMC boot partition %sboot%d", device, active_partition);
    -+	if (!r_emmc_write_bootpart(device, INACTIVE_BOOT_PARTITION(active_partition), &ierror)) {
    ++	if (!r_emmc_write_bootpart(device, activate_partition, &ierror)) {
     +		g_propagate_error(error, ierror);
     +		return FALSE;
     +	}
    @@ src/emmc.c: gboolean r_emmc_extract_base_dev(const gchar *device_path, gchar **b
     +		return FALSE;
     +	}
     +
    -+	if (active_partition == part_active_after) {
    ++	if (activate_partition != part_active_after) {
     +		g_set_error(error, R_UPDATE_ERROR, R_UPDATE_ERROR_FAILED,
    -+				"Toggling the boot partition failed! Your kernel is most-likely affected by the ioctl ext_csd bug: see https://rauc.readthedocs.io/en/latest/advanced.html#update-bootloader-in-emmc-boot-partitions");
    ++				"Activating the boot partition failed! Your kernel is most-likely affected by the ioctl ext_csd bug: see https://rauc.readthedocs.io/en/latest/advanced.html#update-bootloader-in-emmc-boot-partitions");
     +		return FALSE;
     +	}
     +
    @@ src/update_handler.c: static gboolean img_to_boot_emmc_handler(RaucImage *image,
      	g_autoptr(RaucSlot) part_slot = NULL;
      	g_autoptr(GHashTable) vars = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
     @@ src/update_handler.c: static gboolean img_to_boot_emmc_handler(RaucImage *image, RaucSlot *dest_slot,
    - 		return FALSE;
    - 	}
    - 
    --	/* toggle active boot partition in ext_csd register; do this explicitly on
    --	 * determined boot partition to force the kernel to switch to the partition;
    --	 * for simplicity reasons: in case the user partition is active use
    --	 * mmcblkXboot1, in case no partition is active use mmcblkXboot0
    --	 */
    --	g_debug("Toggling active eMMC boot partition %sboot%d -> %s", realdev, part_active,
    --			part_slot->device);
    + 	 */
    + 	g_debug("Toggling active eMMC boot partition %sboot%d -> %s", realdev, part_active,
    + 			part_slot->device);
     -	res = r_emmc_write_bootpart(
     -			part_slot->device,
     -			INACTIVE_BOOT_PARTITION(part_active),
     -			&ierror);
     -	if (!res) {
    -+	if (!r_emmc_toggle_active_bootpart(realdev, part_active, &ierror)) {
    - 		g_propagate_error(error, ierror);
    - 		return FALSE;
    - 	}
    - 
    +-		g_propagate_error(error, ierror);
    +-		return FALSE;
    +-	}
    +-
     -	/* sanity check: read active boot partition from ext_csd
     -	 *
     -	 * Read explicitly from root device (this forces another kernel
    @@ src/update_handler.c: static gboolean img_to_boot_emmc_handler(RaucImage *image,
     -	 */
     -	res = r_emmc_read_bootpart(realdev, &part_active_after, &ierror);
     -	if (!res) {
    --		g_propagate_error(error, ierror);
    --		return FALSE;
    --	}
    --
    ++	if (!r_emmc_activate_bootpart(realdev, INACTIVE_BOOT_PARTITION(part_active), &ierror)) {
    + 		g_propagate_error(error, ierror);
    + 		return FALSE;
    + 	}
    + 
     -	if (part_active == part_active_after) {
     -		g_set_error(error, R_UPDATE_ERROR, R_UPDATE_ERROR_FAILED,
     -				"Toggling the boot partition failed! Your kernel is most-likely affected by the ioctl ext_csd bug: see https://rauc.readthedocs.io/en/latest/advanced.html#update-bootloader-in-emmc-boot-partitions");
 5:  dc5ce6e0 !  5:  003b8fcc src/config_file: Add emmc-boot-linked to config
    @@ Commit message
         gets detected early and the user is notified accordingly.
     
         Signed-off-by: Lars Schmidt <[email protected]>
    +    Signed-off-by: Enrico Jörns <[email protected]>
     
      ## src/config_file.c ##
     @@
    @@ src/config_file.c: static GHashTable *parse_artifact_repos(const char *filename,
     +
     +static gboolean check_emmc_linked_slots(RaucConfig *config, GError **error)
     +{
    -+	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
     +	g_return_val_if_fail(config != NULL, FALSE);
    ++	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
     +
     +	GError *ierror = NULL;
     +
    @@ src/config_file.c: static GHashTable *parse_artifact_repos(const char *filename,
     +	}
     +
     +	/* And all slots need to be on same base device */
    -+	g_autofree gchar *base_dev1 = NULL;
    -+	g_autofree gchar *base_dev2 = NULL;
    -+	if (!r_emmc_extract_base_dev(slot1->device, &base_dev1, &ierror) ||
    -+	    !r_emmc_extract_base_dev(slot2->device, &base_dev2, &ierror)) {
    ++	g_autofree gchar *base_dev1 = r_emmc_extract_base_dev(slot1->device, &ierror);
    ++	if (base_dev1 == NULL) {
    ++		g_propagate_error(error, ierror);
    ++		return FALSE;
    ++	}
    ++	g_autofree gchar *base_dev2 = r_emmc_extract_base_dev(slot2->device, &ierror);
    ++	if (base_dev2 == NULL) {
     +		g_propagate_error(error, ierror);
     +		return FALSE;
     +	}
 6:  877c98fd !  6:  7cf79194 src/update_handler: add handler for emmc-boot-linked
    @@ src/update_handler.c: static gboolean img_to_boot_emmc_handler(RaucImage *image,
      
      	return TRUE;
      }
    -+static gboolean emmc_boot_linked_migration_helper(RaucImage *image, RaucSlot *dest_slot, GError **error)
    ++
    ++static gboolean emmc_boot_linked_migration_helper(RaucSlot *dest_slot, GError **error)
     +{
    -+	g_return_val_if_fail(image, FALSE);
     +	g_return_val_if_fail(dest_slot, FALSE);
     +	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
     +
    @@ src/update_handler.c: static gboolean img_to_boot_emmc_handler(RaucImage *image,
     +	/* Get the base device for out destination partition so we can check which boot partition
     +	 * is actually activated */
     +	GError *ierror = NULL;
    -+	g_autofree gchar *base_device = NULL;
    -+	if (!r_emmc_extract_base_dev(real_dest, &base_device, &ierror)) {
    ++	g_autofree gchar *base_device = r_emmc_extract_base_dev(real_dest, &ierror);
    ++	if (base_device == NULL) {
     +		g_propagate_error(error, ierror);
     +		return FALSE;
     +	}
    @@ src/update_handler.c: static gboolean img_to_boot_emmc_handler(RaucImage *image,
     +		return FALSE;
     +	}
     +
    -+	/* Check if the target slot is the currently active eMMC boot partition */
    ++	/* Skip migration if the target slot is not the currently active eMMC boot partition */
     +	g_message("Active eMMC boot partition for %s: boot%d", real_dest, active_partition);
     +	g_autofree gchar *boot_suffix = g_strdup_printf("boot%d", active_partition);
     +	if (!g_str_has_suffix(dest_slot->device, boot_suffix)) {
    @@ src/update_handler.c: static gboolean img_to_boot_emmc_handler(RaucImage *image,
     +		return FALSE;
     +	}
     +
    -+	if (!r_emmc_toggle_active_bootpart(base_device, active_partition, &ierror)) {
    ++	g_debug("Toggling active eMMC boot partition %sboot%d", base_device, INACTIVE_BOOT_PARTITION(active_partition));
    ++	if (!r_emmc_activate_bootpart(base_device, INACTIVE_BOOT_PARTITION(active_partition), &ierror)) {
     +		g_propagate_error(error, ierror);
     +		return FALSE;
     +	}
    @@ src/update_handler.c: static gboolean img_to_boot_emmc_handler(RaucImage *image,
     +	 * the boot partitions might not yet be aligned as expected from the
     +	 * new linked config. So we have to check how they are aligned and
     +	 * ensure the target boot partition is not the currently activated one. */
    -+	if (!emmc_boot_linked_migration_helper(image, dest_slot, &ierror)) {
    ++	if (!emmc_boot_linked_migration_helper(dest_slot, &ierror)) {
     +		g_propagate_error(error, ierror);
     +		return FALSE;
     +	}
 7:  364a1314 =  7:  3f6eba63 test/emmc: separate emmc tests from write_slots
 8:  f0a710f8 !  8:  0e466b33 test/emmc: Add tests for emmc-boot-linked slot types
    @@ test/conftest.py: class System:
     +    def prepare_emmc_boot_linked_config(self):
     +        device_emmc = EMMC()
     +        self.config["slot.rootfs.0"] = {
    -+            "device": f"{device_emmc.base_dev}.root0",
    ++            "device": f"{device_emmc.base_dev}p1",
     +            "type": "ext4",
     +        }
     +
    @@ test/conftest.py: class System:
     +        }
     +
     +        self.config["slot.rootfs.1"] = {
    -+            "device": f"{device_emmc.base_dev}.root1",
    ++            "device": f"{device_emmc.base_dev}p2",
     +            "type": "ext4",
     +        }
     +
    @@ test/test_emmc.py: def test_write_boot_emmc_size_limit_too_large(system):
     +
     +    out, err, exitcode = run(f"{system.prefix} write-slot bootloader.0 install-content/rootfs.img")
     +    assert exitcode == 1
    -+    assert "No data directory or status file set, falling back to per-slot status."
    -+    assert "Consider setting 'data-directory=<path>' or 'statusfile=<path>/per-slot' explicitly."
    -+    assert "Using per-slot statusfile. System status information not supported!"
    ++    assert "No data directory or status file set, falling back to per-slot status." in err
    ++    assert "Consider setting 'data-directory=<path>' or 'statusfile=<path>/per-slot' explicitly." in err
    ++    assert "Using per-slot statusfile. System status information not supported!" in err
     +    assert (
     +        f"emmc-boot-linked slots 'bootloader.1' and 'bootloader.0' cannot use the same boot device '{emmc.boot0}'"
     +        in err
    @@ test/test_emmc.py: def test_write_boot_emmc_size_limit_too_large(system):
     +    # Make the force_ro file read-only to simulate permission failure
     +    os.chmod(force_ro_path, 0o444)
     +
    -+    out, err, exitcode = run(f"{system.prefix} write-slot bootloader.0 install-content/rootfs.img")
    -+
    -+    # Restore permissions
    -+    os.chmod(force_ro_path, 0o644)
    ++    try:
    ++        out, err, exitcode = run(f"{system.prefix} write-slot bootloader.0 install-content/rootfs.img")
    ++    finally:
    ++        # Restore permissions
    ++        os.chmod(force_ro_path, 0o644)
     +
     +    assert exitcode != 0
     +    assert (
 9:  5e7ab431 !  9:  12d280f7 docs/advanced: describe new emmc-boot-linked slot type
    @@ docs/advanced.rst: A ``system.conf`` could look like this:
     +itself (like the :ref:`default mechanism <sec-emmc-boot>` would)
     +but will leave that to the bootloader.
     +RAUC simply maintains the boot state, which the bootloader then uses
    -+to activate the correct boot partition on the eMMC,  perform a reset,
    ++to activate the correct boot partition on the eMMC, perform a reset,
     +and start the OS with its matching bootloader.
    -+This reduces the effort required to maintain bootloader and OS consistency.
    ++This can reduce the effort required to maintain bootloader and OS compatibility.
     +
     +When migrating to this mechanism, the currently used eMMC boot partition and system slot
     +may not yet match those which are linked.
    -+This case is handled by RAUC in order to avoid inconsistent results and unbootable systems.
    -+During the update process, RAUC compares the bootloader update target with
    -+the currently activated bootloader.
    -+If the bootloader currently active is also the one intended for the bootloader target slot,
    -+it will be copied to the slot that is inactive, which is then activated by RAUC.
    -+The update procedure will then continue.
    ++In order to avoid inconsistent results and unbootable systems, this case is handled by RAUC.
    ++During the update process, RAUC checks if the bootloader update target is
    ++the currently activated bootloader partition.
    ++If the currently active bootloader partition is the intended update target slot,
    ++it's contents will first be copied to the inactive slot, which is then activated by RAUC.
    ++The update procedure will then continue as normal.
     +This ensures that there is still a working fallback in case of an unsuccessful update.
     +
     +.. code-block:: cfg
    @@ docs/reference.rst: separator.
        ``boot-raw-fallback`` for atomically updatable bootloader slots.
     +  The type ``emmc-boot-linked`` can be used to group two eMMC boot slots
     +  on the same device with other partitions, when incompatibilities
    -+  during updates cannot be resolved differently.
    ++  during updates cannot be resolved in another way.
        See table :ref:`sec-slot-type` for a more detailed list of these different types.
        Defaults to ``raw`` if none given.

@ejoerns ejoerns modified the milestones: Release v1.16, Release v1.17 Jul 16, 2026
@jluebbe jluebbe modified the milestone: Release v1.17 Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Adds new functionality or enhanced handling to RAUC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants