blob: 2fd9e5f057975d1b07ca17353dc487afa3efac1c [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/*
* Base FSP (Flexible Service Processor) Support
*
* FSP is the BMC-like thing in some IBM POWER servers
*
* Copyright 2013-2019 IBM Corp.
*/
#include <stdarg.h>
#include <processor.h>
#include <io.h>
#include <fsp.h>
#include <lock.h>
#include <interrupts.h>
#include <device.h>
#include <trace.h>
#include <timebase.h>
#include <cpu.h>
#include <errorlog.h>
#include <opal.h>
#include <opal-msg.h>
#include <ccan/list/list.h>
extern uint32_t hir_trigger;
DEFINE_LOG_ENTRY(OPAL_RC_FSP_POLL_TIMEOUT, OPAL_PLATFORM_ERR_EVT, OPAL_FSP,
OPAL_PLATFORM_FIRMWARE, OPAL_RECOVERED_ERR_GENERAL, OPAL_NA);
DEFINE_LOG_ENTRY(OPAL_RC_FSP_MBOX_ERR, OPAL_PLATFORM_ERR_EVT, OPAL_FSP,
OPAL_PLATFORM_FIRMWARE, OPAL_RECOVERED_ERR_GENERAL, OPAL_NA);
DEFINE_LOG_ENTRY(OPAL_RC_FSP_DISR_HIR_MASK, OPAL_PLATFORM_ERR_EVT, OPAL_FSP,
OPAL_PLATFORM_FIRMWARE, OPAL_RECOVERED_ERR_GENERAL, OPAL_NA);
/* We make this look like a Surveillance error, even though it really
* isn't one.
*/
DEFINE_LOG_ENTRY(OPAL_INJECTED_HIR, OPAL_MISC_ERR_EVT, OPAL_SURVEILLANCE,
OPAL_SURVEILLANCE_ERR, OPAL_PREDICTIVE_ERR_GENERAL,
OPAL_MISCELLANEOUS_INFO_ONLY);
#define FSP_TRACE_MSG
#define FSP_TRACE_EVENT
#define FSP_MAX_IOPATH 4
enum fsp_path_state {
fsp_path_bad,
fsp_path_backup,
fsp_path_active,
};
struct fsp_iopath {
enum fsp_path_state state;
void *fsp_regs;
struct psi *psi;
};
enum fsp_mbx_state {
fsp_mbx_idle, /* Mailbox ready to send */
fsp_mbx_send, /* Mailbox sent, waiting for ack */
fsp_mbx_crit_op, /* Critical operation in progress */
fsp_mbx_prep_for_reset, /* Prepare for reset sent */
fsp_mbx_hir_seq_done, /* HIR sequence done, link forced down */
fsp_mbx_err, /* Mailbox in error state, waiting for r&r */
fsp_mbx_rr, /* Mailbox in r&r */
};
struct fsp {
struct fsp *link;
unsigned int index;
enum fsp_mbx_state state;
struct fsp_msg *pending;
unsigned int iopath_count;
int active_iopath; /* -1: no active IO path */
struct fsp_iopath iopath[FSP_MAX_IOPATH];
};
enum ipl_state {
ipl_initial = 0x00000000,
ipl_opl_sent = 0x00000001,
ipl_got_continue = 0x00000002,
ipl_got_new_role = 0x00000004,
ipl_got_caps = 0x00000008,
ipl_got_fsp_functional = 0x00000010
};
static enum ipl_state ipl_state = ipl_initial;
static struct fsp *first_fsp;
static struct fsp *active_fsp;
static u16 fsp_curseq = 0x8000;
static __be64 *skiboot_constant_addr fsp_tce_table;
#define FSP_INBOUND_SIZE 0x00100000UL
static void *fsp_inbound_buf = NULL;
static u32 fsp_inbound_off;
static struct lock fsp_lock = LOCK_UNLOCKED;
static struct lock fsp_poll_lock = LOCK_UNLOCKED;
static u64 fsp_cmdclass_resp_bitmask;
static u64 timeout_timer;
static u64 fsp_hir_timeout;
#define FSP_CRITICAL_OP_TIMEOUT 128
#define FSP_DRCR_CLEAR_TIMEOUT 128
/* LID numbers. For now we hijack some of pHyp's own until i figure
* out the whole business with the MasterLID
*/
#define KERNEL_LID_PHYP 0x80a00701
#define KERNEL_LID_OPAL 0x80f00101
#define INITRAMFS_LID_OPAL 0x80f00102
/*
* We keep track on last logged values for some things to print only on
* value changes, but also to relieve pressure on the tracer which
* doesn't do a very good job at detecting repeats when called from
* many different CPUs
*/
static u32 disr_last_print;
static u32 drcr_last_print;
static u32 hstate_last_print;
void fsp_handle_resp(struct fsp_msg *msg);
struct fsp_cmdclass {
int timeout;
bool busy;
struct list_head msgq;
struct list_head clientq;
struct list_head rr_queue; /* To queue up msgs during R/R */
u64 timesent;
};
static struct fsp_cmdclass fsp_cmdclass_rr;
static struct fsp_cmdclass fsp_cmdclass[FSP_MCLASS_LAST - FSP_MCLASS_FIRST + 1]
= {
#define DEF_CLASS(_cl, _to) [_cl - FSP_MCLASS_FIRST] = { .timeout = _to }
DEF_CLASS(FSP_MCLASS_SERVICE, 16),
DEF_CLASS(FSP_MCLASS_PCTRL_MSG, 16),
DEF_CLASS(FSP_MCLASS_PCTRL_ABORTS, 16),
DEF_CLASS(FSP_MCLASS_ERR_LOG, 16),
DEF_CLASS(FSP_MCLASS_CODE_UPDATE, 40),
DEF_CLASS(FSP_MCLASS_FETCH_SPDATA, 16),
DEF_CLASS(FSP_MCLASS_FETCH_HVDATA, 16),
DEF_CLASS(FSP_MCLASS_NVRAM, 16),
DEF_CLASS(FSP_MCLASS_MBOX_SURV, 2),
DEF_CLASS(FSP_MCLASS_RTC, 16),
DEF_CLASS(FSP_MCLASS_SMART_CHIP, 20),
DEF_CLASS(FSP_MCLASS_INDICATOR, 180),
DEF_CLASS(FSP_MCLASS_HMC_INTFMSG, 16),
DEF_CLASS(FSP_MCLASS_HMC_VT, 16),
DEF_CLASS(FSP_MCLASS_HMC_BUFFERS, 16),
DEF_CLASS(FSP_MCLASS_SHARK, 16),
DEF_CLASS(FSP_MCLASS_MEMORY_ERR, 16),
DEF_CLASS(FSP_MCLASS_CUOD_EVENT, 16),
DEF_CLASS(FSP_MCLASS_HW_MAINT, 16),
DEF_CLASS(FSP_MCLASS_VIO, 16),
DEF_CLASS(FSP_MCLASS_SRC_MSG, 16),
DEF_CLASS(FSP_MCLASS_DATA_COPY, 16),
DEF_CLASS(FSP_MCLASS_TONE, 16),
DEF_CLASS(FSP_MCLASS_VIRTUAL_NVRAM, 16),
DEF_CLASS(FSP_MCLASS_TORRENT, 16),
DEF_CLASS(FSP_MCLASS_NODE_PDOWN, 16),
DEF_CLASS(FSP_MCLASS_DIAG, 16),
DEF_CLASS(FSP_MCLASS_PCIE_LINK_TOPO, 16),
DEF_CLASS(FSP_MCLASS_OCC, 16),
DEF_CLASS(FSP_MCLASS_TRUSTED_BOOT, 2),
DEF_CLASS(FSP_MCLASS_HBRT, 2),
};
static void fsp_trace_msg(struct fsp_msg *msg, u8 dir __unused)
{
union trace fsp __unused;
#ifdef FSP_TRACE_MSG
size_t len = offsetof(struct trace_fsp_msg, data[msg->dlen]);
fsp.fsp_msg.dlen = msg->dlen;
fsp.fsp_msg.word0 = cpu_to_be32(msg->word0);
fsp.fsp_msg.word1 = cpu_to_be32(msg->word1);
fsp.fsp_msg.dir = dir;
memcpy(fsp.fsp_msg.data, msg->data.bytes, msg->dlen);
trace_add(&fsp, TRACE_FSP_MSG, len);
#endif /* FSP_TRACE_MSG */
assert(msg->dlen <= sizeof(fsp.fsp_msg.data));
}
static struct fsp *fsp_get_active(void)
{
/* XXX Handle transition between FSPs */
return active_fsp;
}
static u64 fsp_get_class_bit(u8 class)
{
/* Alias classes CE and CF as the FSP has a single queue */
if (class == FSP_MCLASS_IPL)
class = FSP_MCLASS_SERVICE;
return 1ul << (class - FSP_MCLASS_FIRST);
}
static struct fsp_cmdclass *__fsp_get_cmdclass(u8 class)
{
struct fsp_cmdclass *ret;
/* RR class is special */
if (class == FSP_MCLASS_RR_EVENT)
return &fsp_cmdclass_rr;
/* Bound check */
if (class < FSP_MCLASS_FIRST || class > FSP_MCLASS_LAST)
return NULL;
/* Alias classes CE and CF as the FSP has a single queue */
if (class == FSP_MCLASS_IPL)
class = FSP_MCLASS_SERVICE;
ret = &fsp_cmdclass[class - FSP_MCLASS_FIRST];
/* Unknown class */
if (ret->timeout == 0)
return NULL;
return ret;
}
static struct fsp_cmdclass *fsp_get_cmdclass(struct fsp_msg *msg)
{
u8 c = msg->word0 & 0xff;
return __fsp_get_cmdclass(c);
}
static struct fsp_msg *__fsp_allocmsg(void)
{
return zalloc(sizeof(struct fsp_msg));
}
struct fsp_msg *fsp_allocmsg(bool alloc_response)
{
struct fsp_msg *msg;
msg = __fsp_allocmsg();
if (!msg)
return NULL;
if (alloc_response) {
msg->resp = __fsp_allocmsg();
if (!msg->resp) {
free(msg);
return NULL;
}
}
return msg;
}
void __fsp_freemsg(struct fsp_msg *msg)
{
free(msg);
}
void fsp_freemsg(struct fsp_msg *msg)
{
if (msg && msg->resp)
__fsp_freemsg(msg->resp);
__fsp_freemsg(msg);
}
void fsp_cancelmsg(struct fsp_msg *msg)
{
bool need_unlock = false;
struct fsp_cmdclass* cmdclass = fsp_get_cmdclass(msg);
if (!fsp_in_rr()) {
prerror("FSP: Message cancel allowed only when"
"FSP is in reset\n");
return;
}
if (!cmdclass)
return;
/* Recursive locking */
need_unlock = lock_recursive(&fsp_lock);
list_del(&msg->link);
msg->state = fsp_msg_cancelled;
if (need_unlock)
unlock(&fsp_lock);
}
static void fsp_wreg(struct fsp *fsp, u32 reg, u32 val)
{
struct fsp_iopath *iop;
if (fsp->active_iopath < 0)
return;
iop = &fsp->iopath[fsp->active_iopath];
if (iop->state == fsp_path_bad)
return;
out_be32(iop->fsp_regs + reg, val);
}
static u32 fsp_rreg(struct fsp *fsp, u32 reg)
{
struct fsp_iopath *iop;
if (fsp->active_iopath < 0)
return 0xffffffff;
iop = &fsp->iopath[fsp->active_iopath];
if (iop->state == fsp_path_bad)
return 0xffffffff;
return in_be32(iop->fsp_regs + reg);
}
static void fsp_reg_dump(void)
{
#define FSP_DUMP_ONE(x) \
prlog(PR_DEBUG, " %20s: %x\n", #x, fsp_rreg(fsp, x));
struct fsp *fsp = fsp_get_active();
if (!fsp)
return;
prlog(PR_DEBUG, "FSP #%d: Register dump (state=%d)\n",
fsp->index, fsp->state);
FSP_DUMP_ONE(FSP_DRCR_REG);
FSP_DUMP_ONE(FSP_DISR_REG);
FSP_DUMP_ONE(FSP_MBX1_HCTL_REG);
FSP_DUMP_ONE(FSP_MBX1_FCTL_REG);
FSP_DUMP_ONE(FSP_MBX2_HCTL_REG);
FSP_DUMP_ONE(FSP_MBX2_FCTL_REG);
FSP_DUMP_ONE(FSP_SDES_REG);
FSP_DUMP_ONE(FSP_HDES_REG);
FSP_DUMP_ONE(FSP_HDIR_REG);
FSP_DUMP_ONE(FSP_HDIM_SET_REG);
FSP_DUMP_ONE(FSP_PDIR_REG);
FSP_DUMP_ONE(FSP_PDIM_SET_REG);
FSP_DUMP_ONE(FSP_SCRATCH0_REG);
FSP_DUMP_ONE(FSP_SCRATCH1_REG);
FSP_DUMP_ONE(FSP_SCRATCH2_REG);
FSP_DUMP_ONE(FSP_SCRATCH3_REG);
}
static void fsp_notify_rr_state(u32 state)
{
struct fsp_client *client, *next;
struct fsp_cmdclass *cmdclass = __fsp_get_cmdclass(FSP_MCLASS_RR_EVENT);
assert(cmdclass);
list_for_each_safe(&cmdclass->clientq, client, next, link)
client->message(state, NULL);
}
static void fsp_reset_cmdclass(void)
{
int i;
struct fsp_msg *msg;
/*
* The FSP is in reset and hence we can't expect any response
* to outstanding messages that we've already sent. Clear the
* bitmap to reflect that.
*/
fsp_cmdclass_resp_bitmask = 0;
for (i = 0; i <= (FSP_MCLASS_LAST - FSP_MCLASS_FIRST); i++) {
struct fsp_cmdclass *cmdclass = &fsp_cmdclass[i];
cmdclass->busy = false;
cmdclass->timesent = 0;
/* Make sure the message queue is empty */
while(!list_empty(&cmdclass->msgq)) {
msg = list_pop(&cmdclass->msgq, struct fsp_msg,
link);
list_add_tail(&cmdclass->rr_queue, &msg->link);
}
}
}
static bool fsp_in_hir(struct fsp *fsp)
{
switch (fsp->state) {
case fsp_mbx_crit_op:
case fsp_mbx_prep_for_reset:
return true;
default:
return false;
}
}
static bool fsp_in_reset(struct fsp *fsp)
{
switch (fsp->state) {
case fsp_mbx_hir_seq_done: /* FSP reset triggered */
case fsp_mbx_err: /* Will be reset soon */
case fsp_mbx_rr: /* Mbx activity stopped pending reset */
return true;
default:
return false;
}
}
bool fsp_in_rr(void)
{
struct fsp *fsp = fsp_get_active();
struct fsp_iopath *iop;
if (fsp->active_iopath < 0)
return true;
iop = &fsp->iopath[fsp->active_iopath];
if (fsp_in_reset(fsp) || fsp_in_hir(fsp) || !(psi_check_link_active(iop->psi)))
return true;
return false;
}
static bool fsp_hir_state_timeout(void)
{
u64 now = mftb();
if (tb_compare(now, fsp_hir_timeout) == TB_AAFTERB)
return true;
return false;
}
static void fsp_set_hir_timeout(u32 seconds)
{
u64 now = mftb();
fsp_hir_timeout = now + secs_to_tb(seconds);
}
static bool fsp_crit_op_in_progress(struct fsp *fsp)
{
u32 disr = fsp_rreg(fsp, FSP_DISR_REG);
if (disr & FSP_DISR_CRIT_OP_IN_PROGRESS)
return true;
return false;
}
/* Notify the FSP that it will be reset soon by writing to the DRCR */
static void fsp_prep_for_reset(struct fsp *fsp)
{
u32 drcr;
/*
* Its possible that the FSP went into reset by itself between the
* time the HIR is triggered and we get here. Check and bail out if so.
*/
if (fsp_in_rr())
return;
drcr = fsp_rreg(fsp, FSP_DRCR_REG);
prlog(PR_TRACE, "FSP: Writing reset to DRCR\n");
drcr_last_print = drcr;
fsp_wreg(fsp, FSP_DRCR_REG, (drcr | FSP_PREP_FOR_RESET_CMD));
fsp->state = fsp_mbx_prep_for_reset;
fsp_set_hir_timeout(FSP_DRCR_CLEAR_TIMEOUT);
}
static void fsp_hir_poll(struct fsp *fsp, struct psi *psi)
{
u32 drcr;
if (fsp_in_reset(fsp) || !(psi_check_link_active(psi)))
return;
switch (fsp->state) {
case fsp_mbx_crit_op:
if (fsp_crit_op_in_progress(fsp)) {
if (fsp_hir_state_timeout())
prerror("FSP: Critical operation timeout\n");
/* XXX What do do next? Check with FSP folks */
} else {
fsp_prep_for_reset(fsp);
}
break;
case fsp_mbx_prep_for_reset:
drcr = fsp_rreg(fsp, FSP_DRCR_REG);
if (drcr != drcr_last_print) {
prlog(PR_TRACE, "FSP: DRCR changed, old = %x,"
" new = %x\n",
drcr_last_print, drcr);
drcr_last_print = drcr;
}
if (drcr & FSP_DRCR_ACK_MASK) {
if (fsp_hir_state_timeout()) {
prerror("FSP: Ack timeout. Triggering reset\n");
psi_reset_fsp(psi);
fsp->state = fsp_mbx_hir_seq_done;
}
} else {
prlog(PR_TRACE, "FSP: DRCR ack received."
" Triggering reset\n");
psi_reset_fsp(psi);
fsp->state = fsp_mbx_hir_seq_done;
}
break;
default:
break;
}
}
/*
* This is the main entry for the host initiated reset case.
* This gets called when:
* a. Surveillance ack is not received in 120 seconds
* b. A mailbox command doesn't get a response within the stipulated time.
*/
static void __fsp_trigger_reset(void)
{
struct fsp *fsp = fsp_get_active();
u32 disr;
/* Already in one of the error processing states */
if (fsp_in_hir(fsp) || fsp_in_reset(fsp))
return;
prerror("FSP: fsp_trigger_reset() entry\n");
drcr_last_print = 0;
/*
* Check if we are allowed to reset the FSP. We aren't allowed to
* reset the FSP if the FSP_DISR_DBG_IN_PROGRESS is set.
*/
disr = fsp_rreg(fsp, FSP_DISR_REG);
if (disr & FSP_DISR_DBG_IN_PROGRESS) {
prerror("FSP: Host initiated reset disabled\n");
return;
}
/*
* Check if some critical operation is in progress as indicated
* by FSP_DISR_CRIT_OP_IN_PROGRESS. Timeout is 128 seconds
*/
if (fsp_crit_op_in_progress(fsp)) {
prlog(PR_NOTICE, "FSP: Critical operation in progress\n");
fsp->state = fsp_mbx_crit_op;
fsp_set_hir_timeout(FSP_CRITICAL_OP_TIMEOUT);
} else
fsp_prep_for_reset(fsp);
}
static uint32_t fsp_hir_reason_plid;
void fsp_trigger_reset(uint32_t plid)
{
lock(&fsp_lock);
fsp_hir_reason_plid = plid;
__fsp_trigger_reset();
unlock(&fsp_lock);
}
/*
* Called when we trigger a HIR or when the FSP tells us via the DISR's
* RR bit that one is impending. We should therefore stop all mbox activity.
*/
static void fsp_start_rr(struct fsp *fsp)
{
struct fsp_iopath *iop;
if (fsp->state == fsp_mbx_rr)
return;
/* We no longer have an active path on that FSP */
if (fsp->active_iopath >= 0) {
iop = &fsp->iopath[fsp->active_iopath];
iop->state = fsp_path_bad;
fsp->active_iopath = -1;
}
fsp->state = fsp_mbx_rr;
disr_last_print = 0;
hstate_last_print = 0;
/*
* Mark all command classes as non-busy and clear their
* timeout, then flush all messages in our staging queue
*/
fsp_reset_cmdclass();
/* Notify clients. We have to drop the lock here */
unlock(&fsp_lock);
fsp_notify_rr_state(FSP_RESET_START);
lock(&fsp_lock);
/*
* Unlike earlier, we don't trigger the PSI link polling
* from this point. We wait for the PSI interrupt to tell
* us the FSP is really down and then start the polling there.
*/
}
/*
* Called on normal/quick shutdown to give up the PSI link
*/
void fsp_reset_links(void)
{
struct fsp *fsp = fsp_get_active();
struct fsp_iopath *iop;
if (!fsp)
return;
/* Already in one of the error states? */
if (fsp_in_hir(fsp) || fsp_in_reset(fsp))
return;
iop = &fsp->iopath[fsp->active_iopath];
prlog(PR_NOTICE, "FSP #%d: Host initiated shutdown."
" Giving up the PSI link\n", fsp->index);
psi_disable_link(iop->psi);
return;
}
static void fsp_trace_event(struct fsp *fsp, u32 evt,
u32 data0, u32 data1, u32 data2, u32 data3)
{
union trace tfsp __unused;
#ifdef FSP_TRACE_EVENT
size_t len = sizeof(struct trace_fsp_event);
tfsp.fsp_evt.event = cpu_to_be16(evt);
tfsp.fsp_evt.fsp_state = cpu_to_be16(fsp->state);
tfsp.fsp_evt.data[0] = cpu_to_be32(data0);
tfsp.fsp_evt.data[1] = cpu_to_be32(data1);
tfsp.fsp_evt.data[2] = cpu_to_be32(data2);
tfsp.fsp_evt.data[3] = cpu_to_be32(data3);
trace_add(&tfsp, TRACE_FSP_EVENT, len);
#endif /* FSP_TRACE_EVENT */
}
static void fsp_handle_errors(struct fsp *fsp)
{
u32 hstate;
struct fsp_iopath *iop;
struct psi *psi;
u32 disr;
if (fsp->active_iopath < 0) {
prerror("FSP #%d: fsp_handle_errors() with no active IOP\n",
fsp->index);
return;
}
iop = &fsp->iopath[fsp->active_iopath];
if (!iop->psi) {
prerror("FSP: Active IOP with no PSI link !\n");
return;
}
psi = iop->psi;
/*
* If the link is not up, start R&R immediately, we do call
* psi_disable_link() in this case as while the link might
* not be up, it might still be enabled and the PSI layer
* "active" bit still set
*/
if (!psi_check_link_active(psi)) {
/* Start R&R process */
fsp_trace_event(fsp, TRACE_FSP_EVT_LINK_DOWN, 0, 0, 0, 0);
prerror("FSP #%d: Link down, starting R&R\n", fsp->index);
fsp_start_rr(fsp);
return;
}
/* Link is up, check for other conditions */
disr = fsp_rreg(fsp, FSP_DISR_REG);
/* If in R&R, log values */
if (disr != disr_last_print) {
fsp_trace_event(fsp, TRACE_FSP_EVT_DISR_CHG, disr, 0, 0, 0);
prlog(PR_TRACE, "FSP #%d: DISR stat change = 0x%08x\n",
fsp->index, disr);
disr_last_print = disr;
}
/* On a deferred mbox error, trigger a HIR
* Note: We may never get here since the link inactive case is handled
* above and the other case is when the iop->psi is NULL, which is
* quite rare.
*/
if (fsp->state == fsp_mbx_err) {
uint32_t plid;
plid = log_simple_error(&e_info(OPAL_RC_FSP_MBOX_ERR),
"FSP #%d: Triggering HIR on mbx_err\n",
fsp->index);
fsp_trigger_reset(plid);
return;
}
/*
* If we get here as part of normal flow, the FSP is telling
* us that there will be an impending R&R, so we stop all mbox
* activity. The actual link down trigger is via a PSI
* interrupt that may arrive in due course.
*/
if (disr & FSP_DISR_FSP_IN_RR) {
/*
* If we get here with DEBUG_IN_PROGRESS also set, the
* FSP is in debug and we should *not* reset it now
*/
if (disr & FSP_DISR_DBG_IN_PROGRESS)
return;
/*
* When the linux comes back up, we still see that bit
* set for a bit, so just move on, nothing to see here
*/
if (fsp->state == fsp_mbx_rr)
return;
if (fsp_dpo_pending) {
/*
* If we are about to process a reset when DPO
* is pending, its possible that the host has
* gone down, and OPAL is on its way down and
* hence will not see the subsequent PSI interrupt.
* So, just give up the link here.
*/
prlog(PR_NOTICE, "FSP #%d: FSP reset with DPO pending."
" Giving up PSI link\n",
fsp->index);
psi_disable_link(psi);
} else {
prlog(PR_NOTICE, "FSP #%d: FSP in Reset."
" Waiting for PSI interrupt\n",
fsp->index);
}
fsp_start_rr(fsp);
}
/*
* However, if any of Unit Check or Runtime Termintated or
* Flash Terminated bits is also set, the FSP is asking us
* to trigger a HIR so it can try to recover via the DRCR route.
*/
if (disr & FSP_DISR_HIR_TRIGGER_MASK) {
const char *reason = "Unknown FSP_DISR_HIR_TRIGGER";
uint32_t plid;
fsp_trace_event(fsp, TRACE_FSP_EVT_SOFT_RR, disr, 0, 0, 0);
if (disr & FSP_DISR_FSP_UNIT_CHECK)
reason = "DISR Unit Check set";
else if (disr & FSP_DISR_FSP_RUNTIME_TERM)
reason = "DISR Runtime Terminate set";
else if (disr & FSP_DISR_FSP_FLASH_TERM)
reason = "DISR Flash Terminate set";
plid = log_simple_error(&e_info(OPAL_RC_FSP_DISR_HIR_MASK),
"FSP: %s. Triggering host initiated "
"reset.", reason);
/* Clear all interrupt conditions */
fsp_wreg(fsp, FSP_HDIR_REG, FSP_DBIRQ_ALL);
/* Make sure this happened */
fsp_rreg(fsp, FSP_HDIR_REG);
fsp_trigger_reset(plid);
return;
}
/*
* We detect an R&R complete indication, acknolwedge it
*/
if (disr & FSP_DISR_FSP_RR_COMPLETE) {
/*
* Acking this bit doens't make it go away immediately, so
* only do it while still in R&R state
*/
if (fsp->state == fsp_mbx_rr) {
fsp_trace_event(fsp, TRACE_FSP_EVT_RR_COMPL, 0,0,0,0);
prlog(PR_NOTICE, "FSP #%d: Detected R&R complete,"
" acking\n", fsp->index);
/* Clear HDATA area */
fsp_wreg(fsp, FSP_MBX1_HDATA_AREA, 0xff);
/* Ack it (XDN) and clear HPEND & counts */
fsp_wreg(fsp, FSP_MBX1_HCTL_REG,
FSP_MBX_CTL_PTS |
FSP_MBX_CTL_XDN |
FSP_MBX_CTL_HPEND |
FSP_MBX_CTL_HCSP_MASK |
FSP_MBX_CTL_DCSP_MASK);
/*
* Mark the mbox as usable again so we can process
* incoming messages
*/
fsp->state = fsp_mbx_idle;
/* Also clear R&R complete bit in DISR */
fsp_wreg(fsp, FSP_DISR_REG, FSP_DISR_FSP_RR_COMPLETE);
psi_enable_fsp_interrupt(psi);
}
}
/*
* XXX
*
* Here we detect a number of errors, should we initiate
* and R&R ?
*/
hstate = fsp_rreg(fsp, FSP_HDES_REG);
if (hstate != hstate_last_print) {
fsp_trace_event(fsp, TRACE_FSP_EVT_HDES_CHG, hstate, 0, 0, 0);
prlog(PR_DEBUG, "FSP #%d: HDES stat change = 0x%08x\n",
fsp->index, hstate);
hstate_last_print = hstate;
}
if (hstate == 0xffffffff)
return;
/* Clear errors */
fsp_wreg(fsp, FSP_HDES_REG, FSP_DBERRSTAT_CLR1);
/*
* Most of those errors shouldn't have happened, we just clear
* the error state and return. In the long run, we might want
* to start retrying commands, switching FSPs or links, etc...
*
* We currently don't set our mailbox to a permanent error state.
*/
if (hstate & FSP_DBERRSTAT_ILLEGAL1)
prerror("FSP #%d: Illegal command error !\n", fsp->index);
if (hstate & FSP_DBERRSTAT_WFULL1)
prerror("FSP #%d: Write to a full mbox !\n", fsp->index);
if (hstate & FSP_DBERRSTAT_REMPTY1)
prerror("FSP #%d: Read from an empty mbox !\n", fsp->index);
if (hstate & FSP_DBERRSTAT_PAR1)
prerror("FSP #%d: Parity error !\n", fsp->index);
}
/*
* This is called by fsp_post_msg() to check if the mbox
* is in a state that allows sending of a message
*
* Due to the various "interesting" contexts fsp_post_msg()
* can be called from, including recursive locks from lock
* error messages or console code, this should avoid doing
* anything more complex than checking a bit of state.
*
* Specifically, we cannot initiate an R&R and call back into
* clients etc... from this function.
*
* The best we can do is to se the mbox in error state and
* handle it later during a poll or interrupts.
*/
static bool fsp_check_can_send(struct fsp *fsp)
{
struct fsp_iopath *iop;
struct psi *psi;
/* Look for FSP in non-idle state */
if (fsp->state != fsp_mbx_idle)
return false;
/* Look for an active IO path */
if (fsp->active_iopath < 0)
goto mbox_error;
iop = &fsp->iopath[fsp->active_iopath];
if (!iop->psi) {
prerror("FSP: Active IOP with no PSI link !\n");
goto mbox_error;
}
psi = iop->psi;
/* Check if link has gone down. This will be handled later */
if (!psi_check_link_active(psi)) {
prerror("FSP #%d: Link seems to be down on send\n", fsp->index);
goto mbox_error;
}
/* XXX Do we want to check for other error conditions ? */
return true;
/*
* An error of some case occurred, we'll handle it later
* from a more normal "poll" context
*/
mbox_error:
fsp->state = fsp_mbx_err;
return false;
}
static bool fsp_post_msg(struct fsp *fsp, struct fsp_msg *msg)
{
u32 ctl, reg;
int i, wlen;
prlog(PR_INSANE, "FSP #%d: fsp_post_msg (w0: 0x%08x w1: 0x%08x)\n",
fsp->index, msg->word0, msg->word1);
/* Note: We used to read HCTL here and only modify some of
* the bits in it. This was bogus, because we would write back
* the incoming bits as '1' and clear them, causing fsp_poll()
* to then miss them. Let's just start with 0, which is how
* I suppose the HW intends us to do.
*/
/* Set ourselves as busy */
fsp->pending = msg;
fsp->state = fsp_mbx_send;
msg->state = fsp_msg_sent;
/* We trace after setting the mailbox state so that if the
* tracing recurses, it ends up just queuing the message up
*/
fsp_trace_msg(msg, TRACE_FSP_MSG_OUT);
/* Build the message in the mailbox */
reg = FSP_MBX1_HDATA_AREA;
fsp_wreg(fsp, reg, msg->word0); reg += 4;
fsp_wreg(fsp, reg, msg->word1); reg += 4;
wlen = (msg->dlen + 3) >> 2;
for (i = 0; i < wlen; i++) {
fsp_wreg(fsp, reg, fsp_msg_get_data_word(msg, i));
reg += 4;
}
/* Write the header */
fsp_wreg(fsp, FSP_MBX1_HHDR0_REG, (msg->dlen + 8) << 16);
/* Write the control register */
ctl = 4 << FSP_MBX_CTL_HCHOST_SHIFT;
ctl |= (msg->dlen + 8) << FSP_MBX_CTL_DCHOST_SHIFT;
ctl |= FSP_MBX_CTL_PTS | FSP_MBX_CTL_SPPEND;
prlog(PR_INSANE, " new ctl: %08x\n", ctl);
fsp_wreg(fsp, FSP_MBX1_HCTL_REG, ctl);
return true;
}
static void fsp_poke_queue(struct fsp_cmdclass *cmdclass)
{
struct fsp *fsp = fsp_get_active();
struct fsp_msg *msg;
if (!fsp)
return;
if (!fsp_check_can_send(fsp))
return;
/* From here to the point where fsp_post_msg() sets fsp->state
* to !idle we must not cause any re-entrancy (no debug or trace)
* in a code path that may hit fsp_post_msg() (it's ok to do so
* if we are going to bail out), as we are committed to calling
* fsp_post_msg() and so a re-entrancy could cause us to do a
* double-send into the mailbox.
*/
if (cmdclass->busy || list_empty(&cmdclass->msgq))
return;
msg = list_top(&cmdclass->msgq, struct fsp_msg, link);
assert(msg);
cmdclass->busy = true;
if (!fsp_post_msg(fsp, msg)) {
prerror("FSP #%d: Failed to send message\n", fsp->index);
cmdclass->busy = false;
return;
}
}
static void __fsp_fillmsg(struct fsp_msg *msg, u32 cmd_sub_mod,
u8 add_words, va_list list)
{
bool response = !!(cmd_sub_mod & 0x1000000);
u8 cmd = (cmd_sub_mod >> 16) & 0xff;
u8 sub = (cmd_sub_mod >> 8) & 0xff;
u8 mod = cmd_sub_mod & 0xff;
int i;
msg->word0 = cmd & 0xff;
msg->word1 = mod << 8 | sub;
msg->response = response;
msg->dlen = add_words << 2;
for (i = 0; i < add_words; i++)
fsp_msg_set_data_word(msg, i, va_arg(list, unsigned int));
}
void fsp_fillmsg(struct fsp_msg *msg, u32 cmd_sub_mod, u32 add_words, ...)
{
va_list list;
va_start(list, add_words);
__fsp_fillmsg(msg, cmd_sub_mod, add_words, list);
va_end(list);
}
struct fsp_msg *fsp_mkmsg(u32 cmd_sub_mod, u32 add_words, ...)
{
struct fsp_msg *msg = fsp_allocmsg(!!(cmd_sub_mod & 0x1000000));
va_list list;
if (!msg) {
prerror("FSP: Failed to allocate struct fsp_msg\n");
return NULL;
}
va_start(list, add_words);
__fsp_fillmsg(msg, cmd_sub_mod, add_words, list);
va_end(list);
return msg;
}
/*
* IMPORTANT NOTE: This is *guaranteed* to not call the completion
* routine recusrively for *any* fsp message, either the
* queued one or a previous one. Thus it is *ok* to call
* this function with a lock held which will itself be
* taken by the completion function.
*
* Any change to this implementation must respect this
* rule. This will be especially true of things like
* reset/reload and error handling, if we fail to queue
* we must just return an error, not call any completion
* from the scope of fsp_queue_msg().
*/
int fsp_queue_msg(struct fsp_msg *msg, void (*comp)(struct fsp_msg *msg))
{
struct fsp_cmdclass *cmdclass;
struct fsp *fsp = fsp_get_active();
bool need_unlock;
u16 seq;
int rc = 0;
if (!fsp || !msg)
return -1;
/* Recursive locking */
need_unlock = lock_recursive(&fsp_lock);
/* Grab a new sequence number */
seq = fsp_curseq;
fsp_curseq = fsp_curseq + 1;
if (fsp_curseq == 0)
fsp_curseq = 0x8000;
msg->word0 = (msg->word0 & 0xffff) | seq << 16;
/* Set completion */
msg->complete = comp;
/* Clear response state */
if (msg->resp)
msg->resp->state = fsp_msg_unused;
/* Queue the message in the appropriate queue */
cmdclass = fsp_get_cmdclass(msg);
if (!cmdclass) {
prerror("FSP: Invalid msg in fsp_queue_msg w0/1=0x%08x/%08x\n",
msg->word0, msg->word1);
rc = -1;
goto unlock;
}
msg->state = fsp_msg_queued;
/*
* If we have initiated or about to initiate a reset/reload operation,
* we stash the message on the R&R backup queue. Otherwise, queue it
* normally and poke the HW
*/
if (fsp_in_hir(fsp) || fsp_in_reset(fsp))
list_add_tail(&cmdclass->rr_queue, &msg->link);
else {
list_add_tail(&cmdclass->msgq, &msg->link);
fsp_poke_queue(cmdclass);
}
unlock:
if (need_unlock)
unlock(&fsp_lock);
return rc;
}
/* WARNING: This will drop the FSP lock !!! */
static void fsp_complete_msg(struct fsp_msg *msg)
{
struct fsp_cmdclass *cmdclass = fsp_get_cmdclass(msg);
void (*comp)(struct fsp_msg *msg);
assert(cmdclass);
prlog(PR_INSANE, " completing msg, word0: 0x%08x\n", msg->word0);
comp = msg->complete;
list_del_from(&cmdclass->msgq, &msg->link);
cmdclass->busy = false;
msg->state = fsp_msg_done;
unlock(&fsp_lock);
if (comp)
(*comp)(msg);
lock(&fsp_lock);
}
/* WARNING: This will drop the FSP lock !!! */
static void fsp_complete_send(struct fsp *fsp)
{
struct fsp_msg *msg = fsp->pending;
struct fsp_cmdclass *cmdclass = fsp_get_cmdclass(msg);
assert(msg);
assert(cmdclass);
fsp->pending = NULL;
prlog(PR_INSANE, " completing send, word0: 0x%08x, resp: %d\n",
msg->word0, msg->response);
if (msg->response) {
u64 setbit = fsp_get_class_bit(msg->word0 & 0xff);
msg->state = fsp_msg_wresp;
fsp_cmdclass_resp_bitmask |= setbit;
cmdclass->timesent = mftb();
} else
fsp_complete_msg(msg);
}
static void fsp_alloc_inbound(struct fsp_msg *msg)
{
u16 func_id = fsp_msg_get_data_word(msg, 0) & 0xffff;
u32 len = fsp_msg_get_data_word(msg, 1);
u32 tce_token = 0, act_len = 0;
u8 rc = 0;
void *buf;
struct fsp_msg *resp;
prlog(PR_DEBUG, "FSP: Allocate inbound buffer func: %04x len: %d\n",
func_id, len);
lock(&fsp_lock);
if ((fsp_inbound_off + len) > FSP_INBOUND_SIZE) {
prerror("FSP: Out of space in buffer area !\n");
rc = 0xeb;
goto reply;
}
if (!fsp_inbound_buf) {
fsp_inbound_buf = memalign(TCE_PSIZE, FSP_INBOUND_SIZE);
if (!fsp_inbound_buf) {
prerror("FSP: could not allocate fsp_inbound_buf!\n");
rc = 0xeb;
goto reply;
}
}
buf = fsp_inbound_buf + fsp_inbound_off;
tce_token = PSI_DMA_INBOUND_BUF + fsp_inbound_off;
len = (len + TCE_MASK) & ~TCE_MASK;
fsp_inbound_off += len;
fsp_tce_map(tce_token, buf, len);
prlog(PR_DEBUG, "FSP: -> buffer at 0x%p, TCE: 0x%08x, alen: 0x%x\n",
buf, tce_token, len);
act_len = len;
reply:
unlock(&fsp_lock);
resp = fsp_mkmsg(FSP_RSP_ALLOC_INBOUND | rc, 3, 0, tce_token, act_len);
if (!resp) {
prerror("FSP: response message allocation failed\n");
return;
}
if (fsp_queue_msg(resp, fsp_freemsg)) {
fsp_freemsg(resp);
prerror("FSP: Failed to queue response message\n");
return;
}
}
void *fsp_inbound_buf_from_tce(u32 tce_token)
{
u32 offset = tce_token - PSI_DMA_INBOUND_BUF;
if (tce_token < PSI_DMA_INBOUND_BUF || offset >= fsp_inbound_off) {
prerror("FSP: TCE token 0x%x out of bounds\n", tce_token);
return NULL;
}
return fsp_inbound_buf + offset;
}
static void fsp_repost_queued_msgs_post_rr(void)
{
struct fsp_msg *msg;
int i;
for (i = 0; i <= (FSP_MCLASS_LAST - FSP_MCLASS_FIRST); i++) {
struct fsp_cmdclass *cmdclass = &fsp_cmdclass[i];
bool poke = false;
while(!list_empty(&cmdclass->rr_queue)) {
msg = list_pop(&cmdclass->rr_queue,
struct fsp_msg, link);
list_add_tail(&cmdclass->msgq, &msg->link);
poke = true;
}
if (poke)
fsp_poke_queue(cmdclass);
}
}
static bool fsp_local_command(u32 cmd_sub_mod, struct fsp_msg *msg)
{
u32 cmd = 0;
u32 rsp_data = 0;
struct fsp_msg *resp;
switch(cmd_sub_mod) {
case FSP_CMD_CONTINUE_IPL:
/* We get a CONTINUE_IPL as a response to OPL */
prlog(PR_NOTICE, "FSP: Got CONTINUE_IPL !\n");
ipl_state |= ipl_got_continue;
return true;
case FSP_CMD_HV_STATE_CHG:
prlog(PR_NOTICE, "FSP: Got HV state change request to %d\n",
msg->data.bytes[0]);
/* Send response synchronously for now, we might want to
* deal with that sort of stuff asynchronously if/when
* we add support for auto-freeing of messages
*/
resp = fsp_mkmsg(FSP_RSP_HV_STATE_CHG, 0);
if (!resp)
prerror("FSP: Failed to allocate HV state response\n");
else {
if (fsp_queue_msg(resp, fsp_freemsg)) {
fsp_freemsg(resp);
prerror("FSP: Failed to queue HV state resp\n");
}
}
return true;
case FSP_CMD_SP_NEW_ROLE:
/* FSP is assuming a new role */
prlog(PR_INFO, "FSP: FSP assuming new role\n");
resp = fsp_mkmsg(FSP_RSP_SP_NEW_ROLE, 0);
if (!resp)
prerror("FSP: Failed to allocate SP role response\n");
else {
if (fsp_queue_msg(resp, fsp_freemsg)) {
fsp_freemsg(resp);
prerror("FSP: Failed to queue SP role resp\n");
}
}
ipl_state |= ipl_got_new_role;
return true;
case FSP_CMD_SP_QUERY_CAPS:
prlog(PR_INFO, "FSP: FSP query capabilities\n");
/* XXX Do something saner. For now do a synchronous
* response and hard code our capabilities
*/
resp = fsp_mkmsg(FSP_RSP_SP_QUERY_CAPS, 4, 0x3ff80000, 0, 0, 0);
if (!resp)
prerror("FSP: Failed to allocate CAPS response\n");
else {
if (fsp_queue_msg(resp, fsp_freemsg)) {
fsp_freemsg(resp);
prerror("FSP: Failed to queue CAPS resp\n");
}
}
ipl_state |= ipl_got_caps;
return true;
case FSP_CMD_FSP_FUNCTNAL:
prlog(PR_INFO, "FSP: Got FSP Functional\n");
ipl_state |= ipl_got_fsp_functional;
return true;
case FSP_CMD_ALLOC_INBOUND:
fsp_alloc_inbound(msg);
return true;
case FSP_CMD_SP_RELOAD_COMP:
if (msg->data.bytes[3] & PPC_BIT8(0)) {
fsp_fips_dump_notify(fsp_msg_get_data_word(msg, 1),
fsp_msg_get_data_word(msg, 2));
if (msg->data.bytes[3] & PPC_BIT8(1))
prlog(PR_DEBUG, " PLID is %x\n",
fsp_msg_get_data_word(msg, 3));
}
if (msg->data.bytes[3] & PPC_BIT8(2)) {
prlog(PR_INFO, "FSP: SP Reset/Reload was NOT done\n");
} else {
prlog(PR_INFO, "FSP: SP says Reset/Reload complete\n");
/* Notify clients that the FSP is back up */
fsp_notify_rr_state(FSP_RELOAD_COMPLETE);
fsp_repost_queued_msgs_post_rr();
}
return true;
case FSP_CMD_CLOSE_HMC_INTF:
/* Close the HMC interface */
/* Though Sapphire does not support a HMC connection, the FSP
* sends this message when it is trying to open any new
* hypervisor session. So returning an error 0x51.
*/
cmd = FSP_RSP_CLOSE_HMC_INTF | FSP_STAUS_INVALID_HMC_ID;
rsp_data = msg->data.bytes[0] << 24 | msg->data.bytes[1] << 16;
rsp_data &= 0xffff0000;
resp = fsp_mkmsg(cmd, 1, rsp_data);
if (!resp)
prerror("FSP: Failed to allocate HMC close response\n");
else {
if (fsp_queue_msg(resp, fsp_freemsg)) {
fsp_freemsg(resp);
prerror("FSP: Failed to queue HMC close resp\n");
}
}
return true;
case FSP_CMD_GET_HIR_PLID:
/* Get Platform Log Id with reason for Host Initiated Reset */
prlog(PR_DEBUG, "FSP: Sending PLID 0x%x as HIR reason\n",
fsp_hir_reason_plid);
resp = fsp_mkmsg(FSP_RSP_GET_HIR_PLID, 1, fsp_hir_reason_plid);
if (!resp)
prerror("FSP: Failed to allocate GET_HIR_PLID response\n");
else {
if (fsp_queue_msg(resp, fsp_freemsg)) {
fsp_freemsg(resp);
prerror("FSP: Failed to queue GET_HIR_PLID resp\n");
}
}
fsp_hir_reason_plid = 0;
return true;
}
return false;
}
/* This is called without the FSP lock */
static void fsp_handle_command(struct fsp_msg *msg)
{
struct fsp_cmdclass *cmdclass = fsp_get_cmdclass(msg);
struct fsp_client *client, *next;
struct fsp_msg *resp;
u32 cmd_sub_mod;
if (!cmdclass) {
prerror("FSP: Got message for unknown class %x\n",
msg->word0 & 0xff);
goto free;
}
cmd_sub_mod = (msg->word0 & 0xff) << 16;
cmd_sub_mod |= (msg->word1 & 0xff) << 8;
cmd_sub_mod |= (msg->word1 >> 8) & 0xff;
/* Some commands are handled locally */
if (fsp_local_command(cmd_sub_mod, msg))
goto free;
/* The rest go to clients */
list_for_each_safe(&cmdclass->clientq, client, next, link) {
if (client->message(cmd_sub_mod, msg))
goto free;
}
prerror("FSP: Unhandled message %06x\n", cmd_sub_mod);
/* We don't know whether the message expected some kind of
* response, so we send one anyway
*/
resp = fsp_mkmsg((cmd_sub_mod & 0xffff00) | 0x008020, 0);
if (!resp)
prerror("FSP: Failed to allocate default response\n");
else {
if (fsp_queue_msg(resp, fsp_freemsg)) {
fsp_freemsg(resp);
prerror("FSP: Failed to queue default response\n");
}
}
free:
fsp_freemsg(msg);
}
static void __fsp_fill_incoming(struct fsp *fsp, struct fsp_msg *msg,
int dlen, u32 w0, u32 w1)
{
unsigned int wlen, i, reg;
msg->dlen = dlen - 8;
msg->word0 = w0;
msg->word1 = w1;
wlen = (dlen + 3) >> 2;
reg = FSP_MBX1_FDATA_AREA + 8;
for (i = 0; i < wlen; i++) {
fsp_msg_set_data_word(msg, i, fsp_rreg(fsp, reg));
reg += 4;
}
/* Ack it (XDN) and clear HPEND & counts */
fsp_wreg(fsp, FSP_MBX1_HCTL_REG,
FSP_MBX_CTL_PTS |
FSP_MBX_CTL_XDN |
FSP_MBX_CTL_HPEND |
FSP_MBX_CTL_HCSP_MASK |
FSP_MBX_CTL_DCSP_MASK);
fsp_trace_msg(msg, TRACE_FSP_MSG_IN);
}
static void __fsp_drop_incoming(struct fsp *fsp)
{
/* Ack it (XDN) and clear HPEND & counts */
fsp_wreg(fsp, FSP_MBX1_HCTL_REG,
FSP_MBX_CTL_PTS |
FSP_MBX_CTL_XDN |
FSP_MBX_CTL_HPEND |
FSP_MBX_CTL_HCSP_MASK |
FSP_MBX_CTL_DCSP_MASK);
}
/* WARNING: This will drop the FSP lock */
static void fsp_handle_incoming(struct fsp *fsp)
{
struct fsp_msg *msg;
u32 h0, w0, w1;
unsigned int dlen;
bool special_response = false;
h0 = fsp_rreg(fsp, FSP_MBX1_FHDR0_REG);
dlen = (h0 >> 16) & 0xff;
w0 = fsp_rreg(fsp, FSP_MBX1_FDATA_AREA);
w1 = fsp_rreg(fsp, FSP_MBX1_FDATA_AREA + 4);
prlog(PR_INSANE, " Incoming: w0: 0x%08x, w1: 0x%08x, dlen: %d\n",
w0, w1, dlen);
/* Some responses are expected out of band */
if ((w0 & 0xff) == FSP_MCLASS_HMC_INTFMSG &&
((w1 & 0xff) == 0x8a || ((w1 & 0xff) == 0x8b)))
special_response = true;
/* Check for response bit */
if (w1 & 0x80 && !special_response) {
struct fsp_cmdclass *cmdclass = __fsp_get_cmdclass(w0 & 0xff);
struct fsp_msg *req;
if (!cmdclass) {
prerror("FSP: Got response for unknown class %x\n",
w0 & 0xff);
__fsp_drop_incoming(fsp);
return;
}
if (!cmdclass->busy || list_empty(&cmdclass->msgq)) {
prerror("FSP #%d: Got orphan response! w0 = 0x%08x w1 = 0x%08x\n",
fsp->index, w0, w1);
__fsp_drop_incoming(fsp);
return;
}
req = list_top(&cmdclass->msgq, struct fsp_msg, link);
/* Check if the response seems to match the message */
if (req->state != fsp_msg_wresp ||
(req->word0 & 0xff) != (w0 & 0xff) ||
(req->word1 & 0xff) != (w1 & 0x7f)) {
__fsp_drop_incoming(fsp);
prerror("FSP #%d: Response doesn't match pending msg. w0 = 0x%08x w1 = 0x%08x\n",
fsp->index, w0, w1);
return;
} else {
u64 resetbit = ~fsp_get_class_bit(req->word0 & 0xff);
fsp_cmdclass_resp_bitmask &= resetbit;
cmdclass->timesent = 0;
}
/* Allocate response if needed XXX We need to complete
* the original message with some kind of error here ?
*/
if (!req->resp) {
req->resp = __fsp_allocmsg();
if (!req->resp) {
__fsp_drop_incoming(fsp);
prerror("FSP #%d: Failed to allocate response\n",
fsp->index);
return;
}
}
/* Populate and complete (will drop the lock) */
req->resp->state = fsp_msg_response;
__fsp_fill_incoming(fsp, req->resp, dlen, w0, w1);
fsp_complete_msg(req);
return;
}
/* Allocate an incoming message */
msg = __fsp_allocmsg();
if (!msg) {
__fsp_drop_incoming(fsp);
prerror("FSP #%d: Failed to allocate incoming msg\n",
fsp->index);
return;
}
msg->state = fsp_msg_incoming;
__fsp_fill_incoming(fsp, msg, dlen, w0, w1);
/* Handle FSP commands. This can recurse into fsp_queue_msg etc.. */
unlock(&fsp_lock);
fsp_handle_command(msg);
lock(&fsp_lock);
}
static void fsp_check_queues(struct fsp *fsp)
{
int i;
/* XXX In the long run, we might want to have a queue of
* classes waiting to be serviced to speed this up, either
* that or a bitmap.
*/
for (i = 0; i <= (FSP_MCLASS_LAST - FSP_MCLASS_FIRST); i++) {
struct fsp_cmdclass *cmdclass = &fsp_cmdclass[i];
if (fsp->state != fsp_mbx_idle)
break;
if (cmdclass->busy || list_empty(&cmdclass->msgq))
continue;
fsp_poke_queue(cmdclass);
}
}
static void __fsp_poll(bool interrupt)
{
struct fsp_iopath *iop;
struct fsp *fsp = fsp_get_active();
u32 ctl, hdir = 0;
bool psi_irq;
/*
* The tracer isn't terribly efficient at detecting dups
* especially when coming from multiple CPUs so we do our
* own change-detection locally
*/
static u32 hdir_last_trace;
static u32 ctl_last_trace;
static bool psi_irq_last_trace;
static bool irq_last_trace;
if (!fsp)
return;
/* Crazy interrupt handling scheme:
*
* In order to avoid "losing" interrupts when polling the mbox
* we only clear interrupt conditions when called as a result of
* an interrupt.
*
* That way, if a poll clears, for example, the HPEND condition,
* the interrupt remains, causing a dummy interrupt later on
* thus allowing the OS to be notified of a state change (ie it
* doesn't need every poll site to monitor every state change).
*
* However, this scheme is complicated by the fact that we need
* to clear the interrupt condition after we have cleared the
* original condition in HCTL, and we might have long stale
* interrupts which we do need to eventually get rid of. However
* clearing interrupts in such a way is racy, so we need to loop
* and re-poll HCTL after having done so or we might miss an
* event. It's a latency risk, but unlikely and probably worth it.
*/
again:
if (fsp->active_iopath < 0) {
/* That should never happen */
if (interrupt && (fsp->state != fsp_mbx_rr))
prerror("FSP: Interrupt with no working IO path\n");
return;
}
iop = &fsp->iopath[fsp->active_iopath];
/* Check for error state and handle R&R completion */
fsp_handle_errors(fsp);
/* Handle host initiated resets */
if (fsp_in_hir(fsp)) {
fsp_hir_poll(fsp, iop->psi);
return;
}
/*
* The above might have triggered and R&R, check that we
* are still functional
*/
if ((fsp->active_iopath < 0) || fsp_in_hir(fsp))
return;
iop = &fsp->iopath[fsp->active_iopath];
/* Read interrupt status (we may or may not use it) */
hdir = fsp_rreg(fsp, FSP_HDIR_REG);
/* Read control now as well so we can trace them */
ctl = fsp_rreg(fsp, FSP_MBX1_HCTL_REG);
/* Ditto with PSI irq state */
psi_irq = psi_poll_fsp_interrupt(iop->psi);
/* Trace it if anything changes */
if (hdir != hdir_last_trace || ctl != ctl_last_trace ||
interrupt != irq_last_trace || psi_irq != psi_irq_last_trace) {
fsp_trace_event(fsp, TRACE_FSP_EVT_POLL_IRQ,
interrupt, hdir, ctl, psi_irq);
hdir_last_trace = hdir;
ctl_last_trace = ctl;
irq_last_trace = interrupt;
psi_irq_last_trace = psi_irq;
}
/*
* We *MUST* ignore the MBOX2 bits here. While MBOX2 cannot generate
* interrupt, it might still latch some bits here (and we found cases
* where the MBOX2 XUP would be set). If that happens, clearing HDIR
* never works (the bit gets set again immediately) because we don't
* clear the condition in HTCL2 and thus we loop forever.
*/
hdir &= FSP_DBIRQ_MBOX1;
/*
* Sanity check: If an interrupt is pending and we are in polling
* mode, check that the PSI side is also pending. If some bit is
* set, just clear and move on.
*/
if (hdir && !interrupt && !psi_irq) {
prerror("FSP: WARNING ! HDIR 0x%08x but no PSI irq !\n", hdir);
fsp_wreg(fsp, FSP_HDIR_REG, hdir);
}
/*
* We should never have the mbox in error state here unless it
* was fine until some printf inside fsp_handle_errors() caused
* the console to poke the FSP which detected a branch new error
* in the process. Let's be safe rather than sorry and handle that
* here
*/
if (fsp_in_hir(fsp) || fsp->state == fsp_mbx_err) {
prerror("FSP: Late error state detection\n");
goto again;
}
/*
* If we are in an R&R state with an active IO path, we
* shouldn't be getting interrupts. If we do, just clear
* the condition and print a message
*/
if (fsp->state == fsp_mbx_rr) {
if (interrupt) {
prerror("FSP: Interrupt in RR state [HDIR=0x%08x]\n",
hdir);
fsp_wreg(fsp, FSP_HDIR_REG, hdir);
}
return;
}
/* Poll FSP CTL */
if (ctl & (FSP_MBX_CTL_XUP | FSP_MBX_CTL_HPEND))
prlog(PR_INSANE, "FSP #%d: poll, ctl: %x\n", fsp->index, ctl);
/* Do we have a pending message waiting to complete ? */
if (ctl & FSP_MBX_CTL_XUP) {
fsp_wreg(fsp, FSP_MBX1_HCTL_REG, FSP_MBX_CTL_XUP);
if (fsp->state == fsp_mbx_send) {
/* mbox is free */
fsp->state = fsp_mbx_idle;
/* Complete message (will break the lock) */
fsp_complete_send(fsp);
/* Lock can have been broken, so ctl is now
* potentially invalid, let's recheck
*/
goto again;
} else {
prerror("FSP #%d: Got XUP with no pending message !\n",
fsp->index);
}
}
if (fsp->state == fsp_mbx_send) {
/* XXX Handle send timeouts!!! */
}
/* Is there an incoming message ? This will break the lock as well */
if (ctl & FSP_MBX_CTL_HPEND)
fsp_handle_incoming(fsp);
/* Note: Lock may have been broken above, thus ctl might be invalid
* now, don't use it any further.
*/
/* Check for something else to send */
if (fsp->state == fsp_mbx_idle)
fsp_check_queues(fsp);
/* Clear interrupts, and recheck HCTL if any occurred */
if (interrupt && hdir) {
fsp_wreg(fsp, FSP_HDIR_REG, hdir);
goto again;
}
}
void fsp_interrupt(void)
{
lock(&fsp_lock);
__fsp_poll(true);
unlock(&fsp_lock);
}
int fsp_sync_msg(struct fsp_msg *msg, bool autofree)
{
int rc;
rc = fsp_queue_msg(msg, NULL);
if (rc)
goto bail;
while(fsp_msg_busy(msg)) {
if (fsp_in_rr()) {
fsp_cancelmsg(msg);
rc = -1;
goto bail;
}
cpu_relax();
opal_run_pollers();
}
switch(msg->state) {
case fsp_msg_done:
rc = 0;
break;
case fsp_msg_timeout:
rc = -1; /* XXX to improve */
break;
default:
rc = -1; /* Should not happen... (assert ?) */
}
if (msg->resp)
rc = (msg->resp->word1 >> 8) & 0xff;
bail:
if (autofree)
fsp_freemsg(msg);
return rc;
}
void fsp_register_client(struct fsp_client *client, u8 msgclass)
{
struct fsp_cmdclass *cmdclass = __fsp_get_cmdclass(msgclass);
if (!fsp_present())
return;
assert(cmdclass);
list_add_tail(&cmdclass->clientq, &client->link);
}
void fsp_unregister_client(struct fsp_client *client, u8 msgclass)
{
struct fsp_cmdclass *cmdclass = __fsp_get_cmdclass(msgclass);
if (!fsp_present())
return;
assert(cmdclass);
list_del_from(&cmdclass->clientq, &client->link);
}
static int fsp_init_mbox(struct fsp *fsp)
{
unsigned int i;
u32 reg;
/*
* Note: The documentation contradicts itself as to
* whether the HDIM bits should be set or cleared to
* enable interrupts
*
* This seems to work...
*/
/* Mask all interrupts */
fsp_wreg(fsp, FSP_HDIM_CLR_REG, FSP_DBIRQ_ALL);
/* Clear all errors */
fsp_wreg(fsp, FSP_HDES_REG, FSP_DBERRSTAT_CLR1 | FSP_DBERRSTAT_CLR2);
/* Initialize data area as the doco says */
for (i = 0; i < 0x40; i += 4)
fsp_wreg(fsp, FSP_MBX1_HDATA_AREA + i, 0);
/*
* Clear whatever crap may remain in HDCR. Do not write XDN as that
* would be interpreted incorrectly as an R&R completion which
* we aren't ready to send yet !
*/
fsp_wreg(fsp, FSP_MBX1_HCTL_REG, FSP_MBX_CTL_XUP | FSP_MBX_CTL_HPEND |
FSP_MBX_CTL_HCSP_MASK | FSP_MBX_CTL_DCSP_MASK |
FSP_MBX_CTL_PTS);
/* Clear all pending interrupts */
fsp_wreg(fsp, FSP_HDIR_REG, FSP_DBIRQ_ALL);
/* Enable all mbox1 interrupts */
fsp_wreg(fsp, FSP_HDIM_SET_REG, FSP_DBIRQ_MBOX1);
/* Decode what FSP we are connected to */
reg = fsp_rreg(fsp, FSP_SCRATCH0_REG);
if (reg & PPC_BIT32(0)) { /* Is it a valid connection */
if (reg & PPC_BIT32(3))
prlog(PR_INFO, "FSP: Connected to FSP-B\n");
else
prlog(PR_INFO, "FSP: Connected to FSP-A\n");
}
return 0;
}
/* We use a single fixed TCE table for all PSI interfaces */
static void fsp_init_tce_table(void)
{
fsp_tce_table = (__be64 *)PSI_TCE_TABLE_BASE;
memset(fsp_tce_table, 0, PSI_TCE_TABLE_SIZE);
}
void fsp_tce_map(u32 offset, void *addr, u32 size)
{
u64 raddr = (u64)addr;
assert(!(offset & TCE_MASK));
assert(!(raddr & TCE_MASK));
assert(!(size & TCE_MASK));
size >>= TCE_SHIFT;
offset >>= TCE_SHIFT;
while(size--) {
fsp_tce_table[offset++] = cpu_to_be64(raddr | 0x3);
raddr += TCE_PSIZE;
}
}
void fsp_tce_unmap(u32 offset, u32 size)
{
assert(!(offset & TCE_MASK));
assert(!(size & TCE_MASK));
size >>= TCE_SHIFT;
offset >>= TCE_SHIFT;
while(size--)
fsp_tce_table[offset++] = 0;
}
static struct fsp *fsp_find_by_index(int index)
{
struct fsp *fsp = first_fsp;
do {
if (fsp->index == index)
return fsp;
} while (fsp->link != first_fsp);
return NULL;
}
static void fsp_init_links(struct dt_node *fsp_node)
{
const struct dt_property *linksprop;
int i, index;
struct fsp *fsp;
struct fsp_iopath *fiop;
linksprop = dt_find_property(fsp_node, "ibm,psi-links");
assert(linksprop);
index = dt_prop_get_u32(fsp_node, "reg");
fsp = fsp_find_by_index(index);
if (!fsp) {
prerror("FSP: FSP with index %d not found\n", index);
return;
}
fsp->state = fsp_mbx_idle;
/* Iterate all links */
for (i = 0; i < fsp->iopath_count; i++) {
u64 reg;
u32 link;
link = dt_property_get_cell(linksprop, i);
fiop = &fsp->iopath[i];
fiop->psi = psi_find_link(link);
if (fiop->psi == NULL) {
prerror("FSP #%d: Couldn't find PSI link\n",
fsp->index);
continue;
}
prlog(PR_DEBUG, "FSP #%d: Found PSI HB link to chip %d\n",
fsp->index, link);
psi_fsp_link_in_use(fiop->psi);
/* Get the FSP register window */
reg = in_be64(fiop->psi->regs + PSIHB_FSPBAR);
fiop->fsp_regs = (void *)(reg | (1ULL << 63) |
dt_prop_get_u32(fsp_node, "reg-offset"));
}
}
static void fsp_update_links_states(struct fsp *fsp)
{
struct fsp_iopath *fiop;
unsigned int i;
/* Iterate all links */
for (i = 0; i < fsp->iopath_count; i++) {
fiop = &fsp->iopath[i];
if (!fiop->psi)
fiop->state = fsp_path_bad;
else if (fiop->psi->active) {
fsp->active_iopath = i;
fiop->state = fsp_path_active;
} else
fiop->state = fsp_path_backup;
}
if (fsp->active_iopath >= 0) {
if (!active_fsp || (active_fsp != fsp))
active_fsp = fsp;
fsp_inbound_off = 0;
fiop = &fsp->iopath[fsp->active_iopath];
psi_init_for_fsp(fiop->psi);
fsp_init_mbox(fsp);
}
}
void fsp_reinit_fsp(void)
{
struct fsp *fsp;
/* Notify all FSPs to check for an updated link state */
for (fsp = first_fsp; fsp; fsp = fsp->link)
fsp_update_links_states(fsp);
}
static void fsp_create_fsp(struct dt_node *fsp_node)
{
const struct dt_property *linksprop;
struct fsp *fsp;
int count, index;
index = dt_prop_get_u32(fsp_node, "reg");
prlog(PR_INFO, "FSP #%d: Found in device-tree, setting up...\n",
index);
linksprop = dt_find_property(fsp_node, "ibm,psi-links");
if (!linksprop || linksprop->len < 4) {
prerror("FSP #%d: No links !\n", index);
return;
}
fsp = zalloc(sizeof(struct fsp));
if (!fsp) {
prerror("FSP #%d: Can't allocate memory !\n", index);
return;
}
fsp->index = index;
fsp->active_iopath = -1;
count = linksprop->len / 4;
prlog(PR_DEBUG, "FSP #%d: Found %d IO PATH\n", index, count);
if (count > FSP_MAX_IOPATH) {
prerror("FSP #%d: WARNING, limited to %d IO PATH\n",
index, FSP_MAX_IOPATH);
count = FSP_MAX_IOPATH;
}
fsp->iopath_count = count;
fsp->link = first_fsp;
first_fsp = fsp;
fsp_init_links(fsp_node);
fsp_update_links_states(fsp);
if (fsp->active_iopath >= 0)
psi_enable_fsp_interrupt(fsp->iopath[fsp->active_iopath].psi);
}
static void fsp_opal_poll(void *data __unused)
{
/* Test the host initiated reset */
if (hir_trigger == 0xdeadbeef) {
uint32_t plid = log_simple_error(&e_info(OPAL_INJECTED_HIR),
"SURV: Injected HIR, initiating FSP R/R\n");
fsp_trigger_reset(plid);
hir_trigger = 0;
}
if (try_lock(&fsp_lock)) {
__fsp_poll(false);
unlock(&fsp_lock);
}
}
int fsp_fatal_msg(struct fsp_msg *msg)
{
int rc = 0;
rc = fsp_queue_msg(msg, NULL);
if (rc)
return rc;
while(fsp_msg_busy(msg)) {
if (fsp_in_rr()) {
fsp_cancelmsg(msg);
return -1;
}
cpu_relax();
fsp_opal_poll(NULL);
}
switch(msg->state) {
case fsp_msg_done:
rc = 0;
break;
case fsp_msg_timeout:
rc = -1; /* XXX to improve */
break;
default:
rc = -1; /* Should not happen... (assert ?) */
}
if (msg->resp)
rc = (msg->resp->word1 >> 8) & 0xff;
return rc;
}
static bool fsp_init_one(const char *compat)
{
struct dt_node *fsp_node;
bool inited = false;
dt_for_each_compatible(dt_root, fsp_node, compat) {
if (!inited) {
int i;
/* Initialize the per-class msg queues */
for (i = 0;
i <= (FSP_MCLASS_LAST - FSP_MCLASS_FIRST); i++) {
list_head_init(&fsp_cmdclass[i].msgq);
list_head_init(&fsp_cmdclass[i].clientq);
list_head_init(&fsp_cmdclass[i].rr_queue);
}
/* Init the queues for RR notifier cmdclass */
list_head_init(&fsp_cmdclass_rr.msgq);
list_head_init(&fsp_cmdclass_rr.clientq);
list_head_init(&fsp_cmdclass_rr.rr_queue);
/* Register poller */
opal_add_poller(fsp_opal_poll, NULL);
inited = true;
}
/* Create the FSP data structure */
fsp_create_fsp(fsp_node);
}
return inited;
}
void fsp_init(void)
{
prlog(PR_DEBUG, "FSP: Looking for FSP...\n");
fsp_init_tce_table();
if (!fsp_init_one("ibm,fsp1") && !fsp_init_one("ibm,fsp2")) {
prlog(PR_DEBUG, "FSP: No FSP on this machine\n");
return;
}
}
bool fsp_present(void)
{
return first_fsp != NULL;
}
static void fsp_timeout_poll(void *data __unused)
{
u64 now = mftb();
u64 timeout_val = 0;
u64 cmdclass_resp_bitmask = fsp_cmdclass_resp_bitmask;
struct fsp_cmdclass *cmdclass = NULL;
struct fsp_msg *req = NULL;
u32 index = 0;
if (timeout_timer == 0)
timeout_timer = now + secs_to_tb(30);
/* The lowest granularity for a message timeout is 30 secs.
* So every 30secs, check if there is any message
* waiting for a response from the FSP
*/
if (tb_compare(now, timeout_timer) == TB_ABEFOREB)
return;
if (!try_lock(&fsp_poll_lock))
return;
if (tb_compare(now, timeout_timer) == TB_ABEFOREB) {
unlock(&fsp_poll_lock);
return;
}
while (cmdclass_resp_bitmask) {
u64 time_sent = 0;
u64 time_to_comp = 0;
if (!(cmdclass_resp_bitmask & 0x1))
goto next_bit;
cmdclass = &fsp_cmdclass[index];
timeout_val = secs_to_tb((cmdclass->timeout) * 60);
time_sent = cmdclass->timesent;
time_to_comp = now - cmdclass->timesent;
/* Now check if the response has timed out */
if (tb_compare(time_to_comp, timeout_val) == TB_AAFTERB) {
u32 w0, w1;
enum fsp_msg_state mstate;
/* Take the FSP lock now and re-check */
lock(&fsp_lock);
if (!(fsp_cmdclass_resp_bitmask & (1ull << index)) ||
time_sent != cmdclass->timesent) {
unlock(&fsp_lock);
goto next_bit;
}
req = list_top(&cmdclass->msgq, struct fsp_msg, link);
if (!req) {
printf("FSP: Timeout state mismatch on class %d\n",
index);
fsp_cmdclass_resp_bitmask &= ~(1ull << index);
cmdclass->timesent = 0;
unlock(&fsp_lock);
goto next_bit;
}
w0 = req->word0;
w1 = req->word1;
mstate = req->state;
prlog(PR_WARNING, "FSP: Response from FSP timed out,"
" cmd = %x subcmd = %x mod = %x state: %d\n",
w0 & 0xff, w1 & 0xff, (w1 >> 8) & 0xff, mstate);
fsp_reg_dump();
fsp_cmdclass_resp_bitmask &= ~(1ull << index);
cmdclass->timesent = 0;
if (req->resp) {
req->resp->state = fsp_msg_timeout;
req->resp->word1 = (FSP_STATUS_BUSY << 8) |
(req->resp->word1 & 0xff);
}
fsp_complete_msg(req);
__fsp_trigger_reset();
unlock(&fsp_lock);
fsp_hir_reason_plid = log_simple_error(
&e_info(OPAL_RC_FSP_POLL_TIMEOUT),
"FSP: Response from FSP timed out,"
" cmd = %x subcmd = %x mod = %x state: %d\n",
w0 & 0xff, w1 & 0xff, (w1 >> 8) & 0xff, mstate);
}
next_bit:
cmdclass_resp_bitmask = cmdclass_resp_bitmask >> 1;
index++;
}
unlock(&fsp_poll_lock);
}
void fsp_opl(void)
{
struct dt_node *iplp;
if (!fsp_present())
return;
/* Send OPL */
ipl_state |= ipl_opl_sent;
fsp_sync_msg(fsp_mkmsg(FSP_CMD_OPL, 0), true);
while(!(ipl_state & ipl_got_continue)) {
opal_run_pollers();
cpu_relax();
}
/* Send continue ACK */
fsp_sync_msg(fsp_mkmsg(FSP_CMD_CONTINUE_ACK, 0), true);
/* Wait for various FSP messages */
prlog(PR_INFO, "INIT: Waiting for FSP to advertise new role...\n");
while(!(ipl_state & ipl_got_new_role)) {
cpu_relax();
opal_run_pollers();
}
prlog(PR_INFO, "INIT: Waiting for FSP to request capabilities...\n");
while(!(ipl_state & ipl_got_caps)) {
cpu_relax();
opal_run_pollers();
}
/* Initiate the timeout poller */
opal_add_poller(fsp_timeout_poll, NULL);
/* Tell FSP we are in standby */
prlog(PR_INFO, "INIT: Sending HV Functional: Standby...\n");
fsp_sync_msg(fsp_mkmsg(FSP_CMD_HV_FUNCTNAL, 1, 0x01000000), true);
/* Wait for FSP functional */
prlog(PR_INFO, "INIT: Waiting for FSP functional\n");
while(!(ipl_state & ipl_got_fsp_functional)) {
cpu_relax();
opal_run_pollers();
}
/* Tell FSP we are in running state */
prlog(PR_INFO, "INIT: Sending HV Functional: Runtime...\n");
fsp_sync_msg(fsp_mkmsg(FSP_CMD_HV_FUNCTNAL, 1, 0x02000000), true);
/*
* For the factory reset case, FSP sends us the PCI Bus
* Reset request. We don't have to do anything special with
* PCI bus numbers here; just send the Power Down message
* with modifier 0x02 to FSP.
*/
iplp = dt_find_by_path(dt_root, "ipl-params/ipl-params");
if (iplp && dt_find_property(iplp, "pci-busno-reset-ipl")) {
prlog(PR_DEBUG, "INIT: PCI Bus Reset requested."
" Sending Power Down\n");
fsp_sync_msg(fsp_mkmsg(FSP_CMD_POWERDOWN_PCIRS, 0), true);
}
/*
* Tell FSP we are in running state with all partitions.
*
* This is need otherwise the FSP will not reset it's reboot count
* on failures. Ideally we should send that when we know the
* OS is up but we don't currently have a very good way to do
* that so this will do as a stop-gap
*/
prlog(PR_NOTICE, "INIT: Sending HV Functional: Runtime all partitions\n");
fsp_sync_msg(fsp_mkmsg(FSP_CMD_HV_FUNCTNAL, 1, 0x04000000), true);
}
uint32_t fsp_adjust_lid_side(uint32_t lid_no)
{
struct dt_node *iplp;
const char *side = NULL;
iplp = dt_find_by_path(dt_root, "ipl-params/ipl-params");
if (iplp)
side = dt_prop_get_def(iplp, "cec-ipl-side", NULL);
if (!side || !strcmp(side, "temp"))
lid_no |= ADJUST_T_SIDE_LID_NO;
return lid_no;
}
struct fsp_fetch_lid_item {
enum resource_id id;
uint32_t idx;
uint32_t lid;
uint32_t lid_no;
uint64_t bsize;
uint32_t offset;
void *buffer;
size_t *length;
size_t remaining;
size_t chunk_requested;
struct list_node link;
int result;
};
/*
* We have a queue of things to fetch
* when fetched, it moves to fsp_fetched_lid until we're asked if it
* has been fetched, in which case it's free()d.
*
* Everything is protected with fsp_fetch_lock.
*
* We use PSI_DMA_FETCH TCE entry for this fetching queue. If something
* is in the fsp_fetch_lid_queue, it means we're using this TCE entry!
*
* If we add the first entry to fsp_fetch_lid_queue, we trigger fetching!
*/
static LIST_HEAD(fsp_fetch_lid_queue);
static LIST_HEAD(fsp_fetched_lid);
static struct lock fsp_fetch_lock = LOCK_UNLOCKED;
/*
* Asynchronous fsp fetch data call
*
* Note:
* buffer = PSI DMA address space
*/
int fsp_fetch_data_queue(uint8_t flags, uint16_t id, uint32_t sub_id,
uint32_t offset, void *buffer, size_t *length,
void (*comp)(struct fsp_msg *msg))
{
struct fsp_msg *msg;
uint32_t chunk = *length;
if (!comp)
return OPAL_PARAMETER;
msg = fsp_mkmsg(FSP_CMD_FETCH_SP_DATA, 0x6, flags << 16 | id,
sub_id, offset, 0, buffer, chunk);
if (!msg) {
prerror("FSP: allocation failed!\n");
return OPAL_INTERNAL_ERROR;
}
if (fsp_queue_msg(msg, comp)) {
fsp_freemsg(msg);
prerror("FSP: Failed to queue fetch data message\n");
return OPAL_INTERNAL_ERROR;
}
return OPAL_SUCCESS;
}
#define CAPP_IDX_VENICE_DD10 0x100ea
#define CAPP_IDX_VENICE_DD20 0x200ea
#define CAPP_IDX_MURANO_DD20 0x200ef
#define CAPP_IDX_MURANO_DD21 0x201ef
#define CAPP_IDX_NAPLES_DD10 0x100d3
#define CAPP_IDX_NIMBUS_DD10 0x100d1
#define CAPP_IDX_NIMBUS_DD20 0x200d1
#define CAPP_IDX_NIMBUS_DD21 0x201d1
#define CAPP_IDX_NIMBUS_DD22 0x202d1
#define CAPP_IDX_NIMBUS_DD23 0x203d1
#define IMA_CATALOG_NIMBUS 0x4e0200
#define IMA_CATALOG_P10_DD1 0x800100
#define IMA_CATALOG_P10_DD2 0x800200
static struct {
enum resource_id id;
uint32_t idx;
uint32_t lid_no;
} fsp_lid_map[] = {
{ RESOURCE_ID_KERNEL, RESOURCE_SUBID_NONE, KERNEL_LID_OPAL },
{ RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE, INITRAMFS_LID_OPAL },
{ RESOURCE_ID_IMA_CATALOG,IMA_CATALOG_NIMBUS, 0x80f00103 },
{ RESOURCE_ID_CAPP, CAPP_IDX_MURANO_DD20, 0x80a02002 },
{ RESOURCE_ID_CAPP, CAPP_IDX_MURANO_DD21, 0x80a02001 },
{ RESOURCE_ID_CAPP, CAPP_IDX_VENICE_DD10, 0x80a02003 },
{ RESOURCE_ID_CAPP, CAPP_IDX_VENICE_DD20, 0x80a02004 },
{ RESOURCE_ID_CAPP, CAPP_IDX_NAPLES_DD10, 0x80a02005 },
{ RESOURCE_ID_CAPP, CAPP_IDX_NIMBUS_DD10, 0x80a02006 },
{ RESOURCE_ID_CAPP, CAPP_IDX_NIMBUS_DD20, 0x80a02007 },
{ RESOURCE_ID_CAPP, CAPP_IDX_NIMBUS_DD21, 0x80a02007 },
{ RESOURCE_ID_CAPP, CAPP_IDX_NIMBUS_DD22, 0x80a02007 },
{ RESOURCE_ID_CAPP, CAPP_IDX_NIMBUS_DD23, 0x80a02007 },
{ RESOURCE_ID_IMA_CATALOG,IMA_CATALOG_P10_DD1, 0x80f00103 },
{ RESOURCE_ID_IMA_CATALOG,IMA_CATALOG_P10_DD2, 0x80f00103 },
};
static void fsp_start_fetching_next_lid(void);
static void fsp_fetch_lid_next_chunk(struct fsp_fetch_lid_item *last);
static void fsp_fetch_lid_complete(struct fsp_msg *msg)
{
struct fsp_fetch_lid_item *last;
uint32_t woffset, wlen;
uint8_t rc;
lock(&fsp_fetch_lock);
last = list_top(&fsp_fetch_lid_queue, struct fsp_fetch_lid_item, link);
fsp_tce_unmap(PSI_DMA_FETCH, last->bsize);
woffset = fsp_msg_get_data_word(msg->resp, 1);
wlen = fsp_msg_get_data_word(msg->resp, 2);
rc = (msg->resp->word1 >> 8) & 0xff;
/* Fall back to a PHYP LID for kernel loads */
if (rc && last->lid_no == KERNEL_LID_OPAL) {
const char *ltype = dt_prop_get_def(dt_root, "lid-type", NULL);
if (!ltype || strcmp(ltype, "opal")) {
prerror("Failed to load in OPAL mode...\n");
last->result = OPAL_PARAMETER;
last = list_pop(&fsp_fetch_lid_queue,
struct fsp_fetch_lid_item, link);
list_add_tail(&fsp_fetched_lid, &last->link);
fsp_start_fetching_next_lid();
unlock(&fsp_fetch_lock);
return;
}
printf("Trying to load as PHYP LID...\n");
last->lid = KERNEL_LID_PHYP;
/* Retry with different LID */
fsp_fetch_lid_next_chunk(last);
}
if (rc !=0 && rc != 2) {
last->result = -EIO;
last = list_pop(&fsp_fetch_lid_queue, struct fsp_fetch_lid_item, link);
prerror("FSP LID %08x load ERROR %d\n", last->lid_no, rc);
list_add_tail(&fsp_fetched_lid, &last->link);
fsp_start_fetching_next_lid();
unlock(&fsp_fetch_lock);
return;
}
/*
* As per documentation, rc=2 means end of file not reached and
* rc=1 means we reached end of file. But it looks like we always
* get rc=0 irrespective of whether end of file is reached or not.
* The old implementation (fsp_sync_msg) used to rely on
* (wlen < chunk) to decide whether we reached end of file.
*
* Ideally FSP folks should be fix their code as per documentation.
* but until they do, adding the old check (hack) here again.
*
* Without this hack some systems would load partial lid and won't
* be able to boot into petitboot kernel.
*/
if (rc == 0 && (wlen < last->chunk_requested))
last->result = OPAL_SUCCESS;
fsp_freemsg(msg);
last->remaining -= wlen;
*(last->length) += wlen;
last->buffer += wlen;
last->offset += wlen;
prlog(PR_DEBUG, "FSP: LID %x Chunk read -> rc=0x%02x off: %08x"
" twritten: %08x\n", last->lid, rc, woffset, wlen);
fsp_fetch_lid_next_chunk(last);
unlock(&fsp_fetch_lock);
}
static void fsp_fetch_lid_next_chunk(struct fsp_fetch_lid_item *last)
{
uint64_t baddr;
uint64_t balign, boff;
uint32_t chunk;
uint32_t taddr;
struct fsp_msg *msg;
uint8_t flags = 0;
uint16_t id = FSP_DATASET_NONSP_LID;
uint32_t sub_id;
assert(lock_held_by_me(&fsp_fetch_lock));
if (last->remaining == 0 || last->result == OPAL_SUCCESS) {
last->result = OPAL_SUCCESS;
last = list_pop(&fsp_fetch_lid_queue,
struct fsp_fetch_lid_item, link);
list_add_tail(&fsp_fetched_lid, &last->link);
fsp_start_fetching_next_lid();
return;
}
baddr = (uint64_t)last->buffer;
balign = baddr & ~TCE_MASK;
boff = baddr & TCE_MASK;
chunk = last->remaining;
if (chunk > (PSI_DMA_FETCH_SIZE - boff))
chunk = PSI_DMA_FETCH_SIZE - boff;
last->bsize = ((boff + chunk) + TCE_MASK) & ~TCE_MASK;
last->chunk_requested = chunk;
prlog(PR_DEBUG, "FSP: LID %08x chunk 0x%08x bytes balign=%llx"
" boff=%llx bsize=%llx\n",
last->lid_no, chunk, balign, boff, last->bsize);
fsp_tce_map(PSI_DMA_FETCH, (void *)balign, last->bsize);
taddr = PSI_DMA_FETCH + boff;
sub_id = last->lid;
msg = fsp_mkmsg(FSP_CMD_FETCH_SP_DATA, 6,
flags << 16 | id, sub_id, last->offset,
0, taddr, chunk);
if (fsp_queue_msg(msg, fsp_fetch_lid_complete)) {
fsp_freemsg(msg);
prerror("FSP: Failed to queue fetch data message\n");
last->result = OPAL_INTERNAL_ERROR;
last = list_pop(&fsp_fetch_lid_queue,
struct fsp_fetch_lid_item, link);
list_add_tail(&fsp_fetched_lid, &last->link);
}
last->result = OPAL_BUSY;
}
static void fsp_start_fetching_next_lid(void)
{
struct fsp_fetch_lid_item *last;
assert(lock_held_by_me(&fsp_fetch_lock));
last = list_top(&fsp_fetch_lid_queue, struct fsp_fetch_lid_item, link);
if (last == NULL)
return;
/* If we're not already fetching */
if (last->result == OPAL_EMPTY)
fsp_fetch_lid_next_chunk(last);
}
int fsp_start_preload_resource(enum resource_id id, uint32_t idx,
void *buf, size_t *size)
{
struct fsp_fetch_lid_item *resource;
uint32_t lid_no = 0;
int i;
resource = malloc(sizeof(struct fsp_fetch_lid_item));
assert(resource != NULL);
resource->id = id;
resource->idx = idx;
resource->offset = 0;
resource->buffer = buf;
resource->remaining = *size;
*size = 0;
resource->length = size;
resource->result = OPAL_EMPTY;
for (i = 0; i < ARRAY_SIZE(fsp_lid_map); i++) {
if (id != fsp_lid_map[i].id)
continue;
if (fsp_lid_map[i].idx == idx) {
lid_no = fsp_lid_map[i].lid_no;
break;
}
}
if (lid_no == 0)
return OPAL_PARAMETER;
printf("Trying to load OPAL LID %08x...\n", lid_no);
resource->lid_no = lid_no;
resource->lid = fsp_adjust_lid_side(lid_no);
lock(&fsp_fetch_lock);
list_add_tail(&fsp_fetch_lid_queue, &resource->link);
fsp_start_fetching_next_lid();
unlock(&fsp_fetch_lock);
return OPAL_SUCCESS;
}
int fsp_resource_loaded(enum resource_id id, uint32_t idx)
{
struct fsp_fetch_lid_item *resource = NULL;
struct fsp_fetch_lid_item *r;
int rc = OPAL_BUSY;
lock(&fsp_fetch_lock);
list_for_each(&fsp_fetched_lid, r, link) {
if (r->id == id && r->idx == idx) {
resource = r;
break;
}
}
if (resource) {
rc = resource->result;
list_del(&resource->link);
free(resource);
}
unlock(&fsp_fetch_lock);
return rc;
}
static int fsp_lid_loaded(uint32_t lid_no)
{
struct fsp_fetch_lid_item *resource = NULL;
struct fsp_fetch_lid_item *r;
int rc = OPAL_BUSY;
lock(&fsp_fetch_lock);
list_for_each(&fsp_fetched_lid, r, link) {
if (r->lid_no == lid_no) {
resource = r;
break;
}
}
if (resource) {
rc = resource->result;
if (rc == OPAL_SUCCESS) {
list_del(&resource->link);
free(resource);
}
}
unlock(&fsp_fetch_lock);
return rc;
}
int fsp_preload_lid(uint32_t lid_no, char *buf, size_t *size)
{
struct fsp_fetch_lid_item *resource;
int r = OPAL_SUCCESS;
resource = malloc(sizeof(struct fsp_fetch_lid_item));
assert(resource != NULL);
resource->id = -1;
resource->idx = -1;
resource->offset = 0;
resource->buffer = buf;
resource->remaining = *size;
*size = 0;
resource->length = size;
resource->result = OPAL_EMPTY;
if (lid_no == 0)
return OPAL_PARAMETER;
printf("Trying to load LID %08x from FSP\n", lid_no);
resource->lid_no = lid_no;
resource->lid = fsp_adjust_lid_side(lid_no);
lock(&fsp_fetch_lock);
list_add_tail(&fsp_fetch_lid_queue, &resource->link);
fsp_start_fetching_next_lid();
unlock(&fsp_fetch_lock);
return r;
}
int fsp_wait_lid_loaded(uint32_t lid_no)
{
int r;
int waited = 0;
r = fsp_lid_loaded(lid_no);
while(r == OPAL_BUSY) {
opal_run_pollers();
time_wait_nopoll(msecs_to_tb(5));
waited+=5;
cpu_relax();
r = fsp_lid_loaded(lid_no);
}
prlog(PR_DEBUG, "FSP: fsp_wait_lid_loaded %x %u ms\n", lid_no, waited);
return r;
}
void fsp_used_by_console(void)
{
fsp_lock.in_con_path = true;
/*
* Some other processor might hold it without having
* disabled the console locally so let's make sure that
* is over by taking/releasing the lock ourselves
*/
lock(&fsp_lock);
unlock(&fsp_lock);
}