xref: /kvm-unit-tests/lib/memregions.c (revision 611fe6dee79c5e9df560562163c085dbd4dfd387)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <libcflat.h>
3 #include <devicetree.h>
4 #include <memregions.h>
5 
6 static struct mem_region __initial_mem_regions[NR_INITIAL_MEM_REGIONS + 1];
7 static size_t nr_regions = NR_INITIAL_MEM_REGIONS;
8 
9 struct mem_region *mem_regions = __initial_mem_regions;
10 
memregions_init(struct mem_region regions[],size_t nr)11 void memregions_init(struct mem_region regions[], size_t nr)
12 {
13 	mem_regions = regions;
14 	nr_regions = nr;
15 }
16 
memregions_add(struct mem_region * r)17 struct 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 
memregions_find(phys_addr_t paddr)31 struct 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 
memregions_get_flags(phys_addr_t paddr)41 uint32_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 
memregions_split(phys_addr_t addr,struct mem_region ** r1,struct mem_region ** r2)48 void 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 
memregions_add_dt_regions(size_t max_nr)68 void 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 }
83 
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  */
memregions_efi_init(struct efi_boot_memmap * mem_map,struct mem_region ** freemem)91 void 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;
115 		case EFI_LOADER_DATA:
116 		case EFI_ACPI_RECLAIM_MEMORY:
117 			break;
118 		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 */
137