230 lines
8.2 KiB
C
230 lines
8.2 KiB
C
/*
|
|
*
|
|
* Copyright (c) 2019 Jonathan Afek <jonyafek@me.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "qemu-common.h"
|
|
#include "hw/arm/boot.h"
|
|
#include "sysemu/sysemu.h"
|
|
#include "qemu/error-report.h"
|
|
#include "hw/arm/xnu_pagetable.h"
|
|
#include "hw/loader.h"
|
|
#include "qemu/osdep.h"
|
|
#include "target/arm/idau.h"
|
|
#include "trace.h"
|
|
#include "cpu.h"
|
|
#include "internals.h"
|
|
#include "exec/gdbstub.h"
|
|
#include "exec/helper-proto.h"
|
|
#include "qemu/host-utils.h"
|
|
#include "sysemu/arch_init.h"
|
|
#include "sysemu/sysemu.h"
|
|
#include "qemu/bitops.h"
|
|
#include "qemu/crc32c.h"
|
|
#include "exec/exec-all.h"
|
|
#include "exec/cpu_ldst.h"
|
|
#include "arm_ldst.h"
|
|
#include "hw/semihosting/semihost.h"
|
|
|
|
#define PHYS_ADDR_SIZE (40)
|
|
#define TG_16K_SIZE (14)
|
|
#define PAGE_MASK_16K (~(((uint64_t)1 << TG_16K_SIZE) - 1))
|
|
#define TE_PHYS_ADDR_MASK ((((uint64_t)1 << PHYS_ADDR_SIZE) - 1) & \
|
|
PAGE_MASK_16K)
|
|
|
|
#define TG_16KB (1)
|
|
#define TG_16KB_LEVEL0_INDEX (47)
|
|
#define TG_16KB_LEVEL0_SIZE (1)
|
|
#define TG_16KB_LEVEL1_INDEX (36)
|
|
#define TG_16KB_LEVEL1_SIZE (11)
|
|
#define TG_16KB_LEVEL2_INDEX (25)
|
|
#define TG_16KB_LEVEL2_SIZE (11)
|
|
#define TG_16KB_LEVEL3_INDEX (14)
|
|
#define TG_16KB_LEVEL3_SIZE (11)
|
|
|
|
#define TCR_IPS_INDEX (32)
|
|
#define TCR_IPS_SIZE (3)
|
|
#define TCR_IPS_40_ADDR_SIZE (2)
|
|
#define TCR_TG1_INDEX (30)
|
|
#define TCR_TG1_SIZE (2)
|
|
#define TCR_T1SZ_INDEX (16)
|
|
#define TCR_T1SZ_SIZE (6)
|
|
#define TCR_TG0_INDEX (14)
|
|
#define TCR_TG0_SIZE (2)
|
|
#define TCR_T0SZ_INDEX (0)
|
|
#define TCR_T0SZ_SIZE (6)
|
|
|
|
#define TE_ACCESS_PERMS_INDEX (6)
|
|
#define TE_ACCESS_PERMS_SIZE (2)
|
|
#define TE_ACCESS_PERMS_MASK ((((uint64_t)1 << TE_ACCESS_PERMS_SIZE) - 1) << \
|
|
TE_ACCESS_PERMS_INDEX)
|
|
#define TE_ACCESS_PERMS_ZERO_MASK (~TE_ACCESS_PERMS_MASK)
|
|
#define TE_ACCESS_PERMS_KERN_RW (0)
|
|
#define TE_XN_INDEX (53)
|
|
#define TE_XN_SIZE (2)
|
|
#define TE_XN_MASK ((((uint64_t)1 << TE_XN_SIZE) - 1) << TE_XN_INDEX)
|
|
#define TE_XN_ZERO_MASK (~TE_XN_MASK)
|
|
#define TE_XN_KERN_EXE ((uint64_t)2 << TE_XN_INDEX)
|
|
#define TE_TYPE_INDEX (0)
|
|
#define TE_TYPE_SIZE (2)
|
|
#define TE_TYPE_TABLE_DESC (3)
|
|
#define TE_TYPE_L3_BLOCK (3)
|
|
|
|
hwaddr pt_tte_el1(ARMCPU *cpu, AddressSpace *as, hwaddr va, bool make_exe)
|
|
{
|
|
CPUARMState *env = &cpu->env;
|
|
uint64_t tcr = env->cp15.tcr_el[1].raw_tcr;
|
|
uint64_t tcr_ips = extract64(tcr, TCR_IPS_INDEX, TCR_IPS_SIZE);
|
|
uint64_t tcr_tg1 = extract64(tcr, TCR_TG1_INDEX, TCR_TG1_SIZE);
|
|
uint64_t tcr_t1sz = extract64(tcr, TCR_T1SZ_INDEX, TCR_T1SZ_SIZE);
|
|
uint64_t tcr_tg0 = extract64(tcr, TCR_TG0_INDEX, TCR_TG0_SIZE);
|
|
uint64_t tcr_t0sz = extract64(tcr, TCR_T0SZ_INDEX, TCR_T0SZ_SIZE);
|
|
|
|
hwaddr tt = 0;
|
|
hwaddr te = 0;
|
|
uint64_t tg = 0;
|
|
uint64_t tsz = 0;
|
|
|
|
//currently only support 40bit addresses configuration
|
|
if (TCR_IPS_40_ADDR_SIZE != tcr_ips) {
|
|
abort();
|
|
}
|
|
//fprintf(stderr, "pt_tte_el1: tcr: 0x%016llx tcr_ips: 0x%016llx tcr_tg1: 0x%016llx tcr_t1sz: 0x%016llx tcr_tg0: %016llx tcr_t0sz: 0x%016llx va: 0x%016llx\n",
|
|
// tcr, tcr_ips, tcr_tg1, tcr_t1sz, tcr_tg0, tcr_t0sz, va);
|
|
|
|
if (extract64(va, 63, 1) == 1) {
|
|
uint64_t one_bits = extract64(va, 64 - tcr_t1sz, tcr_t1sz);
|
|
uint64_t one_bits_verify = (1 << tcr_t1sz) - 1;
|
|
//fprintf(stderr, "90 pt_tte_el1: te: 0x%016llx\n", te);
|
|
if ((one_bits & one_bits_verify) != one_bits_verify) {
|
|
fprintf(stderr, "9 pt_tte_el1: te: 0x%016llx\n", te);
|
|
abort();
|
|
}
|
|
tt = env->cp15.ttbr1_el[1];
|
|
tg = tcr_tg1;
|
|
tsz = tcr_t1sz;
|
|
} else {
|
|
uint64_t zero_bits = extract64(va, 64 - tcr_t0sz, tcr_t0sz);
|
|
//fprintf(stderr, "91 pt_tte_el1: te: 0x%016lx\n", te);
|
|
if (0 != zero_bits) {
|
|
fprintf(stderr, "10 pt_tte_el1: te: 0x%016llx\n", te);
|
|
abort();
|
|
}
|
|
tt = env->cp15.ttbr0_el[1];
|
|
tg = tcr_tg0;
|
|
tsz = tcr_t0sz;
|
|
}
|
|
|
|
//currently only support parsing 16kg granule page tables
|
|
if (TG_16KB != tg) {
|
|
fprintf(stderr, "8 pt_tte_el1: te: 0x%016llx\n", te);
|
|
abort();
|
|
}
|
|
|
|
//currently only support level 1 base entries
|
|
if ((tsz < (64 - TG_16KB_LEVEL0_INDEX)) ||
|
|
(tsz >= (64 - TG_16KB_LEVEL1_INDEX))) {
|
|
fprintf(stderr, "7 pt_tte_el1: te: 0x%016llx\n", te);
|
|
abort();
|
|
}
|
|
|
|
uint64_t l1_index_size = 64 - tsz - TG_16KB_LEVEL1_INDEX;
|
|
uint64_t l1_idx = extract64(va, TG_16KB_LEVEL1_INDEX, l1_index_size);
|
|
uint64_t l2_idx = extract64(va, TG_16KB_LEVEL2_INDEX, TG_16KB_LEVEL2_SIZE);
|
|
uint64_t l3_idx = extract64(va, TG_16KB_LEVEL3_INDEX, TG_16KB_LEVEL3_SIZE);
|
|
|
|
address_space_rw(as, (tt + (sizeof(hwaddr) * l1_idx)),
|
|
MEMTXATTRS_UNSPECIFIED, (uint8_t *)&te, sizeof(te), 0);
|
|
if (0 == te) {
|
|
fprintf(stderr, "6 pt_tte_el1: te: 0x%016llx\n", te);
|
|
abort();
|
|
}
|
|
|
|
uint64_t te_type = extract64(te, TE_TYPE_INDEX, TE_TYPE_SIZE);
|
|
//currently only support table description level1 entries
|
|
if (TE_TYPE_TABLE_DESC != te_type) {
|
|
fprintf(stderr, "5 pt_tte_el1: te: 0x%016llx\n", te);
|
|
abort();
|
|
}
|
|
|
|
//layer 2
|
|
tt = te & TE_PHYS_ADDR_MASK;
|
|
address_space_rw(as, (tt + (sizeof(hwaddr) * l2_idx)),
|
|
MEMTXATTRS_UNSPECIFIED, (uint8_t *)&te, sizeof(te), 0);
|
|
if (0 == te) {
|
|
fprintf(stderr, "4 pt_tte_el1: te: 0x%016llx\n", te);
|
|
abort();
|
|
}
|
|
|
|
te_type = extract64(te, TE_TYPE_INDEX, TE_TYPE_SIZE);
|
|
//currently only support table description level2 entries
|
|
if (TE_TYPE_TABLE_DESC != te_type) {
|
|
fprintf(stderr, "3 pt_tte_el1: te: 0x%016llx\n", te);
|
|
abort();
|
|
}
|
|
|
|
//layer 3
|
|
tt = te & TE_PHYS_ADDR_MASK;
|
|
hwaddr l3_te_addr = tt + (sizeof(hwaddr) * l3_idx);
|
|
address_space_rw(as, l3_te_addr, MEMTXATTRS_UNSPECIFIED, (uint8_t *)&te,
|
|
sizeof(te), 0);
|
|
if (0 == te) {
|
|
fprintf(stderr, "2 pt_tte_el1: te: 0x%016llx\n", te);
|
|
abort();
|
|
}
|
|
|
|
te_type = extract64(te, TE_TYPE_INDEX, TE_TYPE_SIZE);
|
|
//sanity - l3 entries can only be block entries or invalid entries
|
|
if (TE_TYPE_L3_BLOCK != te_type) {
|
|
fprintf(stderr, "1 pt_tte_el1: te: 0x%016llx\n", te);
|
|
abort();
|
|
}
|
|
|
|
//fprintf(stderr, "pt_tte_el1: te: 0x%016llx\n", te);
|
|
|
|
if (make_exe) {
|
|
//fprintf(stderr, "pt_tte_el1: TE_ACCESS_PERMS_ZERO_MASK: 0x%016llx\n", TE_ACCESS_PERMS_ZERO_MASK);
|
|
//fprintf(stderr, "pt_tte_el1: TE_XN_ZERO_MASK: 0x%016llx\n", TE_XN_ZERO_MASK);
|
|
//fprintf(stderr, "pt_tte_el1: TE_ACCESS_PERMS_KERN_RW: 0x%016llx\n", TE_ACCESS_PERMS_KERN_RW);
|
|
//fprintf(stderr, "pt_tte_el1: TE_XN_KERN_EXE: 0x%016llx\n", TE_XN_KERN_EXE);
|
|
te &= TE_ACCESS_PERMS_ZERO_MASK & TE_XN_ZERO_MASK;
|
|
te |= TE_ACCESS_PERMS_KERN_RW | TE_XN_KERN_EXE;
|
|
address_space_rw(as, l3_te_addr, MEMTXATTRS_UNSPECIFIED,
|
|
(uint8_t *)&te, sizeof(te), 1);
|
|
tlb_flush(CPU(cpu));
|
|
}
|
|
|
|
//fprintf(stderr, "pt_tte_el1: te: 0x%016llx\n", te);
|
|
|
|
uint64_t page_offset = extract64(va, 0, TG_16K_SIZE);
|
|
return (te & TE_PHYS_ADDR_MASK) + page_offset;
|
|
}
|
|
|
|
void va_make_exec(ARMCPU *cpu, AddressSpace *as, hwaddr va, hwaddr size)
|
|
{
|
|
hwaddr curr_va = va & PAGE_MASK_16K;
|
|
while (curr_va < va + size) {
|
|
pt_tte_el1(cpu, as, curr_va, true);
|
|
curr_va += ((uint64_t)1 << TG_16K_SIZE);
|
|
}
|
|
}
|