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 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 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 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 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 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 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 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 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 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 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 1393fa2d384SViktor Prutyanov static uint64_t pde_index(uint64_t va) 1403fa2d384SViktor Prutyanov { 1413fa2d384SViktor Prutyanov return (va >> 21) & 0x1FF; 1423fa2d384SViktor Prutyanov } 1433fa2d384SViktor Prutyanov 1443fa2d384SViktor Prutyanov static uint64_t pdba_base(uint64_t pdpe) 1453fa2d384SViktor Prutyanov { 1463fa2d384SViktor Prutyanov return pdpe & 0xFFFFFFFFFF000; 1473fa2d384SViktor Prutyanov } 1483fa2d384SViktor Prutyanov 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 1573fa2d384SViktor Prutyanov static uint64_t pte_index(uint64_t va) 1583fa2d384SViktor Prutyanov { 1593fa2d384SViktor Prutyanov return (va >> 12) & 0x1FF; 1603fa2d384SViktor Prutyanov } 1613fa2d384SViktor Prutyanov 1623fa2d384SViktor Prutyanov static uint64_t ptba_base(uint64_t pde) 1633fa2d384SViktor Prutyanov { 1643fa2d384SViktor Prutyanov return pde & 0xFFFFFFFFFF000; 1653fa2d384SViktor Prutyanov } 1663fa2d384SViktor Prutyanov 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 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 1803fa2d384SViktor Prutyanov static bool is_present(uint64_t entry) 1813fa2d384SViktor Prutyanov { 1823fa2d384SViktor Prutyanov return entry & 0x1; 1833fa2d384SViktor Prutyanov } 1843fa2d384SViktor Prutyanov 1853fa2d384SViktor Prutyanov static bool page_size_flag(uint64_t entry) 1863fa2d384SViktor Prutyanov { 1873fa2d384SViktor Prutyanov return entry & (1 << 7); 1883fa2d384SViktor Prutyanov } 1893fa2d384SViktor Prutyanov 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 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 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 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 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