From: arnd@arndb.de (Arnd Bergmann)
To: linux-arm-kernel@lists.infradead.org
Subject: ARM unaligned MMIO access with attribute((packed))
Date: Wed, 2 Feb 2011 17:00:20 +0100 [thread overview]
Message-ID: <201102021700.20683.arnd@arndb.de> (raw)
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
WARNING: multiple messages have this Message-ID (diff)
From: Arnd Bergmann <arnd@arndb.de>
To: linux-arm-kernel@lists.infradead.org, linux-usb@vger.kernel.org,
Ulrich Weigand <Ulrich.Weigand@de.ibm.com>
Cc: linux-kernel@vger.kernel.org, gcc@gcc.gnu.org,
Peter Maydell <peter.maydell@linaro.org>
Subject: ARM unaligned MMIO access with attribute((packed))
Date: Wed, 2 Feb 2011 17:00:20 +0100 [thread overview]
Message-ID: <201102021700.20683.arnd@arndb.de> (raw)
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
next reply other threads:[~2011-02-02 16:00 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-02-02 16:00 Arnd Bergmann [this message]
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 16:37 ` Russell King - ARM Linux
2011-02-02 17:39 ` Arnd Bergmann
2011-02-02 17:39 ` Arnd Bergmann
2011-02-02 19:14 ` Ian Lance Taylor
2011-02-02 19:14 ` Ian Lance Taylor
2011-02-02 21:38 ` David Miller
2011-02-02 21:38 ` David Miller
2011-02-02 21:45 ` Russell King - ARM Linux
2011-02-02 21:45 ` Russell King - ARM Linux
2011-02-02 21:59 ` David Miller
2011-02-02 21:59 ` David Miller
2011-02-02 23:08 ` Måns Rullgård
2011-02-02 23:23 ` David Miller
2011-02-02 23:23 ` David Miller
2011-02-03 9:26 ` Arnd Bergmann
2011-02-03 9:26 ` Arnd Bergmann
2011-02-03 15:03 ` Arnd Bergmann
2011-02-03 15:03 ` Arnd Bergmann
2011-02-02 16:51 ` Richard Guenther
2011-02-02 16:51 ` Richard Guenther
2011-02-02 17:09 ` Russell King - ARM Linux
2011-02-02 17:09 ` Russell King - ARM Linux
2011-02-02 17:39 ` Joseph S. Myers
2011-02-02 17:39 ` Joseph S. Myers
2011-04-26 15:00 ` Rabin Vincent
2011-04-26 15:00 ` Rabin Vincent
2011-04-26 18:51 ` Alan Stern
2011-04-26 18:51 ` Alan Stern
2011-04-27 14:06 ` Rabin Vincent
2011-04-27 14:06 ` Rabin Vincent
2011-04-27 16:25 ` Alan Stern
2011-04-27 16:25 ` Alan Stern
2011-04-27 16:37 ` Arnd Bergmann
2011-04-27 16:37 ` Arnd Bergmann
2011-04-28 13:35 ` Alan Stern
2011-04-28 13:35 ` Alan Stern
2011-04-28 14:16 ` Arnd Bergmann
2011-04-28 14:16 ` Arnd Bergmann
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=201102021700.20683.arnd@arndb.de \
--to=arnd@arndb.de \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.