linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* ARM unaligned MMIO access with attribute((packed))
@ 2011-02-02 16:00 Arnd Bergmann
  2011-02-02 16:37 ` Russell King - ARM Linux
                   ` (2 more replies)
  0 siblings, 3 replies; 20+ messages in thread
From: Arnd Bergmann @ 2011-02-02 16:00 UTC (permalink / raw)
  To: linux-arm-kernel

As noticed by Peter Maydell, the EHCI device driver in Linux gets
miscompiled by some versions of arm-gcc (still need to find out which)
due to a combination of problems:

1. In include/linux/usb/ehci_def.h, struct ehci_caps is defined
with __attribute__((packed)), for no good reason. This is clearly
a bug and needs to get fixed, but other drivers have the same bug
and it used to work. The attribute forces byte access on all members
accessed through pointer dereference, which is not allowed on
MMIO accesses in general. The specific code triggering the problem
in Peter's case is in ehci-omap.c:
        omap->ehci->regs = hcd->regs
                + HC_LENGTH(readl(&omap->ehci->caps->hc_capbase));


2. The ARM version of the readl() function is implemented as a macro
doing a direct pointer dereference with a typecast:

#define __raw_readl(a)          (__chk_io_ptr(a), *(volatile unsigned int __force   *)(a))
#define readl_relaxed(c) ({ u32 __v = le32_to_cpu((__force __le32) \
                                        __raw_readl(__mem_pci(c))); __v; })
#define readl(c)                ({ u32 __v = readl_relaxed(c); __iormb(); __v; })

On other architectures, readl() is implemented using an inline assembly
specifically to prevent gcc from issuing anything but a single 32-bit
load instruction. readl() only makes sense on aligned memory, so in case
of a misaligned pointer argument, it should cause a trap anyway.

3. gcc does not seem to clearly define what happens during a cast between
aligned an packed pointers. In this case, the original pointer is packed
(byte aligned), while the access is done through a 32-bit aligned
volatile unsigned int pointer. In gcc-4.4, casting from "unsigned int
__attribute__((packed))" to "volatile unsigned int" resulted in a 32-bit
aligned access, while casting to "unsigned int" (without volatile) resulted
in four byte accesses. gcc-4.5 seems to have changed this to always do
a byte access in both cases, but still does not document the behavior.
(need to confirm this).

I would suggest fixing this by:

1. auditing all uses of __attribute__((packed)) in the Linux USB code
and other drivers, removing the ones that are potentially harmful.

2. Changing the ARM MMIO functions to use inline assembly instead of
direct pointer dereference.

3. Documenting the gcc behavior as undefined.

Other suggestions?

	Arnd

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2011-04-28 14:16 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-02-02 16:00 ARM unaligned MMIO access with attribute((packed)) Arnd Bergmann
2011-02-02 16:37 ` Russell King - ARM Linux
2011-02-02 17:39   ` Arnd Bergmann
2011-02-02 19:14     ` Ian Lance Taylor
2011-02-02 21:38   ` David Miller
2011-02-02 21:45     ` Russell King - ARM Linux
2011-02-02 21:59       ` David Miller
     [not found]     ` <yw1xei7qm3vy.fsf@unicorn.mansr.com>
2011-02-02 23:23       ` David Miller
2011-02-03  9:26       ` Arnd Bergmann
2011-02-03 15:03   ` Arnd Bergmann
2011-02-02 16:51 ` Richard Guenther
2011-02-02 17:09   ` Russell King - ARM Linux
2011-02-02 17:39   ` Joseph S. Myers
2011-04-26 15:00 ` Rabin Vincent
2011-04-26 18:51   ` Alan Stern
2011-04-27 14:06     ` Rabin Vincent
2011-04-27 16:25       ` Alan Stern
2011-04-27 16:37         ` Arnd Bergmann
2011-04-28 13:35           ` Alan Stern
2011-04-28 14:16             ` Arnd Bergmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).