Google
  Web www.spinics.net

Misaligned memory accesses handling & ARMv6/ARMv7

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


Hi,

Starting with ARMv6, unaligned memory accesses for the data types which have
size up to 32-bit (halfwords and words) are supported when U bit is set in
CP15 register c1 (and it is always set in linux). But unaligned doubleword or
multiword reads or writes still generate an exception (regardless of the value
in A bit). Results of unaligned memory accesses for various A and U bit
combinations can be found in table 4-3 from ARM1136JF-S technical 
reference manual. And ARMv7 has unaligned memory access support 
permanently enabled, dropping legacy alignment model (with that weird
bit rotation on unaligned accesses).

The problem is that this new alignment support feature of ARMv6 and higher
is not handled properly in 'alignment.c'. More specifically, if CPU encounters
unaligned multiword memory access (a bug in the program), it results in
exception which isn't handled properly in the kernel. The result is that the
buggy program just gets stuck on the problematic instruction, constantly
triggering exceptions. A testcase which can be used to reproduce this 
problem is attached (unaligned_multiword_testcase.c).

One more testcase which uses only C code is in 'testalign.c', but it is less
reliable as the presence/absence of problematic multiword memory access
instruction depends on the optimizations done by the compiler and its version.

The attached patch makes alignment logic behave more reasonably for ARMv6:

echo 0 > /proc/cpu/alignment
/media/mmc2/testalign
i   = 05040302
Bus error (core dumped)                                                         

echo 1 > /proc/cpu/alignment
/media/mmc2/testalign
i   = 05040302
[  131.023437] Alignment trap: testalign (1629) PC=0x00008398 Instr=0xe8940003
Address=0x00010599 FSR 0x011
Bus error (core dumped)                                                         

echo 2 > /proc/cpu/alignment
/media/mmc2/testalign
i   = 05040302
s.a = 05040302, s.b = 09080706                                                  

echo 3 > /proc/cpu/alignment
/media/mmc2/testalign
i   = 05040302
[  148.453125] Alignment trap: testalign (1673) PC=0x00008398 Instr=0xe8940003
Address=0x00010599 FSR 0x011
s.a = 05040302, s.b = 09080706

So normal 32-bit unaligned memory accesses are still fine (by the way, just
setting A bit we would get exceptions on every unaligned memory accesses, even
those that could be correctly and efficiently handled by hardware). And
multiword accesses are either emulated in the kernel or terminate application
depending on the settings in /proc/cpu/alignment

Please review the patch and let me know if anything is wrong with it.

Having this issue fixed will make alignment problems debugging much easier as
buggy applications will crash and generate core dumps instead of deadlocking,
which is pretty confusing.

-- 
Best regards,
Siarhei Siamashka
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index aa109f0..a440177 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -768,6 +768,8 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 
 	if (ai_usermode & 4)
 		force_sig(SIGBUS, current);
+	else if (cpu_architecture() >= CPU_ARCH_ARMv6) /* For ARMv6 we can't skip unaligned instruction */
+		force_sig(SIGBUS, current);
 	else
 		set_cr(cr_no_alignment);
 
@@ -800,6 +802,13 @@ static int __init alignment_init(void)
 	hook_fault_code(1, do_alignment, SIGILL, "alignment exception");
 	hook_fault_code(3, do_alignment, SIGILL, "alignment exception");
 
+	if (cpu_architecture() >= CPU_ARCH_ARMv6) {
+		/* ARMv6 does not particularly need A bit when U bit is set, we get alignment exceptions anyway */
+		cr_alignment &= ~CR_A;
+		cr_no_alignment &= ~CR_A;
+		set_cr(cr_alignment);
+	}
+
 	return 0;
 }
 
int main()
{
    int buffer[3];
    asm volatile ("ldmia %0, {r1, r2}\r\n"
        :
        : "r" ((char *)buffer + 1)
        : "r1", "r2");
    return 0;
}
#include <stdio.h>

struct dword_struct
{
    int a;
    int b;
};

char __attribute__ ((aligned(32))) buffer[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };

int main()
{
    struct dword_struct s;
    int i = *(int *)&buffer[1];
    printf("i   = %08X\n", i);
    s = *(struct dword_struct *)&buffer[1];
    printf("s.a = %08X, s.b = %08X\n", s.a, s.b);
}
-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ:        http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette:  http://www.arm.linux.org.uk/mailinglists/etiquette.php

[Site Home]     [Linux Arm]     [Fedora ARM]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [PDAs]     [Linux]     [Linux Book List]     [Linux MIPS]     [Yosemite Campsites]     [Photos]

Add to Google Google PageRank Checking tool