summaryrefslogtreecommitdiffstats
path: root/hw/fsp/fsp-op-panel.c
blob: 7063cbb3a33558a45d8363233300b757722acc5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/* 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 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");
}
OpenPOWER on IntegriCloud