273 lines
6.9 KiB
C
273 lines
6.9 KiB
C
/* Copyright 2013-2014 IBM Corp.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
* implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <skiboot.h>
|
|
#include <fsp.h>
|
|
#include <lock.h>
|
|
#include <opal.h>
|
|
#include <device.h>
|
|
#include <processor.h>
|
|
#include <opal-msg.h>
|
|
#include <errorlog.h>
|
|
|
|
DEFINE_LOG_ENTRY(OPAL_RC_PANEL_WRITE, OPAL_PLATFORM_ERR_EVT, OPAL_OP_PANEL,
|
|
OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA);
|
|
|
|
/* For OPAL OP_PANEL API we can only have one in flight due to TCEs */
|
|
static struct fsp_msg *op_req;
|
|
static uint64_t op_async_token;
|
|
static struct lock op_lock = LOCK_UNLOCKED;
|
|
|
|
static void fsp_op_display_fatal(uint32_t w0, uint32_t w1)
|
|
{
|
|
static struct fsp_msg op_msg_resp;
|
|
static struct fsp_msg op_msg = {
|
|
.resp = &op_msg_resp,
|
|
};
|
|
|
|
fsp_fillmsg(&op_msg, FSP_CMD_DISP_SRC_DIRECT, 3, 1, w0, w1);
|
|
|
|
/*
|
|
* A special way to send a message: it doesn't run pollers.
|
|
* This means we can call it while in a poller, which we may
|
|
* well be in when we're terminating (and thus displaying a *fatal*
|
|
* message on the op-panel).
|
|
*/
|
|
fsp_fatal_msg(&op_msg);
|
|
}
|
|
|
|
void fsp_op_display(enum op_severity sev, enum op_module mod, uint16_t code)
|
|
{
|
|
struct fsp_msg *op_msg;
|
|
uint32_t w0;
|
|
uint32_t w1;
|
|
|
|
if (!fsp_present())
|
|
return;
|
|
|
|
w0 = sev << 16 | mod;
|
|
|
|
w1 = tohex((code >> 12) & 0xf) << 24;
|
|
w1 |= tohex((code >> 8) & 0xf) << 16;
|
|
w1 |= tohex((code >> 4) & 0xf) << 8;
|
|
w1 |= tohex((code ) & 0xf);
|
|
|
|
if (sev == OP_FATAL) {
|
|
fsp_op_display_fatal(w0, w1);
|
|
} else {
|
|
op_msg = fsp_allocmsg(true);
|
|
if (!op_msg) {
|
|
prerror("Failed to allocate FSP message for PANEL\n");
|
|
return;
|
|
}
|
|
|
|
fsp_fillmsg(op_msg, FSP_CMD_DISP_SRC_DIRECT, 3, 1, w0, w1);
|
|
|
|
if(fsp_queue_msg(op_msg, fsp_freemsg))
|
|
prerror("Failed to queue FSP message for OP PANEL\n");
|
|
}
|
|
}
|
|
|
|
void op_panel_disable_src_echo(void)
|
|
{
|
|
struct fsp_msg op_msg_resp;
|
|
struct fsp_msg op_msg = {
|
|
.resp = &op_msg_resp,
|
|
};
|
|
|
|
if (!fsp_present())
|
|
return;
|
|
|
|
fsp_fillmsg(&op_msg, FSP_CMD_DIS_SRC_ECHO, 0);
|
|
fsp_sync_msg(&op_msg, false);
|
|
}
|
|
|
|
void op_panel_clear_src(void)
|
|
{
|
|
struct fsp_msg op_msg_resp;
|
|
struct fsp_msg op_msg = {
|
|
.resp = &op_msg_resp,
|
|
};
|
|
|
|
if (!fsp_present())
|
|
return;
|
|
|
|
fsp_fillmsg(&op_msg, FSP_CMD_CLEAR_SRC, 0);
|
|
fsp_sync_msg(&op_msg, false);
|
|
}
|
|
|
|
/* opal_write_oppanel - Write to the physical op panel.
|
|
*
|
|
* Pass in an array of oppanel_line_t structs defining the ASCII characters
|
|
* to display on each line of the oppanel. If there are two lines on the
|
|
* physical panel, and you only want to write to the first line, you only
|
|
* need to pass in one line. If you only want to write to the second line,
|
|
* you need to pass in both lines, and set the line_len of the first line
|
|
* to zero.
|
|
*
|
|
* This command is asynchronous. If OPAL_SUCCESS is returned, then the
|
|
* operation was initiated successfully. Subsequent calls will return
|
|
* OPAL_BUSY until the current operation is complete.
|
|
*/
|
|
struct op_src {
|
|
uint8_t version;
|
|
#define OP_SRC_VERSION 2
|
|
uint8_t flags;
|
|
uint8_t reserved;
|
|
uint8_t hex_word_cnt;
|
|
uint16_t reserved2;
|
|
uint16_t total_size;
|
|
uint32_t word2; /* SRC format in low byte */
|
|
uint32_t word3;
|
|
uint32_t word4;
|
|
uint32_t word5;
|
|
uint32_t word6;
|
|
uint32_t word7;
|
|
uint32_t word8;
|
|
uint32_t word9;
|
|
uint8_t ascii[OP_PANEL_NUM_LINES * OP_PANEL_LINE_LEN]; /* Word 11 */
|
|
} __packed __align(4);
|
|
|
|
/* Page align for the sake of TCE mapping */
|
|
static struct op_src op_src __align(0x1000);
|
|
|
|
static void __op_panel_write_complete(struct fsp_msg *msg)
|
|
{
|
|
fsp_tce_unmap(PSI_DMA_OP_PANEL_MISC, 0x1000);
|
|
|
|
lock(&op_lock);
|
|
op_req = NULL;
|
|
unlock(&op_lock);
|
|
|
|
fsp_freemsg(msg);
|
|
}
|
|
|
|
static void op_panel_write_complete(struct fsp_msg *msg)
|
|
{
|
|
uint8_t rc = (msg->resp->word1 >> 8) & 0xff;
|
|
|
|
if (rc)
|
|
prerror("OPPANEL: Error 0x%02x in display command\n", rc);
|
|
|
|
__op_panel_write_complete(msg);
|
|
|
|
opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, 1, op_async_token);
|
|
}
|
|
|
|
static int64_t __opal_write_oppanel(oppanel_line_t *lines, uint64_t num_lines,
|
|
uint64_t async_token)
|
|
{
|
|
int64_t rc = OPAL_ASYNC_COMPLETION;
|
|
int len;
|
|
int i;
|
|
|
|
if (num_lines < 1 || num_lines > OP_PANEL_NUM_LINES)
|
|
return OPAL_PARAMETER;
|
|
|
|
/* Only one in flight */
|
|
lock(&op_lock);
|
|
if (op_req) {
|
|
rc = OPAL_BUSY_EVENT;
|
|
unlock(&op_lock);
|
|
goto bail;
|
|
}
|
|
|
|
op_req = fsp_allocmsg(true);
|
|
if (!op_req) {
|
|
rc = OPAL_NO_MEM;
|
|
unlock(&op_lock);
|
|
goto bail;
|
|
}
|
|
unlock(&op_lock);
|
|
|
|
op_async_token = async_token;
|
|
|
|
memset(&op_src, 0, sizeof(op_src));
|
|
|
|
op_src.version = OP_SRC_VERSION;
|
|
op_src.flags = 0;
|
|
op_src.reserved = 0;
|
|
op_src.hex_word_cnt = 1; /* header word only */
|
|
op_src.reserved2 = 0;
|
|
op_src.total_size = sizeof(op_src);
|
|
op_src.word2 = 0; /* should be unneeded */
|
|
|
|
for (i = 0; i < num_lines; i++) {
|
|
uint8_t *current_line = op_src.ascii + (i * OP_PANEL_LINE_LEN);
|
|
|
|
len = be64_to_cpu(lines[i].line_len);
|
|
if (len < OP_PANEL_LINE_LEN)
|
|
memset(current_line + len, ' ', OP_PANEL_LINE_LEN-len);
|
|
else
|
|
len = OP_PANEL_LINE_LEN;
|
|
memcpy(current_line, (void *) be64_to_cpu(lines[i].line), len);
|
|
}
|
|
|
|
for (i = 0; i < sizeof(op_src.ascii); i++) {
|
|
/*
|
|
* So, there's this interesting thing if you send
|
|
* HTML/Javascript through the Operator Panel.
|
|
* You get to inject it into the ASM web ui!
|
|
* So we filter out anything suspect here,
|
|
* at least for the time being.
|
|
*
|
|
* Allowed characters:
|
|
* . / 0-9 : a-z A-Z SPACE
|
|
*/
|
|
if (! ((op_src.ascii[i] >= '.' && op_src.ascii[i] <= ':') ||
|
|
(op_src.ascii[i] >= 'a' && op_src.ascii[i] <= 'z') ||
|
|
(op_src.ascii[i] >= 'A' && op_src.ascii[i] <= 'Z') ||
|
|
op_src.ascii[i] == ' ')) {
|
|
op_src.ascii[i] = '.';
|
|
}
|
|
}
|
|
|
|
fsp_tce_map(PSI_DMA_OP_PANEL_MISC, &op_src, 0x1000);
|
|
|
|
fsp_fillmsg(op_req, FSP_CMD_DISP_SRC_INDIR, 3, 0,
|
|
PSI_DMA_OP_PANEL_MISC, sizeof(struct op_src));
|
|
rc = fsp_queue_msg(op_req, op_panel_write_complete);
|
|
if (rc) {
|
|
__op_panel_write_complete(op_req);
|
|
rc = OPAL_INTERNAL_ERROR;
|
|
}
|
|
bail:
|
|
log_simple_error(&e_info(OPAL_RC_PANEL_WRITE),
|
|
"FSP: Error updating Op Panel: %lld\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
static int64_t opal_write_oppanel_async(uint64_t async_token,
|
|
oppanel_line_t *lines,
|
|
uint64_t num_lines)
|
|
{
|
|
return __opal_write_oppanel(lines, num_lines, async_token);
|
|
}
|
|
|
|
void fsp_oppanel_init(void)
|
|
{
|
|
struct dt_node *oppanel;
|
|
|
|
if (!fsp_present())
|
|
return;
|
|
|
|
opal_register(OPAL_WRITE_OPPANEL_ASYNC, opal_write_oppanel_async, 3);
|
|
|
|
oppanel = dt_new(opal_node, "oppanel");
|
|
dt_add_property_cells(oppanel, "#length", OP_PANEL_LINE_LEN);
|
|
dt_add_property_cells(oppanel, "#lines", OP_PANEL_NUM_LINES);
|
|
dt_add_property_string(oppanel, "compatible", "ibm,opal-oppanel");
|
|
}
|