diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2015-08-20 20:50:11 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2015-08-20 20:50:11 +0000 |
commit | 49f0034086eb00ff4606c19562bf33b2d198d61b (patch) | |
tree | cf2fa33f21459c6e1c89f1ee32da0ab5b951b0dd /sys/dev/acpi/acpi.c | |
parent | 97b6256aa034ba66129c1372f510a2b4a0964eb0 (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.c | 95 |
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 */ |