dm: Add support for all targets which requires MANUAL_RELOC
authorMichal Simek <michal.simek@xilinx.com>
Mon, 2 Feb 2015 15:31:59 +0000 (16:31 +0100)
committerSimon Glass <sjg@chromium.org>
Thu, 12 Feb 2015 22:17:29 +0000 (15:17 -0700)
Targets with CONFIG_NEEDS_MANUAL_RELOC do not use REL/RELA
relocation (mostly only GOT) where functions aray are not
updated. This patch is fixing function pointers for DM core
and serial-uclass to ensure that relocated functions are called.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Acked-by: Simon Glass <sjg@chromium.org>
drivers/core/root.c
drivers/serial/serial-uclass.c

index 73e3c7228e300e67da317098160d9c9c988446b3..9b5c6bb10cb13d1648c344970838d967494e26a4 100644 (file)
@@ -37,6 +37,65 @@ struct udevice *dm_root(void)
        return gd->dm_root;
 }
 
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+void fix_drivers(void)
+{
+       struct driver *drv =
+               ll_entry_start(struct driver, driver);
+       const int n_ents = ll_entry_count(struct driver, driver);
+       struct driver *entry;
+
+       for (entry = drv; entry != drv + n_ents; entry++) {
+               if (entry->of_match)
+                       entry->of_match = (const struct udevice_id *)
+                               ((u32)entry->of_match + gd->reloc_off);
+               if (entry->bind)
+                       entry->bind += gd->reloc_off;
+               if (entry->probe)
+                       entry->probe += gd->reloc_off;
+               if (entry->remove)
+                       entry->remove += gd->reloc_off;
+               if (entry->unbind)
+                       entry->unbind += gd->reloc_off;
+               if (entry->ofdata_to_platdata)
+                       entry->ofdata_to_platdata += gd->reloc_off;
+               if (entry->child_pre_probe)
+                       entry->child_pre_probe += gd->reloc_off;
+               if (entry->child_post_remove)
+                       entry->child_post_remove += gd->reloc_off;
+               /* OPS are fixed in every uclass post_probe function */
+               if (entry->ops)
+                       entry->ops += gd->reloc_off;
+       }
+}
+
+void fix_uclass(void)
+{
+       struct uclass_driver *uclass =
+               ll_entry_start(struct uclass_driver, uclass);
+       const int n_ents = ll_entry_count(struct uclass_driver, uclass);
+       struct uclass_driver *entry;
+
+       for (entry = uclass; entry != uclass + n_ents; entry++) {
+               if (entry->post_bind)
+                       entry->post_bind += gd->reloc_off;
+               if (entry->pre_unbind)
+                       entry->pre_unbind += gd->reloc_off;
+               if (entry->post_probe)
+                       entry->post_probe += gd->reloc_off;
+               if (entry->pre_remove)
+                       entry->pre_remove += gd->reloc_off;
+               if (entry->init)
+                       entry->init += gd->reloc_off;
+               if (entry->destroy)
+                       entry->destroy += gd->reloc_off;
+               /* FIXME maybe also need to fix these ops */
+               if (entry->ops)
+                       entry->ops += gd->reloc_off;
+       }
+}
+#endif
+
 int dm_init(void)
 {
        int ret;
@@ -47,6 +106,11 @@ int dm_init(void)
        }
        INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST);
 
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+       fix_drivers();
+       fix_uclass();
+#endif
+
        ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);
        if (ret)
                return ret;
index 9131a8f93d9a5adff359d2940ddfd6a6a05c445e..3fc7104359d3ccfd73e97ab81dd466cc6c62877a 100644 (file)
@@ -258,6 +258,22 @@ static int serial_post_probe(struct udevice *dev)
 #endif
        int ret;
 
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+       if (ops->setbrg)
+               ops->setbrg += gd->reloc_off;
+       if (ops->getc)
+               ops->getc += gd->reloc_off;
+       if (ops->putc)
+               ops->putc += gd->reloc_off;
+       if (ops->pending)
+               ops->pending += gd->reloc_off;
+       if (ops->clear)
+               ops->clear += gd->reloc_off;
+#if CONFIG_POST & CONFIG_SYS_POST_UART
+       if (ops->loop)
+               ops->loop += gd->reloc_off
+#endif
+#endif
        /* Set the baud rate */
        if (ops->setbrg) {
                ret = ops->setbrg(dev, gd->baudrate);