historical/m0-applesillicon.git/xnu-qemu-arm64-5.1.0/hw/arm/xnu_pagetable.c
2024-01-16 11:20:27 -06:00

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);
}
}