iBoot/drivers/apple/csi/endpoints/management_ep.c

363 lines
9.9 KiB
C

/*
* Copyright (C) 2011-2012 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <platform.h>
#include <platform/timer.h>
#include <arch.h>
#include <debug.h>
#include <sys/task.h>
#include <drivers/csi.h>
#include <drivers/a7iop/a7iop.h>
#include "csi_private.h"
#include "endpoints/management_ep.h"
#include "endpoints/crashlog_ep.h"
#include "endpoints/console_ep.h"
#include "endpoints/syslog_ep.h"
//
// Global Data
//
ep_mgmt_desc_t *mgmt_descs[CSI_COPROC_MAX] = {NULL};
//
// Private functions
//
int ep_mgmt_task (void *arg);
csi_status_t mgmt_msg_handler (ep_mgmt_desc_t *mgmt, msg_payload_t msg);
csi_status_t handle_hello (ep_mgmt_desc_t *mgmt, ipc_msg_t msg_in);
csi_status_t handle_power_state (ep_mgmt_desc_t *mgmt, ipc_msg_t msg_in);
csi_status_t handle_power_state_ack (ep_mgmt_desc_t *mgmt, ipc_msg_t msg_in);
csi_status_t validate_protocol (ep_mgmt_desc_t *mgmt, csi_protocol_version_t protocol_version);
//
// bi_ep_mgmt_create
//
// create a new management endpoint.
//
void
bi_ep_mgmt_create (csi_coproc_t which_coproc, struct task_event *ps_event)
{
ep_mgmt_desc_t *mgmt;
// enforce single instance
REQUIRE(mgmt_descs[which_coproc] == NULL);
mgmt = malloc(sizeof(ep_mgmt_desc_t));
REQUIRE(mgmt != NULL);
mgmt->coproc = which_coproc;
mgmt->ps_event = ps_event;
mgmt->power_state = CSI_PS_OFF;
mgmt_descs[which_coproc] = mgmt;
task_start(task_create("csi ep mgmt", ep_mgmt_task, mgmt_descs[which_coproc], kTaskStackSize_endpoints));
}
mgmt_state_t
bi_ep_mgmt_get_state (csi_coproc_t which_coproc)
{
if (mgmt_descs[which_coproc] == NULL) {
return MGMT_NONEXISTENT;
}
return mgmt_descs[which_coproc]->state;
}
csi_power_state_t
bi_ep_mgmt_get_power_state (csi_coproc_t which_coproc)
{
if (mgmt_descs[which_coproc] == NULL) {
return CSI_PS_OFF;
}
return mgmt_descs[which_coproc]->power_state;
}
csi_status_t
bi_ep_mgmt_send_ps (csi_coproc_t which_coproc, csi_power_state_t ps)
{
ep_mgmt_desc_t *mgmt;
msg_payload_t ps_msg;
mgmt = mgmt_descs[which_coproc];
csi_mgmt_create_msg (&ps_msg, MGMT_MSG_POWER_STATE, csi_mgmt_create_ps_msg(ps));
return csi_send_message (mgmt->csi_token, ps_msg);
}
//
// mgmt endpoint: task
//
int
ep_mgmt_task (void *arg)
{
csi_status_t result;
msg_payload_t rcv_msg;
ep_mgmt_desc_t *mgmt = (ep_mgmt_desc_t*)arg;
event_init(&mgmt->msg_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
result = csi_register_endpoint(mgmt->coproc, IPC_ENDPOINT_BUILT_IN_MGMT, &mgmt->msg_event, &mgmt->csi_token, &mgmt->name);
REQUIRE(result==CSI_STATUS_OK);
mgmt->state = MGMT_QUIESCED;
mgmt->remoteVersion = MGMT_UNSUPPORTED_VERSION;
// FixMe - pinger_init(&mgmt->pinger);
mgmt->ping_test_waiting = false;
mgmt->ping_test_seqnum = (csi_ping_seqnum_t)0x1234;
for (;;) {
event_wait(&mgmt->msg_event);
// empty the rcv queue
while(csi_receive_message(mgmt->csi_token, &rcv_msg) == CSI_STATUS_OK) {
mgmt_msg_handler(mgmt, rcv_msg);
}
}
return 0;
}
csi_status_t
mgmt_msg_handler (ep_mgmt_desc_t *mgmt, msg_payload_t msg)
{
csi_mgmt_msg_t type;
csi_status_t status;
type = csi_mgmt_get_type(msg);
status = CSI_STATUS_OK;
// get the type and dispatch to the appropriate handler
switch (type) {
case MGMT_MSG_HELLO:
status = handle_hello(mgmt, msg);
break;
case MGMT_MSG_HELLO_ACK:
CSI_EP_LOG(CRITICAL, mgmt->coproc, mgmt->name, "unsupported HELLO_ACK message received: %llx\n", msg);
break;
case MGMT_MSG_PING:
CSI_EP_LOG(CRITICAL, mgmt->coproc, mgmt->name, "unsupported PING message received: %llx\n", msg);
break;
case MGMT_MSG_PING_ACK:
// in lieu of autonomous pinger
if (!mgmt->ping_test_waiting) {
CSI_EP_LOG(CRITICAL, mgmt->coproc, mgmt->name, "unexpected ping ack received\n");
}
mgmt->ping_test_ack = msg;
mgmt->ping_test_waiting = false;
break;
case MGMT_MSG_POWER_STATE:
status = handle_power_state(mgmt, msg);
break;
case MGMT_MSG_POWER_STATE_ACK:
status = handle_power_state_ack(mgmt, msg);
break;
case MGMT_MSG_NOP:
// nop message are silently discarded
status = CSI_STATUS_OK;
break;
case MGMT_MSG_ENDPOINT_STATUS:
// these should never happen
default:
// all other message type are currently unsupported
// ignore them to support upward compatibility
CSI_EP_LOG(CRITICAL, mgmt->coproc, mgmt->name, "invalid or unsupported message type received: %d\n", type);
break;
}
if (CSI_STATUS_OK != status) {
CSI_EP_LOG(CRITICAL, mgmt->coproc, mgmt->name, "failed to process message of type %d\n", type);
}
return status;
}
csi_status_t
handle_hello (ep_mgmt_desc_t *mgmt, ipc_msg_t msg_in)
{
csi_status_t status;
msg_payload_t msg_out;
// Check status of the endpoint.
// If we are not waiting for a hello message (quiesced state) ignore it
if ((MGMT_QUIESCED != mgmt->state) && (MGMT_UNSUPPORTED_PROTOCOL_VERSION != mgmt->state)) {
return CSI_STATUS_ERROR;
}
mgmt->power_state = CSI_PS_ON;
// Validate the other side protocol version
status = validate_protocol(mgmt, csi_mgmt_get_prot(msg_in));
if (CSI_STATUS_OK == status) {
// supported version - send our version in the ACK message
if (csi_is_panic_recovery(mgmt->coproc)) {
// set the panic recovery feature flag
csi_mgmt_create_msg (&msg_out, MGMT_MSG_HELLO_ACK, csi_mgmt_create_hello_msg (CSI_FEATURE_PANIC_RECOVERY, APPLE_CSI_PROTOCOL_VERSION));
} else {
// no feature support by default.
csi_mgmt_create_msg (&msg_out, MGMT_MSG_HELLO_ACK, csi_mgmt_create_hello_msg (CSI_FEATURE_NONE, APPLE_CSI_PROTOCOL_VERSION));
}
CSI_EP_LOG (DEBUG_SPEW, mgmt->coproc, mgmt->name, "coproc running, protocol version %x.%x\n",
csi_mgmt_get_prot_major(mgmt->remoteVersion), csi_mgmt_get_prot_minor(mgmt->remoteVersion));
} else {
// unsupported version - signal the other side we do not support them
CSI_EP_LOG (CRITICAL, mgmt->coproc, mgmt->name, "coproc running unsupported version %x.%x\n",
csi_mgmt_get_prot_major(mgmt->remoteVersion), csi_mgmt_get_prot_minor(mgmt->remoteVersion));
csi_mgmt_create_msg (&msg_out, MGMT_MSG_HELLO_ACK, MGMT_UNSUPPORTED_VERSION);
}
return bi_ep_send_message (mgmt->csi_token, msg_out);
}
csi_status_t
handle_power_state (ep_mgmt_desc_t *mgmt, ipc_msg_t msg_in)
{
msg_payload_t ps_msg;
csi_power_state_t ps;
// the IOP is requesting a power state change.
// Used for debugging purpose, might be removed later.
ps = csi_mgmt_get_ps_state (msg_in);
csi_mgmt_create_msg (&ps_msg, MGMT_MSG_POWER_STATE, csi_mgmt_create_ps_msg(ps));
return bi_ep_send_message (mgmt->csi_token, ps_msg);
}
csi_status_t
handle_power_state_ack (ep_mgmt_desc_t *mgmt, ipc_msg_t msg_in)
{
csi_power_state_t ps = csi_mgmt_get_ps_state (msg_in);
// transition to ON means the IOP is now running
// not used in iBoot.
if (CSI_PS_ON == ps) {
return CSI_STATUS_OK;
}
// if the power state is not ON quiesce built in endpoints.
syslog_quiesce(mgmt->coproc, ps);
console_quiesce(mgmt->coproc, ps);
crashlog_quiesce(mgmt->coproc, ps);
// set the new power state
mgmt->power_state = ps;
mgmt->state = MGMT_QUIESCED;
CSI_EP_LOG(DEBUG, mgmt->coproc, mgmt->name, "IOP state changed to 0x%x (mgmt state=%x)\n", mgmt->power_state, mgmt->state);
// signal the power state change event
event_signal(mgmt->ps_event);
return CSI_STATUS_OK;
}
csi_status_t
validate_protocol (ep_mgmt_desc_t *mgmt, csi_protocol_version_t protocol_version)
{
// store the remote protocol version for future use.
mgmt->remoteVersion = protocol_version;
// Validate the protocol version
// Current version support: major has to match.
if (APPLE_CSI_PROTOCOL_VERSION == protocol_version) {
// valid version
mgmt->state = MGMT_VERSION_OK;
return CSI_STATUS_OK;
} else {
// unsupported version
mgmt->state = MGMT_UNSUPPORTED_PROTOCOL_VERSION;
return CSI_STATUS_UNSUPPORTED;
}
}
csi_status_t
bi_ep_mgmt_send_ping(csi_coproc_t which_coproc, int ping_count)
{
int j;
ep_mgmt_desc_t *mgmt;
u_int64_t cum_start_ticks;
u_int64_t per_ping_start_ticks;
ipc_msg_t ping_msg;
csi_status_t status;
mgmt = mgmt_descs[which_coproc];
if (mgmt==NULL) {
return CSI_STATUS_UNAVAILABLE;
}
cum_start_ticks = timer_get_ticks();
for ( j=ping_count; --j>=0; ) {
mgmt->ping_test_waiting = true;
mgmt->ping_test_seqnum++;
csi_mgmt_create_msg (&ping_msg, MGMT_MSG_PING, csi_mgmt_create_ping_msg(mgmt->ping_test_seqnum, (csi_ping_timestamp_t)timer_get_ticks()));
status = bi_ep_send_message (mgmt->csi_token, ping_msg);
if (CSI_STATUS_OK != status) {
return status;
}
per_ping_start_ticks = timer_get_ticks();
while (mgmt->ping_test_waiting) {
if (timer_ticks_to_usecs(timer_get_ticks()-per_ping_start_ticks) >= 500000) {
// ping ack not received within expected period
// FixMe - expected period is warped for fastsim's notion of time
mgmt->ping_test_waiting = false;
return CSI_STATUS_TIMEOUT;
}
task_yield();
}
if (csi_mgmt_get_ping_seqnum(mgmt->ping_test_ack) != mgmt->ping_test_seqnum) {
return CSI_STATUS_ERROR;
}
}
if (ping_count == 1) {
CSI_EP_LOG(CRITICAL, mgmt->coproc, mgmt->name, "ping rtt: %lld usec\n", timer_ticks_to_usecs(timer_get_ticks()-cum_start_ticks));
} else {
CSI_EP_LOG(CRITICAL, mgmt->coproc, mgmt->name, "%d pings cumulative rtt: %lld usec\n", ping_count, timer_ticks_to_usecs(timer_get_ticks()-cum_start_ticks));
}
return CSI_STATUS_OK;
}