13fa2d384SViktor Prutyanov /*
23fa2d384SViktor Prutyanov * Copyright (c) 2018 Virtuozzo International GmbH
33fa2d384SViktor Prutyanov *
43fa2d384SViktor Prutyanov * This work is licensed under the terms of the GNU GPL, version 2 or later.
53fa2d384SViktor Prutyanov *
63fa2d384SViktor Prutyanov */
73fa2d384SViktor Prutyanov
83fa2d384SViktor Prutyanov #include "qemu/osdep.h"
93fa2d384SViktor Prutyanov #include "addrspace.h"
103fa2d384SViktor Prutyanov
pa_space_find_block(struct pa_space * ps,uint64_t pa)113fa2d384SViktor Prutyanov static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa)
123fa2d384SViktor Prutyanov {
133fa2d384SViktor Prutyanov size_t i;
1405adc48eSViktor Prutyanov
153fa2d384SViktor Prutyanov for (i = 0; i < ps->block_nr; i++) {
163fa2d384SViktor Prutyanov if (ps->block[i].paddr <= pa &&
17d5c27a53SViktor Prutyanov pa < ps->block[i].paddr + ps->block[i].size) {
183fa2d384SViktor Prutyanov return ps->block + i;
193fa2d384SViktor Prutyanov }
203fa2d384SViktor Prutyanov }
213fa2d384SViktor Prutyanov
223fa2d384SViktor Prutyanov return NULL;
233fa2d384SViktor Prutyanov }
243fa2d384SViktor Prutyanov
pa_space_resolve(struct pa_space * ps,uint64_t pa)25a6a62ef5SAkihiko Odaki static void *pa_space_resolve(struct pa_space *ps, uint64_t pa)
263fa2d384SViktor Prutyanov {
273fa2d384SViktor Prutyanov struct pa_block *block = pa_space_find_block(ps, pa);
283fa2d384SViktor Prutyanov
293fa2d384SViktor Prutyanov if (!block) {
303fa2d384SViktor Prutyanov return NULL;
313fa2d384SViktor Prutyanov }
323fa2d384SViktor Prutyanov
333fa2d384SViktor Prutyanov return block->addr + (pa - block->paddr);
343fa2d384SViktor Prutyanov }
353fa2d384SViktor Prutyanov
pa_space_read64(struct pa_space * ps,uint64_t pa,uint64_t * value)36a6a62ef5SAkihiko Odaki static bool pa_space_read64(struct pa_space *ps, uint64_t pa, uint64_t *value)
37a6a62ef5SAkihiko Odaki {
38a6a62ef5SAkihiko Odaki uint64_t *resolved = pa_space_resolve(ps, pa);
39a6a62ef5SAkihiko Odaki
40a6a62ef5SAkihiko Odaki if (!resolved) {
41a6a62ef5SAkihiko Odaki return false;
42a6a62ef5SAkihiko Odaki }
43a6a62ef5SAkihiko Odaki
44a6a62ef5SAkihiko Odaki *value = *resolved;
45a6a62ef5SAkihiko Odaki
46a6a62ef5SAkihiko Odaki return true;
47a6a62ef5SAkihiko Odaki }
48a6a62ef5SAkihiko Odaki
pa_block_align(struct pa_block * b)49d5c27a53SViktor Prutyanov static void pa_block_align(struct pa_block *b)
50d5c27a53SViktor Prutyanov {
51d5c27a53SViktor Prutyanov uint64_t low_align = ((b->paddr - 1) | ELF2DMP_PAGE_MASK) + 1 - b->paddr;
52d5c27a53SViktor Prutyanov uint64_t high_align = (b->paddr + b->size) & ELF2DMP_PAGE_MASK;
53d5c27a53SViktor Prutyanov
54d5c27a53SViktor Prutyanov if (low_align == 0 && high_align == 0) {
55d5c27a53SViktor Prutyanov return;
56d5c27a53SViktor Prutyanov }
57d5c27a53SViktor Prutyanov
58d5c27a53SViktor Prutyanov if (low_align + high_align < b->size) {
59d5c27a53SViktor Prutyanov printf("Block 0x%"PRIx64"+:0x%"PRIx64" will be aligned to "
60d5c27a53SViktor Prutyanov "0x%"PRIx64"+:0x%"PRIx64"\n", b->paddr, b->size,
61d5c27a53SViktor Prutyanov b->paddr + low_align, b->size - low_align - high_align);
62d5c27a53SViktor Prutyanov b->size -= low_align + high_align;
63d5c27a53SViktor Prutyanov } else {
64d5c27a53SViktor Prutyanov printf("Block 0x%"PRIx64"+:0x%"PRIx64" is too small to align\n",
65d5c27a53SViktor Prutyanov b->paddr, b->size);
66d5c27a53SViktor Prutyanov b->size = 0;
67d5c27a53SViktor Prutyanov }
68d5c27a53SViktor Prutyanov
69d5c27a53SViktor Prutyanov b->addr += low_align;
70d5c27a53SViktor Prutyanov b->paddr += low_align;
71d5c27a53SViktor Prutyanov }
72d5c27a53SViktor Prutyanov
pa_space_create(struct pa_space * ps,QEMU_Elf * qemu_elf)73262a0ff8SAkihiko Odaki void pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
743fa2d384SViktor Prutyanov {
753fa2d384SViktor Prutyanov Elf64_Half phdr_nr = elf_getphdrnum(qemu_elf->map);
763fa2d384SViktor Prutyanov Elf64_Phdr *phdr = elf64_getphdr(qemu_elf->map);
773fa2d384SViktor Prutyanov size_t block_i = 0;
783fa2d384SViktor Prutyanov size_t i;
793fa2d384SViktor Prutyanov
803fa2d384SViktor Prutyanov ps->block_nr = 0;
813fa2d384SViktor Prutyanov
823fa2d384SViktor Prutyanov for (i = 0; i < phdr_nr; i++) {
833fa2d384SViktor Prutyanov if (phdr[i].p_type == PT_LOAD) {
843fa2d384SViktor Prutyanov ps->block_nr++;
853fa2d384SViktor Prutyanov }
863fa2d384SViktor Prutyanov }
873fa2d384SViktor Prutyanov
882a052b4eSSuraj Shirvankar ps->block = g_new(struct pa_block, ps->block_nr);
893fa2d384SViktor Prutyanov
903fa2d384SViktor Prutyanov for (i = 0; i < phdr_nr; i++) {
91*66790947SAkihiko Odaki if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset < qemu_elf->size) {
923fa2d384SViktor Prutyanov ps->block[block_i] = (struct pa_block) {
933fa2d384SViktor Prutyanov .addr = (uint8_t *)qemu_elf->map + phdr[i].p_offset,
943fa2d384SViktor Prutyanov .paddr = phdr[i].p_paddr,
95*66790947SAkihiko Odaki .size = MIN(phdr[i].p_filesz,
96*66790947SAkihiko Odaki qemu_elf->size - phdr[i].p_offset),
973fa2d384SViktor Prutyanov };
98d5c27a53SViktor Prutyanov pa_block_align(&ps->block[block_i]);
99d5c27a53SViktor Prutyanov block_i = ps->block[block_i].size ? (block_i + 1) : block_i;
1003fa2d384SViktor Prutyanov }
1013fa2d384SViktor Prutyanov }
1023fa2d384SViktor Prutyanov
103d5c27a53SViktor Prutyanov ps->block_nr = block_i;
1043fa2d384SViktor Prutyanov }
1053fa2d384SViktor Prutyanov
pa_space_destroy(struct pa_space * ps)1063fa2d384SViktor Prutyanov void pa_space_destroy(struct pa_space *ps)
1073fa2d384SViktor Prutyanov {
1083fa2d384SViktor Prutyanov ps->block_nr = 0;
1092a052b4eSSuraj Shirvankar g_free(ps->block);
1103fa2d384SViktor Prutyanov }
1113fa2d384SViktor Prutyanov
va_space_set_dtb(struct va_space * vs,uint64_t dtb)1123fa2d384SViktor Prutyanov void va_space_set_dtb(struct va_space *vs, uint64_t dtb)
1133fa2d384SViktor Prutyanov {
1143fa2d384SViktor Prutyanov vs->dtb = dtb & 0x00ffffffffff000;
1153fa2d384SViktor Prutyanov }
1163fa2d384SViktor Prutyanov
va_space_create(struct va_space * vs,struct pa_space * ps,uint64_t dtb)1173fa2d384SViktor Prutyanov void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb)
1183fa2d384SViktor Prutyanov {
1193fa2d384SViktor Prutyanov vs->ps = ps;
1203fa2d384SViktor Prutyanov va_space_set_dtb(vs, dtb);
1213fa2d384SViktor Prutyanov }
1223fa2d384SViktor Prutyanov
get_pml4e(struct va_space * vs,uint64_t va,uint64_t * value)123a6a62ef5SAkihiko Odaki static bool get_pml4e(struct va_space *vs, uint64_t va, uint64_t *value)
1243fa2d384SViktor Prutyanov {
1253fa2d384SViktor Prutyanov uint64_t pa = (vs->dtb & 0xffffffffff000) | ((va & 0xff8000000000) >> 36);
1263fa2d384SViktor Prutyanov
127a6a62ef5SAkihiko Odaki return pa_space_read64(vs->ps, pa, value);
1283fa2d384SViktor Prutyanov }
1293fa2d384SViktor Prutyanov
get_pdpi(struct va_space * vs,uint64_t va,uint64_t pml4e,uint64_t * value)130a6a62ef5SAkihiko Odaki static bool get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e,
131a6a62ef5SAkihiko Odaki uint64_t *value)
1323fa2d384SViktor Prutyanov {
1333fa2d384SViktor Prutyanov uint64_t pdpte_paddr = (pml4e & 0xffffffffff000) |
1343fa2d384SViktor Prutyanov ((va & 0x7FC0000000) >> 27);
1353fa2d384SViktor Prutyanov
136a6a62ef5SAkihiko Odaki return pa_space_read64(vs->ps, pdpte_paddr, value);
1373fa2d384SViktor Prutyanov }
1383fa2d384SViktor Prutyanov
pde_index(uint64_t va)1393fa2d384SViktor Prutyanov static uint64_t pde_index(uint64_t va)
1403fa2d384SViktor Prutyanov {
1413fa2d384SViktor Prutyanov return (va >> 21) & 0x1FF;
1423fa2d384SViktor Prutyanov }
1433fa2d384SViktor Prutyanov
pdba_base(uint64_t pdpe)1443fa2d384SViktor Prutyanov static uint64_t pdba_base(uint64_t pdpe)
1453fa2d384SViktor Prutyanov {
1463fa2d384SViktor Prutyanov return pdpe & 0xFFFFFFFFFF000;
1473fa2d384SViktor Prutyanov }
1483fa2d384SViktor Prutyanov
get_pgd(struct va_space * vs,uint64_t va,uint64_t pdpe,uint64_t * value)149a6a62ef5SAkihiko Odaki static bool get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe,
150a6a62ef5SAkihiko Odaki uint64_t *value)
1513fa2d384SViktor Prutyanov {
1523fa2d384SViktor Prutyanov uint64_t pgd_entry = pdba_base(pdpe) + pde_index(va) * 8;
1533fa2d384SViktor Prutyanov
154a6a62ef5SAkihiko Odaki return pa_space_read64(vs->ps, pgd_entry, value);
1553fa2d384SViktor Prutyanov }
1563fa2d384SViktor Prutyanov
pte_index(uint64_t va)1573fa2d384SViktor Prutyanov static uint64_t pte_index(uint64_t va)
1583fa2d384SViktor Prutyanov {
1593fa2d384SViktor Prutyanov return (va >> 12) & 0x1FF;
1603fa2d384SViktor Prutyanov }
1613fa2d384SViktor Prutyanov
ptba_base(uint64_t pde)1623fa2d384SViktor Prutyanov static uint64_t ptba_base(uint64_t pde)
1633fa2d384SViktor Prutyanov {
1643fa2d384SViktor Prutyanov return pde & 0xFFFFFFFFFF000;
1653fa2d384SViktor Prutyanov }
1663fa2d384SViktor Prutyanov
get_pte(struct va_space * vs,uint64_t va,uint64_t pgd,uint64_t * value)167a6a62ef5SAkihiko Odaki static bool get_pte(struct va_space *vs, uint64_t va, uint64_t pgd,
168a6a62ef5SAkihiko Odaki uint64_t *value)
1693fa2d384SViktor Prutyanov {
1703fa2d384SViktor Prutyanov uint64_t pgd_val = ptba_base(pgd) + pte_index(va) * 8;
1713fa2d384SViktor Prutyanov
172a6a62ef5SAkihiko Odaki return pa_space_read64(vs->ps, pgd_val, value);
1733fa2d384SViktor Prutyanov }
1743fa2d384SViktor Prutyanov
get_paddr(uint64_t va,uint64_t pte)1753fa2d384SViktor Prutyanov static uint64_t get_paddr(uint64_t va, uint64_t pte)
1763fa2d384SViktor Prutyanov {
1773fa2d384SViktor Prutyanov return (pte & 0xFFFFFFFFFF000) | (va & 0xFFF);
1783fa2d384SViktor Prutyanov }
1793fa2d384SViktor Prutyanov
is_present(uint64_t entry)1803fa2d384SViktor Prutyanov static bool is_present(uint64_t entry)
1813fa2d384SViktor Prutyanov {
1823fa2d384SViktor Prutyanov return entry & 0x1;
1833fa2d384SViktor Prutyanov }
1843fa2d384SViktor Prutyanov
page_size_flag(uint64_t entry)1853fa2d384SViktor Prutyanov static bool page_size_flag(uint64_t entry)
1863fa2d384SViktor Prutyanov {
1873fa2d384SViktor Prutyanov return entry & (1 << 7);
1883fa2d384SViktor Prutyanov }
1893fa2d384SViktor Prutyanov
get_1GB_paddr(uint64_t va,uint64_t pdpte)1903fa2d384SViktor Prutyanov static uint64_t get_1GB_paddr(uint64_t va, uint64_t pdpte)
1913fa2d384SViktor Prutyanov {
1923fa2d384SViktor Prutyanov return (pdpte & 0xfffffc0000000) | (va & 0x3fffffff);
1933fa2d384SViktor Prutyanov }
1943fa2d384SViktor Prutyanov
get_2MB_paddr(uint64_t va,uint64_t pgd_entry)1953fa2d384SViktor Prutyanov static uint64_t get_2MB_paddr(uint64_t va, uint64_t pgd_entry)
1963fa2d384SViktor Prutyanov {
1973fa2d384SViktor Prutyanov return (pgd_entry & 0xfffffffe00000) | (va & 0x00000001fffff);
1983fa2d384SViktor Prutyanov }
1993fa2d384SViktor Prutyanov
va_space_va2pa(struct va_space * vs,uint64_t va)2003fa2d384SViktor Prutyanov static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va)
2013fa2d384SViktor Prutyanov {
2023fa2d384SViktor Prutyanov uint64_t pml4e, pdpe, pgd, pte;
2033fa2d384SViktor Prutyanov
204a6a62ef5SAkihiko Odaki if (!get_pml4e(vs, va, &pml4e) || !is_present(pml4e)) {
2053fa2d384SViktor Prutyanov return INVALID_PA;
2063fa2d384SViktor Prutyanov }
2073fa2d384SViktor Prutyanov
208a6a62ef5SAkihiko Odaki if (!get_pdpi(vs, va, pml4e, &pdpe) || !is_present(pdpe)) {
2093fa2d384SViktor Prutyanov return INVALID_PA;
2103fa2d384SViktor Prutyanov }
2113fa2d384SViktor Prutyanov
2123fa2d384SViktor Prutyanov if (page_size_flag(pdpe)) {
2133fa2d384SViktor Prutyanov return get_1GB_paddr(va, pdpe);
2143fa2d384SViktor Prutyanov }
2153fa2d384SViktor Prutyanov
216a6a62ef5SAkihiko Odaki if (!get_pgd(vs, va, pdpe, &pgd) || !is_present(pgd)) {
2173fa2d384SViktor Prutyanov return INVALID_PA;
2183fa2d384SViktor Prutyanov }
2193fa2d384SViktor Prutyanov
2203fa2d384SViktor Prutyanov if (page_size_flag(pgd)) {
2213fa2d384SViktor Prutyanov return get_2MB_paddr(va, pgd);
2223fa2d384SViktor Prutyanov }
2233fa2d384SViktor Prutyanov
224a6a62ef5SAkihiko Odaki if (!get_pte(vs, va, pgd, &pte) || !is_present(pte)) {
2253fa2d384SViktor Prutyanov return INVALID_PA;
2263fa2d384SViktor Prutyanov }
2273fa2d384SViktor Prutyanov
2283fa2d384SViktor Prutyanov return get_paddr(va, pte);
2293fa2d384SViktor Prutyanov }
2303fa2d384SViktor Prutyanov
va_space_resolve(struct va_space * vs,uint64_t va)2313fa2d384SViktor Prutyanov void *va_space_resolve(struct va_space *vs, uint64_t va)
2323fa2d384SViktor Prutyanov {
2333fa2d384SViktor Prutyanov uint64_t pa = va_space_va2pa(vs, va);
2343fa2d384SViktor Prutyanov
2353fa2d384SViktor Prutyanov if (pa == INVALID_PA) {
2363fa2d384SViktor Prutyanov return NULL;
2373fa2d384SViktor Prutyanov }
2383fa2d384SViktor Prutyanov
2393fa2d384SViktor Prutyanov return pa_space_resolve(vs->ps, pa);
2403fa2d384SViktor Prutyanov }
2413fa2d384SViktor Prutyanov
va_space_rw(struct va_space * vs,uint64_t addr,void * buf,size_t size,int is_write)242a15f9749SAkihiko Odaki bool va_space_rw(struct va_space *vs, uint64_t addr,
2433fa2d384SViktor Prutyanov void *buf, size_t size, int is_write)
2443fa2d384SViktor Prutyanov {
2453fa2d384SViktor Prutyanov while (size) {
2462d0fc797SJiaxun Yang uint64_t page = addr & ELF2DMP_PFN_MASK;
2472d0fc797SJiaxun Yang size_t s = (page + ELF2DMP_PAGE_SIZE) - addr;
2483fa2d384SViktor Prutyanov void *ptr;
2493fa2d384SViktor Prutyanov
2503fa2d384SViktor Prutyanov s = (s > size) ? size : s;
2513fa2d384SViktor Prutyanov
2523fa2d384SViktor Prutyanov ptr = va_space_resolve(vs, addr);
2533fa2d384SViktor Prutyanov if (!ptr) {
254a15f9749SAkihiko Odaki return false;
2553fa2d384SViktor Prutyanov }
2563fa2d384SViktor Prutyanov
2573fa2d384SViktor Prutyanov if (is_write) {
2583fa2d384SViktor Prutyanov memcpy(ptr, buf, s);
2593fa2d384SViktor Prutyanov } else {
2603fa2d384SViktor Prutyanov memcpy(buf, ptr, s);
2613fa2d384SViktor Prutyanov }
2623fa2d384SViktor Prutyanov
2633fa2d384SViktor Prutyanov size -= s;
2643fa2d384SViktor Prutyanov buf = (uint8_t *)buf + s;
2653fa2d384SViktor Prutyanov addr += s;
2663fa2d384SViktor Prutyanov }
2673fa2d384SViktor Prutyanov
268a15f9749SAkihiko Odaki return true;
2693fa2d384SViktor Prutyanov }
270