summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2020-10-27 12:50:50 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2020-10-27 12:50:50 +0000
commit388c90a1983b0aa028c2e0b7a967b50d23adc2b4 (patch)
tree06f26676540da84911063aa47d6517cd494dce73 /sys
parent22bb604dee9e0d458d1053c6d2d38dd80ef4bb25 (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.c57
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);