/* 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 #include #include #include #include #include #include #include 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 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"); }