first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
@@ -0,0 +1,747 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2012 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, 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 Computer, Inc.
|
||||
*/
|
||||
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <arch.h>
|
||||
#include <debug.h>
|
||||
#include <sys/task.h>
|
||||
#include <sys/boot.h>
|
||||
#include <lib/env.h>
|
||||
#include <platform/timer.h>
|
||||
#include <drivers/a7iop/a7iop.h>
|
||||
#include <drivers/csi.h>
|
||||
|
||||
#include "csi_private.h"
|
||||
|
||||
// protocol files
|
||||
#include <csi_system.h>
|
||||
|
||||
#include "endpoints/management_ep.h"
|
||||
#include "endpoints/syslog_ep.h"
|
||||
#include "endpoints/console_ep.h"
|
||||
#include "endpoints/crashlog_ep.h"
|
||||
|
||||
#include <platform/soc/hwclocks.h>
|
||||
#include <platform/clocks.h>
|
||||
#include <platform/soc/pmgr.h>
|
||||
|
||||
// XXX <rdar://problem/10422340> addresses the need for a common copy of this file
|
||||
#include "queue.h"
|
||||
|
||||
|
||||
static const MailboxEndpointConfig endpoint_config[] = MAILBOX_ENDPOINT_INITIALIZERS;
|
||||
|
||||
#define MAILBOX_ENDPOINT_INITIALIZER_COUNT (int)(sizeof(endpoint_config)/sizeof(*endpoint_config))
|
||||
|
||||
#define EP_QUEUE_ADVANCE(_index, _size) ((_index) = ((((_index) + 1) >= _size) ? 0 : ((_index) + 1)))
|
||||
|
||||
|
||||
// A simple mechanism to put a governor on potentially high volume log scenarios
|
||||
#define LOG_GOV_PERIOD_USEC 5000000
|
||||
|
||||
typedef struct {
|
||||
int occ_count;
|
||||
u_int64_t last_log;
|
||||
} log_governor_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
bool registered;
|
||||
csi_coproc_t coproc;
|
||||
uint32_t index;
|
||||
const char *name;
|
||||
uint32_t rcv_queue_size;
|
||||
msg_payload_t *rcv_queue;
|
||||
uint32_t next_in;
|
||||
uint32_t next_out;
|
||||
uint32_t sm_alignment;
|
||||
struct task_event *rcv_not_empty_event;
|
||||
} endpoint_desc_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
COPROC_STATE_RESET = 0,
|
||||
COPROC_STATE_ERROR_FW,
|
||||
COPROC_STATE_ERROR_STARTING,
|
||||
COPROC_STATE_RUNNING,
|
||||
COPROC_STATE_STOPPED,
|
||||
} coproc_state_t;
|
||||
|
||||
typedef struct {
|
||||
csi_coproc_t coproc;
|
||||
coproc_state_t state;
|
||||
coproc_feature_t feature;
|
||||
uint32_t send_timeout;
|
||||
uint32_t endpoint_count;
|
||||
endpoint_desc_t endpoints[CSI_MAX_ENDPOINTS];
|
||||
fw_desc_t fw_desc;
|
||||
struct task_event power_state_event;
|
||||
log_governor_t gov_ep_queue_ovflw;
|
||||
log_governor_t gov_ep_not_reg;
|
||||
} csi_desc_t;
|
||||
|
||||
static csi_desc_t *csi_descs[CSI_COPROC_MAX] = {NULL};
|
||||
|
||||
|
||||
//
|
||||
// Private functions
|
||||
//
|
||||
|
||||
static endpoint_desc_t *find_endpoint(csi_coproc_t which_coproc, ipc_endpoint_t which_ep);
|
||||
|
||||
static int inbox_not_empty_task(void *arg);
|
||||
|
||||
static void message_dispatcher(endpoint_desc_t *ep, msg_payload_t msg_payload);
|
||||
|
||||
static void *allocate_shared_memory(uint32_t capacity, uint32_t alignment);
|
||||
|
||||
static bool check_log_gov(log_governor_t *gov, csi_coproc_t which_coproc);
|
||||
|
||||
|
||||
//
|
||||
// csi_init
|
||||
//
|
||||
csi_status_t
|
||||
csi_init (csi_coproc_t which_coproc)
|
||||
{
|
||||
int j;
|
||||
csi_desc_t *csi;
|
||||
csi_status_t status;
|
||||
endpoint_desc_t *next_ep;
|
||||
|
||||
if (which_coproc >= CSI_COPROC_MAX) {
|
||||
return CSI_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
// enforce single instance
|
||||
REQUIRE(csi_descs[which_coproc]==NULL);
|
||||
csi_descs[which_coproc] = malloc(sizeof(csi_desc_t));
|
||||
REQUIRE(csi_descs[which_coproc]!=NULL);
|
||||
|
||||
csi = csi_descs[which_coproc];
|
||||
|
||||
csi->coproc = which_coproc;
|
||||
csi->state = COPROC_STATE_RESET;
|
||||
csi->send_timeout = CSI_SEND_MESSAGE_TIMEOUT;
|
||||
|
||||
csi->gov_ep_queue_ovflw.occ_count = 0;
|
||||
csi->gov_ep_not_reg.occ_count = 0;
|
||||
|
||||
csi->feature = COPROC_FEATURE_NONE;
|
||||
|
||||
// Create all endpoint structures - all are inactive until registered.
|
||||
|
||||
csi->endpoint_count = 0;
|
||||
next_ep = csi->endpoints;
|
||||
|
||||
for (j=CSI_MAX_ENDPOINTS; --j>=0;) {
|
||||
next_ep->registered = false;
|
||||
next_ep->coproc = csi->coproc;
|
||||
next_ep->index = 0;
|
||||
next_ep->name = "none";
|
||||
next_ep->rcv_queue_size = 0;
|
||||
next_ep->rcv_queue = NULL;
|
||||
next_ep->next_in = 0;
|
||||
next_ep->next_out = 0;
|
||||
next_ep->rcv_not_empty_event = NULL;
|
||||
next_ep++;
|
||||
}
|
||||
|
||||
next_ep = csi->endpoints;
|
||||
|
||||
for (j=0; (j<MAILBOX_ENDPOINT_INITIALIZER_COUNT) && (csi->endpoint_count<CSI_MAX_ENDPOINTS); j++) {
|
||||
if (endpoint_config[j].rcv_queue_size != 0) {
|
||||
next_ep->index = endpoint_config[j].index;
|
||||
next_ep->name = endpoint_config[j].name;
|
||||
next_ep->rcv_queue_size = endpoint_config[j].rcv_queue_size;
|
||||
next_ep->rcv_queue = malloc(sizeof(*next_ep->rcv_queue) * endpoint_config[j].rcv_queue_size);
|
||||
REQUIRE(next_ep->rcv_queue!=NULL);
|
||||
next_ep->next_in = 0;
|
||||
next_ep->next_out = ~(0);
|
||||
|
||||
next_ep++;
|
||||
csi->endpoint_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// create built-in endpoints
|
||||
bi_ep_mgmt_create(csi->coproc, &(csi->power_state_event));
|
||||
bi_ep_crashlog_create(csi->coproc);
|
||||
bi_ep_console_create(csi->coproc);
|
||||
bi_ep_syslog_create(csi->coproc);
|
||||
|
||||
// Find the firmware, prep it, start the coprocessor.
|
||||
if (CSI_COPROC_ANS == which_coproc) {
|
||||
// region is provided by iBoot
|
||||
csi->fw_desc.region_base = ASP_BASE;
|
||||
csi->fw_desc.region_size = (uint32_t)(ASP_SIZE);
|
||||
} else {
|
||||
// Allocate region for all other corpoc
|
||||
// XXX: what size is needed - to be added later when/if other coproc are supported.
|
||||
csi->fw_desc.region_base = 0x00;
|
||||
csi->fw_desc.region_size = 0x00;
|
||||
|
||||
return CSI_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
status = fw_fixup_and_relocate(csi->coproc, &csi->fw_desc);
|
||||
if (status != CSI_STATUS_OK) {
|
||||
CSI_LOG(CRITICAL, which_coproc, "fw_fixup_and_relocate() error (%s)\n", CSI_STATUS_STR(status));
|
||||
csi->state = COPROC_STATE_ERROR_FW;
|
||||
return status;
|
||||
}
|
||||
|
||||
status = csi_start (which_coproc);
|
||||
if (status != CSI_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// create the power state event
|
||||
event_init(&(csi->power_state_event), EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
csi_start (csi_coproc_t which_coproc)
|
||||
{
|
||||
csi_desc_t *csi;
|
||||
addr_t remap_base;
|
||||
uint64_t remap_size;
|
||||
int status;
|
||||
|
||||
csi = csi_descs[which_coproc];
|
||||
|
||||
remap_base = (const addr_t)csi->fw_desc.region_base;
|
||||
remap_size = (const uint64_t)csi->fw_desc.region_size;
|
||||
|
||||
status = akf_start(KFW(csi->coproc), remap_base, remap_size);
|
||||
if (status != 0) {
|
||||
CSI_LOG(CRITICAL, which_coproc, "akf_start() error: %d\n", status);
|
||||
csi->state = COPROC_STATE_ERROR_STARTING;
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
csi_clr_feature (which_coproc, COPROC_FEATURE_SHUTDOWN_IN_PROGRESS);
|
||||
|
||||
CSI_LOG(DEBUG_SPEW, which_coproc, "coproc started: remap base = %p, size = 0x%llx (feature=%x)\n", (void *)remap_base, remap_size, csi_get_feature(which_coproc));
|
||||
csi->state = COPROC_STATE_RUNNING;
|
||||
|
||||
// start the message reading task
|
||||
task_start(task_create("csi inbox_not_empty_task", inbox_not_empty_task, (void*)csi, kTaskStackSize_inbox_not_empty));
|
||||
|
||||
task_yield(); // give them a chance to run
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
csi_panic_recover (csi_coproc_t which_coproc)
|
||||
{
|
||||
csi_desc_t *csi;
|
||||
csi_status_t status;
|
||||
|
||||
csi = csi_descs[which_coproc];
|
||||
|
||||
// stop the AKF to reset it.
|
||||
csi_stop (which_coproc);
|
||||
|
||||
// destroy all the allocated shared memory
|
||||
// force a power state change to off.
|
||||
handle_power_state_ack (mgmt_descs[which_coproc], CSI_PS_OFF);
|
||||
|
||||
clock_reset_device(CLK_ANS);
|
||||
|
||||
// give a chance to all other task to terminate
|
||||
task_yield();
|
||||
|
||||
// reset the memory
|
||||
status = fw_fixup_and_relocate (csi->coproc, &csi->fw_desc);
|
||||
if (status != CSI_STATUS_OK) {
|
||||
CSI_LOG(CRITICAL, which_coproc, "panic recovery failed: fw_fixup_and_relocate() error (%s)\n", CSI_STATUS_STR(status));
|
||||
csi->state = COPROC_STATE_ERROR_FW;
|
||||
return status;
|
||||
}
|
||||
|
||||
// restart the AKF
|
||||
return csi_start (which_coproc);
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
csi_late_init (void) {
|
||||
#if APPLICATION_IBOOT
|
||||
const char *boot_args;
|
||||
char *arg_str;
|
||||
uint32_t verbosity;
|
||||
|
||||
verbosity = 9999;
|
||||
|
||||
boot_args = env_get("boot-args");
|
||||
|
||||
if (boot_args != NULL) {
|
||||
arg_str = strstr(boot_args, "csi_syslog_verbosity=");
|
||||
if (arg_str != NULL) {
|
||||
verbosity = arg_str[strlen("csi_syslog_verbosity=")] - '0';
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < CSI_COPROC_MAX; j++) {
|
||||
bi_ep_syslog_verbosity(j, verbosity);
|
||||
}
|
||||
#endif
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
csi_status_t
|
||||
csi_stop (csi_coproc_t which_coproc)
|
||||
{
|
||||
csi_desc_t *csi;
|
||||
|
||||
csi = csi_descs[which_coproc];
|
||||
|
||||
akf_stop (KFW(csi->coproc));
|
||||
csi->state = COPROC_STATE_STOPPED;
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
csi_quiesce (enum boot_target target)
|
||||
{
|
||||
csi_status_t status = CSI_STATUS_OK;
|
||||
|
||||
for (int j = 0; j < CSI_COPROC_MAX; j++) {
|
||||
csi_desc_t *csi = csi_descs[j];
|
||||
|
||||
if (NULL != csi) {
|
||||
if (csi->state == COPROC_STATE_RUNNING) {
|
||||
// put the coproc in suspend to ram for all state
|
||||
status = hibernate_iop (csi->coproc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
request_iop_power_state (csi_coproc_t which_coproc, csi_power_state_t ps)
|
||||
{
|
||||
csi_desc_t *csi;
|
||||
|
||||
csi = csi_descs[which_coproc];
|
||||
|
||||
// if the power state event was previously signaled (panic recovery for example)
|
||||
// unsignal it was we need to wait for a new transition
|
||||
event_unsignal (&(csi->power_state_event));
|
||||
|
||||
// Request the IOP to transition to the specified power state
|
||||
// power message are always send thru the management channel
|
||||
bi_ep_mgmt_send_ps (which_coproc, ps);
|
||||
|
||||
// Wait for the acknowledge message.
|
||||
event_wait (&(csi->power_state_event));
|
||||
|
||||
// make sure the power state is the one we want
|
||||
if (bi_ep_mgmt_get_power_state(csi->coproc) != ps) {
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
quiesce_iop (csi_coproc_t which_coproc)
|
||||
{
|
||||
csi_desc_t *csi;
|
||||
csi_status_t status;
|
||||
|
||||
csi = csi_descs[which_coproc];
|
||||
|
||||
// Request the new power state
|
||||
status = request_iop_power_state (which_coproc, CSI_PS_QUIESCE);
|
||||
if (CSI_STATUS_OK != status) {
|
||||
CSI_LOG(CRITICAL, csi->coproc, "Failed to quiesce\n");
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
CSI_LOG(CRITICAL, csi->coproc, "quiesced\n");
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
hibernate_iop (csi_coproc_t which_coproc)
|
||||
{
|
||||
csi_desc_t *csi;
|
||||
csi_status_t status;
|
||||
|
||||
csi = csi_descs[which_coproc];
|
||||
|
||||
// Request the new power state
|
||||
csi_set_feature (which_coproc, COPROC_FEATURE_SHUTDOWN_IN_PROGRESS);
|
||||
status = request_iop_power_state (which_coproc, CSI_PS_SLEEP);
|
||||
if (CSI_STATUS_OK != status) {
|
||||
CSI_LOG(CRITICAL, csi->coproc, "Failed to hibernate\n");
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
CSI_LOG(CRITICAL, csi->coproc, "in suspend to RAM\n");
|
||||
|
||||
// now turn off the iop
|
||||
return csi_stop (which_coproc);
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
set_send_timeout (csi_coproc_t which_coproc, uint32_t timeout)
|
||||
{
|
||||
csi_desc_t *csi = csi_descs[which_coproc];
|
||||
|
||||
csi->send_timeout = timeout;
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
get_send_timeout (csi_coproc_t which_coproc)
|
||||
{
|
||||
csi_desc_t *csi = csi_descs[which_coproc];
|
||||
|
||||
return csi->send_timeout;
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
csi_set_feature (csi_coproc_t which_coproc, coproc_feature_t feature)
|
||||
{
|
||||
csi_desc_t *csi = csi_descs[which_coproc];
|
||||
|
||||
csi->feature = feature;
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
coproc_feature_t
|
||||
csi_get_feature (csi_coproc_t which_coproc)
|
||||
{
|
||||
csi_desc_t *csi = csi_descs[which_coproc];
|
||||
|
||||
return csi->feature;
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
csi_clr_feature (csi_coproc_t which_coproc, coproc_feature_t feature)
|
||||
{
|
||||
csi_desc_t *csi = csi_descs[which_coproc];
|
||||
|
||||
csi->feature &= ~(feature);
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
csi_info (csi_coproc_t which_coproc)
|
||||
{
|
||||
fw_print_version (which_coproc, &(csi_descs[which_coproc]->fw_desc));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// allocate shared memory
|
||||
//
|
||||
static void *
|
||||
allocate_shared_memory(uint32_t capacity, uint32_t alignment)
|
||||
{
|
||||
uint32_t actual_capacity;
|
||||
void *sm;
|
||||
|
||||
// We always allocate page-aligned and with multiples of the page size for alignment
|
||||
actual_capacity = roundup(capacity, PAGE_SIZE);
|
||||
alignment = roundup(alignment, PAGE_SIZE);
|
||||
|
||||
// allocate
|
||||
sm = memalign(actual_capacity, alignment);
|
||||
maybe_do_cache_operation(CACHE_CLEAN, sm, actual_capacity);
|
||||
|
||||
return sm;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
free_shared_memory(void *sm)
|
||||
{
|
||||
free(sm);
|
||||
}
|
||||
|
||||
static endpoint_desc_t *
|
||||
find_endpoint(csi_coproc_t which_coproc, ipc_endpoint_t which_ep)
|
||||
{
|
||||
int j;
|
||||
csi_desc_t *this_csi;
|
||||
endpoint_desc_t *next_ep;
|
||||
|
||||
if (which_coproc < CSI_COPROC_MAX) {
|
||||
this_csi = csi_descs[which_coproc];
|
||||
if (this_csi != NULL) {
|
||||
next_ep = this_csi->endpoints;
|
||||
for (j=this_csi->endpoint_count; --j>=0; next_ep++) {
|
||||
if (next_ep->index == which_ep) {
|
||||
return next_ep;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// inbox_not_empty_task
|
||||
//
|
||||
// Wait for and dispatch incoming messages from the IOP.
|
||||
// Get all the pending message in the mailbox and dispatch them to the appropriate
|
||||
// endpoint. Message to non existent endpoint are silently discarded.
|
||||
//
|
||||
static int
|
||||
inbox_not_empty_task(void *token)
|
||||
{
|
||||
volatile csi_desc_t *csi;
|
||||
int status;
|
||||
msg_payload_t msg;
|
||||
endpoint_desc_t *ep;
|
||||
|
||||
REQUIRE (NULL != (csi = (csi_desc_t*)token));
|
||||
|
||||
if (csi->state != COPROC_STATE_RUNNING) {
|
||||
CSI_LOG(CRITICAL, (csi_coproc_t)csi->coproc, "error: inbox_not_empty_task() coproc not running\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// we should only be trying to read message when the AKF is running otherwise data abort can happen.
|
||||
while (COPROC_STATE_RUNNING == csi->state) {
|
||||
// check the coproc status every 100ms
|
||||
while ((status = akf_recv_mbox(KFW(csi->coproc), &msg, 100000)) == 0) {
|
||||
|
||||
// queue the message, the endpoint will ignore it if disabled.
|
||||
ep = find_endpoint(csi->coproc, ipc_msg_get_endpoint(&msg));
|
||||
|
||||
if (ep) {
|
||||
message_dispatcher(ep, (msg&IPC_MSG_PAYLOAD_MASK));
|
||||
} else {
|
||||
CSI_LOG(CRITICAL, (csi_coproc_t)csi->coproc, "error: message received for unknown endpoint => message discarded (%d, 0x%llx)\n", ipc_msg_get_endpoint(&msg), msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// coproc has stopped running, terminate task
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
message_dispatcher(endpoint_desc_t *ep, msg_payload_t msg)
|
||||
{
|
||||
// do some basic filtering before signaling
|
||||
if (!ep->registered) {
|
||||
if (check_log_gov(&csi_descs[ep->coproc]->gov_ep_not_reg, ep->coproc)) {
|
||||
CSI_LOG(CRITICAL, ep->coproc, "error: %s endpoint not registered => message discarded\n", ep->name);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// add the message to the queue
|
||||
|
||||
ep->rcv_queue[ep->next_in] = msg;
|
||||
EP_QUEUE_ADVANCE(ep->next_in, ep->rcv_queue_size);
|
||||
|
||||
// check for overflow. discard oldest message if it happens.
|
||||
if (ep->next_in == ep->next_out) {
|
||||
if (check_log_gov(&csi_descs[ep->coproc]->gov_ep_queue_ovflw, ep->coproc)) {
|
||||
CSI_LOG(CRITICAL, ep->coproc, "error: %s endpoint queue overflow\n", ep->name);
|
||||
}
|
||||
EP_QUEUE_ADVANCE(ep->next_out, ep->rcv_queue_size);
|
||||
}
|
||||
|
||||
// if this was the first message move the _nextOut pointer to the newly received message
|
||||
if ((uint32_t)~(0) == ep->next_out) {
|
||||
ep->next_out = 0;
|
||||
}
|
||||
|
||||
// signal the endpoint that its rcv queue is not empty
|
||||
REQUIRE(ep->rcv_not_empty_event!=NULL);
|
||||
event_signal(ep->rcv_not_empty_event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// endpoint support
|
||||
|
||||
// NOTE: an unregister method could be added if necessary
|
||||
|
||||
csi_status_t
|
||||
csi_register_endpoint(csi_coproc_t which_coproc, uint32_t which_ep, struct task_event *event, void **return_token, const char **name)
|
||||
{
|
||||
endpoint_desc_t *ep;
|
||||
|
||||
ep = find_endpoint(which_coproc, (ipc_endpoint_t)which_ep);
|
||||
|
||||
if (ep==NULL) {
|
||||
CSI_LOG(CRITICAL, which_coproc, "csi_register_endpoint(): no knowledge of endpoint #%d\n", which_ep);
|
||||
return CSI_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (ep->registered) {
|
||||
CSI_LOG(CRITICAL, which_coproc, "csi_register_endpoint(): %s endpoint already registered\n", ep->name);
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
if (name != NULL) {
|
||||
*name = ep->name;
|
||||
}
|
||||
|
||||
ep->rcv_not_empty_event = event;
|
||||
ep->registered = true;
|
||||
ep->sm_alignment = csi_descs[which_coproc]->fw_desc.alignment_req;
|
||||
*return_token = (void*)ep;
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
csi_allocate_shared_memory(void *token, uint32_t capacity)
|
||||
{
|
||||
endpoint_desc_t *ep = (endpoint_desc_t*)token;
|
||||
void *sm;
|
||||
|
||||
sm = allocate_shared_memory(capacity, ep->sm_alignment);
|
||||
|
||||
return sm;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
csi_free_shared_memory(void *sm)
|
||||
{
|
||||
free_shared_memory (sm);
|
||||
|
||||
// remove element from the list
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
csi_receive_message(void *token, msg_payload_t *msg_payload)
|
||||
{
|
||||
endpoint_desc_t *ep = (endpoint_desc_t*)token;
|
||||
|
||||
REQUIRE(ep != NULL);
|
||||
|
||||
if (!ep->registered) {
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
// make sure we have a message available
|
||||
if (((uint32_t)~(0) == ep->next_out) || (ep->next_out == ep->next_in)) {
|
||||
return CSI_STATUS_UNAVAILABLE;
|
||||
}
|
||||
|
||||
// get the message from ep input queue and return it.
|
||||
*msg_payload = ep->rcv_queue[ep->next_out];
|
||||
EP_QUEUE_ADVANCE(ep->next_out, ep->rcv_queue_size);
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
csi_send_message (void *token, msg_payload_t msg_payload)
|
||||
{
|
||||
endpoint_desc_t *ep;
|
||||
ipc_msg_t msg_out;
|
||||
int status;
|
||||
csi_desc_t *csi;
|
||||
|
||||
REQUIRE(token != NULL);
|
||||
|
||||
ep = (endpoint_desc_t*)token;
|
||||
msg_out = (ipc_msg_t)msg_payload;
|
||||
|
||||
if (!ep->registered) {
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
// FixMe - some clients already put in the endpoint field - others expect it to be done for them
|
||||
if ((msg_out & IPC_MSG_ENDPOINT_MASK) == 0) {
|
||||
ipc_msg_set_endpoint(&msg_out, ep->index);
|
||||
}
|
||||
|
||||
if (ipc_msg_get_endpoint(&msg_out) != ep->index) {
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
ipc_msg_set_endpoint(&msg_out, ep->index);
|
||||
|
||||
csi = csi_descs[ep->coproc];
|
||||
|
||||
status = akf_send_mbox(KFW(ep->coproc), msg_out, csi->send_timeout);
|
||||
if (-2 == status) {
|
||||
// timeout
|
||||
CSI_LOG(CRITICAL, ep->coproc, "csi_send_message(): failed to send message %llx in %dus, AKF hanged?\n", msg_payload, csi->send_timeout);
|
||||
return CSI_STATUS_TIMEOUT;
|
||||
}
|
||||
// all other errors are non fatal
|
||||
if (0 != status) {
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
check_log_gov(log_governor_t *gov, csi_coproc_t which_coproc)
|
||||
{
|
||||
gov->occ_count++;
|
||||
|
||||
if ((gov->occ_count == 1) || (timer_ticks_to_usecs(timer_get_ticks() - gov->last_log) >= LOG_GOV_PERIOD_USEC)) {
|
||||
if (gov->occ_count > 2) {
|
||||
CSI_LOG(CRITICAL, which_coproc, "WARNING: The condition logged below has occurred %d times\n", (gov->occ_count-1));
|
||||
}
|
||||
gov->occ_count = 1;
|
||||
gov->last_log = timer_get_ticks();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void maybe_do_cache_operation(int operation, void *address, u_int32_t length)
|
||||
{
|
||||
#if WITH_NON_COHERENT_DMA
|
||||
void *aligned_address;
|
||||
u_int32_t aligned_length;
|
||||
|
||||
aligned_address = (void*)((uintptr_t)address & ~(CPU_CACHELINE_SIZE-1));
|
||||
aligned_length = roundup((length + (address - aligned_address)), CPU_CACHELINE_SIZE);
|
||||
|
||||
platform_cache_operation(operation, aligned_address, aligned_length);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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 <arch.h>
|
||||
#include <debug.h>
|
||||
#include <sys.h>
|
||||
#include <sys/menu.h>
|
||||
#include <sys/task.h>
|
||||
#include <drivers/csi.h>
|
||||
#include <drivers/a7iop/a7iop.h>
|
||||
|
||||
#include <platform/timer.h>
|
||||
#include <platform/soc/hwregbase.h>
|
||||
|
||||
#include "csi_private.h"
|
||||
#include "endpoints/management_ep.h"
|
||||
#include "endpoints/syslog_ep.h"
|
||||
#include "endpoints/console_ep.h"
|
||||
#include "endpoints/crashlog_ep.h"
|
||||
|
||||
|
||||
#if defined(WITH_MENU) && WITH_MENU
|
||||
|
||||
static int csi_debug_main(int argc, struct cmd_arg *args);
|
||||
|
||||
MENU_COMMAND_DEBUG(csi, csi_debug_main, "Test CSI driver", NULL);
|
||||
|
||||
static void usage() {
|
||||
printf("usage: csi ans|<other iop> <command>\n"
|
||||
" info\n"
|
||||
" crashlog|cl nmi|ml|mc\n"
|
||||
" ping [count]\n"
|
||||
" syslog|sl [1|0|on|off]\n"
|
||||
" state|st\n"
|
||||
" quiesce|qs\n"
|
||||
" hibernate|hb\n"
|
||||
" resume|rs\n"
|
||||
" powerstate|ps [ps]\n"
|
||||
" console ...\n"
|
||||
" ... v <verbosity>\n"
|
||||
" ... c <cmd arg>\n"
|
||||
" timebase|tb\n"
|
||||
" stimeout|sto [timeout in us]\n");
|
||||
}
|
||||
|
||||
|
||||
static inline uint64_t read_akf_timebase(uint64_t regs_base)
|
||||
{
|
||||
uint32_t hi1 = rAKF_KIC_GLB_TIME_BASE_HI(regs_base);
|
||||
uint32_t lo = rAKF_KIC_GLB_TIME_BASE_LO(regs_base);
|
||||
uint32_t hi2 = rAKF_KIC_GLB_TIME_BASE_HI(regs_base);
|
||||
return ((((uint64_t)hi2) << 32) | ((hi1!=hi2) ? rAKF_KIC_GLB_TIME_BASE_LO(regs_base) : lo));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
csi_debug_main (int argc, struct cmd_arg *args)
|
||||
{
|
||||
csi_coproc_t which_coproc;
|
||||
const char *s;
|
||||
csi_status_t status;
|
||||
uint64_t akf_base_addr = 0;
|
||||
|
||||
if (argc < 3) {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// Find the targeted coproc
|
||||
//
|
||||
|
||||
s = args[1].str;
|
||||
if (strnicmp(s, "ans", strlen(s)) == 0) {
|
||||
which_coproc = CSI_COPROC_ANS;
|
||||
akf_base_addr = ANS_AKF_BASE_ADDR;
|
||||
} else if (strnicmp(s, "sep", strlen(s)) == 0) {
|
||||
which_coproc = CSI_COPROC_SEP;
|
||||
#if ASEP_AKF_BASE_ADDR
|
||||
akf_base_addr = ASEP_AKF_BASE_ADDR;
|
||||
#endif
|
||||
} else if (strnicmp(s, "isp", strlen(s)) == 0) {
|
||||
which_coproc = CSI_COPROC_ISP;
|
||||
#if ISP_AKF_BASE_ADDR
|
||||
akf_base_addr = ISP_AKF_BASE_ADDR;
|
||||
#endif
|
||||
} else if (strnicmp(s, "sio", strlen(s)) == 0) {
|
||||
which_coproc = CSI_COPROC_SIO;
|
||||
#if ASIO_AKF_BASE_ADDR
|
||||
akf_base_addr = ASIO_AKF_BASE_ADDR;
|
||||
#endif
|
||||
} else {
|
||||
printf("csi: %s unknown\n", s);
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (akf_base_addr == 0) {
|
||||
printf("csi: %s not supported\n", IOP_NAME(which_coproc));
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// Parse command
|
||||
//
|
||||
|
||||
status = CSI_STATUS_OK;
|
||||
s = args[2].str;
|
||||
|
||||
// information
|
||||
if (strnicmp(s, "info", strlen(s)) == 0) {
|
||||
csi_info (which_coproc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// crashlog
|
||||
if ((strnicmp(s, "crashlog", strlen(s)) == 0) || (strcmp(s, "cl") == 0)) {
|
||||
csi_crashlog_t cl;
|
||||
|
||||
if (argc < 4) {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = args[3].str;
|
||||
if (strnicmp(s, "nmi", strlen(s)) == 0) {
|
||||
cl = CSI_CRASHLOG_TYPE_NMI;
|
||||
} else if (strnicmp(s, "ml", strlen(s)) == 0) {
|
||||
cl = CSI_CRASHLOG_TYPE_MSG_LOG;
|
||||
} else if (strnicmp(s, "mc", strlen(s)) == 0) {
|
||||
cl = CSI_CRASHLOG_TYPE_MSG_CRASH;
|
||||
} else {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("csi: force crashlog from %s iop\n", IOP_NAME(which_coproc));
|
||||
status = bi_ep_crashlog_force_event(which_coproc, cl);
|
||||
|
||||
}
|
||||
// ping
|
||||
else if (strnicmp(s, "ping", strlen(s)) == 0) {
|
||||
|
||||
int ping_count = 1;
|
||||
|
||||
if (argc >= 4) {
|
||||
ping_count = atoi(args[3].str);
|
||||
if (ping_count<=0) {
|
||||
ping_count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
status = bi_ep_mgmt_send_ping(which_coproc, ping_count);
|
||||
|
||||
}
|
||||
// syslog
|
||||
else if ((strnicmp(s, "syslog", strlen(s)) == 0) || (strcmp(s, "sl") == 0)) {
|
||||
|
||||
if (argc < 4) {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = bi_ep_syslog_verbosity(which_coproc, atoi(args[3].str));
|
||||
|
||||
}
|
||||
// state
|
||||
else if ((strnicmp(s, "state", strlen(s)) == 0) || (strcmp(s, "st") == 0)) {
|
||||
|
||||
mgmt_state_t state = bi_ep_mgmt_get_state(which_coproc);
|
||||
csi_power_state_t ps = bi_ep_mgmt_get_power_state(which_coproc);
|
||||
|
||||
printf("csi: %s state - %s - ps=0x%x\n", IOP_NAME(which_coproc), COPROC_STATE(state), ps);
|
||||
|
||||
}
|
||||
// quiesce
|
||||
else if ((strnicmp(s, "quiesce", strlen(s)) == 0) || (strcmp(s, "qs") == 0)) {
|
||||
|
||||
status = quiesce_iop (which_coproc);
|
||||
|
||||
}
|
||||
// start
|
||||
else if ((strnicmp(s, "hibernate", strlen(s)) == 0) || (strcmp(s, "hb") == 0)) {
|
||||
|
||||
status = hibernate_iop (which_coproc);
|
||||
|
||||
}
|
||||
// start
|
||||
else if ((strnicmp(s, "resume", strlen(s)) == 0) || (strcmp(s, "rs") == 0)) {
|
||||
|
||||
// restart the IOP
|
||||
status = csi_start (which_coproc);
|
||||
|
||||
}
|
||||
// powerstate
|
||||
else if ((strnicmp(s, "powerstate", strlen(s)) == 0) || (strcmp(s, "ps") == 0)) {
|
||||
|
||||
if (argc < 4) {
|
||||
printf("powerstate requires a powerstate argument\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
csi_power_state_t ps = strtol(args[3].str, (char **)NULL, 0);
|
||||
status = request_iop_power_state (which_coproc, ps);
|
||||
|
||||
}
|
||||
// console
|
||||
else if (strnicmp(s, "console", strlen(s)) == 0) {
|
||||
|
||||
if (argc != 5) {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (args[3].str[0] == 'v') {
|
||||
status = bi_ep_console_set_verbosity(which_coproc, strtol(args[4].str, (char **)NULL, 0));
|
||||
} else if (args[3].str[0] == 'c') {
|
||||
status = bi_ep_console_send_custom_cmd(which_coproc, strtol(args[4].str, (char **)NULL, 0));
|
||||
} else {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// timebase
|
||||
else if ((strnicmp(s, "timebase", strlen(s)) == 0) || (strcmp(s, "tb") == 0)) {
|
||||
|
||||
uint64_t tb_host_1, tb_host_2, tb_iop_1, tb_iop_2;
|
||||
|
||||
tb_host_1 = timer_get_ticks();
|
||||
tb_iop_1 = read_akf_timebase(akf_base_addr);
|
||||
tb_iop_2 = read_akf_timebase(akf_base_addr);
|
||||
tb_host_2 = timer_get_ticks();
|
||||
|
||||
printf("csi: host vs %s timebase\n", IOP_NAME(which_coproc));
|
||||
printf("\thost:\t 0x%llx\n", tb_host_1);
|
||||
printf("\tiop:\t 0x%llx (%llu)\n", tb_iop_1, (tb_iop_1-tb_host_1));
|
||||
printf("\tiop:\t 0x%llx (%llu)\n", tb_iop_2, (tb_iop_2-tb_iop_1));
|
||||
printf("\thost:\t 0x%llx (%llu)\n", tb_host_2, (tb_host_2-tb_iop_2));
|
||||
}
|
||||
// send timeout
|
||||
else if ((strnicmp(s, "stimeout", strlen(s)) == 0) || (strcmp(s, "sto") == 0)) {
|
||||
|
||||
if (argc < 4) {
|
||||
printf("current send timeout for %s: %uus\n", IOP_NAME(which_coproc), get_send_timeout(which_coproc));
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t timeout = strtol(args[3].str, (char **)NULL, 0);
|
||||
if (0 == timeout) {
|
||||
// the A7 express no timeout as a wait forever.
|
||||
timeout = A7IOP_WAIT_FOREVER;
|
||||
printf("csi: %s send timeout disabled (%u)\n", IOP_NAME(which_coproc), timeout);
|
||||
} else {
|
||||
printf("csi: setting %s send timeout to %uus\n", IOP_NAME(which_coproc), timeout);
|
||||
}
|
||||
|
||||
status = set_send_timeout (which_coproc, timeout);
|
||||
}
|
||||
// unknown command
|
||||
else {
|
||||
printf("unknown command %s\n", s);
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status != CSI_STATUS_OK) {
|
||||
printf("csi %s %s - error reported: %s\n", IOP_NAME(which_coproc), args[2].str, CSI_STATUS_STR(status));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // defined(WITH_MENU) && WITH_MENU
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
//
|
||||
// Private Data
|
||||
//
|
||||
|
||||
static volatile csi_status_t last_error = CSI_STATUS_OK;
|
||||
|
||||
|
||||
//
|
||||
// bi_ep_send_message
|
||||
//
|
||||
// Send message for the built-in endpoint. If the IOP becomes unreachable
|
||||
// at some point stop sending message and instead directly fails to avoid
|
||||
// multiple timeout error.
|
||||
//
|
||||
csi_status_t
|
||||
bi_ep_send_message (void *token, msg_payload_t msg_payload)
|
||||
{
|
||||
csi_status_t status;
|
||||
|
||||
// make sure the previous send was not a timeout
|
||||
if (CSI_STATUS_OK != last_error) {
|
||||
// the IOP is not responding, error out right away
|
||||
return last_error;
|
||||
}
|
||||
|
||||
// send the message
|
||||
status = csi_send_message (token, msg_payload);
|
||||
if (CSI_STATUS_TIMEOUT == status) {
|
||||
// the IOP is not responding, save the error status
|
||||
last_error = CSI_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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/console_ep.h"
|
||||
|
||||
#include <csi_ipc_protocol.h>
|
||||
#include <csi_console_defines.h>
|
||||
#include <csi_console_protocol.h>
|
||||
|
||||
|
||||
//
|
||||
// Global Data
|
||||
//
|
||||
|
||||
static ep_console_desc_t *console_descs[CSI_COPROC_MAX] = {NULL};
|
||||
|
||||
//
|
||||
// Private functions
|
||||
//
|
||||
|
||||
int ep_console_task (void *arg);
|
||||
void console_msg_handler (ep_console_desc_t *console, msg_payload_t msg);
|
||||
|
||||
|
||||
//
|
||||
// bi_ep_console_create
|
||||
//
|
||||
void
|
||||
bi_ep_console_create (csi_coproc_t which_coproc)
|
||||
{
|
||||
// enforce single instance
|
||||
REQUIRE(console_descs[which_coproc]==NULL);
|
||||
console_descs[which_coproc] = malloc(sizeof(ep_console_desc_t));
|
||||
REQUIRE(console_descs[which_coproc]!=NULL);
|
||||
|
||||
console_descs[which_coproc]->coproc = which_coproc;
|
||||
|
||||
task_start(task_create("csi ep console", ep_console_task, console_descs[which_coproc], kTaskStackSize_endpoints));
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
console_quiesce (csi_coproc_t which_coproc, csi_power_state_t ps)
|
||||
{
|
||||
ep_console_desc_t *console = console_descs[which_coproc];
|
||||
|
||||
// free the sahred memory
|
||||
if (!(ps & CSI_PS_SHARED_PRESERVED) && (NULL != console->buffers)) {
|
||||
csi_free_shared_memory(console->buffers);
|
||||
console->buffers = NULL;
|
||||
console->allocated = 0;
|
||||
}
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// bi_ep_console_send_custom_cmd
|
||||
//
|
||||
csi_status_t
|
||||
bi_ep_console_send_custom_cmd (csi_coproc_t which_coproc, uint32_t payload)
|
||||
{
|
||||
ep_console_desc_t *console;
|
||||
ipc_msg_t resp;
|
||||
|
||||
console = console_descs[which_coproc];
|
||||
|
||||
if (console == NULL) {
|
||||
return CSI_STATUS_UNAVAILABLE;
|
||||
}
|
||||
|
||||
ipc_console_create_custom_cmd(&resp, payload);
|
||||
|
||||
return bi_ep_send_message (console->csi_token, resp);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// bi_ep_console_set_verbosity
|
||||
//
|
||||
csi_status_t
|
||||
bi_ep_console_set_verbosity (csi_coproc_t which_coproc, uint32_t verbosity)
|
||||
{
|
||||
ep_console_desc_t *console;
|
||||
ipc_msg_t resp;
|
||||
|
||||
console = console_descs[which_coproc];
|
||||
|
||||
if (console == NULL) {
|
||||
return CSI_STATUS_UNAVAILABLE;
|
||||
}
|
||||
|
||||
console->verbosity = verbosity;
|
||||
ipc_console_create_flow_enable_cmd(&resp, console->verbosity);
|
||||
|
||||
return bi_ep_send_message (console->csi_token, resp);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Console Message handler
|
||||
//
|
||||
|
||||
int
|
||||
ep_console_task (void *arg)
|
||||
{
|
||||
csi_status_t result;
|
||||
msg_payload_t rcv_msg;
|
||||
|
||||
ep_console_desc_t *console = (ep_console_desc_t*)arg;
|
||||
|
||||
event_init(&console->msg_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
result = csi_register_endpoint(console->coproc, IPC_ENDPOINT_BUILT_IN_CONSOLE, &console->msg_event, &console->csi_token, &console->name);
|
||||
REQUIRE(result==CSI_STATUS_OK);
|
||||
|
||||
console->verbosity = 1;
|
||||
console->buffers = NULL;
|
||||
console->allocated = 0;
|
||||
console->scratch = malloc(CONSOLE_BUFFER_SIZE+1);
|
||||
REQUIRE(console->scratch!=NULL);
|
||||
|
||||
for (;;) {
|
||||
event_wait(&console->msg_event);
|
||||
|
||||
// empty the rcv queue
|
||||
while (csi_receive_message(console->csi_token, &rcv_msg) == CSI_STATUS_OK) {
|
||||
console_msg_handler(console, rcv_msg);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
console_msg_handler (ep_console_desc_t *console, msg_payload_t msg)
|
||||
{
|
||||
uint64_t paddr;
|
||||
ipc_msg_t resp;
|
||||
uint32_t cmd;
|
||||
uint32_t rd_off;
|
||||
uint32_t wr_off;
|
||||
uint32_t size;
|
||||
console_buffer_t *buffer_desc;
|
||||
csi_status_t status;
|
||||
|
||||
// the console endpoint use the reserved 2 bits of the buffer message to encode commands
|
||||
// decode the command and respond to it
|
||||
cmd = ipc_msg_extract(msg, IPC_MSG_BUFF_MSG_FLAG_RV_LEN, IPC_MSG_BUFF_MSG_FLAG_RV_POS);
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case REQUEST_NEW_BUFFER:
|
||||
// allocate a console buffer of the requested size.
|
||||
console->allocated = ipc_buff_msg_get_size(msg);
|
||||
console->buffers = csi_allocate_shared_memory(console->csi_token, console->allocated);
|
||||
REQUIRE(console->buffers!=NULL);
|
||||
paddr = mem_static_map_physical((uintptr_t)console->buffers);
|
||||
|
||||
// send the new buffer response back to the IOP
|
||||
ipc_buff_create_msg(&resp, paddr, console->allocated);
|
||||
resp |= ipc_msg_set(HERE_IS_A_NEW_BUFFER, IPC_MSG_BUFF_MSG_FLAG_RV_LEN, IPC_MSG_BUFF_MSG_FLAG_RV_POS);
|
||||
status = bi_ep_send_message (console->csi_token, resp);
|
||||
if (CSI_STATUS_OK != status) {
|
||||
CSI_EP_LOG(CRITICAL, console->coproc, console->name, "unable to reply to REQUEST_NEW_BUFFER: %x\n", status);
|
||||
break;
|
||||
}
|
||||
|
||||
// this transaction occurs when the console endpoint is initially up and ready
|
||||
// and so is an appropriate time to communicate console verbosity to the IOP
|
||||
ipc_console_create_flow_enable_cmd(&resp, console->verbosity);
|
||||
status = bi_ep_send_message (console->csi_token, resp);
|
||||
if (CSI_STATUS_OK != status) {
|
||||
CSI_EP_LOG(CRITICAL, console->coproc, console->name, "unable to send console verbosity: %x\n", status);
|
||||
}
|
||||
break;
|
||||
|
||||
case OUTPUT_BUFFER:
|
||||
buffer_desc = &((console_buffer_t*)(console->buffers))[ipc_console_iocmd_get_channel(&msg)];
|
||||
|
||||
rd_off = ipc_console_iocmd_get_rdptr(&msg);
|
||||
wr_off = ipc_console_iocmd_get_wrptr(&msg);
|
||||
|
||||
if (rd_off<=wr_off) {
|
||||
size = wr_off - rd_off;
|
||||
REQUIRE(size<=CONSOLE_BUFFER_SIZE);
|
||||
maybe_do_cache_operation(CACHE_INVALIDATE, &buffer_desc->buffer[rd_off], size);
|
||||
memcpy(console->scratch, &buffer_desc->buffer[rd_off], size);
|
||||
} else {
|
||||
size = CONSOLE_BUFFER_SIZE - rd_off;
|
||||
REQUIRE((size+wr_off)<=CONSOLE_BUFFER_SIZE);
|
||||
maybe_do_cache_operation(CACHE_INVALIDATE, &buffer_desc->buffer[rd_off], size);
|
||||
memcpy(console->scratch, &buffer_desc->buffer[rd_off], size);
|
||||
maybe_do_cache_operation(CACHE_INVALIDATE, buffer_desc->buffer, wr_off);
|
||||
memcpy((console->scratch+size), buffer_desc->buffer, wr_off);
|
||||
size += wr_off;
|
||||
}
|
||||
console->scratch[size] = '\0';
|
||||
|
||||
CSI_EP_LOG(CONSOLE, console->coproc, console->name, "%s\n", console->scratch);
|
||||
|
||||
// only send message if the other side is not shutting down. Otherwise this
|
||||
// might create a lock up in suspend to RAM situation.
|
||||
if (!csi_is_shutdown_in_progress(console->coproc)) {
|
||||
ipc_console_create_iocmd(&resp, DONE_WITH_BUFFER, ipc_console_iocmd_get_channel(&msg), wr_off, wr_off);
|
||||
status = bi_ep_send_message (console->csi_token, resp);
|
||||
}
|
||||
break;
|
||||
|
||||
case BUFFER_OVERFLOWED:
|
||||
CSI_EP_LOG(CONSOLE, console->coproc, console->name, "Console buffer overflow - message truncated\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
CSI_EP_LOG(CRITICAL, console->coproc, console->name, "unimplemented console command: %x\n", cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* 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/crashlog_ep.h"
|
||||
|
||||
#include <csi_ipc_protocol.h>
|
||||
#include <csi_frame.h>
|
||||
#include <csi_crashlog_defines.h>
|
||||
|
||||
|
||||
//
|
||||
// Global Data
|
||||
//
|
||||
|
||||
static ep_crashlog_desc_t *crashlog_descs[CSI_COPROC_MAX] = {NULL};
|
||||
|
||||
//
|
||||
// Private functions
|
||||
//
|
||||
|
||||
int ep_crashlog_task (void *arg);
|
||||
void crashlog_msg_handler (ep_crashlog_desc_t *clog, msg_payload_t msg);
|
||||
|
||||
csi_status_t cl_print (ep_crashlog_desc_t *clog);
|
||||
void cl_print_register_frame (CrashLogDataHeader_t *section);
|
||||
void cl_print_os_info (CrashLogDataHeader_t *section);
|
||||
void cl_print_string (CrashLogDataHeader_t *section);
|
||||
void cl_print_mailbox (CrashLogDataHeader_t *section);
|
||||
void cl_print_call_stack (CrashLogDataHeader_t *section);
|
||||
|
||||
static const char* get_signature_string (CrashLogSignature_t sig);
|
||||
|
||||
|
||||
//
|
||||
// bi_ep_crashlog_create
|
||||
//
|
||||
void bi_ep_crashlog_create (csi_coproc_t which_coproc)
|
||||
{
|
||||
// enforce single instance
|
||||
REQUIRE(crashlog_descs[which_coproc]==NULL);
|
||||
crashlog_descs[which_coproc] = malloc(sizeof(ep_crashlog_desc_t));
|
||||
REQUIRE(crashlog_descs[which_coproc]!=NULL);
|
||||
|
||||
crashlog_descs[which_coproc]->coproc = which_coproc;
|
||||
|
||||
task_start(task_create("csi ep crashlog", ep_crashlog_task, crashlog_descs[which_coproc], kTaskStackSize_endpoints));
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
crashlog_quiesce (csi_coproc_t which_coproc, csi_power_state_t ps)
|
||||
{
|
||||
ep_crashlog_desc_t *clog = crashlog_descs[which_coproc];
|
||||
|
||||
// free all shared memory if we are not preserving it
|
||||
if (!(ps & CSI_PS_SHARED_PRESERVED) && (NULL != clog->buffer)) {
|
||||
csi_free_shared_memory(clog->buffer);
|
||||
clog->buffer = NULL;
|
||||
clog->allocated = 0;
|
||||
}
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// bi_ep_crashlog_force_event
|
||||
//
|
||||
// force a crashlog generation through various means
|
||||
//
|
||||
csi_status_t
|
||||
bi_ep_crashlog_force_event (csi_coproc_t which_coproc, csi_crashlog_t crashlog_type)
|
||||
{
|
||||
if (crashlog_descs[which_coproc]==NULL) {
|
||||
return CSI_STATUS_UNAVAILABLE;
|
||||
}
|
||||
|
||||
switch (crashlog_type) {
|
||||
default:
|
||||
return CSI_STATUS_ERROR;
|
||||
|
||||
case CSI_CRASHLOG_TYPE_NMI:
|
||||
akf_send_nmi(KFW(which_coproc));
|
||||
break;
|
||||
|
||||
case CSI_CRASHLOG_TYPE_MSG_CRASH:
|
||||
case CSI_CRASHLOG_TYPE_MSG_LOG:
|
||||
return bi_ep_send_message (crashlog_descs[which_coproc]->csi_token, (msg_payload_t)((crashlog_type==CSI_CRASHLOG_TYPE_MSG_CRASH) ? CL_NMI_MSG_CRASH : CL_NMI_MSG_LOG));
|
||||
break;
|
||||
}
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Crashlog Message Handler
|
||||
//
|
||||
|
||||
int
|
||||
ep_crashlog_task(void *arg)
|
||||
{
|
||||
csi_status_t result;
|
||||
msg_payload_t rcv_msg;
|
||||
|
||||
ep_crashlog_desc_t *clog = (ep_crashlog_desc_t*)arg;
|
||||
|
||||
event_init(&clog->msg_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
result = csi_register_endpoint(clog->coproc, IPC_ENDPOINT_BUILT_IN_CRASHLOG, &clog->msg_event, &clog->csi_token, &clog->name);
|
||||
REQUIRE(result==CSI_STATUS_OK);
|
||||
|
||||
clog->buffer = NULL;
|
||||
clog->allocated = 0;
|
||||
|
||||
for (;;) {
|
||||
event_wait(&clog->msg_event);
|
||||
|
||||
// empty the rcv queue
|
||||
while(csi_receive_message(clog->csi_token, &rcv_msg) == CSI_STATUS_OK) {
|
||||
crashlog_msg_handler(clog, rcv_msg);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// crashlog_msg_handler
|
||||
//
|
||||
// handle an asynchronous message from the coproc
|
||||
//
|
||||
// All message are using the buffer protocol.
|
||||
// If a zero address is received this is a shared buffer allocation request and a new buffer
|
||||
// physical address is returned.
|
||||
// Otherwise this is interpreted as a crash log and it is displayed on the screen/saved for
|
||||
// future use as appropriate
|
||||
//
|
||||
void
|
||||
crashlog_msg_handler (ep_crashlog_desc_t *clog, msg_payload_t msg)
|
||||
{
|
||||
uint64_t paddr;
|
||||
ipc_msg_t resp;
|
||||
csi_status_t status;
|
||||
|
||||
// the first message is a buffer request, identified by a NULL address in the upper portion
|
||||
// of the payload
|
||||
if (ipc_buff_msg_get_address(msg) == 0x00) {
|
||||
|
||||
// allocate a console buffer of the requested size.
|
||||
clog->allocated = ipc_buff_msg_get_size(msg);
|
||||
clog->buffer = csi_allocate_shared_memory(clog->csi_token, clog->allocated);
|
||||
REQUIRE(clog->buffer!=NULL);
|
||||
paddr = mem_static_map_physical((uintptr_t)clog->buffer);
|
||||
CSI_EP_LOG(DEBUG_SPEW, clog->coproc, clog->name, "buffer request of size %u bytes, allocated @ physical 0x%llx (AP %p)\n",
|
||||
clog->allocated, paddr, clog->buffer);
|
||||
|
||||
// send the response back to the IOP
|
||||
ipc_buff_create_msg(&resp, paddr, clog->allocated);
|
||||
status = bi_ep_send_message (clog->csi_token, resp);
|
||||
if (CSI_STATUS_OK != status) {
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
// A crash log has been received, decode it
|
||||
maybe_do_cache_operation(CACHE_INVALIDATE, clog->buffer, clog->allocated);
|
||||
status = cl_print(clog);
|
||||
if (CSI_STATUS_PANIC_RECOVER == status) {
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "Recoverable panic, reseting\n");
|
||||
status = csi_panic_recover (clog->coproc);
|
||||
}
|
||||
else if (status != CSI_STATUS_OK) {
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "data is corrupt or unrecognizable (%x)\n", status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// *** Crashlog Decoder ***
|
||||
//
|
||||
|
||||
#define RUN_IF_COMPATIBLE(version, version_expected, expr) \
|
||||
do { \
|
||||
if(LOG_MAJOR_VERSION(version) == LOG_MAJOR_VERSION(version_expected)) \
|
||||
expr; \
|
||||
else \
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "Not executing " #expr " because version %u.%u is not compatible with expected version %u.%u\n", \
|
||||
LOG_PRINT_VERSION(version), LOG_PRINT_VERSION(version_expected)); \
|
||||
} while(0)
|
||||
|
||||
static const char*
|
||||
get_exc_type_str(uint16_t exc_type)
|
||||
{
|
||||
switch (exc_type & CL_TYPE_MASK) {
|
||||
case CL_ARM_EXCEPTION_UNDEF: return "UNDEF exception";
|
||||
case CL_ARM_EXCEPTION_PREFETCH_ABORT: return "PREFETCH ABORT";
|
||||
case CL_ARM_EXCEPTION_DATA_ABORT: return "DATA ABORT";
|
||||
case CL_KERNEL_PANIC: return "PANIC";
|
||||
case CL_NMI: return "NMI FIQ";
|
||||
case CL_NMI_MSG_LOG: return "forced log msg";
|
||||
case CL_NMI_MSG_CRASH: return "forced crash msg";
|
||||
case CL_STACK_GUARD_FAULT: return "Stack guard fault";
|
||||
case CL_FLOW_ERROR: return "Mailbox error";
|
||||
case CL_PANIC_RECOVERY: return "Recoverable PANIC";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// cl_print
|
||||
//
|
||||
// Decode and print the crash log using the registered section decoders.
|
||||
// Make sure the section version match the supported version before
|
||||
// decoding them.
|
||||
//
|
||||
csi_status_t
|
||||
cl_print (ep_crashlog_desc_t *clog)
|
||||
{
|
||||
uint8_t *cl_buffer;
|
||||
CrashLogHeader_t *cl_header;
|
||||
uint32_t cl_size;
|
||||
CrashLogHeader_t *end_header;
|
||||
const char *cl_type_str;
|
||||
uint8_t *log;
|
||||
|
||||
cl_buffer = clog->buffer;
|
||||
cl_header = (CrashLogHeader_t*)cl_buffer;
|
||||
cl_size = cl_header->size - sizeof (CrashLogHeader_t);
|
||||
|
||||
// validate the signature
|
||||
if (CRASH_LOG_HEADER_SIGNATURE != cl_header->signature) {
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "invalid log signature: 0x%x, should be 0x%x.\n", cl_header->signature, CRASH_LOG_HEADER_SIGNATURE);
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
// ensure the version is compatible
|
||||
if (LOG_MAJOR_VERSION(CRASH_LOG_VERSION) != LOG_MAJOR_VERSION(cl_header->version)) {
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "invalid log major version: %u, should be %u.\n",
|
||||
LOG_MAJOR_VERSION(cl_header->version), LOG_MAJOR_VERSION(CRASH_LOG_VERSION));
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
// If this is neither a partial nor nested log make sure the header at
|
||||
// the beginning of the log match the one at the end.
|
||||
// Issue a warning if they don't and still try to decode the log.
|
||||
end_header = (CrashLogHeader_t*)(cl_buffer + cl_size);
|
||||
|
||||
if (cl_header->exception & CL_LOG_INITIAL_FLAG) {
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "partial log, log might be incomplete\n");
|
||||
// revert size to the one in the header, as we don't know what is at the end.
|
||||
cl_size = cl_header->size;
|
||||
}
|
||||
else if (memcmp (cl_header, end_header, sizeof (CrashLogHeader_t))) {
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "end header does not match log header, log might be corrupted (cl_size=%d %p %p)\n", cl_size, cl_header, end_header);
|
||||
// revert size to the one in the header, as we don't know what is at the end.
|
||||
cl_size = cl_header->size;
|
||||
}
|
||||
|
||||
// validate the checksum
|
||||
// TBD
|
||||
|
||||
#if !RELEASE_BUILD
|
||||
// Print the log
|
||||
cl_type_str = get_exc_type_str(cl_header->exception);
|
||||
if (cl_type_str != NULL) {
|
||||
CSI_EP_LOG(CRASHLOG, clog->coproc, clog->name, "%s\n", cl_type_str);
|
||||
} else {
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "crash log of unexpected type 0x%0x\n", cl_header->exception);
|
||||
}
|
||||
if (cl_header->exception & CL_LOG_NESTED_FLAG) {
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "a nested %s occured @ 0x%08x\n",
|
||||
get_exc_type_str(cl_header->nestedException), cl_header->nestedLr);
|
||||
}
|
||||
|
||||
// Decode the data section
|
||||
// A duplicate log header is placed at the end of the data section, ignore it.
|
||||
log = (cl_buffer + sizeof (CrashLogHeader_t));
|
||||
|
||||
while (log < (uint8_t*)end_header) {
|
||||
CrashLogDataHeader_t *header = (CrashLogDataHeader_t*)log;
|
||||
switch (header->signature) {
|
||||
case CRASH_LOG_DATA_REGISTER_SIGNATURE:
|
||||
RUN_IF_COMPATIBLE(header->version, CRASH_LOG_DATA_REGISTER_VERSION, cl_print_register_frame(header));
|
||||
break;
|
||||
case CRASH_LOG_DATA_OS_INFO_SIGNATURE:
|
||||
RUN_IF_COMPATIBLE(header->version, CRASH_LOG_DATA_OS_INFO_VERSION, cl_print_os_info(header));
|
||||
break;
|
||||
case CRASH_LOG_DATA_STRING_SIGNATURE:
|
||||
RUN_IF_COMPATIBLE(header->version, CRASH_LOG_DATA_STRING_VERSION, cl_print_string(header));
|
||||
break;
|
||||
case CRASH_LOG_DATA_MAILBOX_SIGNATURE:
|
||||
RUN_IF_COMPATIBLE(header->version, CRASH_LOG_DATA_MAILBOX_VERSION, cl_print_mailbox(header));
|
||||
break;
|
||||
case CRASH_LOG_DATA_CALL_STACK_SIGNATURE:
|
||||
RUN_IF_COMPATIBLE(header->version, CRASH_LOG_DATA_CALL_STACK_VERSION, cl_print_call_stack(header));
|
||||
break;
|
||||
default:
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "found unknown %s section of size %d, version %x.%x\n",
|
||||
get_signature_string(header->signature), header->size, LOG_PRINT_VERSION(header->version));
|
||||
break;
|
||||
}
|
||||
log += header->size;
|
||||
}
|
||||
#else
|
||||
// silence all warnings
|
||||
(void)cl_type_str;
|
||||
(void)log;
|
||||
#endif
|
||||
|
||||
// if we received a recoverable panic reset the IOP
|
||||
if (CL_PANIC_RECOVERY == cl_header->exception) {
|
||||
// if we are not already in panic recovery and one was requested try it
|
||||
if (!csi_is_panic_recovery(clog->coproc)) {
|
||||
csi_set_feature (clog->coproc, COPROC_FEATURE_PANIC_RECOVERY);
|
||||
return CSI_STATUS_PANIC_RECOVER;
|
||||
} else {
|
||||
CSI_EP_LOG(CRITICAL, clog->coproc, clog->name, "failed panic recovery, hanging...\n");
|
||||
}
|
||||
}
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// *** Register section ***
|
||||
//
|
||||
|
||||
typedef struct _fsr_entry_t {
|
||||
const uint32_t value;
|
||||
const char* const reason;
|
||||
} fsr_entry_t;
|
||||
|
||||
static const fsr_entry_t fsr_short_descriptor_table[] = { // From DDI0406C Table B3-23: VMSAv7 Short-descriptor format FSR encodings
|
||||
{1, "Alignment fault"},
|
||||
{4, "Instruction cache maintenance fault"},
|
||||
{12, "Translation table walk synchronous external abort (1st level)"},
|
||||
{14, "Translation table walk synchronous external abort (2nd level)"},
|
||||
{28, "Translation table walk synchronous parity error (1st level)"},
|
||||
{30, "Translation table walk synchronous parity error (2nd level)"},
|
||||
{5, "Translation fault (section)"},
|
||||
{7, "Translation fault (page)"},
|
||||
{3, "Access Flag fault (section)"},
|
||||
{6, "Access Flag fault (page)"},
|
||||
{9, "Domain fault (section)"},
|
||||
{11, "Domain fault (page)"},
|
||||
{13, "Permission fault (section)"},
|
||||
{15, "Permission fault (page)"},
|
||||
{2, "Debug event"},
|
||||
{8, "Synchronous external abort"},
|
||||
{16, "TLB conflict abort"},
|
||||
{20, "Implementation defined (Lockdown)"},
|
||||
{26, "Implementation defined (Coprocessor abort)"},
|
||||
{25, "Memory access synchronous parity error"},
|
||||
{22, "Asynchronous external abort"},
|
||||
{24, "Memory access asynchronous parity error"},
|
||||
{(uint32_t)-1, "Unknown"} // Terminated by value = (unsigned int)-1, which is never a valid encoding
|
||||
};
|
||||
|
||||
static const fsr_entry_t fsr_long_descriptor_table[] = { // From DDI0406C Table B3-24: VMSAv7 Long-descriptor format FSR encodings
|
||||
{4, "Translation fault level %u"},
|
||||
{8, "Access flag fault level %u"},
|
||||
{12, "Permission fault level %u"},
|
||||
{20, "Synchronous external abort on translation table walk level %u"},
|
||||
{28, "Synchronous parity error on memory access on translation table walk level %u"},
|
||||
{60, "Domain fault level %u"},
|
||||
{16, "Synchronous external abort"},
|
||||
{24, "Synchronous parity error on memory access"},
|
||||
{17, "Asynchronous external abort"},
|
||||
{25, "Asynchronous parity error on memory access"},
|
||||
{33, "Alignment fault"},
|
||||
{34, "Debug event"},
|
||||
{48, "TLB conflict abort"},
|
||||
{52, "Implementation defined (Lockdown)"},
|
||||
{58, "Implementation defined (Coprocessor abort)"},
|
||||
{(uint32_t)-1, "Unknown"} // Terminated by value = (unsigned int)-1, which is never a valid encoding
|
||||
};
|
||||
|
||||
|
||||
static const char*
|
||||
cl_decode_fsr(uint32_t fsr_val)
|
||||
{
|
||||
// holder for the processed lookup level FSR entries
|
||||
static char fsr_status_string[80];
|
||||
|
||||
uint32_t fault_status;
|
||||
uint32_t fault_lookup_level = 0;
|
||||
uint32_t fault_status_ll = 0;
|
||||
|
||||
const fsr_entry_t *fsr_table;
|
||||
|
||||
// Check the LPAE bit to see if this is short or long descriptor
|
||||
if ((fsr_val & (1u<<9)) == 0 )
|
||||
{
|
||||
// Non-LPAE, using short-descriptor format
|
||||
fault_status = ((fsr_val & (1u<<10)) >> 6 ) | (fsr_val & 0xf); // [10, 3:0]
|
||||
fsr_table = fsr_short_descriptor_table;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LPAE, using long-descriptor format
|
||||
fault_status = (fsr_val & 0x3f); // [5:0]
|
||||
fault_lookup_level = (fsr_val & 0x03); // [1:0]
|
||||
fault_status_ll = (fsr_val & 0x3c); // [5:2]
|
||||
fsr_table = fsr_long_descriptor_table;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
// if a match for non-lookup level entry or the terminator
|
||||
if( (fault_status == fsr_table->value)
|
||||
|| (fault_status_ll == fsr_table->value)
|
||||
|| (fsr_table->value == (uint32_t)-1) )
|
||||
{
|
||||
snprintf(fsr_status_string, sizeof(fsr_status_string), fsr_table->reason, fault_lookup_level);
|
||||
return fsr_status_string;
|
||||
}
|
||||
|
||||
fsr_table++;
|
||||
}
|
||||
|
||||
// Control should never reach here since fsr tables are terminated by value = -1
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// cl_print_register_frame
|
||||
//
|
||||
// Decode and print the register frame section
|
||||
//
|
||||
void
|
||||
cl_print_register_frame (CrashLogDataHeader_t *section)
|
||||
{
|
||||
CrashLogRegisterSection_t *regSection = (CrashLogRegisterSection_t*) section;
|
||||
|
||||
CSI_LOG_RAW(CRASHLOG, "Failing task register frame:\n");
|
||||
CSI_LOG_RAW(CRASHLOG, " Faulting PC=0x%08x\n", regSection->registerFrame.pc);
|
||||
|
||||
// print additional information register data
|
||||
switch (regSection->exception) {
|
||||
case CL_ARM_EXCEPTION_PREFETCH_ABORT:
|
||||
CSI_LOG_RAW(CRASHLOG, " ifsr=0x%08x (%s) ifar=0x%08x\n",
|
||||
regSection->registerFrame.extra_info1, cl_decode_fsr(regSection->registerFrame.extra_info1),
|
||||
regSection->registerFrame.extra_info2);
|
||||
break;
|
||||
|
||||
case CL_STACK_GUARD_FAULT:
|
||||
case CL_ARM_EXCEPTION_DATA_ABORT:
|
||||
CSI_LOG_RAW(CRASHLOG, " dfsr=0x%08x (%s) dfar=0x%08x\n",
|
||||
regSection->registerFrame.extra_info1, cl_decode_fsr(regSection->registerFrame.extra_info1),
|
||||
regSection->registerFrame.extra_info2);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// print faulting task registers
|
||||
CSI_LOG_RAW(CRASHLOG, " r00=0x%08x r01=0x%08x r02=0x%08x r03=0x%08x\n"
|
||||
" r04=0x%08x r05=0x%08x r06=0x%08x r07=0x%08x\n"
|
||||
" r08=0x%08x r09=0x%08x r10=0x%08x r11=0x%08x\n"
|
||||
" r12=0x%08x sp=0x%08x lr=0x%08x pc=0x%08x\n"
|
||||
" spsr=0x%08x\n",
|
||||
regSection->registerFrame.r0, regSection->registerFrame.r1, regSection->registerFrame.r2, regSection->registerFrame.r3,
|
||||
regSection->registerFrame.r4, regSection->registerFrame.r5, regSection->registerFrame.r6, regSection->registerFrame.r7,
|
||||
regSection->registerFrame.r8, regSection->registerFrame.r9, regSection->registerFrame.r10, regSection->registerFrame.r11,
|
||||
regSection->registerFrame.r12, regSection->registerFrame.sp, regSection->registerFrame.lr, regSection->registerFrame.pc,
|
||||
regSection->registerFrame.spsr);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// *** OS Section ***
|
||||
//
|
||||
|
||||
const char*
|
||||
cl_decode_task_status(uint32_t status)
|
||||
{
|
||||
if (status & 0x100) return "INACTIVE";
|
||||
if (status & 0x080) return "QUEUE_WAIT";
|
||||
if (status & 0x040) return "SEMAPHORE_WAIT";
|
||||
if (status & 0x020) return "MSG_WAIT";
|
||||
if (status & 0x010) return "BLOCK_WAIT";
|
||||
if (status & 0x008) return "RESOURCE_WAIT";
|
||||
if (status & 0x004) return "DELAY_WAIT";
|
||||
if (status & 0x002) return "PARTITION_WAIT";
|
||||
if ((status & 0xffe) == 0x000) return "READY";
|
||||
if ((status & 0x001) == 0x001) return "SUSPENDED";
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
//
|
||||
// cl_print_os_info
|
||||
//
|
||||
// Decode the OS section
|
||||
//
|
||||
void
|
||||
cl_print_os_info (CrashLogDataHeader_t *section)
|
||||
{
|
||||
CrashLogRtxcSection_t *rtxcSection = (CrashLogRtxcSection_t*) section;
|
||||
|
||||
// display the current task list
|
||||
CSI_LOG_RAW(CRASHLOG, "RTXC Task List:\n"
|
||||
" name | pri | lr | stack use | status\n");
|
||||
|
||||
CrashLogRtxcTask_t *taskLog = (CrashLogRtxcTask_t*)((uint8_t*)(section) + sizeof (CrashLogRtxcSection_t));
|
||||
|
||||
for (int i = 0; i < (int)rtxcSection->numTask; i++) {
|
||||
// add a '*' in front of the hipri task
|
||||
if (taskLog->id == rtxcSection->taskId) {
|
||||
CSI_LOG_RAW(CRASHLOG, "*");
|
||||
} else {
|
||||
CSI_LOG_RAW(CRASHLOG, " ");
|
||||
}
|
||||
|
||||
CSI_LOG_RAW(CRASHLOG, "%2d %-15s | %03d | 0x%08x | %04u/%04u | %s\n",
|
||||
taskLog->id, taskLog->name, taskLog->priority,
|
||||
taskLog->lr, taskLog->stackUsed, taskLog->stackSize,
|
||||
cl_decode_task_status(taskLog->status));
|
||||
taskLog++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// *** string section ***
|
||||
//
|
||||
|
||||
void
|
||||
cl_print_string (CrashLogDataHeader_t *section)
|
||||
{
|
||||
// this section is a header with a NULL terminated string
|
||||
CrashLogStringSection_t *stringSection = (CrashLogStringSection_t*) section;
|
||||
|
||||
// make sure the string is NULL terminated, force it if not
|
||||
char *str = &(stringSection->data.string);
|
||||
size_t len = section->size - sizeof (CrashLogStringSection_t) - 1; // length of the string without the null termination
|
||||
|
||||
if ('\0' != str[len]) {
|
||||
str[len] = '\0';
|
||||
}
|
||||
|
||||
// print the section
|
||||
CSI_LOG_RAW(CRASHLOG, "%s\n", str);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// *** Mailbox Section ***
|
||||
//
|
||||
|
||||
void
|
||||
cl_print_mailbox (CrashLogDataHeader_t *section)
|
||||
{
|
||||
CrashLogMailboxData_t *mailSection = &(((CrashLogMailboxSection_t*)section)->data);
|
||||
|
||||
if (CL_MAILBOX_NO_ERROR == mailSection->error) {
|
||||
CSI_LOG_RAW(CRASHLOG, "Mailbox status:\n");
|
||||
} else {
|
||||
CSI_LOG_RAW(CRASHLOG, "Mailbox status: (error = 0x%x\n", mailSection->error);
|
||||
}
|
||||
CSI_LOG_RAW(CRASHLOG, " Inbox AKF_KIC_INBOX_CTRL = 0x%08x, AKF_KIC_MAILBOX_SET = 0x%08x\n"
|
||||
" Outbox AKF_AP_OUTBOX_CTRL = 0x%08x, AKF_AP_MAILBOX_SET = 0x%08x\n",
|
||||
mailSection->inboxCtrl, mailSection->inboxInt,
|
||||
mailSection->outboxCtrl, mailSection->outboxInt);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// *** Call Stack section ***
|
||||
//
|
||||
|
||||
static const char *
|
||||
get_call_stack_error_string(uint32_t error_code)
|
||||
{
|
||||
const char *error_string;
|
||||
|
||||
switch (error_code) {
|
||||
default:
|
||||
case CL_CALL_STACK_NO_ERROR:
|
||||
error_string = "";
|
||||
break;
|
||||
case CL_CALL_STACK_OVERFLOW:
|
||||
error_string = "error: stack too big for crash log";
|
||||
break;
|
||||
case CL_CALL_STACK_FP_NULL:
|
||||
error_string = "error: fp is null => possible stack corruption";
|
||||
break;
|
||||
case CL_CALL_STACK_FP_INVALID:
|
||||
error_string = "error: fp is invalid => possible stack corruption";
|
||||
break;
|
||||
}
|
||||
|
||||
return error_string;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// cl_print_call_stack
|
||||
//
|
||||
// Print the call stack of a task.
|
||||
// There is one of these section per task.
|
||||
//
|
||||
void
|
||||
cl_print_call_stack (CrashLogDataHeader_t *section)
|
||||
{
|
||||
bool isAPanic;
|
||||
CrashLogCallStackSection_t *callStackSection;
|
||||
uint32_t *callStack;
|
||||
|
||||
callStackSection = (CrashLogCallStackSection_t*) section;
|
||||
isAPanic = ((callStackSection->panickingFuncIndex>0) && (callStackSection->panickingFuncIndex<callStackSection->stackDepth));
|
||||
callStack = (uint32_t*)(callStackSection+1);
|
||||
|
||||
CSI_LOG_RAW(CRASHLOG, "Task %2u Call Stack:%s%s - ",
|
||||
callStackSection->taskId,
|
||||
(isAPanic?" (@=>panicking function)":""),
|
||||
get_call_stack_error_string(callStackSection->error));
|
||||
|
||||
for ( uint32_t j=0; j<callStackSection->stackDepth; j++ ) {
|
||||
CSI_LOG_RAW(CRASHLOG, " %s0x%08x", ((isAPanic&&(j==callStackSection->panickingFuncIndex))?"@":" "), *callStack++);
|
||||
}
|
||||
|
||||
CSI_LOG_RAW(CRASHLOG, "\n");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Private utility function
|
||||
//
|
||||
|
||||
const char*
|
||||
get_signature_string (CrashLogSignature_t sig)
|
||||
{
|
||||
static char sig_str[5];
|
||||
// signature are 4 ascii bytes packed into a 32 bit word
|
||||
// extract and print
|
||||
sig_str[0] = (char)((sig >> 24) & 0x00FF);
|
||||
sig_str[1] = (char)((sig >> 16) & 0x00FF);
|
||||
sig_str[2] = (char)((sig >> 8) & 0x00FF);
|
||||
sig_str[3] = (char)(sig & 0x00FF);
|
||||
sig_str[4] = (char)('\0');
|
||||
|
||||
return sig_str;
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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/syslog_ep.h"
|
||||
|
||||
#include <csi_ipc_protocol.h>
|
||||
#include <csi_syslog_defines.h>
|
||||
#include <csi_syslog_protocol.h>
|
||||
|
||||
|
||||
//
|
||||
// Syslog Object
|
||||
//
|
||||
|
||||
typedef struct {
|
||||
csi_coproc_t coproc;
|
||||
struct task_event msg_event;
|
||||
void *csi_token;
|
||||
const char *name;
|
||||
uint32_t verbosity;
|
||||
syslog_database_t *buffer;
|
||||
uint32_t allocated;
|
||||
} ep_syslog_desc_t;
|
||||
|
||||
//
|
||||
// Global Data
|
||||
//
|
||||
|
||||
static ep_syslog_desc_t *syslog_descs[CSI_COPROC_MAX] = {NULL};
|
||||
|
||||
const char* syslog_priority_names[] = SYSLOG_PRIORITY_NAMES;
|
||||
const char* syslog_facility_names[] = SYSLOG_FACILITY_NAMES;
|
||||
|
||||
//
|
||||
// Private functions
|
||||
//
|
||||
|
||||
int ep_syslog_task (void *arg);
|
||||
void syslog_msg_handler (ep_syslog_desc_t *syslog, msg_payload_t msg);
|
||||
|
||||
|
||||
//
|
||||
// bi_ep_syslog_create
|
||||
//
|
||||
// Create a new syslog endpoint
|
||||
//
|
||||
void
|
||||
bi_ep_syslog_create (csi_coproc_t which_coproc)
|
||||
{
|
||||
// enforce single instance
|
||||
REQUIRE(syslog_descs[which_coproc]==NULL);
|
||||
syslog_descs[which_coproc] = malloc(sizeof(ep_syslog_desc_t));
|
||||
REQUIRE(syslog_descs[which_coproc]!=NULL);
|
||||
|
||||
syslog_descs[which_coproc]->coproc = which_coproc;
|
||||
|
||||
task_start(task_create("csi ep syslog", ep_syslog_task, syslog_descs[which_coproc], kTaskStackSize_endpoints));
|
||||
}
|
||||
|
||||
|
||||
csi_status_t
|
||||
syslog_quiesce (csi_coproc_t which_coproc, csi_power_state_t ps)
|
||||
{
|
||||
ep_syslog_desc_t *syslog = syslog_descs[which_coproc];
|
||||
|
||||
// free all shared memory if we are not preserving it
|
||||
if (!(ps & CSI_PS_SHARED_PRESERVED) && (NULL != syslog->buffer)) {
|
||||
csi_free_shared_memory(syslog->buffer);
|
||||
syslog->buffer = NULL;
|
||||
syslog->allocated = 0;
|
||||
}
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// bi_ep_syslog_verbosity
|
||||
//
|
||||
// Set the syslog endpoint verbosity
|
||||
//
|
||||
csi_status_t
|
||||
bi_ep_syslog_verbosity (csi_coproc_t which_coproc, uint32_t verbosity)
|
||||
{
|
||||
|
||||
ep_syslog_desc_t *syslog = syslog_descs[which_coproc];
|
||||
|
||||
if (syslog==NULL) {
|
||||
return CSI_STATUS_UNAVAILABLE;
|
||||
}
|
||||
|
||||
if (verbosity > LOG_DEBUG) {
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
syslog->verbosity = verbosity;
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// syslog endpoint: task
|
||||
//
|
||||
|
||||
int
|
||||
ep_syslog_task(void *arg)
|
||||
{
|
||||
csi_status_t result;
|
||||
msg_payload_t rcv_msg;
|
||||
|
||||
ep_syslog_desc_t *syslog = (ep_syslog_desc_t*)arg;
|
||||
|
||||
event_init(&syslog->msg_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
result = csi_register_endpoint(syslog->coproc, IPC_ENDPOINT_BUILT_IN_SYSLOG, &syslog->msg_event, &syslog->csi_token, &syslog->name);
|
||||
REQUIRE(result==CSI_STATUS_OK);
|
||||
|
||||
syslog->verbosity = LOG_DEFAULT;
|
||||
|
||||
syslog->buffer = NULL;
|
||||
syslog->allocated = 0;
|
||||
|
||||
for (;;) {
|
||||
event_wait(&syslog->msg_event);
|
||||
|
||||
// empty the rcv queue
|
||||
while (csi_receive_message(syslog->csi_token, &rcv_msg) == CSI_STATUS_OK) {
|
||||
syslog_msg_handler(syslog, rcv_msg);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
syslog_msg_handler(ep_syslog_desc_t *syslog, msg_payload_t msg)
|
||||
{
|
||||
uint32_t cmd;
|
||||
uint32_t entry;
|
||||
uint32_t type;
|
||||
uint32_t size;
|
||||
csi_status_t status;
|
||||
|
||||
cmd = syslog_get_cmd(&msg);
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case REQUEST_SYSLOGDB_BUFFER:
|
||||
|
||||
syslog->allocated = ipc_buff_msg_get_size(msg);
|
||||
REQUIRE(syslog->buffer==NULL);
|
||||
syslog->buffer = csi_allocate_shared_memory(syslog->csi_token, syslog->allocated);
|
||||
REQUIRE(syslog->buffer!=NULL);
|
||||
|
||||
// send the response back to the coproc
|
||||
uint64_t buffer_phys = mem_static_map_physical((uintptr_t)syslog->buffer);
|
||||
status = bi_ep_send_message (syslog->csi_token, syslog_construct_msg(HERE_IS_YOUR_SYSLOGDB_BUFFER, buffer_phys, ipc_buff_msg_get_size(msg)));
|
||||
if (status == CSI_STATUS_OK) {
|
||||
CSI_EP_LOG(DEBUG_SPEW, syslog->coproc, syslog->name, "buffer request of size %u bytes, allocated @ physical 0x%llx (AP %p)\n",
|
||||
syslog->allocated, buffer_phys, syslog->buffer);
|
||||
} else {
|
||||
CSI_EP_LOG(CRITICAL, syslog->coproc, syslog->name, "unable to reply to REQUEST_SYSLOGDB_BUFFER cmd.\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case OUTPUT_SYSLOG_ENTRY:
|
||||
entry = (uint32_t)syslog_get_address(&msg);
|
||||
maybe_do_cache_operation(CACHE_INVALIDATE, &syslog->buffer->entries[entry], sizeof(syslog_entry_t));
|
||||
type = syslog->buffer->entries[entry].type;
|
||||
if (LOG_PRI(type) <= syslog->verbosity) {
|
||||
if (LOG_FAC(type) != LOG_KERN) {
|
||||
CSI_EP_LOG (SYSLOG, syslog->coproc, syslog->name, "%s: %s\n", syslog_get_facility_name(type), syslog->buffer->entries[entry].message);
|
||||
} else {
|
||||
CSI_EP_LOG (SYSLOG, syslog->coproc, syslog->name, "task #%d, file: %s: %s\n",
|
||||
syslog->buffer->entries[entry].taskid, syslog->buffer->entries[entry].filename, syslog->buffer->entries[entry].message);
|
||||
}
|
||||
}
|
||||
status = bi_ep_send_message (syslog->csi_token, syslog_construct_msg(OUTPUTTED_SYSLOG_ENTRY, entry, 0));
|
||||
break;
|
||||
|
||||
case SYSLOG_ENTRY_TRUNCATED:
|
||||
size = (uint32_t)syslog_get_address(&msg); // in this message the address field is used to report the log entry length
|
||||
CSI_EP_LOG(CRITICAL, syslog->coproc, syslog->name, "coproc tried to print a 0x%x byte message, but message buffer is only %d bytes long.\n", size, SYSLOG_MESSAGE_SIZE);
|
||||
break;
|
||||
|
||||
case SYSLOGDB_OVERFLOW:
|
||||
CSI_EP_LOG(CRITICAL, syslog->coproc, syslog->name, "coproc database overflowed.\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
CSI_EP_LOG(CRITICAL, syslog->coproc, syslog->name, "unimplemented syslog command: %x\n", cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, 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 Computer, Inc.
|
||||
*/
|
||||
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <arch.h>
|
||||
#include <debug.h>
|
||||
#include <sys/task.h>
|
||||
#include <sys/boot.h>
|
||||
#include <lib/env.h>
|
||||
#include <platform/timer.h>
|
||||
#include <drivers/a7iop/a7iop.h>
|
||||
#include <platform/clocks.h>
|
||||
#include <platform/chipid.h>
|
||||
#include <platform/soc/hwclocks.h>
|
||||
#include <drivers/csi.h>
|
||||
#include <drivers/power.h>
|
||||
|
||||
#include "csi_private.h"
|
||||
#include "csi_firmware.h"
|
||||
|
||||
#include <csi_heap_map.h>
|
||||
#include <csi_platform_config.h>
|
||||
#include <csi_ipc_protocol.h>
|
||||
#include <csi_mgmt_protocol.h>
|
||||
|
||||
|
||||
extern const get_fw_info_t fw_info_ans;
|
||||
|
||||
static const get_fw_info_t *fw_info_getters[CSI_COPROC_MAX] = {
|
||||
&fw_info_ans,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
csi_status_t
|
||||
fw_fixup_and_relocate (csi_coproc_t coproc, fw_desc_t *fw_desc)
|
||||
{
|
||||
const get_fw_info_t *fw_info;
|
||||
void *heap_base;
|
||||
uint32_t heap_size_max;
|
||||
volatile CsiConfig_t *csi_config;
|
||||
|
||||
fw_info = fw_info_getters[coproc];
|
||||
|
||||
if (fw_info == NULL) {
|
||||
return CSI_STATUS_NO_FIRMWARE;
|
||||
}
|
||||
|
||||
// Get the csi configuration structure and validate it.
|
||||
// This structure contain all the relevant firmware information needed for relocation.
|
||||
// Use it as read only until the firmware has been relocated
|
||||
if (fw_info->get_csi_config_offset() == VALUE_NOT_FOUND) {
|
||||
CSI_LOG (CRITICAL, coproc, "coproc firmware does not specifiy a fix-up location for the csi configuration structure\n");
|
||||
return CSI_STATUS_BAD_FIRMWARE;
|
||||
}
|
||||
csi_config = (volatile CsiConfig_t*)(fw_info->get_fw_image() + fw_info->get_csi_config_offset());
|
||||
if (CSI_CONFIG_SIGNATURE != csi_config->signature) {
|
||||
CSI_LOG (CRITICAL, coproc, "Invalid csi config signature: 0x%x != 0x%x\n", csi_config->signature, CSI_CONFIG_SIGNATURE);
|
||||
return CSI_STATUS_BAD_FIRMWARE;
|
||||
}
|
||||
if (CSI_CONFIG_VERSION != csi_config->version) {
|
||||
CSI_LOG (CRITICAL, coproc, "Invalid csi config version: 0x%x != 0x%x\n", csi_config->version, CSI_CONFIG_VERSION);
|
||||
return CSI_STATUS_BAD_FIRMWARE;
|
||||
}
|
||||
|
||||
// Validate the firmware size
|
||||
fw_desc->fw_address = (void*)(fw_desc->region_base);
|
||||
fw_desc->fw_size = csi_config->fwRuntimeSize;
|
||||
if (fw_desc->fw_size > fw_desc->region_size) {
|
||||
CSI_LOG (CRITICAL, coproc, "firmware size (0x%x) is too large (max size=0x%x)\n", fw_desc->fw_size, fw_desc->region_size);
|
||||
return CSI_STATUS_OUT_OF_RESSOURCES;
|
||||
}
|
||||
|
||||
// Validate the alignment. We only support up to a page size, adjust if needed.
|
||||
if (!is_power_of_2(csi_config->fwAlignmentRequired)) {
|
||||
CSI_LOG (CRITICAL, coproc, "firmware alignment requirement (0x%x) is not a power of 2\n", csi_config->fwAlignmentRequired);
|
||||
return CSI_STATUS_BAD_FIRMWARE;
|
||||
}
|
||||
if (csi_config->fwAlignmentRequired > PAGE_SIZE) {
|
||||
fw_desc->alignment_req = PAGE_SIZE;
|
||||
CSI_LOG (DEBUG, coproc, "Requested alignment (0x%x) is greater than a page size (0x%x) - adjusting to 0x%x\n",
|
||||
csi_config->fwAlignmentRequired, PAGE_SIZE, fw_desc->alignment_req);
|
||||
} else {
|
||||
fw_desc->alignment_req = csi_config->fwAlignmentRequired;
|
||||
}
|
||||
|
||||
// copy the fixed up firmware, zero'ing the targeted memory first.
|
||||
memset (fw_desc->fw_address, 0, fw_desc->fw_size);
|
||||
memcpy (fw_desc->fw_address, fw_info->get_fw_image(), fw_info->get_fw_image_size());
|
||||
|
||||
CSI_LOG (CRITICAL, coproc, "firmware: %s-%s %s\n", fw_info->get_name_str(), fw_info->get_build_str(), fw_info->get_version_str());
|
||||
CSI_LOG (DEBUG_SPEW, coproc, " @phys = %p, image size = 0x%x, binary size = 0x%x\n",
|
||||
(void *)mem_static_map_physical((uintptr_t)fw_desc->fw_address), fw_info->get_fw_image_size(), fw_desc->fw_size);
|
||||
|
||||
// Get the relocated config structure and validate it.
|
||||
// It is now read write
|
||||
csi_config = (volatile CsiConfig_t*)(fw_desc->fw_address + fw_info->get_csi_config_offset());
|
||||
if (CSI_CONFIG_SIGNATURE != csi_config->signature) {
|
||||
CSI_LOG (CRITICAL, coproc, "Invalid csi config signature after relocation: %x != %x\n", csi_config->signature, CSI_CONFIG_SIGNATURE);
|
||||
return CSI_STATUS_ERROR;
|
||||
}
|
||||
|
||||
// Fix up the soc information
|
||||
csi_config->soc = chipid_get_chip_id();
|
||||
csi_config->socRevision = chipid_get_chip_revision();
|
||||
csi_config->nClock = clock_get_frequency (CLK_NCLK);
|
||||
csi_config->ancLinkClock = clock_get_frequency (CLK_ANS_LINK);
|
||||
|
||||
// determine base and max size of heap
|
||||
heap_base = (void*)roundup(((uint64_t)fw_desc->fw_address + fw_desc->fw_size), fw_desc->alignment_req);
|
||||
heap_size_max = (uint32_t)(fw_desc->region_size - (uint32_t)(heap_base - fw_desc->fw_address));
|
||||
|
||||
fw_desc->heap_address = NULL;
|
||||
fw_desc->heap_size = 0;
|
||||
|
||||
// Set up the heap
|
||||
if (csi_config->heapMap.numHeap > 0) {
|
||||
// Initialize heap map.
|
||||
for (int j = 1; j < (int)csi_config->heapMap.numHeap; j++) {
|
||||
csi_config->heapMap.heap[j].pbase = 0;
|
||||
csi_config->heapMap.heap[j].size = 0;
|
||||
}
|
||||
|
||||
if (heap_size_max < csi_config->heapRequired) {
|
||||
CSI_LOG (DEBUG_SPEW, coproc, "requested heap size of (0x%x) exceeds available size (0x%x) - proceeding with smaller heap!\n", csi_config->heapRequired, heap_size_max);
|
||||
}
|
||||
|
||||
// the IOP takes remapped address for its heap so substract the region base
|
||||
csi_config->heapMap.heap[0].pbase = (uint64_t)(heap_base - fw_desc->region_base);
|
||||
csi_config->heapMap.heap[0].size = ((heap_size_max < csi_config->heapRequired) ? heap_size_max : csi_config->heapRequired);
|
||||
|
||||
// store real address for the iBoot side
|
||||
fw_desc->heap_address = (void*)heap_base;
|
||||
fw_desc->heap_size = csi_config->heapMap.heap[0].size;
|
||||
|
||||
CSI_LOG (DEBUG_SPEW, coproc, "Heap @ physical %p (remapped %p), size= 0x%x\n", fw_desc->heap_address, (void *)csi_config->heapMap.heap[0].pbase, csi_config->heapMap.heap[0].size);
|
||||
}
|
||||
|
||||
// Fix up whether early-boot tracing is enabled
|
||||
uint8_t iboot_debug = 0;
|
||||
power_get_nvram(kPowerNVRAMiBootDebugKey, &iboot_debug);
|
||||
uint32_t early_debug_val = ((iboot_debug & kPowerNVRAMiBootDebugEarlyTracing) ? 1:0);
|
||||
|
||||
if (early_debug_val) {
|
||||
CSI_LOG (CRITICAL, coproc, "Enabling early boot tracing as requested via powernvram.\n");
|
||||
}
|
||||
csi_config->kDebugEarlyDebug = early_debug_val;
|
||||
|
||||
maybe_do_cache_operation(CACHE_CLEAN, fw_desc->fw_address, fw_desc->fw_size);
|
||||
maybe_do_cache_operation(CACHE_CLEAN, heap_base, fw_desc->heap_size);
|
||||
|
||||
return CSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
fw_print_version (csi_coproc_t coproc, fw_desc_t *fw_desc)
|
||||
{
|
||||
const get_fw_info_t *fw_info;
|
||||
const CsiConfig_t *csi_config;
|
||||
|
||||
// print version information
|
||||
fw_info = fw_info_getters[coproc];
|
||||
csi_config = (CsiConfig_t*)(fw_desc->fw_address + fw_info->get_csi_config_offset());
|
||||
|
||||
CSI_LOG (CRITICAL, coproc, "firmware: %s-%s %s @phys = %p\n",
|
||||
fw_info->get_name_str(), fw_info->get_build_str(), fw_info->get_version_str(),
|
||||
(void *)mem_static_map_physical((uintptr_t)fw_desc->fw_address));
|
||||
CSI_LOG (CRITICAL, coproc, "protocol version %x.%x on soc %x.%x\n",
|
||||
csi_mgmt_get_prot_major(csi_config->version), csi_mgmt_get_prot_minor(csi_config->version),
|
||||
csi_config->soc, csi_config->socRevision);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, 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 Computer, Inc.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <arch.h>
|
||||
#include <debug.h>
|
||||
#include <sys/task.h>
|
||||
#include <drivers/a7iop/a7iop.h>
|
||||
#include <drivers/csi.h>
|
||||
|
||||
#include "csi_private.h"
|
||||
|
||||
|
||||
#define VALUE_NOT_FOUND ((uint32_t)-1)
|
||||
|
||||
typedef const char* (*get_name_str_t)(void);
|
||||
typedef const char* (*get_build_str_t)(void);
|
||||
typedef const char* (*get_version_str_t)(void);
|
||||
typedef const char* (*get_fw_image_t)(void);
|
||||
typedef uint32_t (*get_fw_image_size_t)(void);
|
||||
typedef uint32_t (*get_csi_config_offset_t)(void);
|
||||
typedef uint32_t (*get_fw_mem_req_t)(void);
|
||||
typedef void* (*get_reloc_address_t)(void);
|
||||
typedef uint32_t (*get_reloc_size_t)(void);
|
||||
|
||||
typedef struct {
|
||||
get_name_str_t get_name_str;
|
||||
get_build_str_t get_build_str;
|
||||
get_version_str_t get_version_str;
|
||||
get_fw_image_t get_fw_image;
|
||||
get_fw_image_size_t get_fw_image_size;
|
||||
get_csi_config_offset_t get_csi_config_offset;
|
||||
get_fw_mem_req_t get_fw_memory_required;
|
||||
get_reloc_address_t get_reloc_address;
|
||||
get_reloc_size_t get_reloc_size;
|
||||
} get_fw_info_t;
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, 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 Computer, Inc.
|
||||
*/
|
||||
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <arch.h>
|
||||
#include <debug.h>
|
||||
#include <sys/task.h>
|
||||
#include <drivers/a7iop/a7iop.h>
|
||||
#include <drivers/csi.h>
|
||||
|
||||
#include "csi_firmware.h"
|
||||
|
||||
#if SUB_PLATFORM_S7002
|
||||
#include <AppleCSI.s7002ans.release.h>
|
||||
//#include <AppleCSI.s7002ans.debug.h>
|
||||
#elif SUB_PLATFORM_T8002
|
||||
// XXX
|
||||
// <rdar://problem/18613350> T8002SIM: ASP support
|
||||
#include <AppleCSI.s7002ans.release.h>
|
||||
#else
|
||||
#include <AppleCSI.s5l8960xans.release.h>
|
||||
//#include <AppleCSI.s5l8960xans.debug.h>
|
||||
#endif
|
||||
|
||||
// Validate the IOP protocol against the one required by the firmware file
|
||||
#if !defined(ANS_PROTOCOL_VER_MAJOR) || !defined(ANS_PROTOCOL_VER_MINOR)
|
||||
#error ERROR: ANS firmware does not specify protocol version
|
||||
#else
|
||||
#if (!(ANS_PROTOCOL_VER_MAJOR == APPLE_CSI_PROTOCOL_VER_MAJOR) || (!(ANS_PROTOCOL_VER_MINOR == APPLE_CSI_PROTOCOL_VER_MINOR)))
|
||||
#error ERROR: the CSI protocol version does not match the one required by the ANS firmware.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static const char* fw_ans_get_name_str() { return fwFirmwareTarget; }
|
||||
static const char* fw_ans_get_build_str() { return fwFirmwareBuild; }
|
||||
static const char* fw_ans_get_version_str() { return fwFirmwareVersion; }
|
||||
static const char* fw_ans_get_fw_image() { return (const char*)&fwFirmwareImage[0]; }
|
||||
static uint32_t fw_ans_get_fw_image_size() { return sizeof(fwFirmwareImage); }
|
||||
static uint32_t fw_ans_get_csi_config_offset() { return ((fwConfigurationOffset!=SYMBOL_NOT_FOUND) ? fwConfigurationOffset : VALUE_NOT_FOUND); }
|
||||
static uint32_t fw_ans_get_memory_required() { return fwAspRequiredMemory; }
|
||||
static void* fw_ans_get_reloc_address() { return (void*)ASP_BASE; }
|
||||
static uint32_t fw_ans_get_reloc_size() { return ASP_SIZE; }
|
||||
|
||||
const get_fw_info_t fw_info_ans = {
|
||||
fw_ans_get_name_str,
|
||||
fw_ans_get_build_str,
|
||||
fw_ans_get_version_str,
|
||||
fw_ans_get_fw_image,
|
||||
fw_ans_get_fw_image_size,
|
||||
fw_ans_get_csi_config_offset,
|
||||
fw_ans_get_memory_required,
|
||||
fw_ans_get_reloc_address,
|
||||
fw_ans_get_reloc_size,
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2013 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.
|
||||
*/
|
||||
#ifndef __CSI_PRIVATE_H
|
||||
#define __CSI_PRIVATE_H
|
||||
|
||||
#include <drivers/csi.h>
|
||||
#include <debug.h>
|
||||
|
||||
// NOTE: The following header file is expected to be found via the search paths expressed in
|
||||
// rules.mk via the GLOBAL_INCLUDES macro.
|
||||
#include <csi_prot_common.h>
|
||||
#include <csi_platform_defines.h>
|
||||
#include <csi_system.h>
|
||||
|
||||
// build-time CSI protocol version range check
|
||||
// Make sure you could this in sync with the protoc
|
||||
#define EXPECTED_RANGE_MIN_PROTOCOL_VER_MAJOR 9
|
||||
#define EXPECTED_RANGE_MAX_PROTOCOL_VER_MAJOR 9
|
||||
|
||||
#if !((EXPECTED_RANGE_MIN_PROTOCOL_VER_MAJOR <= APPLE_CSI_PROTOCOL_VER_MAJOR) && (EXPECTED_RANGE_MAX_PROTOCOL_VER_MAJOR >= APPLE_CSI_PROTOCOL_VER_MAJOR))
|
||||
#error ERROR: This iBoot is not compatible with your AppleStorageProcessor firmware
|
||||
#endif
|
||||
|
||||
|
||||
// explicitly specify task stack sizes
|
||||
// empirically derived stack high watermark measurements
|
||||
// csi inbox_not_e: 512
|
||||
// csi ep mgmt: 416
|
||||
// csi ep crashlog: 1728
|
||||
// csi ep console: 448
|
||||
// csi ep syslog: 1424
|
||||
|
||||
// request 8 kb to cover with a healthy margin
|
||||
// further tuning will be handled with <rdar://problem/15208515> Analyze stack usage and tune stack size for csi iBoot tasks
|
||||
|
||||
#define kTaskStackSize_inbox_not_empty 0x2000
|
||||
#define kTaskStackSize_endpoints 0x2000
|
||||
|
||||
|
||||
|
||||
|
||||
// default send message time out
|
||||
#define CSI_SEND_MESSAGE_TIMEOUT (10 * 1000 * 1000) // 10s
|
||||
|
||||
|
||||
#define roundup(x,y) ((((x)+(y)-1)/(y))*(y))
|
||||
|
||||
#define is_power_of_2(x) (((x)==((x)&~((x)-1)))&&((x)!=0))
|
||||
|
||||
|
||||
#ifndef REQUIRE
|
||||
#define REQUIRE(_expr) \
|
||||
do { \
|
||||
if (__builtin_expect(!(_expr), 0)) \
|
||||
panic("%s:%s:%u: REQUIRE failed: %s\n", \
|
||||
__FILE__, \
|
||||
__PRETTY_FUNCTION__, \
|
||||
__LINE__, #_expr); \
|
||||
} while(0);
|
||||
#endif
|
||||
|
||||
|
||||
#define COPROC_STATE(s) \
|
||||
(((s)==MGMT_INITIALIZED) ? "Initialized" : \
|
||||
(((s)==MGMT_QUIESCED) ? "Quiesced" : \
|
||||
(((s)==MGMT_WAITING_FOR_ACK) ? "Waiting For Hello" : \
|
||||
(((s)==MGMT_VERSION_OK) ? "Running" : \
|
||||
(((s)==MGMT_UNSUPPORTED_PROTOCOL_VERSION) ? "Running - Unsupported Protocol Version" : \
|
||||
(((s)==MGMT_NONEXISTENT) ? "Uninitialized" : "???"))))))
|
||||
|
||||
|
||||
|
||||
// CS_LOG levels are passed through to dprintf() and compared against DEBUG_LEVEL.
|
||||
// <debug.h> defines { DEBUG_SILENT DEBUG_CRITICAL DEBUG_INFO DEBUG_SPEW }
|
||||
// one may use DEBUG_LEVEL to enable output and (DEBUG_LEVEL+1) to stifle output
|
||||
|
||||
// these levels should probably be enabled by default
|
||||
#define CSI_LOG_LEVEL_CRITICAL DEBUG_CRITICAL // indication of a serious problem
|
||||
|
||||
// these levels *may* be enabled by default
|
||||
#define CSI_LOG_LEVEL_CRASHLOG DEBUG_CRITICAL // coproc crashlog output
|
||||
#define CSI_LOG_LEVEL_CONSOLE DEBUG_INFO // coproc console output
|
||||
#define CSI_LOG_LEVEL_SYSLOG DEBUG_CRITICAL // coproc syslog output
|
||||
#define CSI_LOG_LEVEL_DEBUG_SPEW DEBUG_SPEW // verbose debug output
|
||||
|
||||
// these levels are disabled by default
|
||||
#if DEBUG_BUILD
|
||||
#define CSI_LOG_LEVEL_DEBUG DEBUG_INFO
|
||||
#else
|
||||
#define CSI_LOG_LEVEL_DEBUG DEBUG_CRITICAL // every little operational detail - TMI - useful for debugging only
|
||||
#endif
|
||||
|
||||
|
||||
// csi log formatting macros
|
||||
#define CSI_LOG_RAW(lev, x...) \
|
||||
do { dprintf(CSI_LOG_LEVEL_##lev, x); } while(0)
|
||||
|
||||
#define CSI_LOG(lev, cop, x...) \
|
||||
do { dprintf(CSI_LOG_LEVEL_##lev, "%s: ", IOP_NAME(cop)); dprintf(CSI_LOG_LEVEL_##lev, x); } while(0)
|
||||
|
||||
#define CSI_EP_LOG(lev, cop, ep_name, x...) \
|
||||
do { dprintf(CSI_LOG_LEVEL_##lev, "[%s %s] ", IOP_NAME(cop), ep_name); dprintf(CSI_LOG_LEVEL_##lev, x); } while(0)
|
||||
|
||||
|
||||
// firmware api
|
||||
|
||||
typedef struct {
|
||||
uint32_t alignment_req;
|
||||
void *fw_address;
|
||||
uint32_t fw_size;
|
||||
void *heap_address;
|
||||
uint32_t heap_size;
|
||||
addr_t region_base;
|
||||
uint32_t region_size;
|
||||
} fw_desc_t;
|
||||
|
||||
csi_status_t fw_fixup_and_relocate (csi_coproc_t which_coproc, fw_desc_t *fw_desc);
|
||||
void fw_print_version (csi_coproc_t coproc, fw_desc_t *fw_desc);
|
||||
|
||||
csi_status_t csi_start (csi_coproc_t which_coproc);
|
||||
csi_status_t csi_stop (csi_coproc_t which_coproc);
|
||||
csi_status_t csi_panic_recover (csi_coproc_t which_coproc);
|
||||
csi_status_t quiesce_iop (csi_coproc_t which_coproc);
|
||||
csi_status_t hibernate_iop (csi_coproc_t which_coproc);
|
||||
csi_status_t request_iop_power_state (csi_coproc_t which_coproc, csi_power_state_t ps);
|
||||
csi_status_t set_send_timeout (csi_coproc_t which_coproc, uint32_t timeout);
|
||||
uint32_t get_send_timeout (csi_coproc_t which_coproc);
|
||||
void csi_info (csi_coproc_t which_coproc);
|
||||
|
||||
|
||||
csi_status_t csi_set_feature (csi_coproc_t which_coproc, coproc_feature_t feature);
|
||||
csi_status_t csi_clr_feature (csi_coproc_t which_coproc, coproc_feature_t feature);
|
||||
|
||||
void maybe_do_cache_operation(int operation, void *address, u_int32_t length);
|
||||
|
||||
|
||||
//
|
||||
// built-in endpoint api
|
||||
//
|
||||
|
||||
csi_status_t bi_ep_send_message (void *token, msg_payload_t msg_payload);
|
||||
|
||||
#endif /* !defined(__CSI_PRIVATE_H) */
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2013 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Console object
|
||||
//
|
||||
|
||||
typedef struct {
|
||||
csi_coproc_t coproc;
|
||||
struct task_event msg_event;
|
||||
void *csi_token;
|
||||
const char *name;
|
||||
uint32_t verbosity;
|
||||
void *buffers;
|
||||
uint32_t allocated;
|
||||
char *scratch;
|
||||
} ep_console_desc_t;
|
||||
|
||||
|
||||
//
|
||||
// Console APIs
|
||||
//
|
||||
|
||||
void bi_ep_console_create (csi_coproc_t which_coproc);
|
||||
csi_status_t bi_ep_console_send_custom_cmd (csi_coproc_t which_coproc, uint32_t payload);
|
||||
csi_status_t bi_ep_console_set_verbosity (csi_coproc_t which_coproc, uint32_t verbosity);
|
||||
|
||||
csi_status_t console_quiesce (csi_coproc_t which_coproc, csi_power_state_t ps);
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2013 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// crash log type
|
||||
typedef enum {
|
||||
CSI_CRASHLOG_TYPE_NMI = 0,
|
||||
CSI_CRASHLOG_TYPE_MSG_CRASH,
|
||||
CSI_CRASHLOG_TYPE_MSG_LOG,
|
||||
} csi_crashlog_t;
|
||||
|
||||
//
|
||||
// Crashlog Object
|
||||
//
|
||||
typedef struct {
|
||||
csi_coproc_t coproc;
|
||||
struct task_event msg_event;
|
||||
void *csi_token;
|
||||
const char *name;
|
||||
void *buffer;
|
||||
uint32_t allocated;
|
||||
} ep_crashlog_desc_t;
|
||||
|
||||
|
||||
//
|
||||
// Public API
|
||||
//
|
||||
|
||||
void bi_ep_crashlog_create (csi_coproc_t which_coproc);
|
||||
csi_status_t bi_ep_crashlog_force_event (csi_coproc_t which_coproc, csi_crashlog_t crashlog_type);
|
||||
|
||||
csi_status_t crashlog_quiesce (csi_coproc_t which_coproc, csi_power_state_t ps);
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2013 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <csi_ipc_protocol.h>
|
||||
#include <csi_mgmt_protocol.h>
|
||||
|
||||
|
||||
// FixMe - auto-pinger not implemented
|
||||
// pinger
|
||||
typedef enum {
|
||||
PINGER_DISABLED = 0x0000,
|
||||
PINGER_OK = 0x0001,
|
||||
PINGER_TIMEOUT = 0x1000,
|
||||
PINGER_SEQ_ERR = 0x1001,
|
||||
PINGER_XMIT_ERR = 0x1002,
|
||||
} pinger_status_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t xmt_count;
|
||||
uint32_t replied_to_count;
|
||||
uint32_t xmt_err_count;
|
||||
uint32_t rcv_err_count;
|
||||
uint32_t timeout_err_count;
|
||||
} ping_stats_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t ping_period_usec;
|
||||
uint32_t report_period_usec;
|
||||
pinger_status_t status;
|
||||
|
||||
csi_ping_seqnum_t xmt_seq_number;
|
||||
csi_ping_seqnum_t expected_rcv_seq_number;
|
||||
csi_ping_seqnum_t rcv_seq_number;
|
||||
|
||||
ping_stats_t stats;
|
||||
} pinger_t;
|
||||
|
||||
|
||||
//
|
||||
// Management Object
|
||||
//
|
||||
|
||||
typedef enum {
|
||||
MGMT_INITIALIZED = 0,
|
||||
MGMT_QUIESCED,
|
||||
MGMT_WAITING_FOR_ACK,
|
||||
MGMT_VERSION_OK,
|
||||
|
||||
// error status
|
||||
MGMT_NONEXISTENT = 0x1000,
|
||||
MGMT_UNSUPPORTED_PROTOCOL_VERSION = 0x1001,
|
||||
MGMT_MBOX_ERROR = 0x1002,
|
||||
} mgmt_state_t;
|
||||
|
||||
typedef struct {
|
||||
csi_coproc_t coproc;
|
||||
struct task_event msg_event;
|
||||
void *csi_token;
|
||||
const char *name;
|
||||
|
||||
csi_protocol_version_t remoteVersion;
|
||||
mgmt_state_t state;
|
||||
|
||||
// pinger
|
||||
pinger_t pinger;
|
||||
|
||||
volatile bool ping_test_waiting;
|
||||
volatile csi_ping_seqnum_t ping_test_seqnum;
|
||||
volatile ipc_msg_t ping_test_ack;
|
||||
|
||||
// power
|
||||
struct task_event *ps_event;
|
||||
csi_power_state_t power_state;
|
||||
} ep_mgmt_desc_t;
|
||||
|
||||
extern ep_mgmt_desc_t *mgmt_descs[];
|
||||
|
||||
|
||||
//
|
||||
// Endpoint API
|
||||
//
|
||||
|
||||
void bi_ep_mgmt_create (csi_coproc_t which_coproc, struct task_event *ps_event);
|
||||
csi_status_t bi_ep_mgmt_send_ps (csi_coproc_t which_coproc, csi_power_state_t ps);
|
||||
csi_status_t bi_ep_mgmt_send_ping (csi_coproc_t which_coproc, int ping_count);
|
||||
mgmt_state_t bi_ep_mgmt_get_state (csi_coproc_t which_coproc);
|
||||
|
||||
csi_power_state_t bi_ep_mgmt_get_power_state (csi_coproc_t which_coproc);
|
||||
|
||||
// to support panic recovery
|
||||
csi_status_t handle_power_state_ack (ep_mgmt_desc_t *mgmt, ipc_msg_t msg_in);
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2013 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if DEBUG_BUILD
|
||||
#define LOG_DEFAULT LOG_DEBUG
|
||||
#else
|
||||
#define LOG_DEFAULT LOG_WARNING
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// Endpoint APIs
|
||||
//
|
||||
|
||||
void bi_ep_syslog_create (csi_coproc_t which_coproc);
|
||||
csi_status_t bi_ep_syslog_verbosity (csi_coproc_t which_coproc, uint32_t verbosity);
|
||||
|
||||
csi_status_t syslog_quiesce (csi_coproc_t which_coproc, csi_power_state_t ps);
|
||||
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. The rights granted to you under the License
|
||||
* may not be used to create, or enable the creation or redistribution of,
|
||||
* unlawful or unlicensed copies of an Apple operating system, or to
|
||||
* circumvent, violate, or enable the circumvention or violation of, any
|
||||
* terms of an Apple operating system software license agreement.
|
||||
*
|
||||
* Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* singly-linked tail queues, lists, tail queues, and circular queues.
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A singly-linked tail queue is headed by a pair of pointers, one to the
|
||||
* head of the list and the other to the tail of the list. The elements are
|
||||
* singly linked for minimum space and pointer manipulation overhead at the
|
||||
* expense of O(n) removal for arbitrary elements. New elements can be added
|
||||
* to the list after an existing element, at the head of the list, or at the
|
||||
* end of the list. Elements being removed from the head of the tail queue
|
||||
* should use the explicit macro for this purpose for optimum efficiency.
|
||||
* A singly-linked tail queue may only be traversed in the forward direction.
|
||||
* Singly-linked tail queues are ideal for applications with large datasets
|
||||
* and few or no removals or for implementing a FIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
* Note that circle queues are deprecated, because, as the removal log
|
||||
* in FreeBSD states, "CIRCLEQs are a disgrace to everything Knuth taught
|
||||
* us in Volume 1 Chapter 2. [...] Use TAILQ instead, it provides the same
|
||||
* functionality." Code using them will continue to compile, but they
|
||||
* are no longer documented on the man page.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*
|
||||
*
|
||||
* SLIST LIST STAILQ TAILQ CIRCLEQ
|
||||
* _HEAD + + + + +
|
||||
* _HEAD_INITIALIZER + + + + -
|
||||
* _ENTRY + + + + +
|
||||
* _INIT + + + + +
|
||||
* _EMPTY + + + + +
|
||||
* _FIRST + + + + +
|
||||
* _NEXT + + + + +
|
||||
* _PREV - - - + +
|
||||
* _LAST - - + + +
|
||||
* _FOREACH + + + + +
|
||||
* _FOREACH_SAFE + + + + -
|
||||
* _FOREACH_REVERSE - - - + -
|
||||
* _FOREACH_REVERSE_SAFE - - - + -
|
||||
* _INSERT_HEAD + + + + +
|
||||
* _INSERT_BEFORE - + - + +
|
||||
* _INSERT_AFTER + + + + +
|
||||
* _INSERT_TAIL - - + + +
|
||||
* _CONCAT - - + + -
|
||||
* _REMOVE_AFTER + - + - -
|
||||
* _REMOVE_HEAD + - + - -
|
||||
* _REMOVE_HEAD_UNTIL - - + - -
|
||||
* _REMOVE + + + + +
|
||||
* _SWAP - + + + -
|
||||
*
|
||||
*/
|
||||
#ifdef QUEUE_MACRO_DEBUG
|
||||
/* Store the last 2 places the queue element or head was altered */
|
||||
struct qm_trace {
|
||||
char * lastfile;
|
||||
int lastline;
|
||||
char * prevfile;
|
||||
int prevline;
|
||||
};
|
||||
|
||||
#define TRACEBUF struct qm_trace trace;
|
||||
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
|
||||
|
||||
#define QMD_TRACE_HEAD(head) do { \
|
||||
(head)->trace.prevline = (head)->trace.lastline; \
|
||||
(head)->trace.prevfile = (head)->trace.lastfile; \
|
||||
(head)->trace.lastline = __LINE__; \
|
||||
(head)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TRACE_ELEM(elem) do { \
|
||||
(elem)->trace.prevline = (elem)->trace.lastline; \
|
||||
(elem)->trace.prevfile = (elem)->trace.lastfile; \
|
||||
(elem)->trace.lastline = __LINE__; \
|
||||
(elem)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define QMD_TRACE_ELEM(elem)
|
||||
#define QMD_TRACE_HEAD(head)
|
||||
#define TRACEBUF
|
||||
#define TRASHIT(x)
|
||||
#endif /* QUEUE_MACRO_DEBUG */
|
||||
|
||||
/*
|
||||
* Singly-linked List declarations.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
||||
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
|
||||
for ((varp) = &SLIST_FIRST((head)); \
|
||||
((var) = *(varp)) != NULL; \
|
||||
(varp) = &SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_INIT(head) do { \
|
||||
SLIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
||||
SLIST_NEXT((slistelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
||||
SLIST_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if (SLIST_FIRST((head)) == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = SLIST_FIRST((head)); \
|
||||
while (SLIST_NEXT(curelm, field) != (elm)) \
|
||||
curelm = SLIST_NEXT(curelm, field); \
|
||||
SLIST_REMOVE_AFTER(curelm, field); \
|
||||
} \
|
||||
TRASHIT((elm)->field.sle_next); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||
SLIST_NEXT(elm, field) = \
|
||||
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue declarations.
|
||||
*/
|
||||
#define STAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *stqh_first;/* first element */ \
|
||||
struct type **stqh_last;/* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).stqh_first }
|
||||
|
||||
#define STAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue functions.
|
||||
*/
|
||||
#define STAILQ_CONCAT(head1, head2) do { \
|
||||
if (!STAILQ_EMPTY((head2))) { \
|
||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
|
||||
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for((var) = STAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
|
||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = STAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_INIT(head) do { \
|
||||
STAILQ_FIRST((head)) = NULL; \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_NEXT((tqelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
STAILQ_NEXT((elm), field) = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_LAST(head, type, field) \
|
||||
(STAILQ_EMPTY((head)) ? \
|
||||
NULL : \
|
||||
((struct type *)(void *) \
|
||||
((char *)((head)->stqh_last) - __offsetof(struct type, field))))
|
||||
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = STAILQ_FIRST((head)); \
|
||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
||||
curelm = STAILQ_NEXT(curelm, field); \
|
||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
||||
} \
|
||||
TRASHIT((elm)->field.stqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = \
|
||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT(elm, field) = \
|
||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_SWAP(head1, head2, type) do { \
|
||||
struct type *swap_first = STAILQ_FIRST(head1); \
|
||||
struct type **swap_last = (head1)->stqh_last; \
|
||||
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_FIRST(head2) = swap_first; \
|
||||
(head2)->stqh_last = swap_last; \
|
||||
if (STAILQ_EMPTY(head1)) \
|
||||
(head1)->stqh_last = &STAILQ_FIRST(head1); \
|
||||
if (STAILQ_EMPTY(head2)) \
|
||||
(head2)->stqh_last = &STAILQ_FIRST(head2); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* List declarations.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
|
||||
#if (defined(_KERNEL) && defined(INVARIANTS)) || defined(QUEUE_MACRO_DEBUG)
|
||||
#define QMD_LIST_CHECK_HEAD(head, field) do { \
|
||||
if (LIST_FIRST((head)) != NULL && \
|
||||
LIST_FIRST((head))->field.le_prev != \
|
||||
&LIST_FIRST((head))) \
|
||||
panic("Bad list head %p first->prev != head", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_LIST_CHECK_NEXT(elm, field) do { \
|
||||
if (LIST_NEXT((elm), field) != NULL && \
|
||||
LIST_NEXT((elm), field)->field.le_prev != \
|
||||
&((elm)->field.le_next)) \
|
||||
panic("Bad link elm %p next->prev != elm", (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_LIST_CHECK_PREV(elm, field) do { \
|
||||
if (*(elm)->field.le_prev != (elm)) \
|
||||
panic("Bad link elm %p prev->next != elm", (elm)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define QMD_LIST_CHECK_HEAD(head, field)
|
||||
#define QMD_LIST_CHECK_NEXT(elm, field)
|
||||
#define QMD_LIST_CHECK_PREV(elm, field)
|
||||
#endif /* (_KERNEL && INVARIANTS) || QUEUE_MACRO_DEBUG */
|
||||
|
||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
||||
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = LIST_NEXT((var), field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
QMD_LIST_CHECK_NEXT(listelm, field); \
|
||||
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
|
||||
LIST_NEXT((listelm), field)->field.le_prev = \
|
||||
&LIST_NEXT((elm), field); \
|
||||
LIST_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QMD_LIST_CHECK_PREV(listelm, field); \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
LIST_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
QMD_LIST_CHECK_HEAD((head), field); \
|
||||
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
|
||||
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
|
||||
LIST_FIRST((head)) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
QMD_LIST_CHECK_NEXT(elm, field); \
|
||||
QMD_LIST_CHECK_PREV(elm, field); \
|
||||
if (LIST_NEXT((elm), field) != NULL) \
|
||||
LIST_NEXT((elm), field)->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
|
||||
TRASHIT((elm)->field.le_next); \
|
||||
TRASHIT((elm)->field.le_prev); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_SWAP(head1, head2, type, field) do { \
|
||||
struct type *swap_tmp = LIST_FIRST((head1)); \
|
||||
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
|
||||
LIST_FIRST((head2)) = swap_tmp; \
|
||||
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
|
||||
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Tail queue declarations.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
||||
if (!TAILQ_EMPTY(head2)) { \
|
||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
TAILQ_INIT((head2)); \
|
||||
QMD_TRACE_HEAD(head1); \
|
||||
QMD_TRACE_HEAD(head2); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = TAILQ_NEXT((var), field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var); \
|
||||
(var) = TAILQ_PREV((var), headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_INIT(head) do { \
|
||||
TAILQ_FIRST((head)) = NULL; \
|
||||
(head)->tqh_last = &TAILQ_FIRST((head)); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else { \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} \
|
||||
TAILQ_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
QMD_TRACE_ELEM(&listelm->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
TAILQ_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
QMD_TRACE_ELEM(&listelm->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
|
||||
TAILQ_FIRST((head))->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
TAILQ_FIRST((head)) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
TAILQ_NEXT((elm), field) = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field)) != NULL) \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else { \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} \
|
||||
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
|
||||
TRASHIT((elm)->field.tqe_next); \
|
||||
TRASHIT((elm)->field.tqe_prev); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_SWAP(head1, head2, type, field) do { \
|
||||
struct type *swap_first = (head1)->tqh_first; \
|
||||
struct type **swap_last = (head1)->tqh_last; \
|
||||
(head1)->tqh_first = (head2)->tqh_first; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
(head2)->tqh_first = swap_first; \
|
||||
(head2)->tqh_last = swap_last; \
|
||||
if ((swap_first = (head1)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head1)->tqh_first; \
|
||||
else \
|
||||
(head1)->tqh_last = &(head1)->tqh_first; \
|
||||
if ((swap_first = (head2)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head2)->tqh_first; \
|
||||
else \
|
||||
(head2)->tqh_last = &(head2)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
|
||||
|
||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
||||
|
||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||
for((var) = (head)->cqh_first; \
|
||||
(var) != (void *)(head); \
|
||||
(var) = (var)->field.cqe_next)
|
||||
|
||||
#define CIRCLEQ_INIT(head) do { \
|
||||
(head)->cqh_first = (void *)(head); \
|
||||
(head)->cqh_last = (void *)(head); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = (void *)(head); \
|
||||
if ((head)->cqh_last == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (void *)(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
||||
|
||||
#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
|
||||
|
||||
#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||
if ((elm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
} while (0)
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
#if NOTFB31
|
||||
|
||||
/*
|
||||
* XXX insque() and remque() are an old way of handling certain queues.
|
||||
* They bogusly assumes that all queue heads look alike.
|
||||
*/
|
||||
|
||||
struct quehead {
|
||||
struct quehead *qh_link;
|
||||
struct quehead *qh_rlink;
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
|
||||
static __inline void
|
||||
insque(void *a, void *b)
|
||||
{
|
||||
struct quehead *element = (struct quehead *)a,
|
||||
*head = (struct quehead *)b;
|
||||
|
||||
element->qh_link = head->qh_link;
|
||||
element->qh_rlink = head;
|
||||
head->qh_link = element;
|
||||
element->qh_link->qh_rlink = element;
|
||||
}
|
||||
|
||||
static __inline void
|
||||
remque(void *a)
|
||||
{
|
||||
struct quehead *element = (struct quehead *)a;
|
||||
|
||||
element->qh_link->qh_rlink = element->qh_rlink;
|
||||
element->qh_rlink->qh_link = element->qh_link;
|
||||
element->qh_rlink = 0;
|
||||
}
|
||||
|
||||
#else /* !__GNUC__ */
|
||||
|
||||
void insque(void *a, void *b);
|
||||
void remque(void *a);
|
||||
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
#endif
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
@@ -0,0 +1,31 @@
|
||||
# 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.
|
||||
#
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
OPTIONS += \
|
||||
WITH_CSI=1
|
||||
|
||||
# The additional paths below are used to find the ans iop firmware
|
||||
# file, "AppleCSI.*.h" and a protocol definition file,
|
||||
# "AppleCSIProtocol.h" which defines communications between coproc firmware
|
||||
# and the csi driver. The $(DSTROOT) path is useful only during development.
|
||||
GLOBAL_INCLUDES += $(LOCAL_DIR)/include \
|
||||
$(DSTROOT)/usr/local/standalone/firmware \
|
||||
$(SDKROOT)/../../../usr/local/standalone/firmware
|
||||
|
||||
ALL_OBJS += $(LOCAL_DIR)/csi.o \
|
||||
$(LOCAL_DIR)/debug.o \
|
||||
$(LOCAL_DIR)/endpoints/builtin.o \
|
||||
$(LOCAL_DIR)/endpoints/management_ep.o \
|
||||
$(LOCAL_DIR)/endpoints/syslog_ep.o \
|
||||
$(LOCAL_DIR)/endpoints/console_ep.o \
|
||||
$(LOCAL_DIR)/endpoints/crashlog_ep.o \
|
||||
$(LOCAL_DIR)/firmware/csi_firmware.o \
|
||||
$(LOCAL_DIR)/firmware/fw_ans.o \
|
||||
Reference in new issue
Block a user