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