Skip to content

Instantly share code, notes, and snippets.

@ktemkin
Created September 9, 2016 00:37
Show Gist options
  • Select an option

  • Save ktemkin/049755ede87c8af20b344b18cbb0c77f to your computer and use it in GitHub Desktop.

Select an option

Save ktemkin/049755ede87c8af20b344b18cbb0c77f to your computer and use it in GitHub Desktop.
Tampoline E0->EL2 PoC
/**
* Stuck inside the Xen domain-setup code...
*/
static struct mmio_handler_ops poc_mmio_ops;
static void memory_trampoline_demo(struct domain *d)
{
/*
* Remove domain permissions for the guest-physical memory address
* 0x12345000. This should cause a second-level translation fault if
* that memory address is accessed.
*/
unmap_mmio_regions(d, _gfn(0x12345000), 4096, _mfn(0x12345000));
/*
* Ask Xen to route any second-level faults to those addresses to us.
* If you were writing the hypervisor, you'd provide a fast-path to
* your handler interface here.
*/
register_mmio_handler(d, &poc_mmio_ops, 0x12345000, 4096, NULL);
}
/**
* The target code up at EL2. Since I decided to abuse Xen's MMIO virtualization
* to get Xen to route the trap to me, I get a bunch of additional information
* I don't need. In a custom hypervisor, you'd just check the syndrome register
* in your synchronous abort handler and then jump to your desired EL2 code.
*/
static int demo_trampoline_target(struct vcpu *v, mmio_info_t *not_important,
register_t *ignore_me, void *ignore_me_too)
{
printk(XENLOG_G_ERR "Bounced up to EL2!\n");
vcpu_show_registers(v);
return 1;
}
static struct mmio_handler_ops poc_mmio_ops = {
.read = demo_trampoline_target,
};
/**
* Set up the hardware domain for the Tegra, giving it mediated access to the
* platform's legacy interrupt controller.
*/
static int tegra_specific_mapping(struct domain *d)
{
int rc;
unsigned long pfn_start, pfn_end;
pfn_start = paddr_to_pfn(TEGRA_ICTLR_BASE);
pfn_end = DIV_ROUND_UP(TEGRA_ICTLR_BASE + (TEGRA_ICTLR_SIZE * TEGRA_ICTLR_COUNT), PAGE_SIZE);
/* XXX HAX FOR POC */
memory_trampoline_demo(d);
/* Force all access to the ictlr to go through our mediators. */
rc = iomem_deny_access(d, pfn_start, pfn_end);
if (rc)
panic("Could not deny access to the Tegra LIC iomem!\n");
rc = unmap_mmio_regions(d, _gfn(pfn_start), pfn_end - pfn_start + 1,
_mfn(pfn_start));
if (rc)
panic("Could not deny access to the Tegra LIC!\n");
register_mmio_handler(d, &tegra_mmio_ops_ictlr,
TEGRA_ICTLR_BASE, TEGRA_ICTLR_SIZE * TEGRA_ICTLR_COUNT, NULL);
return 0;
}
/**
* linux kernel module
*/
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <asm/uaccess.h>
static int map_in_bounce(struct file *filp, struct vm_area_struct *vma)
{
/* You'd want to check permissions/sanity here, blah blah etc. */
int rc = remap_pfn_range(vma, vma->vm_start, 0x12345UL, PAGE_SIZE, vma->vm_page_prot);
printk(KERN_WARNING "Remap result %d.\n", rc);
return rc;
}
static struct file_operations bounce_fops = {
.owner = THIS_MODULE,
.mmap = map_in_bounce
};
static struct miscdevice bounce_device = {
MISC_DYNAMIC_MINOR, "bounce", &bounce_fops
};
void __init trap_to_el2(void)
{
volatile int target;
int * bounce = (int *)ioremap(0x12345000UL, PAGE_SIZE);
printk(KERN_WARNING "Prodding buffer from EL1...\n");
target = *bounce;
iounmap(bounce);
}
int __init init_example(void)
{
/* First, demo using the trampoline from EL1: */
trap_to_el2();
/* And then set up our bounce device. */
misc_register(&bounce_device);
return 0;
}
void __exit exit_example(void)
{
/* Tear down our bounce device. */
misc_deregister(&bounce_device);
}
module_init(init_example);
module_exit(exit_example);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PoC for EL0->EL2 without EL1");
Prodding buffer from EL0...
(XEN) Bounced up to EL2!
(XEN) ----[ Xen-4.8-unstable arm64 debug=y Not tainted ]----
(XEN) CPU: 2
(XEN) PC: 00000000004007d0
(XEN) LR: 00000000004008b0
(XEN) SP_EL0: 0000007fe8471030
(XEN) SP_EL1: ffffffc035dbbff0
(XEN) CPSR: 60000000 MODE:64-bit EL0t (Guest User)
(XEN) X0: 00000000deadbeef X1: 00000000cafebabe X2: 00000000badcafe5
(XEN) X3: 000000005afec0de X4: 00000079012e8000 X5: 00000000deadbeef
(XEN) X6: 00000000cafebabe X7: 00000000badcafe5 X8: 000000005afec0de
(XEN) X9: 00000079012e8000 X10: 0101010101010101 X11: 0000000000000038
(XEN) X12: 0000000000000000 X13: 000000790116c910 X14: 00000079012ebc70
(XEN) X15: 000000790115e098 X16: 0000000000000000 X17: 0000000000410cd8
(XEN) X18: 0000000000040600 X19: 00000000004008e0 X20: 0000000000000000
(XEN) X21: 0000000000000000 X22: 0000000000000000 X23: 0000000000000000
(XEN) X24: 0000000000000000 X25: 0000000000000000 X26: 0000000000000000
(XEN) X27: 0000000000000000 X28: 0000000000000000 FP: 0000007fe8471060
(XEN)
(XEN) ELR_EL1: 0000007901216cd8
(XEN) ESR_EL1: 56000000
(XEN) FAR_EL1: 0000007958d73654
(XEN)
(XEN) SCTLR_EL1: 34d5d91d
(XEN) TCR_EL1: 34b5193519
(XEN) TTBR0_EL1: 031a0000b9a81000
(XEN) TTBR1_EL1: 00000000a1033000
(XEN)
(XEN) VTCR_EL2: 80043594
(XEN) VTTBR_EL2: 000100017ff62000
(XEN)
(XEN) SCTLR_EL2: 30cd183d
(XEN) HCR_EL2: 000000008038663f
(XEN) TTBR0_EL2: 000000017fefe000
(XEN)
(XEN) ESR_EL2: 93c58005
(XEN) HPFAR_EL2: 0000000000123450
(XEN) FAR_EL2: 00000079012e8000
(XEN)
And back at EL0!
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>
void try_bounce(int *bounce, uint64_t a, uint64_t b, uint64_t c, uint64_t d)
{
/* This isn't special, it just makes it clear what the registers are
* when we call bounce. */
asm volatile(
"mov x0, %0\n\t"
"mov x1, %1\n\t"
"mov x2, %2\n\t"
"mov x3, %3\n\t"
"mov x4, %4\n\t"
"ldr x5, [x4]\n\t"
:
: "r" (a), "r" (b), "r" (c), "r" (d), "r" (bounce)
: "r0", "r1", "r2", "r3", "r4"
);
}
int main(int argc, char **argv)
{
int fd;
int *bounce;
fd = open("/dev/bounce", O_RDONLY);
if(fd < 0)
{
printf("Whoops, couldn't open. Are you root?\n");
return -1;
}
printf("Opened %d.\n", fd);
/* Get our handle on the trampoline pages. */
bounce = mmap(NULL, 4096, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
if(bounce == MAP_FAILED) {
printf("Whoops, couldn't map (%d).\n", errno);
return -1;
}
printf("Prodding buffer from EL0...\n");
try_bounce(bounce, 0xDEADBEEF, 0xCAFEBABE, 0xBADCAFE5, 0x5AFEC0DE);
printf("And back at EL0!\n");
munmap(bounce, 4096);
close(fd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment