blob: db7522cef6685c14ab545a5bcda3d03a0d933e4e [file] [log] [blame]
Andrew Jones2479ae52024-01-19 14:23:57 +01001// SPDX-License-Identifier: GPL-2.0-only
2#include <libcflat.h>
3#include <devicetree.h>
4#include <memregions.h>
5
6static struct mem_region __initial_mem_regions[NR_INITIAL_MEM_REGIONS + 1];
7static size_t nr_regions = NR_INITIAL_MEM_REGIONS;
8
9struct mem_region *mem_regions = __initial_mem_regions;
10
11void memregions_init(struct mem_region regions[], size_t nr)
12{
13 mem_regions = regions;
14 nr_regions = nr;
15}
16
17struct mem_region *memregions_add(struct mem_region *r)
18{
19 struct mem_region *r_next = mem_regions;
20 int i = 0;
21
22 for (; r_next->end; ++r_next, ++i)
23 ;
24 assert(i < nr_regions);
25
26 *r_next = *r;
27
28 return r_next;
29}
30
31struct mem_region *memregions_find(phys_addr_t paddr)
32{
33 struct mem_region *r;
34
35 for (r = mem_regions; r->end; ++r)
36 if (paddr >= r->start && paddr < r->end)
37 return r;
38 return NULL;
39}
40
41uint32_t memregions_get_flags(phys_addr_t paddr)
42{
43 struct mem_region *r = memregions_find(paddr);
44
45 return r ? r->flags : MR_F_UNKNOWN;
46}
47
48void memregions_split(phys_addr_t addr, struct mem_region **r1, struct mem_region **r2)
49{
50 *r1 = memregions_find(addr);
51 assert(*r1);
52
53 if ((*r1)->start == addr) {
54 *r2 = *r1;
55 *r1 = NULL;
56 return;
57 }
58
59 *r2 = memregions_add(&(struct mem_region){
60 .start = addr,
61 .end = (*r1)->end,
62 .flags = (*r1)->flags,
63 });
64
65 (*r1)->end = addr;
66}
67
68void memregions_add_dt_regions(size_t max_nr)
69{
70 struct dt_pbus_reg regs[max_nr];
71 int nr_regs, i;
72
73 nr_regs = dt_get_memory_params(regs, max_nr);
74 assert(nr_regs > 0);
75
76 for (i = 0; i < nr_regs; ++i) {
77 memregions_add(&(struct mem_region){
78 .start = regs[i].addr,
79 .end = regs[i].addr + regs[i].size,
80 });
81 }
82}
Andrew Jonesfa8e5d62024-03-05 17:46:38 +010083
84#ifdef CONFIG_EFI
85/*
86 * Add memory regions based on the EFI memory map. Also set a pointer to the
87 * memory region which corresponds to the largest EFI_CONVENTIONAL_MEMORY
88 * region, as that region is the largest free, continuous region, making it
89 * a good choice for the memory allocator.
90 */
91void memregions_efi_init(struct efi_boot_memmap *mem_map,
92 struct mem_region **freemem)
93{
94 u8 *buffer = (u8 *)*mem_map->map;
95 u64 freemem_pages = 0;
96
97 *freemem = NULL;
98
99 for (int i = 0; i < *mem_map->map_size; i += *mem_map->desc_size) {
100 efi_memory_desc_t *d = (efi_memory_desc_t *)&buffer[i];
101 struct mem_region r = {
102 .start = d->phys_addr,
103 .end = d->phys_addr + d->num_pages * EFI_PAGE_SIZE,
104 .flags = 0,
105 };
106
107 switch (d->type) {
108 case EFI_MEMORY_MAPPED_IO:
109 case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
110 r.flags = MR_F_IO;
111 break;
112 case EFI_LOADER_CODE:
113 r.flags = MR_F_CODE;
114 break;
Andrew Jones6182cb92024-03-05 17:46:39 +0100115 case EFI_LOADER_DATA:
116 case EFI_ACPI_RECLAIM_MEMORY:
117 break;
Andrew Jonesfa8e5d62024-03-05 17:46:38 +0100118 case EFI_PERSISTENT_MEMORY:
119 r.flags = MR_F_PERSISTENT;
120 break;
121 case EFI_CONVENTIONAL_MEMORY:
122 if (freemem_pages < d->num_pages) {
123 freemem_pages = d->num_pages;
124 *freemem = memregions_add(&r);
125 continue;
126 }
127 break;
128 default:
129 r.flags = MR_F_RESERVED;
130 break;
131 }
132
133 memregions_add(&r);
134 }
135}
136#endif /* CONFIG_EFI */