#include <linux/slab.h>
 #include <linux/bootmem.h>
 #include <linux/elf.h>
+#include <linux/memblock.h>
 #include <asm/os_info.h>
 #include <asm/elf.h>
 #include <asm/ipl.h>
 #define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
 #define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y))))
 
+static struct memblock_region oldmem_region;
+
+static struct memblock_type oldmem_type = {
+       .cnt = 1,
+       .max = 1,
+       .total_size = 0,
+       .regions = &oldmem_region,
+};
+
+#define for_each_dump_mem_range(i, nid, p_start, p_end, p_nid)         \
+       for (i = 0, __next_mem_range(&i, nid, &memblock.physmem,        \
+                                    &oldmem_type, p_start,             \
+                                    p_end, p_nid);                     \
+            i != (u64)ULLONG_MAX;                                      \
+            __next_mem_range(&i, nid, &memblock.physmem,               \
+                             &oldmem_type,                             \
+                             p_start, p_end, p_nid))
+
 struct dump_save_areas dump_save_areas;
 
 /*
        return rc;
 }
 
-/*
- * Get memory layout and create hole for oldmem
- */
-static struct mem_chunk *get_memory_layout(void)
-{
-       struct mem_chunk *chunk_array;
-
-       chunk_array = kzalloc_panic(MEMORY_CHUNKS * sizeof(struct mem_chunk));
-       detect_memory_layout(chunk_array, 0);
-       create_mem_hole(chunk_array, OLDMEM_BASE, OLDMEM_SIZE);
-       return chunk_array;
-}
-
 /*
  * Initialize ELF note
  */
  */
 static int get_mem_chunk_cnt(void)
 {
-       struct mem_chunk *chunk_array, *mem_chunk;
-       int i, cnt = 0;
+       int cnt = 0;
+       u64 idx;
 
-       chunk_array = get_memory_layout();
-       for (i = 0; i < MEMORY_CHUNKS; i++) {
-               mem_chunk = &chunk_array[i];
-               if (chunk_array[i].type != CHUNK_READ_WRITE &&
-                   chunk_array[i].type != CHUNK_READ_ONLY)
-                       continue;
-               if (mem_chunk->size == 0)
-                       continue;
+       for_each_dump_mem_range(idx, NUMA_NO_NODE, NULL, NULL, NULL)
                cnt++;
-       }
-       kfree(chunk_array);
        return cnt;
 }
 
 /*
  * Initialize ELF loads (new kernel)
  */
-static int loads_init(Elf64_Phdr *phdr, u64 loads_offset)
+static void loads_init(Elf64_Phdr *phdr, u64 loads_offset)
 {
-       struct mem_chunk *chunk_array, *mem_chunk;
-       int i;
+       phys_addr_t start, end;
+       u64 idx;
 
-       chunk_array = get_memory_layout();
-       for (i = 0; i < MEMORY_CHUNKS; i++) {
-               mem_chunk = &chunk_array[i];
-               if (mem_chunk->size == 0)
-                       continue;
-               if (chunk_array[i].type != CHUNK_READ_WRITE &&
-                   chunk_array[i].type != CHUNK_READ_ONLY)
-                       continue;
-               else
-                       phdr->p_filesz = mem_chunk->size;
+       for_each_dump_mem_range(idx, NUMA_NO_NODE, &start, &end, NULL) {
+               phdr->p_filesz = end - start;
                phdr->p_type = PT_LOAD;
-               phdr->p_offset = mem_chunk->addr;
-               phdr->p_vaddr = mem_chunk->addr;
-               phdr->p_paddr = mem_chunk->addr;
-               phdr->p_memsz = mem_chunk->size;
+               phdr->p_offset = start;
+               phdr->p_vaddr = start;
+               phdr->p_paddr = start;
+               phdr->p_memsz = end - start;
                phdr->p_flags = PF_R | PF_W | PF_X;
                phdr->p_align = PAGE_SIZE;
                phdr++;
        }
-       kfree(chunk_array);
-       return i;
 }
 
 /*
        /* If we cannot get HSA size for zfcpdump return error */
        if (ipl_info.type == IPL_TYPE_FCP_DUMP && !sclp_get_hsa_size())
                return -ENODEV;
+
+       /* For kdump, exclude previous crashkernel memory */
+       if (OLDMEM_BASE) {
+               oldmem_region.base = OLDMEM_BASE;
+               oldmem_region.size = OLDMEM_SIZE;
+               oldmem_type.total_size = OLDMEM_SIZE;
+       }
+
        mem_chunk_cnt = get_mem_chunk_cnt();
 
        alloc_size = 0x1000 + get_cpu_cnt() * 0x300 +
 
 unsigned long elf_hwcap = 0;
 char elf_platform[ELF_PLATFORM_SIZE];
 
-struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS];
-
 int __initdata memory_end_set;
 unsigned long __initdata memory_end;
