dm: Add dm_remove_devices_active() for ordered device removal

This replaces dm_remove_devices_flags() calls in all boot
implementations to ensure non vital devices are consistently removed
first. All boot implementation except arch/arm/lib/bootm.c currently
just call dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL). This can result
in crashes when dependencies between devices exists. The driver model's
design document describes DM_FLAG_VITAL as "indicates that the device is
'vital' to the operation of other devices". Device removal at boot
should follow this.

Instead of adding dm_remove_devices_flags() with (DM_REMOVE_ACTIVE_ALL |
DM_REMOVE_NON_VITAL) everywhere add dm_remove_devices_active() which
does this.

Fixes a NULL pointer deref in the apple dart IOMMU driver during EFI
boot. The xhci-pci (driver which depends on the IOMMU to work) removes
its mapping on removal. This explodes when the IOMMU device was removed
first.

dm_remove_devices_flags() is kept since it is used for testing of
device_remove() calls in dm.

Signed-off-by: Janne Grunau <j@jannau.net>
This commit is contained in:
Janne Grunau
2024-11-23 22:44:05 +01:00
committed by Tom Rini
parent 544a76bac3
commit dabaa4ae32
7 changed files with 73 additions and 7 deletions

View File

@@ -999,6 +999,56 @@ static int dm_test_remove_vital(struct unit_test_state *uts)
}
DM_TEST(dm_test_remove_vital, 0);
/* Test removal of 'active' devices */
static int dm_test_remove_active(struct unit_test_state *uts)
{
struct udevice *normal, *dma, *vital, *dma_vital;
/* Skip the behaviour in test_post_probe() */
uts->skip_post_probe = 1;
ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual,
&normal));
ut_assertnonnull(normal);
ut_assertok(device_bind_by_name(uts->root, false, &driver_info_act_dma,
&dma));
ut_assertnonnull(dma);
ut_assertok(device_bind_by_name(uts->root, false,
&driver_info_vital_clk, &vital));
ut_assertnonnull(vital);
ut_assertok(device_bind_by_name(uts->root, false,
&driver_info_act_dma_vital_clk,
&dma_vital));
ut_assertnonnull(dma_vital);
/* Probe the devices */
ut_assertok(device_probe(normal));
ut_assertok(device_probe(dma));
ut_assertok(device_probe(vital));
ut_assertok(device_probe(dma_vital));
/* Check that devices are active right now */
ut_asserteq(true, device_active(normal));
ut_asserteq(true, device_active(dma));
ut_asserteq(true, device_active(vital));
ut_asserteq(true, device_active(dma_vital));
/* Remove active devices in an ordered way */
dm_remove_devices_active();
/* Check that all devices are inactive right now */
ut_asserteq(true, device_active(normal));
ut_asserteq(false, device_active(dma));
ut_asserteq(true, device_active(vital));
ut_asserteq(false, device_active(dma_vital));
return 0;
}
DM_TEST(dm_test_remove_active, 0);
static int dm_test_uclass_before_ready(struct unit_test_state *uts)
{
struct uclass *uc;