diff --git a/nyx/hypercall/hypercall.c b/nyx/hypercall/hypercall.c index 64703657e2..a7dd7b90c5 100644 --- a/nyx/hypercall/hypercall.c +++ b/nyx/hypercall/hypercall.c @@ -210,7 +210,7 @@ static void handle_hypercall_get_payload(struct kvm_run *run, CPUState *cpu, uin CPUX86State *env = &(X86_CPU(cpu))->env; GET_GLOBAL_STATE()->parent_cr3 = env->cr[3] & 0xFFFFFFFFFFFFF000ULL; QEMU_PT_PRINTF(CORE_PREFIX, "Payload CR3:\t%lx", (uint64_t)GET_GLOBAL_STATE()->parent_cr3 ); - //print_48_paging2(GET_GLOBAL_STATE()->parent_cr3); + //print_48_pagetables(GET_GLOBAL_STATE()->parent_cr3); if(hypercall_arg&0xFFF){ fprintf(stderr, "[QEMU-Nyx] Error: Payload buffer is not page-aligned! (0x%lx)\n", hypercall_arg); diff --git a/nyx/memory_access.c b/nyx/memory_access.c index ad6dfec8d5..cb4560d293 100644 --- a/nyx/memory_access.c +++ b/nyx/memory_access.c @@ -563,221 +563,240 @@ void remove_all_breakpoints(CPUState *cpu){ } - - - - - - - - - - - -#define PPAGE_SIZE 0x1000 #define PENTRIES 0x200 -#define PLEVEL_4_SHIFT 12 -#define PLEVEL_3_SHIFT 21 -#define PLEVEL_2_SHIFT 30 -#define PLEVEL_1_SHIFT 39 -#define SIGN_EXTEND_TRESHOLD 0x100 -#define SIGN_EXTEND 0xFFFF000000000000ULL -#define PAGETABLE_MASK 0x1FFFFFFFFF000ULL -#define PML4_ENTRY_MASK 0x1FFFFFFFFF000ULL -#define PML3_ENTRY_MASK 0x1FFFFC0000000ULL -#define PML2_ENTRY_MASK 0x1FFFFFFE00000ULL +#define PPAGE_SIZE 0x1000 -#define CHECK_BIT(var,pos) !!(((var) & (1ULL<<(pos)))) - - -static void write_address(uint64_t address, uint64_t size, uint64_t prot){ - //fprintf(stderr, "%s %lx %lx %lx\n", __func__, address, size, prot); - static uint64_t next_address = PAGETABLE_MASK; - static uint64_t last_address = 0x0; - static uint64_t last_prot = 0; - - if((address != next_address || prot != last_prot)){ - /* do not print guard pages or empty pages without any permissions */ - if(last_address && prot && (last_address+size != next_address || prot != last_prot)){ - fprintf(stderr, "%016lx - %016lx %c%c%c\n", - last_address, next_address, - CHECK_BIT(last_prot, 1) ? 'W' : '-', - CHECK_BIT(last_prot, 2) ? 'U' : 'K', - !CHECK_BIT(last_prot, 63)? 'X' : '-'); - } - last_address = address; +static bool read_memory(uint64_t address, uint64_t* buffer, size_t size, bool read_from_snapshot) { + if (unlikely(address == INVALID_ADDRESS)) { + return false; } - next_address = address+size; - last_prot = prot; + + if (unlikely(read_from_snapshot)) { + return read_snapshot_memory( + get_fast_reload_snapshot(), + address, (uint8_t *)buffer, size); + } + // NB: This API exposed by exec.h doesn't signal failure, although it can + // fail. Figure out how to expose the address space object instead and then + // we can actually check the return value here. Until then, will clear the + // buffer contents first. + memset(buffer, 0, size); + cpu_physical_memory_rw(address, (uint8_t*)buffer, size, false); + return true; } -void print_48_paging2(uint64_t cr3){ - uint64_t paging_entries_level_1[PENTRIES]; - uint64_t paging_entries_level_2[PENTRIES]; - uint64_t paging_entries_level_3[PENTRIES]; - uint64_t paging_entries_level_4[PENTRIES]; - - uint64_t address_identifier_1, address_identifier_2, address_identifier_3, address_identifier_4; - uint32_t i1, i2, i3,i4; - - cpu_physical_memory_rw((cr3&PAGETABLE_MASK), (uint8_t *) paging_entries_level_1, PPAGE_SIZE, false); - for(i1 = 0; i1 < 512; i1++){ - if(paging_entries_level_1[i1]){ - address_identifier_1 = ((uint64_t)i1) << PLEVEL_1_SHIFT; - if (i1 & SIGN_EXTEND_TRESHOLD){ - address_identifier_1 |= SIGN_EXTEND; - } - if(CHECK_BIT(paging_entries_level_1[i1], 0)){ /* otherwise swapped out */ - cpu_physical_memory_rw((paging_entries_level_1[i1]&PAGETABLE_MASK), (uint8_t *) paging_entries_level_2, PPAGE_SIZE, false); - for(i2 = 0; i2 < PENTRIES; i2++){ - if(paging_entries_level_2[i2]){ - address_identifier_2 = (((uint64_t)i2) << PLEVEL_2_SHIFT) + address_identifier_1; - if (CHECK_BIT(paging_entries_level_2[i2], 0)){ /* otherwise swapped out */ - if((paging_entries_level_2[i2]&PAGETABLE_MASK) == (paging_entries_level_1[i1]&PAGETABLE_MASK)){ - /* loop */ - continue; - } - - if (CHECK_BIT(paging_entries_level_2[i2], 7)){ - write_address(address_identifier_2, 0x40000000, (uint64_t)paging_entries_level_2[i2] & ((1ULL<<63) | (1ULL<<2) | (1ULL<<1))); - } - else{ - /* otherwise this PDPE references a 1GB page */ - cpu_physical_memory_rw((paging_entries_level_2[i2]&PAGETABLE_MASK), (uint8_t *) paging_entries_level_3, PPAGE_SIZE, false); - for(i3 = 0; i3 < PENTRIES; i3++){ - if(paging_entries_level_3[i3]){ - address_identifier_3 = (((uint64_t)i3) << PLEVEL_3_SHIFT) + address_identifier_2; - if (CHECK_BIT(paging_entries_level_3[i3], 0)){ /* otherwise swapped out */ - if (CHECK_BIT(paging_entries_level_3[i3], 7)){ - write_address(address_identifier_3, 0x200000, (uint64_t)paging_entries_level_3[i3] & ((1ULL<<63) | (1ULL<<2) | (1ULL<<1))); - } - else{ - cpu_physical_memory_rw((paging_entries_level_3[i3]&PAGETABLE_MASK), (uint8_t *) paging_entries_level_4, PPAGE_SIZE, false); - for(i4 = 0; i4 < PENTRIES; i4++){ - if(paging_entries_level_4[i4]){ - address_identifier_4 = (((uint64_t)i4) << PLEVEL_4_SHIFT) + address_identifier_3; - if (CHECK_BIT(paging_entries_level_4[i4], 0)){ - write_address(address_identifier_4, 0x1000, (uint64_t)paging_entries_level_4[i4] & ((1ULL<<63) | (1ULL<<2) | (1ULL<<1))); - } - } - } - } - } - } - } - - } - } - } - } - } - } - } - write_address(0, 0x1000, 0); +__attribute__((always_inline)) +static bool bit(uint64_t value, uint8_t lsb) { + return (value >> lsb) & 1; } - -static uint64_t* load_page_table(uint64_t page_table_address, uint64_t* paging_entries_buffer, uint8_t level, bool read_from_snapshot, bool *success){ - if(page_table_address == INVALID_ADDRESS){ - *success = false; - } - - if (read_from_snapshot){ - *success = read_snapshot_memory(get_fast_reload_snapshot(), page_table_address, (uint8_t *) paging_entries_buffer, PPAGE_SIZE); - } - else{ - cpu_physical_memory_rw(page_table_address, (uint8_t *) paging_entries_buffer, PPAGE_SIZE, false); - *success = true; /* fix this */ - } - return paging_entries_buffer; +__attribute__((always_inline)) +static uint64_t bits(uint64_t value, uint8_t lsb, uint8_t msb) { + return (value & ((0xffffffffffffffffull >> (64 - (msb - lsb + 1))) << lsb)) >> lsb; } -static uint64_t get_48_paging_phys_addr(uint64_t cr3, uint64_t addr, bool read_from_snapshot){ - /* signedness broken af -> fix me! */ - uint16_t pml_4_index = (addr & 0xFF8000000000ULL) >> 39; - uint16_t pml_3_index = (addr & 0x0007FC0000000UL) >> 30; - uint16_t pml_2_index = (addr & 0x000003FE00000UL) >> 21; - uint16_t pml_1_index = (addr & 0x00000001FF000UL) >> 12; - - uint64_t address_identifier_4; - uint64_t paging_entries_buffer[PENTRIES]; - uint64_t* paging_entries_buffer_ptr = NULL; - uint64_t page_table_address = 0; - - bool success = false; - - page_table_address = (cr3&PAGETABLE_MASK); - paging_entries_buffer_ptr = load_page_table(page_table_address, paging_entries_buffer, 0, read_from_snapshot, &success); - - if (unlikely(success == false)){ - goto fail; +// Helper function to load an entire pagetable table. These are PENTRIES +// 64-bit entries, so entries must point to a sufficiently large buffer. +static bool load_table(uint64_t address, uint64_t* entries, bool read_from_snapshot) { + if (unlikely(!read_memory(address, entries, 512 * sizeof(*entries), read_from_snapshot))) { + return false; } - if(paging_entries_buffer_ptr[pml_4_index]){ - address_identifier_4 = ((uint64_t)pml_4_index) << PLEVEL_1_SHIFT; - if (pml_4_index & SIGN_EXTEND_TRESHOLD){ - address_identifier_4 |= SIGN_EXTEND; - } - if(CHECK_BIT(paging_entries_buffer_ptr[pml_4_index], 0)){ /* otherwise swapped out */ + return true; +} - page_table_address = (paging_entries_buffer_ptr[pml_4_index]&PAGETABLE_MASK); - paging_entries_buffer_ptr = load_page_table(page_table_address, paging_entries_buffer, 1, read_from_snapshot, &success); - - if (unlikely(success == false)){ - goto fail; - } - - if(paging_entries_buffer_ptr[pml_3_index]){ - - if (CHECK_BIT(paging_entries_buffer_ptr[pml_3_index], 0)){ /* otherwise swapped out */ - - if (CHECK_BIT(paging_entries_buffer_ptr[pml_3_index], 7)){ - /* 1GB PAGE */ - return (paging_entries_buffer_ptr[pml_3_index] & PML3_ENTRY_MASK) | (0x7FFFFFFF & addr); - } - else{ - - page_table_address = (paging_entries_buffer_ptr[pml_3_index]&PAGETABLE_MASK); - paging_entries_buffer_ptr = load_page_table(page_table_address, paging_entries_buffer, 2, read_from_snapshot, &success); - - if (unlikely(success == false)){ - goto fail; - } - - if(paging_entries_buffer_ptr[pml_2_index]){ - if (CHECK_BIT(paging_entries_buffer_ptr[pml_2_index], 0)){ /* otherwise swapped out */ - if (CHECK_BIT(paging_entries_buffer_ptr[pml_2_index], 7)){ - /* 2MB PAGE */ - return (paging_entries_buffer_ptr[pml_2_index] & PML2_ENTRY_MASK) | (0x3FFFFF & addr); - } - else{ - - page_table_address = (paging_entries_buffer_ptr[pml_2_index]&PAGETABLE_MASK); - paging_entries_buffer_ptr = load_page_table(page_table_address, paging_entries_buffer, 3, read_from_snapshot, &success); - - if (unlikely(success == false)){ - goto fail; - } - - if(paging_entries_buffer_ptr[pml_1_index]){ - if (CHECK_BIT(paging_entries_buffer_ptr[pml_1_index], 0)){ - /* 4 KB PAGE */ - return (paging_entries_buffer_ptr[pml_1_index] & PML4_ENTRY_MASK) | (0xFFF & addr); - } - } - } - } - } - } - } - } - } +// Helper function to load a single pagetable entry. We simplify things by +// returning the same invalid value (0) for both non-present entries and +// any other error conditions, since we don't need to handle these cases +// differently. +static uint64_t load_entry(uint64_t address, uint64_t index, + bool read_from_snapshot) { + uint64_t entry = 0; + if (unlikely(!read_memory(address + (index * sizeof(entry)), &entry, sizeof(entry), + read_from_snapshot))) { + return 0; } - fail: - - return INVALID_ADDRESS; + // Check that the entry is present. + if (unlikely(!bit(entry, 0))) { + return 0; + } + + return entry; +} + +static void print_page(uint64_t address, uint64_t entry, size_t size, bool s, bool w, bool x) { + fprintf(stderr, " %c%c%c %016llx %zx", + s ? 's' : 'u', w ? 'w' : 'r', x ? 'x' : '-', + (bits(entry, 12, 51) << 12) & ~(size - 1), size); +} + +static void print_48_pte(uint64_t address, uint64_t pde_entry, bool read_from_snapshot, + bool s, bool w, bool x) { + uint64_t pte_address = bits(pde_entry, 12, 51) << 12; + uint64_t pte_table[PENTRIES]; + + if (!load_table(pte_address, pte_table, read_from_snapshot)) { + return; + } + + for (size_t i = 0; i < PENTRIES; ++i) { + uint64_t entry = pte_table[i]; + + if (entry) { + fprintf(stderr, "\n 1 %016llx", address | i << 12, entry); + } + + if (!bit(entry, 0)) { + // Not present. + } else { + print_page(address | i << 12, entry, 0x1000, + s & !bit(entry, 2), w & bit(entry, 1), x & !bit(entry, 63)); + } + } +} + +static void print_48_pde(uint64_t address, uint64_t pdpte_entry, bool read_from_snapshot, + bool s, bool w, bool x) { + uint64_t pde_address = bits(pdpte_entry, 12, 51) << 12; + uint64_t pde_table[PENTRIES]; + + if (!load_table(pde_address, pde_table, read_from_snapshot)) { + return; + } + + for (size_t i = 0; i < PENTRIES; ++i) { + uint64_t entry = pde_table[i]; + + if (entry) { + fprintf(stderr, "\n 2 %016llx", address | i << 21, entry); + } + + if (!bit(entry, 0)) { + // Not present. + } else if (bit(entry, 7)) { + print_page(address | i << 21, entry, 0x200000, + s & !bit(entry, 2), w & bit(entry, 1), x & !bit(entry, 63)); + } else { + print_48_pte(address | i << 21, entry, read_from_snapshot, + s & !bit(entry, 2), w & bit(entry, 1), x & !bit(entry, 63)); + } + } +} + +static void print_48_pdpte(uint64_t address, uint64_t pml4_entry, bool read_from_snapshot, + bool s, bool w, bool x) { + uint64_t pdpte_address = bits(pml4_entry, 12, 51) << 12; + uint64_t pdpte_table[PENTRIES]; + + if (!load_table(pdpte_address, pdpte_table, read_from_snapshot)) { + return; + } + + for (size_t i = 0; i < PENTRIES; ++i) { + uint64_t entry = pdpte_table[i]; + + if (entry) { + fprintf(stderr, "\n 3 %016llx", address | i << 30, entry); + } + + if (!bit(entry, 0)) { + // Not present. + } else if (bit(entry, 7)) { + print_page(address | i << 30, entry, 0x40000000, + s & !bit(entry, 2), w & bit(entry, 1), x & !bit(entry, 63)); + } else { + print_48_pde(address | i << 30, entry, read_from_snapshot, + s & !bit(entry, 2), w & bit(entry, 1), x & !bit(entry, 63)); + } + } +} + +static void print_48_pagetables_(uint64_t cr3, bool read_from_snapshot) { + uint64_t pml4_address = bits(cr3, 12, 51) << 12; + uint64_t pml4_table[PENTRIES]; + + if (!load_table(pml4_address, pml4_table, read_from_snapshot)) { + return; + } + + for (size_t i = 0; i < PENTRIES; ++i) { + uint64_t entry = pml4_table[i]; + uint64_t address = i << 39; + // Ensure canonical virtual address + if (bit(address, 47)) { + address |= 0xffff000000000000ul; + } + + if (entry) { + fprintf(stderr, "\n4 %016llx", address, entry); + } + + if (bit(entry, 0)) { + print_48_pdpte(address, entry, read_from_snapshot, + !bit(entry, 2), bit(entry, 1), !bit(entry, 63)); + } + } +} + +void print_48_pagetables(uint64_t cr3) { + static bool printed = false; + if (!printed) { + fprintf(stderr, "pagetables for cr3 %lx", cr3); + print_48_pagetables_(cr3, false); + printed = true; + fprintf(stderr, "\n"); + } +} + +static uint64_t get_48_paging_phys_addr(uint64_t cr3, uint64_t addr, bool read_from_snapshot) { + uint64_t pml4_address = bits(cr3, 12, 51) << 12; + uint64_t pml4_offset = bits(addr, 39, 47); + uint64_t pml4_entry = load_entry(pml4_address, pml4_offset, read_from_snapshot); + if (unlikely(!pml4_entry)) { + return INVALID_ADDRESS; + } + + uint64_t pdpte_address = bits(pml4_entry, 12, 51) << 12; + uint64_t pdpte_offset = bits(addr, 30, 38); + uint64_t pdpte_entry = load_entry(pdpte_address, pdpte_offset, read_from_snapshot); + if (unlikely(!pdpte_entry)) { + return INVALID_ADDRESS; + } + + if (unlikely(bit(pdpte_entry, 7))) { + // 1GByte page translation. + uint64_t page_address = bits(pdpte_entry, 12, 51) << 12; + uint64_t page_offset = bits(addr, 0, 29); + return page_address + page_offset; + } + + uint64_t pde_address = bits(pdpte_entry, 12, 51) << 12; + uint64_t pde_offset = bits(addr, 21, 29); + uint64_t pde_entry = load_entry(pde_address, pde_offset, read_from_snapshot); + if (unlikely(!pde_entry)) { + return INVALID_ADDRESS; + } + + if (unlikely(bit(pde_entry, 7))) { + // 2MByte page translation. + uint64_t page_address = bits(pde_entry, 12, 51) << 12; + uint64_t page_offset = bits(addr, 0, 20); + return page_address + page_offset; + } + + uint64_t pte_address = bits(pde_entry, 12, 51) << 12; + uint64_t pte_offset = bits(addr, 12, 20); + uint64_t pte_entry = load_entry(pte_address, pte_offset, read_from_snapshot); + if (unlikely(!pte_entry)) { + return INVALID_ADDRESS; + } + + // 4Kbyte page translation. + uint64_t page_address = bits(pte_entry, 12, 51) << 12; + uint64_t page_offset = bits(addr, 0, 11); + return page_address + page_offset; } //#define DEBUG_48BIT_WALK diff --git a/nyx/memory_access.h b/nyx/memory_access.h index 893f59982a..2181d25542 100644 --- a/nyx/memory_access.h +++ b/nyx/memory_access.h @@ -63,7 +63,7 @@ bool dump_page_cr3_snapshot(uint64_t address, uint8_t* data, CPUState *cpu, uint bool dump_page_cr3_ht(uint64_t address, uint8_t* data, CPUState *cpu, uint64_t cr3); bool is_addr_mapped_cr3_snapshot(uint64_t address, CPUState *cpu, uint64_t cr3); -void print_48_paging2(uint64_t cr3); +void print_48_pagetables(uint64_t cr3); bool dump_page_ht(uint64_t address, uint8_t* data, CPUState *cpu);