+unsigned long __initdata max_physmem_end;
 
 unsigned long VMALLOC_START;
 EXPORT_SYMBOL(VMALLOC_START);
 static int __init early_parse_mem(char *p)
 {
        memory_end = memparse(p, &p);
+       memory_end &= PAGE_MASK;
        memory_end_set = 1;
        return 0;
 }
 static void __init setup_resources(void)
 {
        struct resource *res, *std_res, *sub_res;
-       int i, j;
+       struct memblock_region *reg;
+       int j;
 
        code_resource.start = (unsigned long) &_text;
        code_resource.end = (unsigned long) &_etext - 1;
        bss_resource.start = (unsigned long) &__bss_start;
        bss_resource.end = (unsigned long) &__bss_stop - 1;
 
-       for (i = 0; i < MEMORY_CHUNKS; i++) {
-               if (!memory_chunk[i].size)
-                       continue;
+       for_each_memblock(memory, reg) {
                res = alloc_bootmem_low(sizeof(*res));
                res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
-               switch (memory_chunk[i].type) {
-               case CHUNK_READ_WRITE:
-                       res->name = "System RAM";
-                       break;
-               case CHUNK_READ_ONLY:
-                       res->name = "System ROM";
-                       res->flags |= IORESOURCE_READONLY;
-                       break;
-               default:
-                       res->name = "reserved";
-               }
-               res->start = memory_chunk[i].addr;
-               res->end = res->start + memory_chunk[i].size - 1;
+
+               res->name = "System RAM";
+               res->start = reg->base;
+               res->end = reg->base + reg->size - 1;
                request_resource(&iomem_resource, res);
 
                for (j = 0; j < ARRAY_SIZE(standard_resources); j++) {
 static void __init setup_memory_end(void)
 {
        unsigned long vmax, vmalloc_size, tmp;
-       unsigned long real_memory_size = 0;
-       int i;
-
-
-#ifdef CONFIG_ZFCPDUMP
-       if (ipl_info.type == IPL_TYPE_FCP_DUMP &&
-           !OLDMEM_BASE && sclp_get_hsa_size()) {
-               memory_end = sclp_get_hsa_size();
-               memory_end_set = 1;
-       }
-#endif
-       memory_end &= PAGE_MASK;
-
-       /*
-        * Make sure all chunks are MAX_ORDER aligned so we don't need the
-        * extra checks that HOLES_IN_ZONE would require.
-        */
-       for (i = 0; i < MEMORY_CHUNKS; i++) {
-               unsigned long start, end;
-               struct mem_chunk *chunk;
-               unsigned long align;
-
-               chunk = &memory_chunk[i];
-               if (!chunk->size)
-                       continue;
-               align = 1UL << (MAX_ORDER + PAGE_SHIFT - 1);
-               start = (chunk->addr + align - 1) & ~(align - 1);
-               end = (chunk->addr + chunk->size) & ~(align - 1);
-               if (start >= end)
-                       memset(chunk, 0, sizeof(*chunk));
-               else {
-                       chunk->addr = start;
-                       chunk->size = end - start;
-               }
-               real_memory_size = max(real_memory_size,
-                                      chunk->addr + chunk->size);
-       }
 
        /* Choose kernel address space layout: 2, 3, or 4 levels. */
 #ifdef CONFIG_64BIT
        vmalloc_size = VMALLOC_END ?: (128UL << 30) - MODULES_LEN;
-       tmp = (memory_end ?: real_memory_size) / PAGE_SIZE;
+       tmp = (memory_end ?: max_physmem_end) / PAGE_SIZE;
        tmp = tmp * (sizeof(struct page) + PAGE_SIZE) + vmalloc_size;
        if (tmp <= (1UL << 42))
                vmax = 1UL << 42;       /* 3-level kernel page table */
        vmemmap = (struct page *) tmp;
 
        /* Take care that memory_end is set and <= vmemmap */
-       memory_end = min(memory_end ?: real_memory_size, tmp);
-
-       /* Fixup memory chunk array to fit into 0..memory_end */
-       for (i = 0; i < MEMORY_CHUNKS; i++) {
-               struct mem_chunk *chunk = &memory_chunk[i];
+       memory_end = min(memory_end ?: max_physmem_end, tmp);
+       max_pfn = max_low_pfn = PFN_DOWN(memory_end);
+       memblock_remove(memory_end, ULONG_MAX);
 
-               if (!chunk->size)
-                       continue;
-               if (chunk->addr >= memory_end) {
-                       memset(chunk, 0, sizeof(*chunk));
-                       continue;
-               }
-               if (chunk->addr + chunk->size > memory_end)
-                       chunk->size = memory_end - chunk->addr;
-       }
+       pr_notice("Max memory size: %luMB\n", memory_end >> 20);
 }
 
 static void __init setup_vmcoreinfo(void)
 
 #ifdef CONFIG_CRASH_DUMP
 
-/*
- * Find suitable location for crashkernel memory
- */
-static unsigned long __init find_crash_base(unsigned long crash_size,
-                                           char **msg)
-{
-       unsigned long crash_base;
-       struct mem_chunk *chunk;
-       int i;
-
-       if (memory_chunk[0].size < crash_size) {
-               *msg = "first memory chunk must be at least crashkernel size";
-               return 0;
-       }
-       if (OLDMEM_BASE && crash_size == OLDMEM_SIZE)
-               return OLDMEM_BASE;
-
-       for (i = MEMORY_CHUNKS - 1; i >= 0; i--) {
-               chunk = &memory_chunk[i];
-               if (chunk->size == 0)
-                       continue;
-               if (chunk->type != CHUNK_READ_WRITE)
-                       continue;
-               if (chunk->size < crash_size)
-                       continue;
-               crash_base = (chunk->addr + chunk->size) - crash_size;
-               if (crash_base < crash_size)
-                       continue;
-               if (crash_base < sclp_get_hsa_size())
-                       continue;
-               if (crash_base < (unsigned long) INITRD_START + INITRD_SIZE)
-                       continue;
-               return crash_base;
-       }
-       *msg = "no suitable area found";
-       return 0;
-}
-
-/*
- * Check if crash_base and crash_size is valid
- */
-static int __init verify_crash_base(unsigned long crash_base,
-                                   unsigned long crash_size,
-                                   char **msg)
-{
-       struct mem_chunk *chunk;
-       int i;
-
-       /*
-        * Because we do the swap to zero, we must have at least 'crash_size'
-        * bytes free space before crash_base
-        */
-       if (crash_size > crash_base) {
-               *msg = "crashkernel offset must be greater than size";
-               return -EINVAL;
-       }
-
-       /* First memory chunk must be at least crash_size */
-       if (memory_chunk[0].size < crash_size) {
-               *msg = "first memory chunk must be at least crashkernel size";
-               return -EINVAL;
-       }
-       /* Check if we fit into the respective memory chunk */
-       for (i = 0; i < MEMORY_CHUNKS; i++) {
-               chunk = &memory_chunk[i];
-               if (chunk->size == 0)
-                       continue;
-               if (crash_base < chunk->addr)
-                       continue;
-               if (crash_base >= chunk->addr + chunk->size)
-                       continue;
-               /* we have found the memory chunk */
-               if (crash_base + crash_size > chunk->addr + chunk->size) {
-                       *msg = "selected memory chunk is too small for "
-                               "crashkernel memory";
-                       return -EINVAL;
-               }
-               return 0;
-       }
-       *msg = "invalid memory range specified";
-       return -EINVAL;
-}
-
 /*
  * When kdump is enabled, we have to ensure that no memory from
  * the area [0 - crashkernel memory size] and
 
 #endif
 
+/*
+ * Make sure that the area behind memory_end is protected
+ */
+static void reserve_memory_end(void)
+{
+#ifdef CONFIG_ZFCPDUMP
+       if (ipl_info.type == IPL_TYPE_FCP_DUMP &&
+           !OLDMEM_BASE && sclp_get_hsa_size()) {
+               memory_end = sclp_get_hsa_size();
+               memory_end &= PAGE_MASK;
+               memory_end_set = 1;
+       }
+#endif
+       if (!memory_end_set)
+               return;
+       memblock_reserve(memory_end, ULONG_MAX);
+}
+
 /*
  * Make sure that oldmem, where the dump is stored, is protected
  */
 static void reserve_oldmem(void)
 {
 #ifdef CONFIG_CRASH_DUMP
-       unsigned long real_size = 0;
-       int i;
-
-       if (!OLDMEM_BASE)
-               return;
-       for (i = 0; i < MEMORY_CHUNKS; i++) {
-               struct mem_chunk *chunk = &memory_chunk[i];
+       if (OLDMEM_BASE)
+               /* Forget all memory above the running kdump system */
+               memblock_reserve(OLDMEM_SIZE, (phys_addr_t)ULONG_MAX);
+#endif
+}
 
-               real_size = max(real_size, chunk->addr + chunk->size);
-       }
-       create_mem_hole(memory_chunk, OLDMEM_BASE, OLDMEM_SIZE);
-       create_mem_hole(memory_chunk, OLDMEM_SIZE, real_size - OLDMEM_SIZE);
+/*
+ * Make sure that oldmem, where the dump is stored, is protected
+ */
+static void remove_oldmem(void)
+{
+#ifdef CONFIG_CRASH_DUMP
+       if (OLDMEM_BASE)
+               /* Forget all memory above the running kdump system */
+               memblock_remove(OLDMEM_SIZE, (phys_addr_t)ULONG_MAX);
 #endif
 }
 
 {
 #ifdef CONFIG_CRASH_DUMP
        unsigned long long crash_base, crash_size;
-       char *msg = NULL;
+       phys_addr_t low, high;
        int rc;
 
        rc = parse_crashkernel(boot_command_line, memory_end, &crash_size,
                               &crash_base);
-       if (rc || crash_size == 0)
-               return;
+
        crash_base = ALIGN(crash_base, KEXEC_CRASH_MEM_ALIGN);
        crash_size = ALIGN(crash_size, KEXEC_CRASH_MEM_ALIGN);
-       if (register_memory_notifier(&kdump_mem_nb))
+       if (rc || crash_size == 0)
                return;
-       if (!crash_base)
-               crash_base = find_crash_base(crash_size, &msg);
-       if (!crash_base) {
-               pr_info("crashkernel reservation failed: %s\n", msg);
-               unregister_memory_notifier(&kdump_mem_nb);
+
+       if (memblock.memory.regions[0].size < crash_size) {
+               pr_info("crashkernel reservation failed: %s\n",
+                       "first memory chunk must be at least crashkernel size");
                return;
        }
-       if (verify_crash_base(crash_base, crash_size, &msg)) {
-               pr_info("crashkernel reservation failed: %s\n", msg);
-               unregister_memory_notifier(&kdump_mem_nb);
+
+       low = crash_base ?: OLDMEM_BASE;
+       high = low + crash_size;
+       if (low >= OLDMEM_BASE && high <= OLDMEM_BASE + OLDMEM_SIZE) {
+               /* The crashkernel fits into OLDMEM, reuse OLDMEM */
+               crash_base = low;
+       } else {
+               /* Find suitable area in free memory */
+               low = max_t(unsigned long, crash_size, sclp_get_hsa_size());
+               high = crash_base ? crash_base + crash_size : ULONG_MAX;
+
+               if (crash_base && crash_base < low) {
+                       pr_info("crashkernel reservation failed: %s\n",
+                               "crash_base too low");
+                       return;
+               }
+               low = crash_base ?: low;
+               crash_base = memblock_find_in_range(low, high, crash_size,
+                                                   KEXEC_CRASH_MEM_ALIGN);
+       }
+
+       if (!crash_base) {
+               pr_info("crashkernel reservation failed: %s\n",
+                       "no suitable area found");
                return;
        }
+
+       if (register_memory_notifier(&kdump_mem_nb))
+               return;
+
        if (!OLDMEM_BASE && MACHINE_IS_VM)
                diag10_range(PFN_DOWN(crash_base), PFN_DOWN(crash_size));
        crashk_res.start = crash_base;
        crashk_res.end = crash_base + crash_size - 1;
        insert_resource(&iomem_resource, &crashk_res);
-       create_mem_hole(memory_chunk, crash_base, crash_size);
+       memblock_remove(crash_base, crash_size);
        pr_info("Reserving %lluMB of memory at %lluMB "
                "for crashkernel (System RAM: %luMB)\n",
-               crash_size >> 20, crash_base >> 20, memory_end >> 20);
+               crash_size >> 20, crash_base >> 20,
+               (unsigned long)memblock.memory.total_size >> 20);
        os_info_crashkernel_add(crash_base, crash_size);
 #endif
 }
 
-static void __init setup_memory(void)
+/*
+ * Reserve the initrd from being used by memblock
+ */
+static void __init reserve_initrd(void)
 {
-        unsigned long bootmap_size;
-       unsigned long start_pfn, end_pfn;
-       int i;
+#ifdef CONFIG_BLK_DEV_INITRD
+       initrd_start = INITRD_START;
+       initrd_end = initrd_start + INITRD_SIZE;
+       memblock_reserve(INITRD_START, INITRD_SIZE);
+#endif
+}
 
-       /*
-        * partially used pages are not usable - thus
-        * we are rounding upwards:
-        */
+/*
+ * Check for initrd being in usable memory
+ */
+static void __init check_initrd(void)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (INITRD_START && INITRD_SIZE &&
+           !memblock_is_region_memory(INITRD_START, INITRD_SIZE)) {
+               pr_err("initrd does not fit memory.\n");
+               memblock_free(INITRD_START, INITRD_SIZE);
+               initrd_start = initrd_end = 0;
+       }
+#endif
+}
+
+/*
+ * Reserve all kernel text
+ */
+static void __init reserve_kernel(void)
+{
+       unsigned long start_pfn;
        start_pfn = PFN_UP(__pa(&_end));
-       end_pfn = max_pfn = PFN_DOWN(memory_end);
 
-#ifdef CONFIG_BLK_DEV_INITRD
        /*
-        * Move the initrd in case the bitmap of the bootmem allocater
-        * would overwrite it.
+        * Reserve memory used for lowcore/command line/kernel image.
         */
+       memblock_reserve(0, (unsigned long)_ehead);
+       memblock_reserve((unsigned long)_stext, PFN_PHYS(start_pfn)
+                        - (unsigned long)_stext);
+}
 
-       if (INITRD_START && INITRD_SIZE) {
-               unsigned long bmap_size;
-               unsigned long start;
-
-               bmap_size = bootmem_bootmap_pages(end_pfn - start_pfn + 1);
-               bmap_size = PFN_PHYS(bmap_size);
-
-               if (PFN_PHYS(start_pfn) + bmap_size > INITRD_START) {
-                       start = PFN_PHYS(start_pfn) + bmap_size + PAGE_SIZE;
-
+static void __init reserve_elfcorehdr(void)
+{
 #ifdef CONFIG_CRASH_DUMP
-                       if (OLDMEM_BASE) {
-                               /* Move initrd behind kdump oldmem */
-                               if (start + INITRD_SIZE > OLDMEM_BASE &&
-                                   start < OLDMEM_BASE + OLDMEM_SIZE)
-                                       start = OLDMEM_BASE + OLDMEM_SIZE;
-                       }
-#endif
-                       if (start + INITRD_SIZE > memory_end) {
-                               pr_err("initrd extends beyond end of "
-                                      "memory (0x%08lx > 0x%08lx) "
-                                      "disabling initrd\n",
-                                      start + INITRD_SIZE, memory_end);
-                               INITRD_START = INITRD_SIZE = 0;
-                       } else {
-                               pr_info("Moving initrd (0x%08lx -> "
-                                       "0x%08lx, size: %ld)\n",
-                                       INITRD_START, start, INITRD_SIZE);
-                               memmove((void *) start, (void *) INITRD_START,
-                                       INITRD_SIZE);
-                               INITRD_START = start;
-                       }
-               }
-       }
+       if (is_kdump_kernel())
+               memblock_reserve(elfcorehdr_addr - OLDMEM_BASE,
+                                PAGE_ALIGN(elfcorehdr_size));
 #endif
+}
 
-       /*
-        * Initialize the boot-time allocator
-        */
-       bootmap_size = init_bootmem(start_pfn, end_pfn);
+static void __init setup_memory(void)
+{
+       struct memblock_region *reg;
 
        /*
-        * Register RAM areas with the bootmem allocator.
+        * Init storage key for present memory
         */
-
-       for (i = 0; i < MEMORY_CHUNKS; i++) {
-               unsigned long start_chunk, end_chunk, pfn;
-
-               if (!memory_chunk[i].size)
-                       continue;
-               start_chunk = PFN_DOWN(memory_chunk[i].addr);
-               end_chunk = start_chunk + PFN_DOWN(memory_chunk[i].size);
-               end_chunk = min(end_chunk, end_pfn);
-               if (start_chunk >= end_chunk)
-                       continue;
-               memblock_add_node(PFN_PHYS(start_chunk),
-                                 PFN_PHYS(end_chunk - start_chunk), 0);
-               pfn = max(start_chunk, start_pfn);
-               storage_key_init_range(PFN_PHYS(pfn), PFN_PHYS(end_chunk));
+       for_each_memblock(memory, reg) {
+               storage_key_init_range(reg->base, reg->base + reg->size);
        }
-
        psw_set_key(PAGE_DEFAULT_KEY);
 
-       free_bootmem_with_active_regions(0, max_pfn);
-
-       /*
-        * Reserve memory used for lowcore/command line/kernel image.
-        */
-       reserve_bootmem(0, (unsigned long)_ehead, BOOTMEM_DEFAULT);
-       reserve_bootmem((unsigned long)_stext,
-                       PFN_PHYS(start_pfn) - (unsigned long)_stext,
-                       BOOTMEM_DEFAULT);
-       /*
-        * Reserve the bootmem bitmap itself as well. We do this in two
-        * steps (first step was init_bootmem()) because this catches
-        * the (very unlikely) case of us accidentally initializing the
-        * bootmem allocator with an invalid RAM area.
-        */
-       reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size,
-                       BOOTMEM_DEFAULT);
-
-#ifdef CONFIG_CRASH_DUMP
-       if (crashk_res.start)
-               reserve_bootmem(crashk_res.start,
-                               crashk_res.end - crashk_res.start + 1,
-                               BOOTMEM_DEFAULT);
-       if (is_kdump_kernel())
-               reserve_bootmem(elfcorehdr_addr - OLDMEM_BASE,
-                               PAGE_ALIGN(elfcorehdr_size), BOOTMEM_DEFAULT);
-#endif
-#ifdef CONFIG_BLK_DEV_INITRD
-       if (INITRD_START && INITRD_SIZE) {
-               if (INITRD_START + INITRD_SIZE <= memory_end) {
-                       reserve_bootmem(INITRD_START, INITRD_SIZE,
-                                       BOOTMEM_DEFAULT);
-                       initrd_start = INITRD_START;
-                       initrd_end = initrd_start + INITRD_SIZE;
-               } else {
-                       pr_err("initrd extends beyond end of "
-                              "memory (0x%08lx > 0x%08lx) "
-                              "disabling initrd\n",
-                              initrd_start + INITRD_SIZE, memory_end);
-                       initrd_start = initrd_end = 0;
-               }
-       }
-#endif
+       /* Only cosmetics */
+       memblock_enforce_memory_limit(memblock_end_of_DRAM());
 }
 
 /*
 
         ROOT_DEV = Root_RAM0;
 
+       /* Is init_mm really needed? */
        init_mm.start_code = PAGE_OFFSET;
        init_mm.end_code = (unsigned long) &_etext;
        init_mm.end_data = (unsigned long) &_edata;
        init_mm.brk = (unsigned long) &_end;
 
        parse_early_param();
-       detect_memory_layout(memory_chunk, memory_end);
        os_info_init();
        setup_ipl();
+
+       /* Do some memory reservations *before* memory is added to memblock */
+       reserve_memory_end();
        reserve_oldmem();
+       reserve_kernel();
+       reserve_initrd();
+       reserve_elfcorehdr();
+       memblock_allow_resize();
+
+       /* Get information about *all* installed memory */
+       detect_memory_memblock();
+
+       remove_oldmem();
+
+       /*
+        * Make sure all chunks are MAX_ORDER aligned so we don't need the
+        * extra checks that HOLES_IN_ZONE would require.
+        *
+        * Is this still required?
+        */
+       memblock_trim_memory(1UL << (MAX_ORDER - 1 + PAGE_SHIFT));
+
        setup_memory_end();
-       reserve_crashkernel();
        setup_memory();
+
+       check_initrd();
+       reserve_crashkernel();
+
        setup_resources();
        setup_vmcoreinfo();
        setup_lowcore();
-
        smp_fill_possible_mask();
         cpu_init();
        s390_init_cpu_topology();
 
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/memblock.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <asm/ipl.h>
 #include <asm/sclp.h>
 #include <asm/setup.h>
 
 #define ADDR2G (1ULL << 31)
 
-static void find_memory_chunks(struct mem_chunk chunk[], unsigned long maxsize)
+#define CHUNK_READ_WRITE 0
+#define CHUNK_READ_ONLY  1
+
+static inline void memblock_physmem_add(phys_addr_t start, phys_addr_t size)
+{
+       memblock_add_range(&memblock.memory, start, size, 0, 0);
+       memblock_add_range(&memblock.physmem, start, size, 0, 0);
+}
+
+void __init detect_memory_memblock(void)
 {
        unsigned long long memsize, rnmax, rzm;
-       unsigned long addr = 0, size;
-       int i = 0, type;
+       unsigned long addr, size;
+       int type;
 
        rzm = sclp_get_rzm();
        rnmax = sclp_get_rnmax();
        memsize = rzm * rnmax;
        if (!rzm)
                rzm = 1ULL << 17;
-       if (sizeof(long) == 4) {
+       if (IS_ENABLED(CONFIG_32BIT)) {
                rzm = min(ADDR2G, rzm);
-               memsize = memsize ? min(ADDR2G, memsize) : ADDR2G;
+               memsize = min(ADDR2G, memsize);
        }
-       if (maxsize)
-               memsize = memsize ? min((unsigned long)memsize, maxsize) : maxsize;
+       max_physmem_end = memsize;
+       addr = 0;
+       /* keep memblock lists close to the kernel */
+       memblock_set_bottom_up(true);
        do {
                size = 0;
                type = tprot(addr);
                do {
                        size += rzm;
-                       if (memsize && addr + size >= memsize)
+                       if (max_physmem_end && addr + size >= max_physmem_end)
                                break;
                } while (type == tprot(addr + size));
                if (type == CHUNK_READ_WRITE || type == CHUNK_READ_ONLY) {
-                       if (memsize && (addr + size > memsize))
-                               size = memsize - addr;
-                       chunk[i].addr = addr;
-                       chunk[i].size = size;
-                       chunk[i].type = type;
-                       i++;
+                       if (max_physmem_end && (addr + size > max_physmem_end))
+                               size = max_physmem_end - addr;
+                       memblock_physmem_add(addr, size);
                }
                addr += size;
-       } while (addr < memsize && i < MEMORY_CHUNKS);
-}
-
-/**
- * detect_memory_layout - fill mem_chunk array with memory layout data
- * @chunk: mem_chunk array to be filled
- * @maxsize: maximum address where memory detection should stop
- *
- * Fills the passed in memory chunk array with the memory layout of the
- * machine. The array must have a size of at least MEMORY_CHUNKS and will
- * be fully initialized afterwards.
- * If the maxsize paramater has a value > 0 memory detection will stop at
- * that address. It is guaranteed that all chunks have an ending address
- * that is smaller than maxsize.
- * If maxsize is 0 all memory will be detected.
- */
-void detect_memory_layout(struct mem_chunk chunk[], unsigned long maxsize)
-{
-       unsigned long flags, flags_dat, cr0;
-
-       memset(chunk, 0, MEMORY_CHUNKS * sizeof(struct mem_chunk));
-       /*
-        * Disable IRQs, DAT and low address protection so tprot does the
-        * right thing and we don't get scheduled away with low address
-        * protection disabled.
-        */
-       local_irq_save(flags);
-       flags_dat = __arch_local_irq_stnsm(0xfb);
-       /*
-        * In case DAT was enabled, make sure chunk doesn't reside in vmalloc
-        * space. We have disabled DAT and any access to vmalloc area will
-        * cause an exception.
-        * If DAT was disabled we are called from early ipl code.
-        */
-       if (test_bit(5, &flags_dat)) {
-               if (WARN_ON_ONCE(is_vmalloc_or_module_addr(chunk)))
-                       goto out;
-       }
-       __ctl_store(cr0, 0, 0);
-       __ctl_clear_bit(0, 28);
-       find_memory_chunks(chunk, maxsize);
-       __ctl_load(cr0, 0, 0);
-out:
-       __arch_local_irq_ssm(flags_dat);
-       local_irq_restore(flags);
-}
-EXPORT_SYMBOL(detect_memory_layout);
-
-/*
- * Create memory hole with given address and size.
- */
-void create_mem_hole(struct mem_chunk mem_chunk[], unsigned long addr,
-                    unsigned long size)
-{
-       int i;
-
-       for (i = 0; i < MEMORY_CHUNKS; i++) {
-               struct mem_chunk *chunk = &mem_chunk[i];
-
-               if (chunk->size == 0)
-                       continue;
-               if (addr > chunk->addr + chunk->size)
-                       continue;
-               if (addr + size <= chunk->addr)
-                       continue;
-               /* Split */
-               if ((addr > chunk->addr) &&
-                   (addr + size < chunk->addr + chunk->size)) {
-                       struct mem_chunk *new = chunk + 1;
-
-                       memmove(new, chunk, (MEMORY_CHUNKS-i-1) * sizeof(*new));
-                       new->addr = addr + size;
-                       new->size = chunk->addr + chunk->size - new->addr;
-                       chunk->size = addr - chunk->addr;
-                       continue;
-               } else if ((addr <= chunk->addr) &&
-                          (addr + size >= chunk->addr + chunk->size)) {
-                       memmove(chunk, chunk + 1, (MEMORY_CHUNKS-i-1) * sizeof(*chunk));
-                       memset(&mem_chunk[MEMORY_CHUNKS-1], 0, sizeof(*chunk));
-               } else if (addr + size < chunk->addr + chunk->size) {
-                       chunk->size =  chunk->addr + chunk->size - addr - size;
-                       chunk->addr = addr + size;
-               } else if (addr > chunk->addr) {
-                       chunk->size = addr - chunk->addr;
-               }
-       }
+       } while (addr < max_physmem_end);
+       memblock_set_bottom_up(false);
+       if (!max_physmem_end)
+               max_physmem_end = memblock_end_of_DRAM();
 }