Page MenuHomeFreeBSD

D56984.id177779.diff
No OneTemporary

D56984.id177779.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,235 @@
#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_severity:3;
+ uint8_t re_disposition:2;
+ uint8_t re_optpart:1;
+ uint8_t re_reserved:2;
+#else
+ uint8_t re_reserved:2;
+ uint8_t re_optpart:1;
+ uint8_t re_disposition:2;
+ uint8_t re_severity:3;
+#endif
+
+ /* byte 2 */
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+ uint8_t re_initiator:4;
+ uint8_t re_target:4;
+#else
+ uint8_t re_target:4;
+ uint8_t re_initiator:4;
+#endif
+
+ uint8_t re_type;
+ uint32_t re_extlen; /* length of extended data */
+} __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.
+ */
+
+/* Platform Event Log Format, Main-A section */
+struct rtas_extlog_plt_main_a {
+ char pma_section_id[2]; /* always 'PH' */
+ uint16_t pma_len; /* always 48 */
+ 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 {
+ char pmb_section_id[2]; /* always 'UH' */
+ uint16_t pmb_len; /* always 24 */
+ 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 {
+ char ep_section_id[2]; /* always 'EP' */
+ uint16_t ep_len; /* always 20 */
+ uint8_t ep_version;
+ uint8_t ep_subtype;
+ uint16_t ep_ccid;
+#define EPOW_ACTION_SYSTEM_SHUTDOWN 0x03
+ 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");
+
#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,66 @@
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";
+ }
+}
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,59 @@
#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/intr_machdep.h>
#include <machine/rtas.h>
#include "clock_if.h"
+//#define RTAS_DEBUG
+
+#ifdef RTAS_DEBUG
+#define RTAS_DPRINTF(dev, msg, ...) device_printf(dev, msg, ##__VA_ARGS__)
+#else
+#define RTAS_DPRINTF(dev, 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(struct rtas_event_v6 *, const void *, size_t);
-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 +98,258 @@
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;
};
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;
+};
+
+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");
+ 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_DPRINTF(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 +370,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 +409,263 @@
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");
+ return;
+ }
+
+ RTAS_DPRINTF(dev, "event log\n");
+ RTAS_DPRINTF(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)
+{
+ static char buf[RTAS_ES_BUFSIZE];
+ 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(&buf[0]);
+ buflen = sizeof(buf);
+
+ 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(&buf[0], sizeof(buf));
+
+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_DPRINTF(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_DPRINTF(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_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)
+{
+ static char buf[RTAS_ES_BUFSIZE];
+ 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_DPRINTF(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(&buf[0]);
+ buflen = sizeof(buf);
+
+ 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_DPRINTF(dev, "status=%d\n", (int)status);
+ if (status == 0)
+ rtas_handle_event(buf, sizeof(buf));
+ } while (status == 0);
+}
+
+static void
+rtas_handle_event(const void *buf, size_t buflen)
+{
+ struct rtas_event_v6 event;
+
+ if (buflen < sizeof(event))
+ return;
+
+ memcpy(&event, buf, sizeof(event));
+
+ if (event.re_version != RTAS_V6_VERSION)
+ return;
+
+ if (event.re_type == RTAS_V6_TYPE_EPOW)
+ if (rtas_handle_epow(&event, (const char *)buf + sizeof(event),
+ buflen - sizeof(event)))
+ 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(struct rtas_event_v6 *event, const void *buf, size_t buflen)
+{
+ const struct rtas_extlog_v6 *extlog;
+ const struct rtas_extlog_plt_v6 *pltlog;
+ const struct rtas_extlog_epow_v6 *epow;
+ const char *reason, *type;
+
+ if (event->re_optpart != RTAS_V6_OPTPART_PRESENT)
+ return false;
+
+ if (be32toh(event->re_extlen) < (sizeof(struct rtas_extlog_v6) +
+ sizeof(struct rtas_extlog_plt_v6) +
+ sizeof(struct rtas_extlog_epow_v6)))
+ return false;
+
+ extlog = buf;
+ if (memcmp(extlog->rx_vendor, "IBM\0", 4) != 0)
+ return false;
+
+ if (extlog->rx_log_format != RTAS_V6_EXT_LOG_FORMAT_PLATFORM)
+ return false;
+
+ pltlog = (const struct rtas_extlog_plt_v6 *)&extlog->rx_vendordata[0];
+
+ /* 0xA0 - 0xAF: External environment */
+ if (pltlog->rp_main_b.pmb_subsystem < 0xA0 ||
+ pltlog->rp_main_b.pmb_subsystem > 0xAF)
+ return false;
+
+ epow = (const struct rtas_extlog_epow_v6 *)&pltlog->data[0];
+
+ if (epow->ep_sensor_value != EPOW_ACTION_SYSTEM_SHUTDOWN)
+ 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;
+ }
+
+ printf("RTAS: EPOW %s shutdown request, reason=%s\n", type, reason);
+ shutdown_nice(RB_POWEROFF);
+
+ return true;
+}

File Metadata

Mime Type
text/plain
Expires
Wed, Jul 22, 2:23 PM (7 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35367033
Default Alt Text
D56984.id177779.diff (24 KB)

Event Timeline