Page MenuHomeFreeBSD

D56984.diff
No OneTemporary

D56984.diff

diff --git a/sys/powerpc/include/rtas.h b/sys/powerpc/include/rtas.h
--- a/sys/powerpc/include/rtas.h
+++ b/sys/powerpc/include/rtas.h
@@ -41,9 +41,11 @@
* status codes from the bottom of this file.
*/
+#ifdef _KERNEL
int rtas_exists(void);
int rtas_call_method(cell_t token, int nargs, int nreturns, ...);
cell_t rtas_token_lookup(const char *method);
+#endif
/* RTAS Status Codes: see CHRP or PAPR specification */
#define RTAS_OK 0
@@ -56,4 +58,268 @@
#define RTAS_ISOLATION_ERROR -9000
#define RTAS_VENDOR_ERROR_BEGIN -9004
+/*
+ * RTAS event log.
+ */
+
+#define RTAS_ES_BUFSIZE 2048 /* max size of one event log entry */
+
+/* Event class */
+#define RTAS_EV_CLASS_INTERNAL 0 /* internal-errors */
+#define RTAS_EV_CLASS_EPOW 1 /* epow-events */
+#define RTAS_EV_CLASS_RSVD1 2 /* reserved */
+#define RTAS_EV_CLASS_HOTPLUG 3 /* hot-plug-events */
+#define RTAS_EV_CLASS_IO 4 /* ibm,io-events */
+#define RTAS_EV_CLASS_MASK(x) (1u << (31 - (x)))
+
+/* RTAS V6 event log format from "Linux on Power Architecture Reference". */
+
+/* re_severity */
+#define RTAS_V6_SEV_NO_ERROR 0
+#define RTAS_V6_SEV_EVENT 1
+#define RTAS_V6_SEV_WARNING 2
+#define RTAS_V6_SEV_ERROR_SYNC 3
+#define RTAS_V6_SEV_ERROR 4
+#define RTAS_V6_SEV_FATAL 5
+#define RTAS_V6_SEV_ALREADY_REPORTED 6
+
+/* re_disposition */
+#define RTAS_V6_DISP_FULLY_RECOVERED 0
+#define RTAS_V6_DISP_LIMITED_RECOVERY 1
+#define RTAS_V6_DISP_NOT_RECOVERED 2
+
+/* re_optpart */
+#define RTAS_V6_OPTPART_PRESENT 1
+#define RTAS_V6_OPTPART_NOT_PRESENT 0
+
+/* re_initiator, re_target */
+#define RTAS_V6_SUBSYS_UNKNOWN 0
+#define RTAS_V6_SUBSYS_CPU 1
+#define RTAS_V6_SUBSYS_PCI 2
+#define RTAS_V6_SUBSYS_MEMORY 4
+
+/* re_type */
+#define RTAS_V6_TYPE_OTHER 0
+#define RTAS_V6_TYPE_RETRY 1
+#define RTAS_V6_TYPE_TCE_ERR 2
+#define RTAS_V6_TYPE_INTERN_DEV_FAIL 3
+#define RTAS_V6_TYPE_TIMEOUT 4
+#define RTAS_V6_TYPE_DATA_PARITY 5
+#define RTAS_V6_TYPE_ADDR_PARITY 6
+#define RTAS_V6_TYPE_CACHE_PARITY 7
+#define RTAS_V6_TYPE_ADDR_INVALID 8
+#define RTAS_V6_TYPE_ECC_UNCORR 9
+#define RTAS_V6_TYPE_ECC_CORR 10
+ /* 11-63: reserved for future use */
+#define RTAS_V6_TYPE_EPOW 64
+ /* 65-95: reserved for future use */
+ /* 96-159: reserved - do not reuse */
+#define RTAS_V6_TYPE_PRR 160 /* platform resource reassignment */
+ /* 161-223: reserved for future use */
+#define RTAS_V6_TYPE_PLATFORM_ERROR 224
+#define RTAS_V6_TYPE_IO_EVENTS 225
+#define RTAS_V6_TYPE_PLATFORM_INFO 226
+#define RTAS_V6_TYPE_RESOURCE_DEALLOC 227
+#define RTAS_V6_TYPE_DUMP_NOTIFY 228
+#define RTAS_V6_TYPE_HOTPLUG 229
+ /* 230-255: vendor-specific */
+
+struct rtas_event_v6 {
+#define RTAS_V6_VERSION 6
+ uint8_t re_version;
+
+ /* byte 1 */
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+ uint8_t re_reserved:2;
+ uint8_t re_optpart:1;
+ uint8_t re_disposition:2;
+ uint8_t re_severity:3;
+#else
+ uint8_t re_severity:3;
+ uint8_t re_disposition:2;
+ uint8_t re_optpart:1;
+ uint8_t re_reserved:2;
+#endif
+
+ /* byte 2 */
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+ uint8_t re_target:4;
+ uint8_t re_initiator:4;
+#else
+ uint8_t re_initiator:4;
+ uint8_t re_target:4;
+#endif
+
+ uint8_t re_type;
+ uint32_t re_extlen; /* length of extended data */
+ uint8_t re_extdata[];
+} __packed;
+
+#ifdef _KERNEL
+const char *rtas_name_severity(uint8_t);
+const char *rtas_name_disposition(uint8_t);
+const char *rtas_name_subsystem(uint8_t);
+const char *rtas_name_type(uint8_t);
+#endif
+
+/* RTAS General Extended Event Log Format, Version 6 */
+struct rtas_extlog_v6 {
+ /* byte 0 */
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+ uint8_t rx_rsvd1:1;
+ uint8_t rx_big_endian:1;
+ uint8_t rx_new:1;
+ uint8_t rx_predictive:1;
+ uint8_t rx_unrecoverable_bypassed:1;
+ uint8_t rx_recoverable:1;
+ uint8_t rx_unrecoverable:1;
+ uint8_t rx_log_valid:1;
+#else
+ uint8_t rx_log_valid:1;
+ uint8_t rx_unrecoverable:1;
+ uint8_t rx_recoverable:1;
+ uint8_t rx_unrecoverable_bypassed:1;
+ uint8_t rx_predictive:1;
+ uint8_t rx_new:1;
+ uint8_t rx_big_endian:1;
+ uint8_t rx_rsvd1:1;
+#endif
+ /* byte 1 */
+ uint8_t rx_rsvd2;
+ /* byte 2 */
+#define RTAS_V6_EXT_LOG_FORMAT_PLATFORM 14
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+ uint8_t rx_log_format:4;
+ uint8_t rx_rsvd3:3;
+ uint8_t rx_ppc_format:1; /* always 1 */
+#else
+ uint8_t rx_ppc_format:1; /* always 1 */
+ uint8_t rx_rsvd3:3;
+ uint8_t rx_log_format:4;
+#endif
+ /* byte 3-11 */
+ uint8_t rx_rsvd4;
+ uint8_t rx_rsvd5[8];
+ /* byte 12-15 */
+ char rx_vendor[4]; /* NOT nul terminated */
+ /* byte 16-2047 */
+ uint8_t rx_vendordata[];
+} __packed;
+
+_Static_assert(sizeof(struct rtas_extlog_v6) == 16,
+ "struct rtas_extlog_v6 has the wrong size");
+
+/*
+ * Platform Event Log Format, Version 6. This is used when rx_vendor
+ * is "IBM\0" and rx_log_format is RTAS_V6_EXT_LOG_FORMAT_PLATFORM.
+ */
+
+/* Generic section header */
+struct rtas_extlog_plt_header {
+ char ph_section_id[2];
+ uint16_t ph_len;
+} __packed;
+
+/* Platform Event Log Format, Main-A section */
+struct rtas_extlog_plt_main_a {
+ struct rtas_extlog_plt_header
+ pma_header;
+ uint8_t pma_version;
+ uint8_t pma_subtype;
+ uint16_t pma_ccid;
+ uint32_t pma_date; /* BCD YYYYMMDD */
+ uint32_t pma_time; /* BCD HHMMSSmm */
+ uint64_t pma_vendor1;
+#define PMA_CREATOR_SERVICE_PROCESSOR 'E'
+#define PMA_CREATOR_HYPERVISOR 'H'
+#define PMA_CREATOR_POWER_CONTROL 'W'
+#define PMA_CREATOR_PARTITION_FIRMWARE 'L'
+ uint8_t pma_creator;
+ uint16_t pma_rsvd1;
+ uint8_t pma_sections;
+ uint32_t pma_rsvd2;
+ uint64_t pma_vendor2;
+ uint32_t pma_plid;
+ uint32_t pma_vendor3;
+} __packed;
+
+_Static_assert(sizeof(struct rtas_extlog_plt_main_a) == 48,
+ "struct rtas_extlog_plt_main_a has the wrong size");
+
+/* Platform Event Log Format, Main-B section */
+struct rtas_extlog_plt_main_b {
+ struct rtas_extlog_plt_header
+ pmb_header;
+ uint8_t pmb_version;
+ uint8_t pmb_subtype;
+ uint16_t pmb_ccid;
+ uint8_t pmb_subsystem;
+ uint8_t pmb_vendor1;
+ uint8_t pmb_event_severity;
+ uint8_t pmb_event_subtype;
+ uint32_t pmb_vendor2;
+ uint16_t pmb_rsvd1;
+ uint16_t pmb_flags;
+ uint32_t pmb_rsvd2;
+} __packed;
+
+_Static_assert(sizeof(struct rtas_extlog_plt_main_b) == 24,
+ "struct rtas_extlog_plt_main_b has the wrong size");
+
+struct rtas_extlog_plt_v6 {
+ struct rtas_extlog_plt_main_a rp_main_a;
+ struct rtas_extlog_plt_main_b rp_main_b;
+ uint8_t data[]; /* event-specific data */
+} __packed;
+
+/* Platform Event Log Format, EPOW section */
+struct rtas_extlog_epow_v6 {
+ struct rtas_extlog_plt_header
+ ep_header;
+ uint8_t ep_version;
+ uint8_t ep_subtype;
+ uint16_t ep_ccid;
+#define EPOW_ACTION_RESET_MESSAGE 0x00
+#define EPOW_ACTION_WARN_COOLING 0x01
+#define EPOW_ACTION_WARN_POWER 0x02
+#define EPOW_ACTION_SYSTEM_SHUTDOWN 0x03
+#define EPOW_ACTION_SYSTEM_HALT 0x04
+#define EPOW_ACTION_MAIN_ENCLOSURE 0x05
+#define EPOW_ACTION_POWER_OFF 0x07
+ uint8_t ep_sensor_value;
+#define EPOW_MOD_NORMAL_SHUTDOWN 0x01
+#define EPOW_MOD_UTILITY_POWER_LOSS 0x02
+#define EPOW_MOD_CRITICAL_FUNCTION_LOSS 0x03
+#define EPOW_MOD_TEMP_TOO_HIGH 0x04
+ uint8_t ep_modifier;
+#define EPOW_EXTMOD_SYSTEM_SHUTDOWN 0x00
+#define EPOW_EXTMOD_PARTITION_SHUTDOWN 0x01
+ uint8_t ep_ext_modifier;
+ uint8_t ep_rsvd1;
+ uint64_t ep_vendor_reason;
+} __packed;
+
+_Static_assert(sizeof(struct rtas_extlog_epow_v6) == 20,
+ "struct rtas_extlog_epow_v6 has the wrong size");
+
+/*
+ * Find an extended data section with the given length from an event using its
+ * two-character id. If the section is not found, or the actual length doesn't
+ * match the expected length, return NULL.
+ *
+ * Note: the caller should have already validated that the event's extended
+ * data doesn't exceed the buffer size.
+ */
+#define RTAS_V6_SECTION_MAIN_A_ID "PH"
+#define RTAS_V6_SECTION_MAIN_A_SIZE (sizeof(struct rtas_extlog_plt_main_a))
+#define RTAS_V6_SECTION_MAIN_B_ID "UH"
+#define RTAS_V6_SECTION_MAIN_B_SIZE (sizeof(struct rtas_extlog_plt_main_b))
+#define RTAS_V6_SECTION_EPOW_ID "EP"
+#define RTAS_V6_SECTION_EPOW_SIZE (sizeof(struct rtas_extlog_epow_v6))
+
+#ifdef _KERNEL
+const void *rtas_find_plt_section(const struct rtas_event_v6 *,
+ const char *, size_t);
+#endif
+
#endif /* _MACHINE_RTAS_H_ */
diff --git a/sys/powerpc/ofw/rtas.c b/sys/powerpc/ofw/rtas.c
--- a/sys/powerpc/ofw/rtas.c
+++ b/sys/powerpc/ofw/rtas.c
@@ -267,3 +267,118 @@
return (token);
}
+
+const char *
+rtas_name_severity(uint8_t sev)
+{
+ switch (sev) {
+ case RTAS_V6_SEV_NO_ERROR: return ("NO_ERROR");
+ case RTAS_V6_SEV_EVENT: return ("EVENT");
+ case RTAS_V6_SEV_WARNING: return ("WARNING");
+ case RTAS_V6_SEV_ERROR_SYNC: return ("ERROR_SYNC");
+ case RTAS_V6_SEV_ERROR: return ("ERROR");
+ case RTAS_V6_SEV_FATAL: return ("FATAL");
+ case RTAS_V6_SEV_ALREADY_REPORTED:
+ return ("ALREADY_REPORTED");
+ default: return ("UNKNOWN");
+ }
+}
+
+const char *
+rtas_name_disposition(uint8_t disp)
+{
+ switch (disp) {
+ case RTAS_V6_DISP_FULLY_RECOVERED: return ("FULLY_RECOVERED");
+ case RTAS_V6_DISP_LIMITED_RECOVERY: return ("LIMITED_RECOVERY");
+ case RTAS_V6_DISP_NOT_RECOVERED: return ("NOT_RECOVERED");
+ default: return ("UNKNOWN");
+ }
+}
+
+const char *rtas_name_subsystem(uint8_t subsys)
+{
+ switch (subsys) {
+ case RTAS_V6_SUBSYS_CPU: return ("CPU");
+ case RTAS_V6_SUBSYS_PCI: return ("PCI");
+ case RTAS_V6_SUBSYS_MEMORY: return ("MEMORY");
+ default: return ("UNKNOWN");
+ }
+}
+
+const char *rtas_name_type(uint8_t type)
+{
+ switch (type) {
+ case RTAS_V6_TYPE_OTHER: return ("OTHER");
+ case RTAS_V6_TYPE_RETRY: return ("RETRY");
+ case RTAS_V6_TYPE_TCE_ERR: return ("TCE_ERR");
+ case RTAS_V6_TYPE_INTERN_DEV_FAIL: return ("INTERN_DEV_FAIL");
+ case RTAS_V6_TYPE_TIMEOUT: return ("TIMEOUT");
+ case RTAS_V6_TYPE_DATA_PARITY: return ("DATA_PARITY");
+ case RTAS_V6_TYPE_ADDR_PARITY: return ("ADDR_PARITY");
+ case RTAS_V6_TYPE_CACHE_PARITY: return ("CACHE_PARITY");
+ case RTAS_V6_TYPE_ADDR_INVALID: return ("ADDR_INVALID");
+ case RTAS_V6_TYPE_ECC_UNCORR: return ("ECC_UNCORR");
+ case RTAS_V6_TYPE_ECC_CORR: return ("ECC_CORR");
+ case RTAS_V6_TYPE_EPOW: return ("EPOW");
+ case RTAS_V6_TYPE_PRR: return ("PRR");
+ case RTAS_V6_TYPE_PLATFORM_ERROR: return ("PLATFORM_ERROR");
+ case RTAS_V6_TYPE_IO_EVENTS: return ("IO_EVENTS");
+ case RTAS_V6_TYPE_PLATFORM_INFO: return ("PLATFORM_INFO");
+ case RTAS_V6_TYPE_RESOURCE_DEALLOC: return ("RESOURCE_DEALLOC");
+ case RTAS_V6_TYPE_DUMP_NOTIFY: return ("DUMP_NOTIFY");
+ case RTAS_V6_TYPE_HOTPLUG: return ("HOTPLUG");
+ default: return ("UNKNOWN");
+ }
+}
+
+const void *
+rtas_find_plt_section(const struct rtas_event_v6 *event, const char *name,
+ size_t size)
+{
+ const struct rtas_extlog_v6 *extlog;
+ const char *bufp;
+ size_t bufleft;
+
+ if (event->re_version != RTAS_V6_VERSION)
+ return (NULL);
+
+ if (event->re_optpart != RTAS_V6_OPTPART_PRESENT)
+ return (NULL);
+
+ if (be32toh(event->re_extlen) < sizeof(struct rtas_extlog_v6))
+ return (NULL);
+ extlog = (const struct rtas_extlog_v6 *)&event->re_extdata[0];
+
+ if (!extlog->rx_big_endian)
+ return (NULL); /* We only know how to read BE logs */
+ if (!extlog->rx_ppc_format)
+ return (NULL);
+ if (memcmp(extlog->rx_vendor, "IBM\0", 4) != 0)
+ return (NULL);
+ if (extlog->rx_log_format != RTAS_V6_EXT_LOG_FORMAT_PLATFORM)
+ return (NULL);
+
+ bufp = &extlog->rx_vendordata[0];
+ bufleft = be32toh(event->re_extlen) - sizeof(struct rtas_extlog_v6);
+
+ while (bufleft >= sizeof(struct rtas_extlog_plt_header)) {
+ const struct rtas_extlog_plt_header *hdr;
+ uint16_t sectlen;
+
+ hdr = (const struct rtas_extlog_plt_header *)bufp;
+ sectlen = be16toh(hdr->ph_len);
+ if (sectlen > bufleft)
+ return (NULL);
+
+ if (memcmp(hdr->ph_section_id, name, 2) == 0) {
+ if (sectlen != size)
+ return (NULL);
+ return (hdr);
+ }
+
+ bufleft -= sectlen;
+ bufp += sectlen;
+ }
+
+ return (NULL);
+}
diff --git a/sys/powerpc/pseries/rtas_dev.c b/sys/powerpc/pseries/rtas_dev.c
--- a/sys/powerpc/pseries/rtas_dev.c
+++ b/sys/powerpc/pseries/rtas_dev.c
@@ -37,23 +37,62 @@
#include <sys/kernel.h>
#include <sys/reboot.h>
#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/openfirm.h>
+#include <machine/bus.h>
+#include <machine/intr_machdep.h>
#include <machine/rtas.h>
#include "clock_if.h"
+//#define RTAS_DEBUG
+
+#ifdef RTAS_DEBUG
+#define RTAS_DEVPRINTF(dev, msg, ...) device_printf(dev, msg, ##__VA_ARGS__)
+#define RTAS_DPRINTF(msg, ...) printf(msg, ##__VA_ARGS__)
+#else
+#define RTAS_DEVPRINTF(dev, msg, ...)
+#define RTAS_DPRINTF(msg, ...)
+#endif
+
static int rtasdev_probe(device_t);
static int rtasdev_attach(device_t);
+static void rtasdev_find_children(device_t);
/* clock interface */
static int rtas_gettime(device_t dev, struct timespec *ts);
static int rtas_settime(device_t dev, struct timespec *ts);
+/* bus interface */
+static const struct ofw_bus_devinfo *
+ rtasbus_get_devinfo(device_t dev, device_t child);
+static struct resource *
+ rtasbus_alloc_resource(device_t, device_t, int, int, rman_res_t,
+ rman_res_t, rman_res_t, u_int);
+static int rtasbus_print_child(device_t dev, device_t child);
+static struct resource_list *
+ rtasbus_get_resource_list(device_t dev, device_t child);
static void rtas_shutdown(void *arg, int howto);
+static void rtas_handle_event(const void *buf, size_t buflen);
+static bool rtas_handle_epow(const struct rtas_event_v6 *);
-static device_method_t rtasdev_methods[] = {
+/*
+ * The root rtas node.
+ */
+
+struct rtas_devinfo {
+ struct ofw_bus_devinfo rdi_obdinfo;
+ struct resource_list rdi_resources;
+ int rdi_base_irq;
+};
+
+static device_method_t rtasdev_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, rtasdev_probe),
DEVMETHOD(device_attach, rtasdev_attach),
@@ -62,55 +101,262 @@
DEVMETHOD(clock_gettime, rtas_gettime),
DEVMETHOD(clock_settime, rtas_settime),
- { 0, 0 },
+ /* Bus interface */
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+ DEVMETHOD(bus_child_pnpinfo, ofw_bus_gen_child_pnpinfo),
+ DEVMETHOD(bus_print_child, rtasbus_print_child),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+ DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_get_resource_list, rtasbus_get_resource_list),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_devinfo, rtasbus_get_devinfo),
+ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
+ DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
+ DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
+ DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
+ DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
+
+ DEVMETHOD_END
+};
+
+struct rtas_softc {
+ struct mtx sc_mtx;
+
+ phandle_t sc_phandle;
+
+ /* tod clock */
+ cell_t sc_gtod_token; /* get-time-of-day */
+ /* event log */
+ cell_t sc_es_token; /* event-scan */
+ struct callout sc_es_callout;
+ struct task sc_es_task;
+ int sc_es_interval;
+ void *sc_buffer;
+ size_t sc_buflen;
};
static driver_t rtasdev_driver = {
"rtas",
rtasdev_methods,
- 0
+ sizeof(struct rtas_softc),
};
DRIVER_MODULE(rtasdev, ofwbus, rtasdev_driver, 0, 0);
+/*
+ * The exception event handler; this attaches to children of /event-sources.
+ */
+
+static int rtas_esrc_probe(device_t);
+static int rtas_esrc_attach(device_t);
+static void rtas_esrc_ithread(void *);
+
+static device_method_t rtas_esrc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rtas_esrc_probe),
+ DEVMETHOD(device_attach, rtas_esrc_attach),
+
+ DEVMETHOD_END
+};
+
+struct rtas_esrc_softc {
+ struct mtx sc_mtx;
+ cell_t sc_token;
+ int sc_irq_rid;
+ struct resource *sc_irq;
+ void *sc_intr_cookie;
+ void *sc_buffer;
+ size_t sc_buflen;
+};
+
+static driver_t rtas_esrc_driver = {
+ "rtas_esrc",
+ rtas_esrc_methods,
+ sizeof(struct rtas_esrc_softc),
+};
+
+DRIVER_MODULE(rtas_esrc, rtas, rtas_esrc_driver, 0, 0);
+
+/* event log interface */
+static void rtas_event_init(device_t, struct rtas_softc *);
+static void rtas_event_callout(void *);
+static void rtas_event_task(void *, int);
+
static int
rtasdev_probe(device_t dev)
{
- const char *name = ofw_bus_get_name(dev);
+ const char *name;
- if (strcmp(name, "rtas") != 0)
+ name = ofw_bus_get_name(dev);
+ if (name == NULL || strcmp(name, "rtas") != 0)
return (ENXIO);
+
if (!rtas_exists())
return (ENXIO);
device_set_desc(dev, "Run-Time Abstraction Services");
+
return (0);
}
static int
rtasdev_attach(device_t dev)
{
- if (rtas_token_lookup("get-time-of-day") != -1)
+ struct rtas_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->sc_phandle = OF_finddevice("/rtas");
+ if (sc->sc_phandle == -1) {
+ device_printf(dev, "cannot find RTAS device\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mtx, "rtas", NULL, MTX_DEF);
+
+ callout_init_mtx(&sc->sc_es_callout, &sc->sc_mtx, 0);
+ TASK_INIT(&sc->sc_es_task, 0, rtas_event_task, dev);
+
+ /* Attach the tod clock */
+ sc->sc_gtod_token = rtas_token_lookup("get-time-of-day");
+ if (sc->sc_gtod_token != -1)
clock_register(dev, 2000);
+ /* Attach the periodic log scanner */
+ rtas_event_init(dev, sc);
+
+ /* Attach event sources */
+ rtasdev_find_children(dev);
+
EVENTHANDLER_REGISTER(shutdown_final, rtas_shutdown, NULL,
SHUTDOWN_PRI_LAST);
return (0);
}
+static void
+rtasdev_find_children(device_t dev)
+{
+ struct rtas_devinfo *dinfo;
+ cell_t irq[2];
+ device_t cdev;
+ phandle_t event_sources, child;
+ int err;
+
+ event_sources = OF_finddevice("/event-sources");
+ if (event_sources == -1)
+ return;
+
+ powerpc_register_pic(root_pic, OF_xref_from_node(event_sources),
+ 1 << 24 /* 24-bit XIRR field */, 1 /* Number of IPIs */, FALSE);
+
+ for (child = OF_child(event_sources); child != 0;
+ child = OF_peer(child)) {
+ dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
+
+ if (ofw_bus_gen_setup_devinfo(&dinfo->rdi_obdinfo, child) != 0) {
+ free(dinfo, M_DEVBUF);
+ continue;
+ }
+
+ /*
+ * We need to save the underlying IRQ so we can pass it to
+ * check-exception later.
+ */
+ err = OF_getencprop(child, "interrupts", &irq[0], sizeof(irq));
+ if (err == -1) {
+ device_printf(dev, "<%s>: failed to fetch interrupts\n",
+ dinfo->rdi_obdinfo.obd_name);
+ ofw_bus_gen_destroy_devinfo(&dinfo->rdi_obdinfo);
+ free(dinfo, M_DEVBUF);
+ continue;
+ }
+ dinfo->rdi_base_irq = irq[0];
+
+ resource_list_init(&dinfo->rdi_resources);
+ ofw_bus_intr_to_rl(dev, child, &dinfo->rdi_resources, NULL);
+
+ cdev = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
+ if (cdev == NULL) {
+ device_printf(dev, "<%s>: device_add_child failed\n",
+ dinfo->rdi_obdinfo.obd_name);
+ ofw_bus_gen_destroy_devinfo(&dinfo->rdi_obdinfo);
+ free(dinfo, M_DEVBUF);
+ continue;
+ }
+
+ device_set_ivars(cdev, dinfo);
+ }
+
+ bus_attach_children(dev);
+}
+
+static struct resource_list *
+rtasbus_get_resource_list(device_t dev, device_t child)
+{
+ struct rtas_devinfo *dinfo;
+
+ dinfo = device_get_ivars(child);
+ return (&dinfo->rdi_resources);
+}
+
+static struct resource *
+rtasbus_alloc_resource(device_t bus, device_t child, int type, int rid,
+ rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+ struct rtas_devinfo *dinfo;
+
+ RTAS_DEVPRINTF(child, "alloc_resource, type=%d\n", type);
+ dinfo = device_get_ivars(child);
+
+ if (type != SYS_RES_IRQ)
+ return (NULL);
+
+ return (resource_list_alloc(&dinfo->rdi_resources, bus, child, type,
+ rid, start, end, count, flags));
+}
+
+static const struct ofw_bus_devinfo *
+rtasbus_get_devinfo(device_t dev, device_t child)
+{
+ return (device_get_ivars(child));
+}
+
+static int
+rtasbus_print_child(device_t dev, device_t child)
+{
+ struct rtas_devinfo *dinfo;
+ struct resource_list *rl;
+ int retval = 0;
+
+ dinfo = device_get_ivars(child);
+ rl = &dinfo->rdi_resources;
+
+ retval += bus_print_child_header(dev, child);
+ retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd");
+ retval += bus_print_child_footer(dev, child);
+
+ return (retval);
+}
+
static int
rtas_gettime(device_t dev, struct timespec *ts) {
struct clocktime ct;
+ struct rtas_softc *sc;
cell_t tod[8];
- cell_t token;
int error;
- token = rtas_token_lookup("get-time-of-day");
- if (token == -1)
+ sc = device_get_softc(dev);
+
+ if (sc->sc_gtod_token == -1)
return (ENXIO);
- error = rtas_call_method(token, 0, 8, &tod[0], &tod[1], &tod[2],
- &tod[3], &tod[4], &tod[5], &tod[6], &tod[7]);
+ error = rtas_call_method(sc->sc_gtod_token, 0, 8, &tod[0], &tod[1],
+ &tod[2], &tod[3], &tod[4], &tod[5], &tod[6], &tod[7]);
if (error < 0)
return (ENXIO);
if (tod[0] != 0)
@@ -131,16 +377,18 @@
rtas_settime(device_t dev, struct timespec *ts)
{
struct clocktime ct;
- cell_t token, status;
+ struct rtas_softc *sc;
+ cell_t status;
int error;
- token = rtas_token_lookup("set-time-of-day");
- if (token == -1)
+ sc = device_get_softc(dev);
+
+ if (sc->sc_gtod_token == -1)
return (ENXIO);
clock_ts_to_ct(ts, &ct);
- error = rtas_call_method(token, 7, 1, ct.year, ct.mon, ct.day, ct.hour,
- ct.min, ct.sec, ct.nsec, &status);
+ error = rtas_call_method(sc->sc_gtod_token, 7, 1, ct.year, ct.mon,
+ ct.day, ct.hour, ct.min, ct.sec, ct.nsec, &status);
if (error < 0)
return (ENXIO);
if (status != 0)
@@ -168,3 +416,305 @@
rtas_call_method(token, 0, 1, &status);
}
}
+
+static void
+rtas_event_init(device_t dev, struct rtas_softc *sc)
+{
+ cell_t scan_rate;
+ int err;
+
+ sc->sc_es_token = rtas_token_lookup("event-scan");
+ if (sc->sc_es_token == -1)
+ return;
+
+ err = OF_getencprop(sc->sc_phandle, "rtas-event-scan-rate", &scan_rate,
+ sizeof(scan_rate));
+ if (err == -1 || scan_rate <= 0) {
+ device_printf(dev, "cannot fetch rtas-event-scan-rate, "
+ "event log scan disabled\n");
+ return;
+ }
+
+ sc->sc_buflen = RTAS_ES_BUFSIZE;
+ sc->sc_buffer = contigmalloc(sc->sc_buflen, M_DEVBUF, 0,
+ 0, BUS_SPACE_MAXADDR_32BIT, 8, 256*1024*1024);
+ if (sc->sc_buffer == NULL) {
+ device_printf(dev, "cannot allocate event log buffer, "
+ "event log scan disabled\n");
+ return;
+ }
+
+ RTAS_DEVPRINTF(dev, "event-scan every 1/%u sec\n",
+ (unsigned)scan_rate);
+
+ sc->sc_es_interval = hz / scan_rate;
+ callout_reset(&sc->sc_es_callout, sc->sc_es_interval,
+ rtas_event_callout, dev);
+}
+
+static void
+rtas_event_callout(void *arg)
+{
+ device_t dev;
+ struct rtas_softc *sc;
+
+ dev = arg;
+ sc = device_get_softc(dev);
+
+ taskqueue_enqueue(taskqueue_thread, &sc->sc_es_task);
+}
+
+static void
+rtas_event_task(void *arg, int pending)
+{
+ device_t dev;
+ struct rtas_softc *sc;
+ int ret;
+ cell_t status, event_mask, critical, bufaddr, buflen;
+
+ dev = arg;
+ sc = device_get_softc(dev);
+
+ event_mask = -1;
+ critical = 0;
+ bufaddr = (cell_t)vtophys(sc->sc_buffer);
+ buflen = sc->sc_buflen;
+
+ ret = rtas_call_method(sc->sc_es_token, 4, 1, event_mask, critical,
+ bufaddr, buflen, &status);
+ if (ret < 0) {
+ device_printf(dev, "event-scan failed, status=%d\n",
+ (int)status);
+ goto done;
+ }
+
+ rtas_handle_event(sc->sc_buffer, sc->sc_buflen);
+
+done:
+ callout_reset(&sc->sc_es_callout, sc->sc_es_interval,
+ rtas_event_callout, dev);
+}
+
+static int
+rtas_esrc_probe(device_t dev)
+{
+ const char *name;
+
+ RTAS_DEVPRINTF(dev, "probe\n");
+
+ name = ofw_bus_get_name(dev);
+ if (strcmp(name, "epow-events") == 0)
+ device_set_desc(dev, "Environmental and Power Warnings");
+ else if (strcmp(name, "hot-plug-events") == 0)
+ device_set_desc(dev, "Hot Plug Events");
+ else
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+rtas_esrc_attach(device_t dev)
+{
+ struct rtas_esrc_softc *sc;
+ int err;
+
+ RTAS_DEVPRINTF(dev, "attach\n");
+
+ sc = device_get_softc(dev);
+
+ sc->sc_token = rtas_token_lookup("check-exception");
+ if (sc->sc_token == -1) {
+ device_printf(dev, "cannot look up check-exception token\n");
+ return (ENXIO);
+ }
+
+ sc->sc_buflen = RTAS_ES_BUFSIZE;
+ sc->sc_buffer = contigmalloc(sc->sc_buflen, M_DEVBUF, 0,
+ 0, BUS_SPACE_MAXADDR_32BIT, 8, 256*1024*1024);
+ if (sc->sc_buffer == NULL) {
+ device_printf(dev, "cannot allocate buffer\n");
+ return (ENXIO);
+ }
+
+ sc->sc_irq_rid = 0;
+ sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irq_rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq == NULL) {
+ device_printf(dev, "cannot allocate IRQ\n");
+ return (ENXIO);
+ }
+
+ err = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, rtas_esrc_ithread, dev, &sc->sc_intr_cookie);
+ if (err != 0) {
+ device_printf(dev, "bus_setup_intr failed: %d\n", err);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static void
+rtas_esrc_ithread(void *arg)
+{
+ device_t dev;
+ struct rtas_esrc_softc *sc;
+ struct rtas_devinfo *dinfo;
+ int ret;
+ cell_t status, voff, addl, mask, critical, bufaddr, buflen;
+
+ dev = arg;
+ sc = device_get_softc(dev);
+ dinfo = device_get_ivars(dev);
+
+ RTAS_DEVPRINTF(dev, "interrupt! irq=%d\n", dinfo->rdi_base_irq);
+
+ voff = 0;
+ addl = dinfo->rdi_base_irq;
+ mask = RTAS_EV_CLASS_MASK(RTAS_EV_CLASS_EPOW);
+ critical = 0;
+ bufaddr = (cell_t)vtophys(sc->sc_buffer);
+ buflen = sizeof(sc->sc_buflen);
+
+ do {
+ ret = rtas_call_method(sc->sc_token, 6, 1, voff, addl, mask,
+ critical, bufaddr, buflen, &status);
+ if (ret < 0) {
+ device_printf(dev, "call-exception failed: %d\n", ret);
+ return;
+ }
+
+ RTAS_DEVPRINTF(dev, "status=%d\n", (int)status);
+ if (status == 0) {
+ cpu_flush_dcache(sc->sc_buffer, sc->sc_buflen);
+ rtas_handle_event(sc->sc_buffer, sc->sc_buflen);
+ }
+ } while (status == 0);
+}
+
+static void
+rtas_handle_event(const void *buf, size_t buflen)
+{
+ const struct rtas_event_v6 *event;
+ bool handled;
+
+ event = buf;
+
+ if (event->re_version != RTAS_V6_VERSION)
+ return;
+
+ /*
+ * Ensure the total size of the event is within our buffer,
+ * which is guaranteed by the spec.
+ */
+ if (buflen < sizeof(*event) ||
+ (buflen - sizeof(*event)) < be32toh(event->re_extlen))
+ return;
+
+ /* See if we have a type-specific handler */
+ switch (event->re_type) {
+ case RTAS_V6_TYPE_EPOW:
+ handled = rtas_handle_epow(event);
+ break;
+ default:
+ handled = false;
+ break;
+ }
+
+ if (handled)
+ return;
+
+ /* If we don't recognise the event, log it and do nothing */
+ printf("RTAS: Event: severity=%s, disposition=%s, initiator=%s, "
+ "target=%s, type=%s\n",
+ rtas_name_severity(event->re_severity),
+ rtas_name_disposition(event->re_disposition),
+ rtas_name_subsystem(event->re_initiator),
+ rtas_name_subsystem(event->re_target),
+ rtas_name_type(event->re_type));
+}
+
+static bool
+rtas_handle_epow(const struct rtas_event_v6 *event)
+{
+ const struct rtas_extlog_plt_main_b *mainb;
+ const struct rtas_extlog_epow_v6 *epow;
+ const char *reason, *type;
+
+ mainb = rtas_find_plt_section(event, RTAS_V6_SECTION_MAIN_B_ID,
+ RTAS_V6_SECTION_MAIN_B_SIZE);
+ if (mainb == NULL)
+ return (false); /* This is not a platform event log */
+
+ /* 0xA0 - 0xAF: External environment */
+ if (mainb->pmb_subsystem < 0xA0 || mainb->pmb_subsystem > 0xAF)
+ return (false);
+
+ epow = rtas_find_plt_section(event, RTAS_V6_SECTION_EPOW_ID,
+ RTAS_V6_SECTION_EPOW_SIZE);
+ if (epow == NULL)
+ return (false);
+
+ switch (epow->ep_modifier) {
+ case EPOW_MOD_NORMAL_SHUTDOWN:
+ reason = "normal shutdown";
+ break;
+ case EPOW_MOD_UTILITY_POWER_LOSS:
+ reason = "utility power loss";
+ break;
+ case EPOW_MOD_CRITICAL_FUNCTION_LOSS:
+ reason = "critical function loss";
+ break;
+ case EPOW_MOD_TEMP_TOO_HIGH:
+ reason = "ambient temperature too high";
+ break;
+ default:
+ reason = "unknown";
+ break;
+ }
+
+ switch (epow->ep_ext_modifier) {
+ case EPOW_EXTMOD_SYSTEM_SHUTDOWN:
+ type = "system-wide";
+ break;
+ case EPOW_EXTMOD_PARTITION_SHUTDOWN:
+ type = "partition-specific";
+ break;
+ default:
+ type = "unknown";
+ break;
+ }
+
+ /*
+ * According to the spec:
+ *
+ * * SYSTEM_SHUTDOWN means the system must begin to shut down after
+ * an OS defined delay, which is 10 minutes by default.
+ *
+ * * SYSTEM_HALT means to shut down within 20 seconds.
+ *
+ * * POWER_OFF means the system must halt immediately and we are only
+ * guaranteed 4ms of remaining power.
+ *
+ * We don't currently have a good way to indiciate SYSTEM_SHUTDOWN
+ * to userland, so treat both SHUTDOWN and HALT as an immediate
+ * shutdown.
+ */
+
+ switch (epow->ep_sensor_value) {
+ case EPOW_ACTION_SYSTEM_SHUTDOWN:
+ case EPOW_ACTION_SYSTEM_HALT:
+ printf("RTAS: EPOW %s shutdown request, reason=%s\n",
+ type, reason);
+ shutdown_nice(RB_POWEROFF);
+ return (true);
+ case EPOW_ACTION_POWER_OFF:
+ printf("RTAS: EPOW %s emergency halt, reason=%s\n",
+ type, reason);
+ kern_reboot(RB_POWEROFF);
+ return (true);
+ default:
+ return (false);
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Tue, May 19, 7:00 AM (7 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33298396
Default Alt Text
D56984.diff (28 KB)

Event Timeline