]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
dm: Add dm_remove_devices_active() for ordered device removal
authorJanne Grunau <j@jannau.net>
Sat, 23 Nov 2024 21:44:05 +0000 (22:44 +0100)
committerTom Rini <trini@konsulko.com>
Sun, 24 Nov 2024 21:41:28 +0000 (15:41 -0600)
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>
arch/arm/lib/bootm.c
arch/riscv/lib/bootm.c
arch/x86/lib/bootm.c
drivers/core/root.c
include/dm/root.h
lib/efi_loader/efi_boottime.c
test/dm/core.c

index 192c120a7d2ebe8a2c57f92b424c1699fd8e0e35..974cbfe8400eafb180e99039b4f1a61d52c898b8 100644 (file)
@@ -73,11 +73,10 @@ static void announce_and_cleanup(int fake)
         * Call remove function of all devices with a removal flag set.
         * This may be useful for last-stage operations, like cancelling
         * of DMA operation or releasing device internal buffers.
+        * dm_remove_devices_active() ensures that vital devices are removed in
+        * a second round.
         */
-       dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL | DM_REMOVE_NON_VITAL);
-
-       /* Remove all active vital devices next */
-       dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+       dm_remove_devices_active();
 
        cleanup_before_linux();
 }
index 82502972eec5148b8a36b45eef412a3ec2adb48c..76c610bcee03034cb34d850dbbdbe83cfd75cbd3 100644 (file)
@@ -57,7 +57,7 @@ static void announce_and_cleanup(int fake)
         * This may be useful for last-stage operations, like cancelling
         * of DMA operation or releasing device internal buffers.
         */
-       dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+       dm_remove_devices_active();
 
        cleanup_before_linux();
 }
index 55f581836dfa6ea76f2b6ba63709544757d3bd52..0f79a5d54959fd50ffaadfefb3f7fedb841699c9 100644 (file)
@@ -49,7 +49,7 @@ void bootm_announce_and_cleanup(void)
         * This may be useful for last-stage operations, like cancelling
         * of DMA operation or releasing device internal buffers.
         */
-       dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+       dm_remove_devices_active();
 }
 
 #if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL)
index 7a714f5478a9f3953b33de0e0ca78fe95db569f3..c7fb58285ca21ecb8c659ec9f3c203819056abec 100644 (file)
@@ -147,6 +147,13 @@ int dm_remove_devices_flags(uint flags)
 
        return 0;
 }
+
+void dm_remove_devices_active(void)
+{
+       /* Remove non-vital devices first */
+       device_remove(dm_root(), DM_REMOVE_ACTIVE_ALL | DM_REMOVE_NON_VITAL);
+       device_remove(dm_root(), DM_REMOVE_ACTIVE_ALL);
+}
 #endif
 
 int dm_scan_plat(bool pre_reloc_only)
index b2f30a842f515d70982ec8af7599a86d12002176..5651b868c8b88267d0b7fd7564d53fb519140b76 100644 (file)
@@ -167,8 +167,18 @@ int dm_uninit(void);
  * Return: 0 if OK, -ve on error
  */
 int dm_remove_devices_flags(uint flags);
+
+/**
+ * dm_remove_devices_active - Call remove function of all active drivers heeding
+ *                            device dependencies as far as know, i.e. removing
+ *                            devices marked with DM_FLAG_VITAL last.
+ *
+ * All active devices will be removed
+ */
+void dm_remove_devices_active(void);
 #else
 static inline int dm_remove_devices_flags(uint flags) { return 0; }
+static inline void dm_remove_devices_active(void) { }
 #endif
 
 /**
index 4f52284b4c653c252b0ed6c0c87da8901448d4b4..080e7f78ae37a45d1e83566806b1fe2582160f2c 100644 (file)
@@ -2234,7 +2234,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
                if (IS_ENABLED(CONFIG_USB_DEVICE))
                        udc_disconnect();
                board_quiesce_devices();
-               dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+               dm_remove_devices_active();
        }
 
        /* Patch out unsupported runtime function */
index 7371d3ff42695b4eabaf8ff4b31e4197b3a0c8f8..c59ffc6f61126cad3f7b22b5bd66a5126824be8e 100644 (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;