xref: /openbmc/qemu/contrib/elf2dmp/addrspace.c (revision 51e47cf8)
1 /*
2  * Copyright (c) 2018 Virtuozzo International GmbH
3  *
4  * This work is licensed under the terms of the GNU GPL, version 2 or later.
5  *
6  */
7 
8 #include "qemu/osdep.h"
9 #include "addrspace.h"
10 
11 static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa)
12 {
13     size_t i;
14 
15     for (i = 0; i < ps->block_nr; i++) {
16         if (ps->block[i].paddr <= pa &&
17                 pa <= ps->block[i].paddr + ps->block[i].size) {
18             return ps->block + i;
19         }
20     }
21 
22     return NULL;
23 }
24 
25 static uint8_t *pa_space_resolve(struct pa_space *ps, uint64_t pa)
26 {
27     struct pa_block *block = pa_space_find_block(ps, pa);
28 
29     if (!block) {
30         return NULL;
31     }
32 
33     return block->addr + (pa - block->paddr);
34 }
35 
36 int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
37 {
38     Elf64_Half phdr_nr = elf_getphdrnum(qemu_elf->map);
39     Elf64_Phdr *phdr = elf64_getphdr(qemu_elf->map);
40     size_t block_i = 0;
41     size_t i;
42 
43     ps->block_nr = 0;
44 
45     for (i = 0; i < phdr_nr; i++) {
46         if (phdr[i].p_type == PT_LOAD) {
47             ps->block_nr++;
48         }
49     }
50 
51     ps->block = malloc(sizeof(*ps->block) * ps->block_nr);
52     if (!ps->block) {
53         return 1;
54     }
55 
56     for (i = 0; i < phdr_nr; i++) {
57         if (phdr[i].p_type == PT_LOAD) {
58             ps->block[block_i] = (struct pa_block) {
59                 .addr = (uint8_t *)qemu_elf->map + phdr[i].p_offset,
60                 .paddr = phdr[i].p_paddr,
61                 .size = phdr[i].p_filesz,
62             };
63             block_i++;
64         }
65     }
66 
67     return 0;
68 }
69 
70 void pa_space_destroy(struct pa_space *ps)
71 {
72     ps->block_nr = 0;
73     free(ps->block);
74 }
75 
76 void va_space_set_dtb(struct va_space *vs, uint64_t dtb)
77 {
78     vs->dtb = dtb & 0x00ffffffffff000;
79 }
80 
81 void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb)
82 {
83     vs->ps = ps;
84     va_space_set_dtb(vs, dtb);
85 }
86 
87 static uint64_t get_pml4e(struct va_space *vs, uint64_t va)
88 {
89     uint64_t pa = (vs->dtb & 0xffffffffff000) | ((va & 0xff8000000000) >> 36);
90 
91     return *(uint64_t *)pa_space_resolve(vs->ps, pa);
92 }
93 
94 static uint64_t get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e)
95 {
96     uint64_t pdpte_paddr = (pml4e & 0xffffffffff000) |
97         ((va & 0x7FC0000000) >> 27);
98 
99     return *(uint64_t *)pa_space_resolve(vs->ps, pdpte_paddr);
100 }
101 
102 static uint64_t pde_index(uint64_t va)
103 {
104     return (va >> 21) & 0x1FF;
105 }
106 
107 static uint64_t pdba_base(uint64_t pdpe)
108 {
109     return pdpe & 0xFFFFFFFFFF000;
110 }
111 
112 static uint64_t get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe)
113 {
114     uint64_t pgd_entry = pdba_base(pdpe) + pde_index(va) * 8;
115 
116     return *(uint64_t *)pa_space_resolve(vs->ps, pgd_entry);
117 }
118 
119 static uint64_t pte_index(uint64_t va)
120 {
121     return (va >> 12) & 0x1FF;
122 }
123 
124 static uint64_t ptba_base(uint64_t pde)
125 {
126     return pde & 0xFFFFFFFFFF000;
127 }
128 
129 static uint64_t get_pte(struct va_space *vs, uint64_t va, uint64_t pgd)
130 {
131     uint64_t pgd_val = ptba_base(pgd) + pte_index(va) * 8;
132 
133     return *(uint64_t *)pa_space_resolve(vs->ps, pgd_val);
134 }
135 
136 static uint64_t get_paddr(uint64_t va, uint64_t pte)
137 {
138     return (pte & 0xFFFFFFFFFF000) | (va & 0xFFF);
139 }
140 
141 static bool is_present(uint64_t entry)
142 {
143     return entry & 0x1;
144 }
145 
146 static bool page_size_flag(uint64_t entry)
147 {
148     return entry & (1 << 7);
149 }
150 
151 static uint64_t get_1GB_paddr(uint64_t va, uint64_t pdpte)
152 {
153     return (pdpte & 0xfffffc0000000) | (va & 0x3fffffff);
154 }
155 
156 static uint64_t get_2MB_paddr(uint64_t va, uint64_t pgd_entry)
157 {
158     return (pgd_entry & 0xfffffffe00000) | (va & 0x00000001fffff);
159 }
160 
161 static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va)
162 {
163     uint64_t pml4e, pdpe, pgd, pte;
164 
165     pml4e = get_pml4e(vs, va);
166     if (!is_present(pml4e)) {
167         return INVALID_PA;
168     }
169 
170     pdpe = get_pdpi(vs, va, pml4e);
171     if (!is_present(pdpe)) {
172         return INVALID_PA;
173     }
174 
175     if (page_size_flag(pdpe)) {
176         return get_1GB_paddr(va, pdpe);
177     }
178 
179     pgd = get_pgd(vs, va, pdpe);
180     if (!is_present(pgd)) {
181         return INVALID_PA;
182     }
183 
184     if (page_size_flag(pgd)) {
185         return get_2MB_paddr(va, pgd);
186     }
187 
188     pte = get_pte(vs, va, pgd);
189     if (!is_present(pte)) {
190         return INVALID_PA;
191     }
192 
193     return get_paddr(va, pte);
194 }
195 
196 void *va_space_resolve(struct va_space *vs, uint64_t va)
197 {
198     uint64_t pa = va_space_va2pa(vs, va);
199 
200     if (pa == INVALID_PA) {
201         return NULL;
202     }
203 
204     return pa_space_resolve(vs->ps, pa);
205 }
206 
207 int va_space_rw(struct va_space *vs, uint64_t addr,
208         void *buf, size_t size, int is_write)
209 {
210     while (size) {
211         uint64_t page = addr & ELF2DMP_PFN_MASK;
212         size_t s = (page + ELF2DMP_PAGE_SIZE) - addr;
213         void *ptr;
214 
215         s = (s > size) ? size : s;
216 
217         ptr = va_space_resolve(vs, addr);
218         if (!ptr) {
219             return 1;
220         }
221 
222         if (is_write) {
223             memcpy(ptr, buf, s);
224         } else {
225             memcpy(buf, ptr, s);
226         }
227 
228         size -= s;
229         buf = (uint8_t *)buf + s;
230         addr += s;
231     }
232 
233     return 0;
234 }
235