dm: core: Fix allocation of empty of-platdata
authorSimon Glass <sjg@chromium.org>
Thu, 4 Feb 2021 04:29:43 +0000 (21:29 -0700)
committerSimon Glass <sjg@chromium.org>
Thu, 4 Mar 2021 02:17:25 +0000 (19:17 -0700)
With of-platdata we always have a dtv struct that holds the platform data
provided by the driver_info record. However, this struct can be empty if
there are no actual devicetree properties provided.

The upshot of empty platform data is that it will end up as a zero-size
member in the BSS section, which is fine. But if the driver specifies
plat_auto then it expects the correct amount of space to be allocated.

At present this does not happen, since device_bind() assumes that the
platform-data size will always be >0. As a result we end up not
allocating the space and just use the BSS region, overwriting whatever
other contents are present.

Fix this by removing the condition that platform data be non-empty, always
allocating space if requested.

This fixes a strange bug that has been lurking since of-platdata was
implemented. It has likely never been noticed since devices normally have
at least some devicetree properties, BSS is seldom used on SPL, the dtv
structs are normally at the end of bss and the overwriting only happens
if a driver changes its platform data.

It was discovered using sandbox_spl, which exercises more features than
a normal board might, and the critical global_data variable 'gd' happened
to be at the end of BSS.

Fixes: 9fa28190091 ("dm: core: Expand platdata for of-platdata devices")
Signed-off-by: Simon Glass <sjg@chromium.org>
drivers/core/device.c

index 6251349..d1098a3 100644 (file)
@@ -92,15 +92,19 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv,
        if (auto_seq && !(uc->uc_drv->flags & DM_UC_FLAG_NO_AUTO_SEQ))
                dev->seq_ = uclass_find_next_free_seq(uc);
 
+       /* Check if we need to allocate plat */
        if (drv->plat_auto) {
                bool alloc = !plat;
 
+               /*
+                * For of-platdata, we try use the existing data, but if
+                * plat_auto is larger, we must allocate a new space
+                */
                if (CONFIG_IS_ENABLED(OF_PLATDATA)) {
-                       if (of_plat_size) {
+                       if (of_plat_size)
                                dev_or_flags(dev, DM_FLAG_OF_PLATDATA);
-                               if (of_plat_size < drv->plat_auto)
-                                       alloc = true;
-                       }
+                       if (of_plat_size < drv->plat_auto)
+                               alloc = true;
                }
                if (alloc) {
                        dev_or_flags(dev, DM_FLAG_ALLOC_PDATA);
@@ -109,6 +113,11 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv,
                                ret = -ENOMEM;
                                goto fail_alloc1;
                        }
+
+                       /*
+                        * For of-platdata, copy the old plat into the new
+                        * space
+                        */
                        if (CONFIG_IS_ENABLED(OF_PLATDATA) && plat)
                                memcpy(ptr, plat, of_plat_size);
                        dev_set_plat(dev, ptr);