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