summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2015-07-21 15:44:05 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2015-07-21 15:44:05 +0000
commita4a31ae724225c93dc860e9b4709104f8e22cc4b (patch)
treeae133b80aaced4e319abad7a1c3b2d29ad92e1fa
parent6e16abaad5f20a77912e306af0cbef6d7787dd89 (diff)
iRespect the access size when reading or writing to pci config space.
Fixes battery status passthrough in vmware and who knows what else. Tested by many. ok mlarkin@, deraadt@
-rw-r--r--sys/dev/acpi/acpi.c94
1 files changed, 83 insertions, 11 deletions
diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c
index a0be1187fbb..f57b51774d1 100644
--- a/sys/dev/acpi/acpi.c
+++ b/sys/dev/acpi/acpi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: acpi.c,v 1.288 2015/07/17 22:34:00 mlarkin Exp $ */
+/* $OpenBSD: acpi.c,v 1.289 2015/07/21 15:44:04 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;