summaryrefslogtreecommitdiff
path: root/sys/dev/acpi/acpi.c
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2015-08-20 20:50:11 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2015-08-20 20:50:11 +0000
commit49f0034086eb00ff4606c19562bf33b2d198d61b (patch)
treecf2fa33f21459c6e1c89f1ee32da0ab5b951b0dd /sys/dev/acpi/acpi.c
parent97b6256aa034ba66129c1372f510a2b4a0964eb0 (diff)
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@
Diffstat (limited to 'sys/dev/acpi/acpi.c')
-rw-r--r--sys/dev/acpi/acpi.c95
1 files changed, 83 insertions, 12 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 */