Date: Tue, 15 Jan 2008 15:50:05 GMT From: John Baldwin <jhb@FreeBSD.org> To: freebsd-acpi@FreeBSD.org Subject: Re: kern/119675: [acpi] apic_hpet0 probe causes divide by zero kernel panic Message-ID: <200801151550.m0FFo5RL069839@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
The following reply was made to PR kern/119675; it has been noted by GNATS. From: John Baldwin <jhb@FreeBSD.org> To: bug-followup@freebsd.org, bicknell@ufp.org Cc: Subject: Re: kern/119675: [acpi] apic_hpet0 probe causes divide by zero kernel panic Date: Tue, 15 Jan 2008 10:13:07 -0500 You can try the patch below. It fixes a couple of places where we don't honor the spec (we don't shut it off in S1 and S2 as required and we don't preserve reserved bits in the global configuration register). It also fails the attach if the period is zero which should fix your panic and just leave you with no HPET. Index: acpi_hpet.c =================================================================== RCS file: /host/cvs/usr/cvs/src/sys/dev/acpica/acpi_hpet.c,v retrieving revision 1.12 diff -u -r1.12 acpi_hpet.c --- acpi_hpet.c 9 Oct 2007 07:48:07 -0000 1.12 +++ acpi_hpet.c 15 Jan 2008 14:53:21 -0000 @@ -82,6 +82,24 @@ return (bus_read_4(sc->mem_res, HPET_OFFSET_VALUE)); } +static void +hpet_enable(struct acpi_hpet_softc *sc) +{ + uint32_t val; + + val = bus_read_4(sc->mem_res, HPET_OFFSET_ENABLE); + bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, val | 1); +} + +static void +hpet_disable(struct acpi_hpet_softc *sc) +{ + uint32_t val; + + val = bus_read_4(sc->mem_res, HPET_OFFSET_ENABLE); + bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, val & ~1); +} + /* Discover the HPET via the ACPI table of the same name. */ static void acpi_hpet_identify(driver_t *driver, device_t parent) @@ -166,10 +184,16 @@ } /* Be sure timer is enabled. */ - bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 1); + hpet_enable(sc); /* Read basic statistics about the timer. */ val = bus_read_4(sc->mem_res, HPET_OFFSET_PERIOD); + if (val == 0) { + device_printf(dev, "invalid period\n"); + hpet_disable(sc); + bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res); + } + freq = (1000000000000000LL + val / 2) / val; if (bootverbose) { val = bus_read_4(sc->mem_res, HPET_OFFSET_INFO); @@ -192,7 +216,7 @@ val2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE); if (val == val2) { device_printf(dev, "HPET never increments, disabling\n"); - bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 0); + hpet_disable(sc); bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res); return (ENXIO); } @@ -214,13 +238,29 @@ } static int +acpi_hpet_suspend(device_t dev) +{ + struct acpi_hpet_softc *sc; + + /* + * Disable the timer during suspend. The timer will not lose + * its state in S1 or S2, but we are required to disable + * it. + */ + sc = device_get_softc(dev); + hpet_disable(sc); + + return (0); +} + +static int acpi_hpet_resume(device_t dev) { struct acpi_hpet_softc *sc; /* Re-enable the timer after a resume to keep the clock advancing. */ sc = device_get_softc(dev); - bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 1); + hpet_enable(sc); return (0); } @@ -260,6 +300,7 @@ DEVMETHOD(device_probe, acpi_hpet_probe), DEVMETHOD(device_attach, acpi_hpet_attach), DEVMETHOD(device_detach, acpi_hpet_detach), + DEVMETHOD(device_suspend, acpi_hpet_suspend), DEVMETHOD(device_resume, acpi_hpet_resume), {0, 0} -- John Baldwin
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200801151550.m0FFo5RL069839>