summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2015-08-20 20:50:10 +0000
committerkettenis <kettenis@openbsd.org>2015-08-20 20:50:10 +0000
commitd447bc8096b4d7734a9f0f7ed71498025ea75d81 (patch)
tree0ea7b4f8f994b3442fcc40475c52e4be248811bc
parentAvoid left-shifting a negative integer, which is undefined. Replace (diff)
downloadwireguard-openbsd-d447bc8096b4d7734a9f0f7ed71498025ea75d81.tar.xz
wireguard-openbsd-d447bc8096b4d7734a9f0f7ed71498025ea75d81.zip
Respect the access size when reading or writing to pci config space.
This version also makes sure writes are properly aligned, preventing the panics seen with the previous incarnation of this change. Fixes battery status passthrough in vmware, brightness keys on my x220 and who knows what else. ok mlarkin@, deraadt@
-rw-r--r--sys/dev/acpi/acpi.c95
-rw-r--r--sys/dev/acpi/dsdt.c5
2 files changed, 86 insertions, 14 deletions
diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c
index 2b05c37bba1..f4338e2ad88 100644
--- a/sys/dev/acpi/acpi.c
+++ b/sys/dev/acpi/acpi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: acpi.c,v 1.291 2015/08/04 15:21:59 deraadt Exp $ */
+/* $OpenBSD: acpi.c,v 1.292 2015/08/20 20:50:10 kettenis Exp $ */
/*
* Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
* Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
@@ -218,6 +218,50 @@ struct acpi_softc *acpi_softc;
#define acpi_bus_space_map _bus_space_map
#define acpi_bus_space_unmap _bus_space_unmap
+uint8_t
+acpi_pci_conf_read_1(pci_chipset_tag_t pc, pcitag_t tag, int reg)
+{
+ uint32_t val = pci_conf_read(pc, tag, reg & ~0x3);
+ return (val >> ((reg & 0x3) << 3));
+}
+
+uint16_t
+acpi_pci_conf_read_2(pci_chipset_tag_t pc, pcitag_t tag, int reg)
+{
+ uint32_t val = pci_conf_read(pc, tag, reg & ~0x2);
+ return (val >> ((reg & 0x2) << 3));
+}
+
+uint32_t
+acpi_pci_conf_read_4(pci_chipset_tag_t pc, pcitag_t tag, int reg)
+{
+ return pci_conf_read(pc, tag, reg);
+}
+
+void
+acpi_pci_conf_write_1(pci_chipset_tag_t pc, pcitag_t tag, int reg, uint8_t val)
+{
+ uint32_t tmp = pci_conf_read(pc, tag, reg & ~0x3);
+ tmp &= ~(0xff << ((reg & 0x3) << 3));
+ tmp |= (val << ((reg & 0x3) << 3));
+ pci_conf_write(pc, tag, reg & ~0x3, tmp);
+}
+
+void
+acpi_pci_conf_write_2(pci_chipset_tag_t pc, pcitag_t tag, int reg, uint16_t val)
+{
+ uint32_t tmp = pci_conf_read(pc, tag, reg & ~0x2);
+ tmp &= ~(0xffff << ((reg & 0x2) << 3));
+ tmp |= (val << ((reg & 0x2) << 3));
+ pci_conf_write(pc, tag, reg & ~0x2, tmp);
+}
+
+void
+acpi_pci_conf_write_4(pci_chipset_tag_t pc, pcitag_t tag, int reg, uint32_t val)
+{
+ pci_conf_write(pc, tag, reg, val);
+}
+
int
acpi_gasio(struct acpi_softc *sc, int iodir, int iospace, uint64_t address,
int access_size, int len, void *buffer)
@@ -227,7 +271,7 @@ acpi_gasio(struct acpi_softc *sc, int iodir, int iospace, uint64_t address,
bus_space_handle_t ioh;
pci_chipset_tag_t pc;
pcitag_t tag;
- int reg, idx, ival, sval;
+ int reg, idx;
dnprintf(50, "gasio: %.2x 0x%.8llx %s\n",
iospace, address, (iodir == ACPI_IOWRITE) ? "write" : "read");
@@ -326,19 +370,47 @@ acpi_gasio(struct acpi_softc *sc, int iodir, int iospace, uint64_t address,
ACPI_PCI_BUS(address), ACPI_PCI_DEV(address),
ACPI_PCI_FN(address));
- /* XXX: This is ugly. read-modify-write does a byte at a time */
reg = ACPI_PCI_REG(address);
- for (idx = reg; idx < reg+len; idx++) {
- ival = pci_conf_read(pc, tag, idx & ~0x3);
+ for (idx = 0; idx < len; idx += access_size) {
if (iodir == ACPI_IOREAD) {
- *pb = ival >> (8 * (idx & 0x3));
+ switch (access_size) {
+ case 1:
+ *(uint8_t *)(pb + idx) =
+ acpi_pci_conf_read_1(pc, tag, reg + idx);
+ break;
+ case 2:
+ *(uint16_t *)(pb + idx) =
+ acpi_pci_conf_read_2(pc, tag, reg + idx);
+ break;
+ case 4:
+ *(uint32_t *)(pb + idx) =
+ acpi_pci_conf_read_4(pc, tag, reg + idx);
+ break;
+ default:
+ printf("%s: rdcfg: invalid size %d\n",
+ DEVNAME(sc), access_size);
+ return (-1);
+ }
} else {
- sval = *pb;
- ival &= ~(0xFF << (8* (idx & 0x3)));
- ival |= sval << (8* (idx & 0x3));
- pci_conf_write(pc, tag, idx & ~0x3, ival);
+ switch (access_size) {
+ case 1:
+ acpi_pci_conf_write_1(pc, tag, reg + idx,
+ *(uint8_t *)(pb + idx));
+ break;
+ case 2:
+ acpi_pci_conf_write_2(pc, tag, reg + idx,
+ *(uint16_t *)(pb + idx));
+ break;
+ case 4:
+ acpi_pci_conf_write_4(pc, tag, reg + idx,
+ *(uint32_t *)(pb + idx));
+ break;
+ default:
+ printf("%s: wrcfg: invalid size %d\n",
+ DEVNAME(sc), access_size);
+ return (-1);
+ }
}
- pb++;
}
break;
@@ -2278,7 +2350,6 @@ acpi_sleep_state(struct acpi_softc *sc, int state)
#ifdef HIBERNATE
if (state == ACPI_STATE_S4) {
uvm_pmr_dirty_everything();
- uvm_pmr_zero_everything();
hib_getentropy(&rndbuf, &rndbuflen);
}
#endif /* HIBERNATE */
diff --git a/sys/dev/acpi/dsdt.c b/sys/dev/acpi/dsdt.c
index 97e55f38542..85885949141 100644
--- a/sys/dev/acpi/dsdt.c
+++ b/sys/dev/acpi/dsdt.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dsdt.c,v 1.217 2015/05/04 10:42:06 jmatthew Exp $ */
+/* $OpenBSD: dsdt.c,v 1.218 2015/08/20 20:50:10 kettenis Exp $ */
/*
* Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
*
@@ -2260,7 +2260,8 @@ aml_rwgas(struct aml_value *rgn, int bpos, int blen, struct aml_value *val,
break;
}
- pi.addr = rgn->v_opregion.iobase + ((bpos >> 3) & ~(sz - 1));
+ pi.addr = (rgn->v_opregion.iobase + (bpos >> 3)) & ~(sz - 1);
+ bpos += ((rgn->v_opregion.iobase & (sz - 1)) << 3);
bpos &= ((sz << 3) - 1);
if (rgn->v_opregion.iospace == GAS_PCI_CFG_SPACE) {