diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2020-10-27 12:50:50 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2020-10-27 12:50:50 +0000 |
commit | 388c90a1983b0aa028c2e0b7a967b50d23adc2b4 (patch) | |
tree | 06f26676540da84911063aa47d6517cd494dce73 /sys | |
parent | 22bb604dee9e0d458d1053c6d2d38dd80ef4bb25 (diff) |
Add limited emulation of unaligned access in the kernel. The radeondrm(4)
and amdgpu(4) drivers do unaligned access to uncached memory because the
Linux DRM code doesn't mark pointers to device memory (VRAM) as volatile.
I'm still investigating a better solution but this gets things going such
that we can also look at the userland side of this issue.
discussed with deraadt@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/powerpc64/powerpc64/trap.c | 57 |
1 files changed, 56 insertions, 1 deletions
diff --git a/sys/arch/powerpc64/powerpc64/trap.c b/sys/arch/powerpc64/powerpc64/trap.c index e3bac5a5df5..3be8dde4f45 100644 --- a/sys/arch/powerpc64/powerpc64/trap.c +++ b/sys/arch/powerpc64/powerpc64/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.45 2020/10/22 13:41:49 deraadt Exp $ */ +/* $OpenBSD: trap.c,v 1.46 2020/10/27 12:50:49 kettenis Exp $ */ /* * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org> @@ -162,6 +162,61 @@ trap(struct trapframe *frame) printf("dar 0x%lx dsisr 0x%lx\n", frame->dar, frame->dsisr); goto fatal; + case EXC_ALI: + { + /* + * In general POWER allows unaligned loads and stores + * and executes those instructions in an efficient + * way. As a result compilers may combine word-sized + * stores into a single doubleword store instruction + * even if the address is not guaranteed to be + * doubleword aligned. Such unaligned stores are not + * supported in storage that is Caching Inibited. + * Access to such storage should be done through + * volatile pointers which inhibit the aforementioned + * optimizations. Unfortunately code in the amdgpu(4) + * and radeondrm(4) drivers happens to run into such + * unaligned access because pointers aren't always + * marked as volatile. For that reason we emulate + * certain store instructions here. + */ + uint32_t insn = *(uint32_t *)frame->srr0; + + /* std and stdu */ + if ((insn & 0xfc000002) == 0xf8000000) { + uint32_t rs = (insn >> 21) & 0x1f; + uint32_t ra = (insn >> 16) & 0x1f; + uint64_t ds = insn & 0xfffc; + uint64_t ea; + + if ((insn & 0x00000001) == 0 && ra == 0) + panic("invalid stdu instruction form"); + + if (ds & 0x8000) + ds |= ~0x7fff; /* sign extend */ + if (ra == 0) + ea = ds; + else + ea = frame->fixreg[ra] + ds; + + /* + * If the effective address isn't 32-bit + * aligned, or if data access cannot be + * performed because of the access violates + * storage protection, this will trigger + * another trap, which we can handle. + */ + *(volatile uint32_t *)ea = frame->fixreg[rs] >> 32; + *(volatile uint32_t *)(ea + 4) = frame->fixreg[rs]; + if (insn & 0x00000001) + frame->fixreg[ra] = ea; + frame->srr0 += 4; + return; + } + printf("dar 0x%lx dsisr 0x%lx\n", frame->dar, frame->dsisr); + goto fatal; + } + case EXC_DSE|EXC_USER: pm = p->p_vmspace->vm_map.pmap; error = pmap_slbd_fault(pm, frame->dar); |