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,bool need_unlock)103 static bool __flush_console(bool flush_to_drivers, bool need_unlock)
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 	 * Also if it recursed on con_lock (need_unlock is false). This
119 	 * can happen due to debug code firing (e.g., list or stack
120 	 * debugging).
121 	 */
122 	if (cpu->con_suspend || !need_unlock) {
123 		cpu->con_need_flush = true;
124 		return false;
125 	}
126 	cpu->con_need_flush = false;
127 
128 	/*
129 	 * We must call the underlying driver with the console lock
130 	 * dropped otherwise we get some deadlocks if anything down
131 	 * that path tries to printf() something.
132 	 *
133 	 * So instead what we do is we keep a static in_flush flag
134 	 * set/released with the lock held, which is used to prevent
135 	 * concurrent attempts at flushing the same chunk of buffer
136 	 * by other processors.
137 	 */
138 	if (in_flush) {
139 		more_flush = true;
140 		return false;
141 	}
142 	in_flush = true;
143 
144 	/*
145 	 * NB: this must appear after the in_flush check since it modifies
146 	 *     con_out.
147 	 */
148 	if (!flush_to_drivers) {
149 		con_out = con_in;
150 		in_flush = false;
151 		return false;
152 	}
153 
154 	do {
155 		more_flush = false;
156 
157 		if (con_out > con_in) {
158 			req = INMEM_CON_OUT_LEN - con_out;
159 			more_flush = true;
160 		} else
161 			req = con_in - con_out;
162 
163 		unlock(&con_lock);
164 		len = con_driver->write(con_buf + con_out, req);
165 		lock(&con_lock);
166 
167 		con_out = (con_out + len) % INMEM_CON_OUT_LEN;
168 
169 		/* write error? */
170 		if (len < req)
171 			break;
172 	} while(more_flush);
173 
174 	in_flush = false;
175 	return con_out != con_in;
176 }
177 
flush_console(void)178 bool flush_console(void)
179 {
180 	bool ret;
181 
182 	lock(&con_lock);
183 	ret = __flush_console(true, true);
184 	unlock(&con_lock);
185 
186 	return ret;
187 }
188 
inmem_write(char c)189 static void inmem_write(char c)
190 {
191 	uint32_t opos;
192 
193 	if (!c)
194 		return;
195 	con_buf[con_in++] = c;
196 	if (con_in >= INMEM_CON_OUT_LEN) {
197 		con_in = 0;
198 		con_wrapped = true;
199 	}
200 
201 	/*
202 	 * We must always re-generate memcons.out_pos because
203 	 * under some circumstances, the console script will
204 	 * use a broken putmemproc that does RMW on the full
205 	 * 8 bytes containing out_pos and in_prod, thus corrupting
206 	 * out_pos
207 	 */
208 	opos = con_in;
209 	if (con_wrapped)
210 		opos |= MEMCONS_OUT_POS_WRAP;
211 	lwsync();
212 	memcons.out_pos = opos;
213 
214 	/* If head reaches tail, push tail around & drop chars */
215 	if (con_in == con_out)
216 		con_out = (con_in + 1) % INMEM_CON_OUT_LEN;
217 }
218 
inmem_read(char * buf,size_t req)219 static size_t inmem_read(char *buf, size_t req)
220 {
221 	size_t read = 0;
222 	char *ibuf = (char *)memcons.ibuf_phys;
223 
224 	while (req && memcons.in_prod != memcons.in_cons) {
225 		*(buf++) = ibuf[memcons.in_cons];
226 		lwsync();
227 		memcons.in_cons = (memcons.in_cons + 1) % INMEM_CON_IN_LEN;
228 		req--;
229 		read++;
230 	}
231 	return read;
232 }
233 
write_char(char c)234 static void write_char(char c)
235 {
236 #ifdef MAMBO_DEBUG_CONSOLE
237 	mambo_console_write(&c, 1);
238 #endif
239 	inmem_write(c);
240 }
241 
console_write(bool flush_to_drivers,const void * buf,size_t count)242 ssize_t console_write(bool flush_to_drivers, const void *buf, size_t count)
243 {
244 	/* We use recursive locking here as we can get called
245 	 * from fairly deep debug path
246 	 */
247 	bool need_unlock = lock_recursive(&con_lock);
248 	const char *cbuf = buf;
249 
250 	while(count--) {
251 		char c = *(cbuf++);
252 		if (c == '\n')
253 			write_char('\r');
254 		write_char(c);
255 	}
256 
257 	__flush_console(flush_to_drivers, need_unlock);
258 
259 	if (need_unlock)
260 		unlock(&con_lock);
261 
262 	return count;
263 }
264 
write(int fd __unused,const void * buf,size_t count)265 ssize_t write(int fd __unused, const void *buf, size_t count)
266 {
267 	return console_write(true, buf, count);
268 }
269 
read(int fd __unused,void * buf,size_t req_count)270 ssize_t read(int fd __unused, void *buf, size_t req_count)
271 {
272 	bool need_unlock = lock_recursive(&con_lock);
273 	size_t count = 0;
274 
275 	if (con_driver && con_driver->read)
276 		count = con_driver->read(buf, req_count);
277 	if (!count)
278 		count = inmem_read(buf, req_count);
279 	if (need_unlock)
280 		unlock(&con_lock);
281 	return count;
282 }
283 
284 /* Helper function to perform a full synchronous flush */
console_complete_flush(void)285 void console_complete_flush(void)
286 {
287 	/*
288 	 * Using term 0 here is a dumb hack that works because the UART
289 	 * only has term 0 and the FSP doesn't have an explicit flush method.
290 	 */
291 	int64_t ret = opal_con_driver->flush(0);
292 
293 	if (ret == OPAL_UNSUPPORTED || ret == OPAL_PARAMETER)
294 		return;
295 
296 	while (ret != OPAL_SUCCESS) {
297 		ret = opal_con_driver->flush(0);
298 	}
299 }
300 
301 /*
302  * set_console()
303  *
304  * This sets the driver used internally by Skiboot. This is different to the
305  * OPAL console driver.
306  */
set_console(struct con_ops * driver)307 void set_console(struct con_ops *driver)
308 {
309 	con_driver = driver;
310 	if (driver)
311 		flush_console();
312 }
313 
314 /*
315  * set_opal_console()
316  *
317  * Configure the console driver to handle the console provided by the OPAL API.
318  * They are different to the above in that they are typically buffered, and used
319  * by the host OS rather than skiboot.
320  */
321 static bool opal_cons_init = false;
322 
set_opal_console(struct opal_con_ops * driver)323 void set_opal_console(struct opal_con_ops *driver)
324 {
325 	assert(!opal_cons_init);
326 	opal_con_driver = driver;
327 }
328 
init_opal_console(void)329 void init_opal_console(void)
330 {
331 	assert(!opal_cons_init);
332 	opal_cons_init = true;
333 
334 	if (dummy_console_enabled() && opal_con_driver != &dummy_opal_con) {
335 		prlog(PR_WARNING, "OPAL: Dummy console forced, %s ignored\n",
336 		      opal_con_driver->name);
337 
338 		opal_con_driver = &dummy_opal_con;
339 	}
340 
341 	prlog(PR_INFO, "OPAL: Using %s\n", opal_con_driver->name);
342 
343 	if (opal_con_driver->init)
344 		opal_con_driver->init();
345 
346 	opal_register(OPAL_CONSOLE_READ, opal_con_driver->read, 3);
347 	opal_register(OPAL_CONSOLE_WRITE, opal_con_driver->write, 3);
348 	opal_register(OPAL_CONSOLE_FLUSH, opal_con_driver->flush, 1);
349 	opal_register(OPAL_CONSOLE_WRITE_BUFFER_SPACE,
350 			opal_con_driver->space, 2);
351 }
352 
memcons_add_properties(void)353 void memcons_add_properties(void)
354 {
355 	dt_add_property_u64(opal_node, "ibm,opal-memcons", (u64) &memcons);
356 }
357 
358 /*
359  * The default OPAL console.
360  *
361  * In the absence of a "real" OPAL console driver we handle the OPAL_CONSOLE_*
362  * calls by writing into the skiboot log buffer. Reads are a little more
363  * complicated since they can come from the in-memory console (BML) or from the
364  * internal skiboot console driver.
365  */
dummy_console_write(int64_t term_number,int64_t * length,const uint8_t * buffer)366 static int64_t dummy_console_write(int64_t term_number, int64_t *length,
367 				   const uint8_t *buffer)
368 {
369 	if (term_number != 0)
370 		return OPAL_PARAMETER;
371 
372 	if (!opal_addr_valid(length) || !opal_addr_valid(buffer))
373 		return OPAL_PARAMETER;
374 
375 	write(0, buffer, *length);
376 
377 	return OPAL_SUCCESS;
378 }
379 
dummy_console_write_buffer_space(int64_t term_number,int64_t * length)380 static int64_t dummy_console_write_buffer_space(int64_t term_number,
381 						int64_t *length)
382 {
383 	if (term_number != 0)
384 		return OPAL_PARAMETER;
385 
386 	if (!opal_addr_valid(length))
387 		return OPAL_PARAMETER;
388 
389 	if (length)
390 		*length = INMEM_CON_OUT_LEN;
391 
392 	return OPAL_SUCCESS;
393 }
394 
dummy_console_read(int64_t term_number,int64_t * length,uint8_t * buffer)395 static int64_t dummy_console_read(int64_t term_number, int64_t *length,
396 				  uint8_t *buffer)
397 {
398 	if (term_number != 0)
399 		return OPAL_PARAMETER;
400 
401 	if (!opal_addr_valid(length) || !opal_addr_valid(buffer))
402 		return OPAL_PARAMETER;
403 
404 	*length = read(0, buffer, *length);
405 	opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, 0);
406 
407 	return OPAL_SUCCESS;
408 }
409 
dummy_console_flush(int64_t term_number __unused)410 static int64_t dummy_console_flush(int64_t term_number __unused)
411 {
412 	return OPAL_UNSUPPORTED;
413 }
414 
dummy_console_poll(void * data __unused)415 static void dummy_console_poll(void *data __unused)
416 {
417 	bool has_data = false;
418 
419 	lock(&con_lock);
420 	if (con_driver && con_driver->poll_read)
421 		has_data = con_driver->poll_read();
422 	if (memcons.in_prod != memcons.in_cons)
423 		has_data = true;
424 	if (has_data)
425 		opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT,
426 					OPAL_EVENT_CONSOLE_INPUT);
427 	else
428 		opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, 0);
429 	unlock(&con_lock);
430 }
431 
dummy_console_add_nodes(void)432 void dummy_console_add_nodes(void)
433 {
434 	struct dt_property *p;
435 
436 	add_opal_console_node(0, "raw", memcons.obuf_size);
437 
438 	/* Mambo might have left a crap one, clear it */
439 	p = __dt_find_property(dt_chosen, "linux,stdout-path");
440 	if (p)
441 		dt_del_property(dt_chosen, p);
442 
443 	dt_add_property_string(dt_chosen, "linux,stdout-path",
444 			       "/ibm,opal/consoles/serial@0");
445 
446 	opal_add_poller(dummy_console_poll, NULL);
447 }
448 
449 struct opal_con_ops dummy_opal_con = {
450 	.name = "Dummy Console",
451 	.init = dummy_console_add_nodes,
452 	.read = dummy_console_read,
453 	.write = dummy_console_write,
454 	.space = dummy_console_write_buffer_space,
455 	.flush = dummy_console_flush,
456 };
457