diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2015-07-21 15:44:05 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2015-07-21 15:44:05 +0000 |
commit | a4a31ae724225c93dc860e9b4709104f8e22cc4b (patch) | |
tree | ae133b80aaced4e319abad7a1c3b2d29ad92e1fa /sys | |
parent | 6e16abaad5f20a77912e306af0cbef6d7787dd89 (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@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/acpi/acpi.c | 94 |
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; |