dm: core: Handle global_data moving in SPL
authorSimon Glass <sjg@chromium.org>
Sun, 13 Nov 2016 21:21:58 +0000 (14:21 -0700)
committerSimon Glass <sjg@chromium.org>
Sat, 3 Dec 2016 03:53:19 +0000 (20:53 -0700)
When CONFIG_SPL_STACK_R is enabled, and spl_init() is called before
board_init_r(), spl_relocate_stack_gd() will move global_data to a new
place in memory. This affects driver model since it uses a list for the
uclasses. Unless this is updated the list will become invalid. When
looking for a non-existent uclass, such as when adding a new one, the loop
in uclass_find() may continue forever, thus causing a hang.

Add a function to correct this rather obscure bug.

Signed-off-by: Simon Glass <sjg@chromium.org>
common/spl/spl.c
drivers/core/root.c
include/dm/root.h

index 32b9f1e95cd06e153b7aab21a8b4be81364d9a7a..9bcbd09ff380516f0a5ad042f68c8b47e3670f8d 100644 (file)
@@ -513,6 +513,9 @@ ulong spl_relocate_stack_gd(void)
        ptr = CONFIG_SPL_STACK_R_ADDR - roundup(sizeof(gd_t),16);
        new_gd = (gd_t *)ptr;
        memcpy(new_gd, (void *)gd, sizeof(gd_t));
+#if CONFIG_IS_ENABLED(DM)
+       dm_fixup_for_gd_move(new_gd);
+#endif
 #if !defined(CONFIG_ARM)
        gd = new_gd;
 #endif
index 33dc9c0b2db5acd8b2a7c1bd4be1b762eab70e48..9edfc1efb661bddb6bff7ba2af604123f4657819 100644 (file)
@@ -41,6 +41,13 @@ struct udevice *dm_root(void)
        return gd->dm_root;
 }
 
+void dm_fixup_for_gd_move(struct global_data *new_gd)
+{
+       /* The sentinel node has moved, so update things that point to it */
+       new_gd->uclass_root.next->prev = &new_gd->uclass_root;
+       new_gd->uclass_root.prev->next = &new_gd->uclass_root;
+}
+
 fdt_addr_t dm_get_translation_offset(void)
 {
        struct udevice *root = dm_root();
index c7f0c1d5ca302acd8bd89887b5bc8c53119af4dd..3cf730dcee1cb50e9aa5ba3376ae48fc97603cdb 100644 (file)
@@ -21,6 +21,16 @@ struct udevice;
  */
 struct udevice *dm_root(void);
 
+struct global_data;
+/**
+ * dm_fixup_for_gd_move() - Handle global_data moving to a new place
+ *
+ * The uclass list is part of global_data. Due to the way lists work, moving
+ * the list will cause it to become invalid. This function fixes that up so
+ * that the uclass list will work correctly.
+ */
+void dm_fixup_for_gd_move(struct global_data *new_gd);
+
 /**
  * dm_scan_platdata() - Scan all platform data and bind drivers
  *