Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F157131210
D56984.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
28 KB
Referenced Files
None
Subscribers
None
D56984.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Tue, May 19, 4:00 PM (16 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33298396
Default Alt Text
D56984.diff (28 KB)
Attached To
Mode
D56984: powerpc: Implement RTAS event log support
Attached
Detach File
Event Timeline
Log In to Comment