MLK-21885 lmb: Handle the overlap case for lmb reserve

lmb reserve is used to reserve some memory so that when loading images
(like kernel, dtb, initrd), images won't be loaded into the reserved memory.

The problem in current lmb is it does not handle the overlap case. When adding
a new reserved memory, if the memory region is overlap with regions already been
added in lmb, it will fail. One example is reserved memory in DTB may overlap with
u-boot relocate address. lmb reserves the u-boot relocate address firstly, so when
adding reserved memory from DTB, we will meet failure.

Actually if we handle the overlap case, we can resolve the overlap by using a max
common region for the overlap regions. So that this case won't fail.

Signed-off-by: Ye Li <ye.li@nxp.com>
Reviewed-by: Peng Fan <peng.fan@nxp.com>
(cherry picked from commit 37d86c68816dffde3dc8dcda5b9d67a195b2f9c2)
diff --git a/lib/lmb.c b/lib/lmb.c
index b3b84e4..4f86132 100644
--- a/lib/lmb.c
+++ b/lib/lmb.c
@@ -172,7 +172,7 @@
 			break;
 		} else if (lmb_addrs_overlap(base, size, rgnbase, rgnsize)) {
 			/* regions overlap */
-			return -1;
+			return -2;
 		}
 	}
 
@@ -265,13 +265,6 @@
 	return lmb_add_region(rgn, end + 1, rgnend - end);
 }
 
-long lmb_reserve(struct lmb *lmb, phys_addr_t base, phys_size_t size)
-{
-	struct lmb_region *_rgn = &(lmb->reserved);
-
-	return lmb_add_region(_rgn, base, size);
-}
-
 static long lmb_overlaps_region(struct lmb_region *rgn, phys_addr_t base,
 				phys_size_t size)
 {
@@ -287,6 +280,41 @@
 	return (i < rgn->cnt) ? i : -1;
 }
 
+long lmb_reserve(struct lmb *lmb, phys_addr_t base, phys_size_t size)
+{
+	struct lmb_region *_rgn = &(lmb->reserved);
+	long ret = lmb_add_region(_rgn, base, size);
+	long overlap_rgn;
+	phys_addr_t res_base;
+	phys_size_t res_size;
+
+	/* Handle the overlap */
+	if (ret == -2) {
+		overlap_rgn = lmb_overlaps_region(_rgn, base, size);
+		res_base = lmb->reserved.region[overlap_rgn].base;
+		res_size = lmb->reserved.region[overlap_rgn].size;
+
+		if ((base >= res_base) && ((base + size) <= (res_base + res_size))) {
+			/* new region is inside reserved region, so it is already reserved */
+			return 0;
+		} else {
+			if (base < res_base) {
+				ret = lmb_reserve(lmb, base, res_base - base);
+				if (ret < 0)
+					return ret;
+			}
+
+			if ((base + size) > (res_base + res_size)) {
+				ret = lmb_reserve(lmb, res_base + res_size, (base + size) - (res_base + res_size));
+				if (ret < 0)
+					return ret;
+			}
+		}
+	}
+
+	return ret;
+}
+
 phys_addr_t lmb_alloc(struct lmb *lmb, phys_size_t size, ulong align)
 {
 	return lmb_alloc_base(lmb, size, align, LMB_ALLOC_ANYWHERE);