1 /* Copyright 2013-2014 IBM Corp.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * 	http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * Console IO routine for use by libc
19  *
20  * fd is the classic posix 0,1,2 (stdin, stdout, stderr)
21  */
22 #include <skiboot.h>
23 #include <unistd.h>
24 #include <console.h>
25 #include <opal.h>
26 #include <device.h>
27 #include <processor.h>
28 #include <cpu.h>
29 
30 static char *con_buf = (char *)INMEM_CON_START;
31 static size_t con_in;
32 static size_t con_out;
33 static bool con_wrapped;
34 
35 /* Internal console driver ops */
36 static struct con_ops *con_driver;
37 
38 /* External (OPAL) console driver ops */
39 static struct opal_con_ops *opal_con_driver = &dummy_opal_con;
40 
41 static struct lock con_lock = LOCK_UNLOCKED;
42 
43 /* This is mapped via TCEs so we keep it alone in a page */
44 struct memcons memcons __section(".data.memcons") = {
45 	.magic		= MEMCONS_MAGIC,
46 	.obuf_phys	= INMEM_CON_START,
47 	.ibuf_phys	= INMEM_CON_START + INMEM_CON_OUT_LEN,
48 	.obuf_size	= INMEM_CON_OUT_LEN,
49 	.ibuf_size	= INMEM_CON_IN_LEN,
50 };
51 
dummy_console_enabled(void)52 static bool dummy_console_enabled(void)
53 {
54 #ifdef FORCE_DUMMY_CONSOLE
55 	return true;
56 #else
57 	return dt_has_node_property(dt_chosen,
58 				    "sapphire,enable-dummy-console", NULL);
59 #endif
60 }
61 
62 /*
63  * Helper function for adding /ibm,opal/consoles/serial@<xyz> nodes
64  */
add_opal_console_node(int index,const char * type,uint32_t write_buffer_size)65 struct dt_node *add_opal_console_node(int index, const char *type,
66 	uint32_t write_buffer_size)
67 {
68 	struct dt_node *con, *consoles;
69 	char buffer[32];
70 
71 	consoles = dt_find_by_name(opal_node, "consoles");
72 	if (!consoles) {
73 		consoles = dt_new(opal_node, "consoles");
74 		assert(consoles);
75 		dt_add_property_cells(consoles, "#address-cells", 1);
76 		dt_add_property_cells(consoles, "#size-cells", 0);
77 	}
78 
79 	con = dt_new_addr(consoles, "serial", index);
80 	assert(con);
81 
82 	snprintf(buffer, sizeof(buffer), "ibm,opal-console-%s", type);
83 	dt_add_property_string(con, "compatible", buffer);
84 
85 	dt_add_property_cells(con, "#write-buffer-size", write_buffer_size);
86 	dt_add_property_cells(con, "reg", index);
87 	dt_add_property_string(con, "device_type", "serial");
88 
89 	return con;
90 }
91 
clear_console(void)92 void clear_console(void)
93 {
94 	memset(con_buf, 0, INMEM_CON_LEN);
95 }
96 
97 /*
98  * Flush the console buffer into the driver, returns true
99  * if there is more to go.
100  * Optionally can skip flushing to drivers, leaving messages
101  * just in memory console.
102  */
__flush_console(bool flush_to_drivers)103 static bool __flush_console(bool flush_to_drivers)
104 {
105 	struct cpu_thread *cpu = this_cpu();
106 	size_t req, len = 0;
107 	static bool in_flush, more_flush;
108 
109 	/* Is there anything to flush ? Bail out early if not */
110 	if (con_in == con_out || !con_driver)
111 		return false;
112 
113 	/*
114 	 * Console flushing is suspended on this CPU, typically because
115 	 * some critical locks are held that would potentially cause a
116 	 * flush to deadlock
117 	 */
118 	if (cpu->con_suspend) {
119 		cpu->con_need_flush = true;
120 		return false;
121 	}
122 	cpu->con_need_flush = false;
123 
124 	/*
125 	 * We must call the underlying driver with the console lock
126 	 * dropped otherwise we get some deadlocks if anything down
127 	 * that path tries to printf() something.
128 	 *
129 	 * So instead what we do is we keep a static in_flush flag
130 	 * set/released with the lock held, which is used to prevent
131 	 * concurrent attempts at flushing the same chunk of buffer
132 	 * by other processors.
133 	 */
134 	if (in_flush) {
135 		more_flush = true;
136 		return false;
137 	}
138 	in_flush = true;
139 
140 	/*
141 	 * NB: this must appear after the in_flush check since it modifies
142 	 *     con_out.
143 	 */
144 	if (!flush_to_drivers) {
145 		con_out = con_in;
146 		in_flush = false;
147 		return false;
148 	}
149 
150 	do {
151 		more_flush = false;
152 
153 		if (con_out > con_in) {
154 			req = INMEM_CON_OUT_LEN - con_out;
155 			more_flush = true;
156 		} else
157 			req = con_in - con_out;
158 
159 		unlock(&con_lock);
160 		len = con_driver->write(con_buf + con_out, req);
161 		lock(&con_lock);
162 
163 		con_out = (con_out + len) % INMEM_CON_OUT_LEN;
164 
165 		/* write error? */
166 		if (len < req)
167 			break;
168 	} while(more_flush);
169 
170 	in_flush = false;
171 	return con_out != con_in;
172 }
173 
flush_console(void)174 bool flush_console(void)
175 {
176 	bool ret;
177 
178 	lock(&con_lock);
179 	ret = __flush_console(true);
180 	unlock(&con_lock);
181 
182 	return ret;
183 }
184 
inmem_write(char c)185 static void inmem_write(char c)
186 {
187 	uint32_t opos;
188 
189 	if (!c)
190 		return;
191 	con_buf[con_in++] = c;
192 	if (con_in >= INMEM_CON_OUT_LEN) {
193 		con_in = 0;
194 		con_wrapped = true;
195 	}
196 
197 	/*
198 	 * We must always re-generate memcons.out_pos because
199 	 * under some circumstances, the console script will
200 	 * use a broken putmemproc that does RMW on the full
201 	 * 8 bytes containing out_pos and in_prod, thus corrupting
202 	 * out_pos
203 	 */
204 	opos = con_in;
205 	if (con_wrapped)
206 		opos |= MEMCONS_OUT_POS_WRAP;
207 	lwsync();
208 	memcons.out_pos = opos;
209 
210 	/* If head reaches tail, push tail around & drop chars */
211 	if (con_in == con_out)
212 		con_out = (con_in + 1) % INMEM_CON_OUT_LEN;
213 }
214 
inmem_read(char * buf,size_t req)215 static size_t inmem_read(char *buf, size_t req)
216 {
217 	size_t read = 0;
218 	char *ibuf = (char *)memcons.ibuf_phys;
219 
220 	while (req && memcons.in_prod != memcons.in_cons) {
221 		*(buf++) = ibuf[memcons.in_cons];
222 		lwsync();
223 		memcons.in_cons = (memcons.in_cons + 1) % INMEM_CON_IN_LEN;
224 		req--;
225 		read++;
226 	}
227 	return read;
228 }
229 
write_char(char c)230 static void write_char(char c)
231 {
232 #ifdef MAMBO_DEBUG_CONSOLE
233 	mambo_console_write(&c, 1);
234 #endif
235 	inmem_write(c);
236 }
237 
console_write(bool flush_to_drivers,const void * buf,size_t count)238 ssize_t console_write(bool flush_to_drivers, const void *buf, size_t count)
239 {
240 	/* We use recursive locking here as we can get called
241 	 * from fairly deep debug path
242 	 */
243 	bool need_unlock = lock_recursive(&con_lock);
244 	const char *cbuf = buf;
245 
246 	while(count--) {
247 		char c = *(cbuf++);
248 		if (c == '\n')
249 			write_char('\r');
250 		write_char(c);
251 	}
252 
253 	__flush_console(flush_to_drivers);
254 
255 	if (need_unlock)
256 		unlock(&con_lock);
257 
258 	return count;
259 }
260 
write(int fd __unused,const void * buf,size_t count)261 ssize_t write(int fd __unused, const void *buf, size_t count)
262 {
263 	return console_write(true, buf, count);
264 }
265 
read(int fd __unused,void * buf,size_t req_count)266 ssize_t read(int fd __unused, void *buf, size_t req_count)
267 {
268 	bool need_unlock = lock_recursive(&con_lock);
269 	size_t count = 0;
270 
271 	if (con_driver && con_driver->read)
272 		count = con_driver->read(buf, req_count);
273 	if (!count)
274 		count = inmem_read(buf, req_count);
275 	if (need_unlock)
276 		unlock(&con_lock);
277 	return count;
278 }
279 
280 /* Helper function to perform a full synchronous flush */
console_complete_flush(void)281 void console_complete_flush(void)
282 {
283 	/*
284 	 * Using term 0 here is a dumb hack that works because the UART
285 	 * only has term 0 and the FSP doesn't have an explicit flush method.
286 	 */
287 	int64_t ret = opal_con_driver->flush(0);
288 
289 	if (ret == OPAL_UNSUPPORTED || ret == OPAL_PARAMETER)
290 		return;
291 
292 	while (ret != OPAL_SUCCESS) {
293 		ret = opal_con_driver->flush(0);
294 	}
295 }
296 
297 /*
298  * set_console()
299  *
300  * This sets the driver used internally by Skiboot. This is different to the
301  * OPAL console driver.
302  */
set_console(struct con_ops * driver)303 void set_console(struct con_ops *driver)
304 {
305 	con_driver = driver;
306 	if (driver)
307 		flush_console();
308 }
309 
310 /*
311  * set_opal_console()
312  *
313  * Configure the console driver to handle the console provided by the OPAL API.
314  * They are different to the above in that they are typically buffered, and used
315  * by the host OS rather than skiboot.
316  */
317 static bool opal_cons_init = false;
318 
set_opal_console(struct opal_con_ops * driver)319 void set_opal_console(struct opal_con_ops *driver)
320 {
321 	assert(!opal_cons_init);
322 	opal_con_driver = driver;
323 }
324 
init_opal_console(void)325 void init_opal_console(void)
326 {
327 	assert(!opal_cons_init);
328 	opal_cons_init = true;
329 
330 	if (dummy_console_enabled() && opal_con_driver != &dummy_opal_con) {
331 		prlog(PR_WARNING, "OPAL: Dummy console forced, %s ignored\n",
332 		      opal_con_driver->name);
333 
334 		opal_con_driver = &dummy_opal_con;
335 	}
336 
337 	prlog(PR_NOTICE, "OPAL: Using %s\n", opal_con_driver->name);
338 
339 	if (opal_con_driver->init)
340 		opal_con_driver->init();
341 
342 	opal_register(OPAL_CONSOLE_READ, opal_con_driver->read, 3);
343 	opal_register(OPAL_CONSOLE_WRITE, opal_con_driver->write, 3);
344 	opal_register(OPAL_CONSOLE_FLUSH, opal_con_driver->flush, 1);
345 	opal_register(OPAL_CONSOLE_WRITE_BUFFER_SPACE,
346 			opal_con_driver->space, 2);
347 }
348 
memcons_add_properties(void)349 void memcons_add_properties(void)
350 {
351 	dt_add_property_u64(opal_node, "ibm,opal-memcons", (u64) &memcons);
352 }
353 
354 /*
355  * The default OPAL console.
356  *
357  * In the absence of a "real" OPAL console driver we handle the OPAL_CONSOLE_*
358  * calls by writing into the skiboot log buffer. Reads are a little more
359  * complicated since they can come from the in-memory console (BML) or from the
360  * internal skiboot console driver.
361  */
dummy_console_write(int64_t term_number,int64_t * length,const uint8_t * buffer)362 static int64_t dummy_console_write(int64_t term_number, int64_t *length,
363 				   const uint8_t *buffer)
364 {
365 	if (term_number != 0)
366 		return OPAL_PARAMETER;
367 
368 	if (!opal_addr_valid(length) || !opal_addr_valid(buffer))
369 		return OPAL_PARAMETER;
370 
371 	write(0, buffer, *length);
372 
373 	return OPAL_SUCCESS;
374 }
375 
dummy_console_write_buffer_space(int64_t term_number,int64_t * length)376 static int64_t dummy_console_write_buffer_space(int64_t term_number,
377 						int64_t *length)
378 {
379 	if (term_number != 0)
380 		return OPAL_PARAMETER;
381 
382 	if (!opal_addr_valid(length))
383 		return OPAL_PARAMETER;
384 
385 	if (length)
386 		*length = INMEM_CON_OUT_LEN;
387 
388 	return OPAL_SUCCESS;
389 }
390 
dummy_console_read(int64_t term_number,int64_t * length,uint8_t * buffer)391 static int64_t dummy_console_read(int64_t term_number, int64_t *length,
392 				  uint8_t *buffer)
393 {
394 	if (term_number != 0)
395 		return OPAL_PARAMETER;
396 
397 	if (!opal_addr_valid(length) || !opal_addr_valid(buffer))
398 		return OPAL_PARAMETER;
399 
400 	*length = read(0, buffer, *length);
401 	opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, 0);
402 
403 	return OPAL_SUCCESS;
404 }
405 
dummy_console_flush(int64_t term_number __unused)406 static int64_t dummy_console_flush(int64_t term_number __unused)
407 {
408 	return OPAL_UNSUPPORTED;
409 }
410 
dummy_console_poll(void * data __unused)411 static void dummy_console_poll(void *data __unused)
412 {
413 	bool has_data = false;
414 
415 	lock(&con_lock);
416 	if (con_driver && con_driver->poll_read)
417 		has_data = con_driver->poll_read();
418 	if (memcons.in_prod != memcons.in_cons)
419 		has_data = true;
420 	if (has_data)
421 		opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT,
422 					OPAL_EVENT_CONSOLE_INPUT);
423 	else
424 		opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, 0);
425 	unlock(&con_lock);
426 }
427 
dummy_console_add_nodes(void)428 void dummy_console_add_nodes(void)
429 {
430 	struct dt_property *p;
431 
432 	add_opal_console_node(0, "raw", memcons.obuf_size);
433 
434 	/* Mambo might have left a crap one, clear it */
435 	p = __dt_find_property(dt_chosen, "linux,stdout-path");
436 	if (p)
437 		dt_del_property(dt_chosen, p);
438 
439 	dt_add_property_string(dt_chosen, "linux,stdout-path",
440 			       "/ibm,opal/consoles/serial@0");
441 
442 	opal_add_poller(dummy_console_poll, NULL);
443 }
444 
445 struct opal_con_ops dummy_opal_con = {
446 	.name = "Dummy Console",
447 	.init = dummy_console_add_nodes,
448 	.read = dummy_console_read,
449 	.write = dummy_console_write,
450 	.space = dummy_console_write_buffer_space,
451 	.flush = dummy_console_flush,
452 };
453