Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * Hypervisor Software File: vdev_console.s | |
5 | * | |
6 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
7 | * | |
8 | * - Do no alter or remove copyright notices | |
9 | * | |
10 | * - Redistribution and use of this software in source and binary forms, with | |
11 | * or without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistribution of source code must retain the above copyright notice, | |
15 | * this list of conditions and the following disclaimer. | |
16 | * | |
17 | * - Redistribution in binary form must reproduce the above copyright notice, | |
18 | * this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
20 | * | |
21 | * Neither the name of Sun Microsystems, Inc. or the names of contributors | |
22 | * may be used to endorse or promote products derived from this software | |
23 | * without specific prior written permission. | |
24 | * | |
25 | * This software is provided "AS IS," without a warranty of any kind. | |
26 | * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
27 | * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
28 | * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
29 | * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
30 | * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
31 | * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
32 | * OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
33 | * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
34 | * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
35 | * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
36 | * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
37 | * | |
38 | * You acknowledge that this software is not designed, licensed or | |
39 | * intended for use in the design, construction, operation or maintenance of | |
40 | * any nuclear facility. | |
41 | * | |
42 | * ========== Copyright Header End ============================================ | |
43 | */ | |
44 | /* | |
45 | * Copyright 2007 Sun Microsystems, Inc. All rights reserved. | |
46 | * Use is subject to license terms. | |
47 | */ | |
48 | ||
49 | .ident "@(#)vdev_console.s 1.12 07/02/14 SMI" | |
50 | ||
51 | .file "vdev_console.s" | |
52 | ||
53 | /* | |
54 | * Virtual console device implementation | |
55 | */ | |
56 | ||
57 | #include <sys/asm_linkage.h> | |
58 | #include <sys/htypes.h> | |
59 | #include <hprivregs.h> | |
60 | #include <asi.h> | |
61 | #include <fpga.h> | |
62 | #include <sun4v/traps.h> | |
63 | #include <sun4v/mmu.h> | |
64 | #include <sun4v/asi.h> | |
65 | #include <sparcv9/asi.h> | |
66 | #include <sun4v/queue.h> | |
67 | #include <devices/pc16550.h> | |
68 | ||
69 | #include <guest.h> | |
70 | #include <offsets.h> | |
71 | #include <util.h> | |
72 | #include <svc.h> | |
73 | #include <vdev_intr.h> | |
74 | #include <abort.h> | |
75 | #include <vdev_console.h> | |
76 | #include <debug.h> | |
77 | #include <vcpu.h> | |
78 | #include <mmu.h> | |
79 | #include <ldc.h> | |
80 | ||
81 | /* | |
82 | * Virtual console guest interfaces (hcalls) | |
83 | */ | |
84 | ||
85 | /* | |
86 | * Service Channel implementation | |
87 | */ | |
88 | ||
89 | ||
90 | /* | |
91 | * cons_putchar | |
92 | * | |
93 | * arg0 char (%o0) | |
94 | * -- | |
95 | * ret0 status (%o0) | |
96 | */ | |
97 | ENTRY_NP(hcall_cons_putchar) | |
98 | #if 0 /* XXX check for invalid char -or- magic values (BREAK) */ | |
99 | cmp %o0, MAX_CHAR | |
100 | bgu,pn %xcc, herr_inval | |
101 | #endif | |
102 | ||
103 | VCPU_GUEST_STRUCT(%g4, %g3) | |
104 | ! %g3 = guestp | |
105 | ! %g4 = cpup | |
106 | ||
107 | ! if not initialized, then just swallow the characters. | |
108 | ! OK What type of console do we have. | |
109 | ldub [%g3 + GUEST_CONSOLE + CONS_TYPE], %g5 | |
110 | cmp %g5, CONS_TYPE_UNCONFIG | |
111 | beq,pn %xcc, hret_ok | |
112 | nop | |
113 | cmp %g5, CONS_TYPE_LDC | |
114 | beq,pt %xcc, .use_ldc_put | |
115 | nop | |
116 | #ifdef CONFIG_CN_UART /* { */ | |
117 | cmp %g5, CONS_TYPE_UART | |
118 | beq,pt %xcc, .use_uart_put | |
119 | nop | |
120 | #endif /* } */ | |
121 | ba,pt %xcc, herr_inval ! Return inval if console not configd | |
122 | nop | |
123 | ||
124 | /* | |
125 | * Console put char using a LDC channel as output | |
126 | */ | |
127 | .use_ldc_put: | |
128 | ||
129 | setx GUEST_CONSOLE, %g2, %g6 | |
130 | add %g6, %g3, %g6 | |
131 | ||
132 | ldub [%g6 + CONS_STATUS], %g2 ! chk if ready | |
133 | andcc %g2, LDC_CONS_READY, %g2 | |
134 | bz,pn %xcc, herr_wouldblock | |
135 | nop | |
136 | ||
137 | ldx [%g6 + CONS_ENDPT], %g1 | |
138 | ||
139 | ! %g1 = channel | |
140 | ! %g3 = guest struct | |
141 | ||
142 | mulx %g1, LDC_ENDPOINT_SIZE, %g1 | |
143 | set GUEST_LDC_ENDPOINT, %g2 | |
144 | add %g1, %g2, %g1 | |
145 | add %g1, %g3, %g2 | |
146 | ||
147 | ! %g2 = our endpoint | |
148 | ! %g3 = guest struct | |
149 | ||
150 | ! Since the SRAM LDC is a bottleneck (for performance) | |
151 | ! we have to try and buffer up more than one char per LDC | |
152 | ! packet. | |
153 | ldub [ %g2 + LDC_TARGET_TYPE ], %g5 | |
154 | cmp %g5, LDC_SP_ENDPOINT | |
155 | bne %xcc, 1f | |
156 | nop | |
157 | ||
158 | ! SRAM LDC | |
159 | ! | |
160 | ! Since any code which pulls data out of our TX queue and into | |
161 | ! the SRAM will have to obtain the SP_LDC_TX_LOCK, we can go | |
162 | ! ahead and grab that lock now and try to "pack" the console | |
163 | ! LDC packets a little better so that we are not simply sending | |
164 | ! one character per packet in all cases. | |
165 | ||
166 | ROOT_STRUCT(%g1) | |
167 | ldx [%g1 + CONFIG_SP_LDCS], %g1 | |
168 | ldx [%g2 + LDC_TARGET_CHANNEL], %g4 | |
169 | mulx %g4, SP_LDC_ENDPOINT_SIZE, %g4 | |
170 | add %g1, %g4, %g1 ! target endpoint | |
171 | ||
172 | add %g1, SP_LDC_TX_LOCK, %g4 | |
173 | SPINLOCK_ENTER(%g4, %g5, %g6) | |
174 | ||
175 | lduw [ %g2 + LDC_TX_QHEAD ], %g6 | |
176 | lduw [ %g2 + LDC_TX_QTAIL ], %g4 | |
177 | ||
178 | cmp %g4, %g6 ! Is the TX queue empty? | |
179 | be %xcc, .putchar_release_sp_lk ! If so, nothing to compact | |
180 | nop ! so proceed as normal. | |
181 | ||
182 | ldx [ %g2 + LDC_TX_QSIZE ], %g6 ! "tail" points to next | |
183 | dec Q_EL_SIZE, %g6 ! available packet, so we | |
184 | dec Q_EL_SIZE, %g4 ! actually want the previous | |
185 | and %g4, %g6, %g4 ! packet. | |
186 | ||
187 | ldx [ %g2 + LDC_TX_QBASE_PA ], %g6 | |
188 | add %g4, %g6, %g4 ! Find the last packet | |
189 | ||
190 | ldub [%g4 + LDC_CONS_SIZE], %g5 ! read current size (#chars) | |
191 | ||
192 | cmp %g5, LDC_CONS_PAYLOAD_SZ ! is packet already full? | |
193 | bgeu,a %xcc, .putchar_release_sp_lk ! if so, just start the next | |
194 | nop ! packet as usual (unpacked) | |
195 | ||
196 | add %g4, %g5, %g6 ! offset of this char. | |
197 | stb %o0, [%g6 + LDC_CONS_PAYLOAD] ! store char in payload. | |
198 | ||
199 | inc %g5 ! # chars in payload now. | |
200 | stb %g5, [%g4 + LDC_CONS_SIZE] ! store new payload size. | |
201 | ||
202 | add %g1, SP_LDC_TX_LOCK, %g4 | |
203 | SPINLOCK_EXIT(%g4) | |
204 | ||
205 | ! Queue was not empty, so no need to send notification. | |
206 | HCALL_RET(EOK) | |
207 | ||
208 | .putchar_release_sp_lk: | |
209 | ||
210 | add %g1, SP_LDC_TX_LOCK, %g4 | |
211 | SPINLOCK_EXIT(%g4) | |
212 | ||
213 | ba,a .send_pkt | |
214 | ||
215 | 1: | |
216 | ! target is a guest endpoint | |
217 | ! if it has no receive queue configured drop the | |
218 | ! console char and return back | |
219 | ldx [ %g2 + LDC_TARGET_GUEST ], %g6 | |
220 | ldx [ %g2 + LDC_TARGET_CHANNEL ], %g4 | |
221 | mulx %g4, LDC_ENDPOINT_SIZE, %g4 | |
222 | set GUEST_LDC_ENDPOINT, %g5 | |
223 | add %g6, %g5, %g6 | |
224 | add %g6, %g4, %g6 ! g6 is the target endpoint | |
225 | ldx [ %g6 + LDC_RX_QSIZE ], %g4 ! check if queue is configured | |
226 | brnz,a,pn %g4, .send_pkt | |
227 | nop | |
228 | HCALL_RET(EOK) | |
229 | ||
230 | .send_pkt: | |
231 | ! %g2 = our endpoint | |
232 | ! %g3 = guest struct | |
233 | ||
234 | lduw [ %g2 + LDC_TX_QHEAD ], %g6 | |
235 | lduw [ %g2 + LDC_TX_QTAIL ], %g4 | |
236 | ||
237 | ldx [ %g2 + LDC_TX_QSIZE ], %g5 | |
238 | dec Q_EL_SIZE, %g5 | |
239 | add %g4, Q_EL_SIZE, %g1 | |
240 | and %g1, %g5, %g1 | |
241 | ||
242 | cmp %g1, %g6 ! Does TX queue have room? | |
243 | bne,pt %xcc, 2f ! If so, continue. | |
244 | nop | |
245 | ||
246 | ! TX queue is full. Have we already marked it as such? | |
247 | ldub [ %g2 + LDC_TXQ_FULL ], %g5 | |
248 | set 1, %g6 | |
249 | brz,a %g5, 1b ! If not, mark it and try one | |
250 | stb %g6, [ %g2 + LDC_TXQ_FULL ] ! last time to avoid lost intr. | |
251 | ||
252 | ba herr_wouldblock | |
253 | nop | |
254 | 2: | |
255 | ! %g1 = new tail value | |
256 | ! %g2 = our endpoint | |
257 | ! %g3 = guest struct | |
258 | ! %g4 = old tail | |
259 | ||
260 | ldx [ %g2 + LDC_TX_QBASE_PA ], %g3 | |
261 | add %g4, %g3, %g4 | |
262 | ||
263 | ! %g1 = new tail value | |
264 | ! %g2 = sender's endpoint | |
265 | ! %g4 = pointer to outgoing queue entry | |
266 | ||
267 | stx %g0, [%g4] | |
268 | stx %g0, [%g4 + 0x08] | |
269 | stx %g0, [%g4 + 0x10] | |
270 | stx %g0, [%g4 + 0x18] | |
271 | stx %g0, [%g4 + 0x20] | |
272 | stx %g0, [%g4 + 0x28] | |
273 | stx %g0, [%g4 + 0x30] | |
274 | stx %g0, [%g4 + 0x38] | |
275 | ||
276 | set LDC_CONSOLE_DATA, %g5 | |
277 | stb %g5, [%g4 + LDC_CONS_TYPE] | |
278 | mov 1, %g5 ! size=1, one char is | |
279 | stb %g5, [%g4 + LDC_CONS_SIZE] ! being sent | |
280 | stb %o0, [%g4 + LDC_CONS_PAYLOAD] | |
281 | ||
282 | ldub [ %g2 + LDC_TARGET_TYPE ], %g5 | |
283 | cmp %g5, LDC_GUEST_ENDPOINT | |
284 | be %xcc, 3f | |
285 | nop | |
286 | ||
287 | ! %g1 = new tail value | |
288 | ! %g2 = sender's endpoint | |
289 | ||
290 | HVCALL(guest_to_sp_tx_set_tail) ! clobbers all %g1,%g3-%g7 | |
291 | ba 4f | |
292 | nop | |
293 | 3: | |
294 | ||
295 | HVCALL(guest_to_guest_tx_set_tail) ! clobbers all %g1,%g3-%g7 | |
296 | 4: | |
297 | ! %g2 = sender's endpoint | |
298 | ||
299 | HCALL_RET(EOK) | |
300 | ||
301 | /* | |
302 | * Console put char using a service channel as output | |
303 | */ | |
304 | ||
305 | ||
306 | #ifdef CONFIG_CN_UART /* { */ | |
307 | .use_uart_put: | |
308 | ldx [%g3 + GUEST_CONSOLE + CONS_UARTBASE], %g1 | |
309 | ! %g1 = uartp | |
310 | 0: | |
311 | ldub [%g1 + LSR_ADDR], %g4 | |
312 | btst LSR_THRE, %g4 | |
313 | bz,pn %xcc, herr_wouldblock | |
314 | nop | |
315 | stb %o0, [%g1] | |
316 | HCALL_RET(EOK) | |
317 | ||
318 | #endif /* } */ | |
319 | ||
320 | SET_SIZE(hcall_cons_putchar) | |
321 | ||
322 | ||
323 | /* | |
324 | * cons_getchar | |
325 | * | |
326 | * no arguments | |
327 | * -- | |
328 | * ret0 status (%o0) | |
329 | * ret1 char (%o1) | |
330 | */ | |
331 | ENTRY_NP(hcall_cons_getchar) | |
332 | ||
333 | GUEST_STRUCT(%g1) | |
334 | ! %g1 = guestp | |
335 | ||
336 | ! if not initialized, then return EWOULDBLOCK. | |
337 | ! OK What type of console do we have. | |
338 | ldub [%g1 + GUEST_CONSOLE + CONS_TYPE], %g5 | |
339 | cmp %g5, CONS_TYPE_UNCONFIG | |
340 | beq,pn %xcc, herr_wouldblock | |
341 | nop | |
342 | cmp %g5, CONS_TYPE_LDC | |
343 | beq,pt %xcc, .use_ldc_get | |
344 | nop | |
345 | #ifdef CONFIG_CN_UART /* { */ | |
346 | cmp %g5, CONS_TYPE_UART | |
347 | beq,pt %xcc, .use_uart_get | |
348 | nop | |
349 | #endif /* } */ | |
350 | ba,pt %xcc, herr_inval ! Return inval if console not configd | |
351 | nop | |
352 | ||
353 | #ifdef CONFIG_CN_UART /* { */ | |
354 | ||
355 | .use_uart_get: | |
356 | ldx [%g1 + GUEST_CONSOLE + CONS_UARTBASE], %g2 | |
357 | ||
358 | ! %g2 = uartp | |
359 | ldub [%g2 + LSR_ADDR], %g3 ! line status register | |
360 | btst LSR_BINT, %g3 ! BREAK? | |
361 | bz,pt %xcc, 1f | |
362 | nop | |
363 | ||
364 | ! BREAK | |
365 | andn %g3, LSR_BINT, %g3 | |
366 | stb %g3, [%g2 + LSR_ADDR] ! XXX clear BREAK? need w1c | |
367 | mov CONS_BREAK, %o1 | |
368 | HCALL_RET(EOK) | |
369 | ||
370 | 1: btst LSR_DRDY, %g3 ! character ready? | |
371 | bz,pt %xcc, herr_wouldblock | |
372 | nop | |
373 | ||
374 | ldub [%g2], %o1 ! input data register | |
375 | HCALL_RET(EOK) | |
376 | ||
377 | #endif /* } CONFIG_CN_UART */ | |
378 | ||
379 | ! read character from LDC internal buffer | |
380 | ! | |
381 | .use_ldc_get: | |
382 | ||
383 | ! %g1 = guestp | |
384 | ||
385 | mov %g1, %g4 | |
386 | ||
387 | ! LDC based console processing | |
388 | setx GUEST_CONSOLE, %g2, %g3 | |
389 | add %g1, %g3, %g1 | |
390 | ||
391 | ldx [%g1 + CONS_ENDPT], %g2 | |
392 | ||
393 | mulx %g2, LDC_ENDPOINT_SIZE, %g2 | |
394 | set GUEST_LDC_ENDPOINT, %g3 | |
395 | add %g2, %g3, %g2 | |
396 | add %g2, %g4, %g2 | |
397 | ||
398 | ! %g2 = our endpoint | |
399 | ||
400 | ldub [ %g2 + LDC_TARGET_TYPE ], %g3 | |
401 | cmp %g3, LDC_SP_ENDPOINT | |
402 | be %xcc, 1f | |
403 | nop | |
404 | ||
405 | HVCALL(guest_to_guest_pull_data) ! clobbers all %g1,%g3-%g7 | |
406 | ba 2f | |
407 | nop | |
408 | 1: | |
409 | HVCALL(sp_to_guest_pull_data) ! clobbers all %g1,%g3-%g7 | |
410 | 2: | |
411 | ! %g2 = our endpoint | |
412 | ||
413 | lduw [%g2 + LDC_RX_QHEAD], %g3 ! check if there is any data | |
414 | lduw [%g2 + LDC_RX_QTAIL], %g4 ! in our RX queue. | |
415 | cmp %g3, %g4 | |
416 | be %xcc, 1f | |
417 | ||
418 | CPU_PUSH(%g2, %g4, %g5, %g6) ! save off endpoint struct | |
419 | CPU_PUSH(%g3, %g4, %g5, %g6) ! save off head pointer | |
420 | ||
421 | ! There is data in the RX queue, so process the next console packet. | |
422 | ldx [%g2 + LDC_RX_QBASE_PA], %g4 | |
423 | add %g3, %g4, %g2 | |
424 | ||
425 | GUEST_STRUCT(%g1) | |
426 | ||
427 | HVCALL(cons_ldc_callback) | |
428 | ||
429 | CPU_POP(%g3, %g4, %g5, %g6) ! restore head pointer | |
430 | CPU_POP(%g2, %g4, %g5, %g6) ! restore endpoint struct | |
431 | ||
432 | ! Now we have to incriment the head pointer | |
433 | ldx [%g2 + LDC_RX_QSIZE], %g5 | |
434 | dec Q_EL_SIZE, %g5 | |
435 | add %g3, Q_EL_SIZE, %g3 | |
436 | and %g3, %g5, %g5 | |
437 | stw %g5, [%g2 + LDC_RX_QHEAD] | |
438 | ||
439 | 1: | |
440 | GUEST_STRUCT(%g1) | |
441 | ||
442 | ! LDC based console processing | |
443 | setx GUEST_CONSOLE, %g2, %g3 | |
444 | add %g1, %g3, %g1 | |
445 | ||
446 | ! %g1 = guest console | |
447 | ||
448 | ldub [%g1 + CONS_STATUS], %g2 ! chk if ready | |
449 | andcc %g2, LDC_CONS_READY, %g0 | |
450 | bz,pn %xcc, herr_wouldblock | |
451 | nop | |
452 | ||
453 | andcc %g2, LDC_CONS_BREAK, %g0 | |
454 | bz,pt %xcc, 2f | |
455 | nop | |
456 | mov LDC_CONS_READY, %g2 | |
457 | stb %g2, [%g1 + CONS_STATUS] ! clear break | |
458 | mov CONS_BREAK, %o1 | |
459 | HCALL_RET(EOK) | |
460 | 2: | |
461 | andcc %g2, LDC_CONS_HUP, %g0 | |
462 | bz,pt %xcc, 3f | |
463 | nop | |
464 | mov LDC_CONS_READY, %g2 | |
465 | stb %g2, [%g1 + CONS_STATUS] ! clear break | |
466 | mov CONS_HUP, %o1 | |
467 | HCALL_RET(EOK) | |
468 | ||
469 | 3: ! LDC Console data | |
470 | ldx [%g1 + CONS_INHEAD], %g2 ! chk if head=tail | |
471 | ldx [%g1 + CONS_INTAIL], %g3 | |
472 | cmp %g2, %g3 | |
473 | beq,pt %xcc, herr_wouldblock | |
474 | nop | |
475 | ||
476 | add %g1, CONS_INBUF, %g3 ! get inbuf addr | |
477 | add %g3, %g2, %g3 | |
478 | ||
479 | ldub [%g3], %o1 ! input data register | |
480 | ||
481 | inc %g2 ! inc the head | |
482 | and %g2, (CONS_INBUF_SIZE - 1), %g2 | |
483 | stx %g2, [%g1 + CONS_INHEAD] | |
484 | ||
485 | HCALL_RET(EOK) | |
486 | SET_SIZE(hcall_cons_getchar) | |
487 | ||
488 | ||
489 | /* | |
490 | * cons_read - read characters from the console | |
491 | * | |
492 | * Read arg1 characters from the console and place into buffer at arg0. | |
493 | * If arg1 is zero the call immediately returns success, no data | |
494 | * is consumed. | |
495 | * On success ret1 contains either a magic character (CONS_BREAK, CONS_HUP) | |
496 | * or the number of characters placed into the buffer. | |
497 | * | |
498 | * arg0 buffer RA (%o0) | |
499 | * arg1 length (%o1) | |
500 | * -- | |
501 | * ret0 status (%o0) | |
502 | * ret1 length completed (%o1) | |
503 | */ | |
504 | ENTRY_NP(hcall_cons_read) | |
505 | /* | |
506 | * read buffer size is 0, return success | |
507 | */ | |
508 | brz,pn %o1, hret_ok | |
509 | nop | |
510 | ||
511 | GUEST_STRUCT(%g1) | |
512 | ||
513 | ldub [%g1 + GUEST_CONSOLE + CONS_TYPE], %g5 | |
514 | cmp %g5, CONS_TYPE_UNCONFIG | |
515 | beq,pn %xcc, herr_wouldblock | |
516 | nop | |
517 | cmp %g5, CONS_TYPE_LDC | |
518 | beq,pt %xcc, .use_ldc_read | |
519 | nop | |
520 | #ifdef CONFIG_CN_UART /* { */ | |
521 | cmp %g5, CONS_TYPE_UART | |
522 | beq,pt %xcc, .use_uart_read | |
523 | nop | |
524 | #endif /* } */ | |
525 | ba,pt %xcc, herr_inval ! Return inval if console not configd | |
526 | nop | |
527 | ||
528 | #ifdef CONFIG_CN_UART /* { */ | |
529 | ||
530 | .use_uart_read: | |
531 | ldx [%g1 + GUEST_CONSOLE + CONS_UARTBASE], %g2 | |
532 | ! %g2 = uartp | |
533 | ||
534 | ldub [%g2 + LSR_ADDR], %g3 ! line status register | |
535 | btst LSR_BINT, %g3 ! BREAK? | |
536 | bz,pt %xcc, 1f | |
537 | nop | |
538 | ||
539 | ! BREAK | |
540 | andn %g3, LSR_BINT, %g3 | |
541 | stb %g3, [%g2 + LSR_ADDR] ! XXX clear BREAK? need w1c | |
542 | mov CONS_BREAK, %o1 | |
543 | HCALL_RET(EOK) | |
544 | ||
545 | 1: btst LSR_DRDY, %g3 ! character ready? | |
546 | bz,pt %xcc, herr_wouldblock | |
547 | nop | |
548 | ||
549 | RA2PA_RANGE_CONV_UNK_SIZE(%g1, %o0, %o1, herr_noraddr, %g5, %g6) | |
550 | mov %g6, %o0 | |
551 | ! %o0 buf PA | |
552 | ||
553 | ldub [%g2], %g3 ! input data register | |
554 | stb %g3, [%o0] | |
555 | mov 1, %o1 ! Always one character | |
556 | HCALL_RET(EOK) | |
557 | ||
558 | #endif /* } CONFIG_CN_UART */ | |
559 | ||
560 | .use_ldc_read: | |
561 | ! LDC based console processing | |
562 | setx GUEST_CONSOLE, %g2, %g3 | |
563 | add %g1, %g3, %g6 | |
564 | ||
565 | ldx [%g6 + CONS_ENDPT], %g2 | |
566 | ||
567 | mulx %g2, LDC_ENDPOINT_SIZE, %g2 | |
568 | set GUEST_LDC_ENDPOINT, %g3 | |
569 | add %g2, %g3, %g2 | |
570 | add %g2, %g1, %g2 | |
571 | ||
572 | ! %g2 = our endpoint | |
573 | ||
574 | ldub [ %g2 + LDC_TARGET_TYPE ], %g3 | |
575 | cmp %g3, LDC_SP_ENDPOINT | |
576 | be %xcc, 1f | |
577 | nop | |
578 | ||
579 | HVCALL(guest_to_guest_pull_data) ! clobbers all %g1,%g3-%g7 | |
580 | ba 2f | |
581 | nop | |
582 | 1: | |
583 | HVCALL(sp_to_guest_pull_data) ! clobbers all %g1,%g3-%g7 | |
584 | 2: | |
585 | ! %g2 = our endpoint | |
586 | ||
587 | lduw [%g2 + LDC_RX_QHEAD], %g3 ! check if there is any data | |
588 | lduw [%g2 + LDC_RX_QTAIL], %g4 ! in our RX queue. | |
589 | cmp %g3, %g4 | |
590 | be %xcc, 1f | |
591 | ||
592 | CPU_PUSH(%g2, %g4, %g5, %g6) ! save off endpoint struct | |
593 | CPU_PUSH(%g3, %g4, %g5, %g6) ! save off head pointer | |
594 | ||
595 | ! There is data in the RX queue, so process the next console packet. | |
596 | ldx [%g2 + LDC_RX_QBASE_PA], %g4 | |
597 | add %g3, %g4, %g2 | |
598 | ||
599 | GUEST_STRUCT(%g1) | |
600 | ||
601 | HVCALL(cons_ldc_callback) | |
602 | ||
603 | CPU_POP(%g3, %g4, %g5, %g6) ! restore head pointer | |
604 | CPU_POP(%g2, %g4, %g5, %g6) ! restore endpoint struct | |
605 | ||
606 | ! Now we have to incriment the head pointer | |
607 | ldx [%g2 + LDC_RX_QSIZE], %g5 | |
608 | dec Q_EL_SIZE, %g5 | |
609 | add %g3, Q_EL_SIZE, %g3 | |
610 | and %g3, %g5, %g5 | |
611 | stw %g5, [%g2 + LDC_RX_QHEAD] | |
612 | ||
613 | 1: | |
614 | GUEST_STRUCT(%g6) | |
615 | ||
616 | ! LDC based console processing | |
617 | setx GUEST_CONSOLE, %g2, %g3 | |
618 | add %g6, %g3, %g6 | |
619 | ||
620 | ! %g6 = guest console struct | |
621 | ldub [%g6 + CONS_STATUS], %g2 ! chk if ready | |
622 | andcc %g2, LDC_CONS_READY, %g0 | |
623 | bz,pn %xcc, herr_wouldblock | |
624 | nop | |
625 | ||
626 | andcc %g2, LDC_CONS_BREAK, %g0 | |
627 | bz,pt %xcc, 2f | |
628 | nop | |
629 | mov LDC_CONS_READY, %g2 | |
630 | stb %g2, [%g6 + CONS_STATUS] ! clear break | |
631 | mov CONS_BREAK, %o1 | |
632 | HCALL_RET(EOK) | |
633 | 2: | |
634 | andcc %g2, LDC_CONS_HUP, %g0 | |
635 | bz,pt %xcc, 3f | |
636 | nop | |
637 | mov LDC_CONS_READY, %g2 | |
638 | stb %g2, [%g6 + CONS_STATUS] ! clear hup | |
639 | mov CONS_HUP, %o1 | |
640 | HCALL_RET(EOK) | |
641 | 3: | |
642 | ! LDC Console data | |
643 | ldx [%g6 + CONS_INHEAD], %g2 ! chk if head=tail | |
644 | ldx [%g6 + CONS_INTAIL], %g3 | |
645 | cmp %g2, %g3 | |
646 | beq,pt %xcc, herr_wouldblock | |
647 | nop | |
648 | ||
649 | GUEST_STRUCT(%g1) | |
650 | ||
651 | RA2PA_RANGE_CONV_UNK_SIZE(%g1, %o0, %o1, herr_noraddr, %g5, %g4) | |
652 | mov %g4, %o0 | |
653 | ! %o0 buf PA | |
654 | ||
655 | ||
656 | add %g6, CONS_INBUF, %g3 ! get inbuf addr | |
657 | mov %g0, %g4 | |
658 | ||
659 | ! g2 = cons buf head idx | |
660 | ! g3 = cons buf ptr | |
661 | ! g4 = count of chars read | |
662 | ! g5 = current buf tail idx | |
663 | ||
664 | 4: | |
665 | ldub [%g3 + %g2], %g5 | |
666 | stb %g5, [%o0 + %g4] | |
667 | inc %g4 ! inc count | |
668 | inc %g2 ! inc the head | |
669 | and %g2, (CONS_INBUF_SIZE - 1), %g2 | |
670 | stx %g2, [%g6 + CONS_INHEAD] | |
671 | ||
672 | cmp %g4, %o1 | |
673 | bgeu,pt %xcc, 5f | |
674 | nop | |
675 | ldx [%g6 + CONS_INTAIL], %g5 | |
676 | cmp %g2, %g5 | |
677 | beq,pt %xcc, 5f | |
678 | nop | |
679 | ba 4b ! next char | |
680 | nop | |
681 | 5: | |
682 | mov %g4, %o1 ! characters read | |
683 | HCALL_RET(EOK) | |
684 | ||
685 | SET_SIZE(hcall_cons_read) | |
686 | ||
687 | ||
688 | /* | |
689 | * cons_write - write characters to the console | |
690 | * | |
691 | * Writes arg1 characters from the buffer at arg0 to the console. | |
692 | * If arg1 is zero the call immediately returns success, no data | |
693 | * is consumed. | |
694 | * On success ret1 contains the actual number of characters consumed | |
695 | * from the buffer. | |
696 | * | |
697 | * arg0 buffer RA (%o0) | |
698 | * arg1 length (%o1) | |
699 | * -- | |
700 | * ret0 status (%o0) | |
701 | * ret1 length completed (%o1) | |
702 | */ | |
703 | ENTRY_NP(hcall_cons_write) | |
704 | brz,pn %o1, hret_ok | |
705 | nop | |
706 | VCPU_GUEST_STRUCT(%g4, %g3) | |
707 | ||
708 | ! %g3 = guestp | |
709 | ! %g4 = cpup | |
710 | RA2PA_RANGE_CONV_UNK_SIZE(%g3, %o0, %o1, herr_noraddr, %g5, %g2) | |
711 | mov %g2, %o0 | |
712 | ! %o0 buf PA | |
713 | ||
714 | ldub [%g3 + GUEST_CONSOLE + CONS_TYPE], %g5 | |
715 | cmp %g5, CONS_TYPE_UNCONFIG | |
716 | beq,pn %xcc, hret_ok | |
717 | nop | |
718 | cmp %g5, CONS_TYPE_LDC | |
719 | beq,pt %xcc, .use_ldc_write | |
720 | nop | |
721 | #ifdef CONFIG_CN_UART /* { */ | |
722 | cmp %g5, CONS_TYPE_UART | |
723 | beq,pt %xcc, .use_uart_write | |
724 | nop | |
725 | #endif /* } */ | |
726 | ba,pt %xcc, herr_inval ! Return inval if console not configd | |
727 | nop | |
728 | ||
729 | #ifdef CONFIG_CN_UART /* { */ | |
730 | ||
731 | .use_uart_write: | |
732 | ldx [%g3 + GUEST_CONSOLE + CONS_UARTBASE], %g1 | |
733 | ! %g1 = uartp | |
734 | ||
735 | ldub [%g1 + LSR_ADDR], %g4 | |
736 | btst LSR_THRE, %g4 | |
737 | bz,pn %xcc, herr_wouldblock | |
738 | nop | |
739 | ||
740 | mov 0, %g2 | |
741 | ! %g2 count of characters written | |
742 | 1: | |
743 | ldub [%o0 + %g2], %g3 | |
744 | stb %g3, [%g1] | |
745 | inc %g2 | |
746 | cmp %g2, %o1 | |
747 | bgeu,pn %xcc, 2f | |
748 | nop | |
749 | ldub [%g1 + LSR_ADDR], %g4 | |
750 | btst LSR_THRE, %g4 | |
751 | bnz,pt %xcc, 1b | |
752 | nop | |
753 | ||
754 | 2: | |
755 | mov %g2, %o1 | |
756 | HCALL_RET(EOK) | |
757 | ||
758 | #endif /* } CONFIG_CN_UART */ | |
759 | ||
760 | .use_ldc_write: | |
761 | setx GUEST_CONSOLE, %g2, %g6 | |
762 | add %g6, %g3, %g6 | |
763 | ||
764 | ldub [%g6 + CONS_STATUS], %g2 ! chk if ready | |
765 | andcc %g2, LDC_CONS_READY, %g2 | |
766 | bz,pn %xcc, herr_wouldblock | |
767 | nop | |
768 | ||
769 | ldx [%g6 + CONS_ENDPT], %g1 | |
770 | ||
771 | ! %g1 = channel | |
772 | ! %g3 = guest struct | |
773 | ||
774 | mulx %g1, LDC_ENDPOINT_SIZE, %g1 | |
775 | set GUEST_LDC_ENDPOINT, %g2 | |
776 | add %g1, %g2, %g1 | |
777 | add %g1, %g3, %g2 | |
778 | ||
779 | ! target is a guest endpoint | |
780 | ! if it has no receive queue configured drop the | |
781 | ! console char and return back | |
782 | ldub [ %g2 + LDC_TARGET_TYPE ], %g5 | |
783 | cmp %g5, LDC_GUEST_ENDPOINT | |
784 | bne %xcc, 1f | |
785 | nop | |
786 | ||
787 | ldx [ %g2 + LDC_TARGET_GUEST ], %g6 | |
788 | ldx [ %g2 + LDC_TARGET_CHANNEL ], %g4 | |
789 | mulx %g4, LDC_ENDPOINT_SIZE, %g4 | |
790 | set GUEST_LDC_ENDPOINT, %g5 | |
791 | add %g6, %g5, %g6 | |
792 | add %g6, %g4, %g6 ! g6 is the target endpoint | |
793 | ldx [ %g6 + LDC_RX_QSIZE ], %g4 ! check if queue is configured | |
794 | brnz,a,pn %g4, 1f | |
795 | nop | |
796 | HCALL_RET(EOK) | |
797 | ||
798 | ! %g2 = our endpoint | |
799 | ! %g3 = guest struct | |
800 | 1: | |
801 | lduw [ %g2 + LDC_TX_QHEAD ], %g6 | |
802 | lduw [ %g2 + LDC_TX_QTAIL ], %g4 | |
803 | ||
804 | ldx [ %g2 + LDC_TX_QSIZE ], %g5 | |
805 | dec Q_EL_SIZE, %g5 | |
806 | add %g4, Q_EL_SIZE, %g1 | |
807 | and %g1, %g5, %g1 | |
808 | ||
809 | cmp %g1, %g6 ! Does TX queue have room? | |
810 | bne,pt %xcc, 2f ! If so, continue. | |
811 | nop | |
812 | ||
813 | ! TX queue is full. Have we already marked it as such? | |
814 | ldub [ %g2 + LDC_TXQ_FULL ], %g5 | |
815 | set 1, %g6 | |
816 | brz,a %g5, 1b ! If not, mark it and try one | |
817 | stb %g6, [ %g2 + LDC_TXQ_FULL ] ! last time to avoid lost intr. | |
818 | ||
819 | ba herr_wouldblock | |
820 | nop | |
821 | 2: | |
822 | ! %g2 = our endpoint | |
823 | ! %g3 = guest struct | |
824 | ||
825 | ldx [ %g2 + LDC_TX_QBASE_PA ], %g3 | |
826 | add %g4, %g3, %g4 | |
827 | ||
828 | ! %g1 = new tail value | |
829 | ! %g2 = sender's endpoint | |
830 | ! %g4 = pointer to outgoing queue entry | |
831 | ||
832 | set LDC_CONSOLE_DATA, %g5 | |
833 | stb %g5, [%g4 + LDC_CONS_TYPE] | |
834 | mov %g0, %g6 | |
835 | add %g4, LDC_CONS_PAYLOAD, %g3 ! payload buffer | |
836 | 3: | |
837 | ldub [%o0 + %g6], %g5 | |
838 | stb %g5, [%g3 + %g6] | |
839 | inc %g6 | |
840 | cmp %g6, %o1 ! copied all chars ? | |
841 | bgeu,pn %xcc, 4f | |
842 | nop | |
843 | cmp %g6, LDC_CONS_PAYLOAD_SZ ! payload buf full ? | |
844 | bgeu,pn %xcc, 4f | |
845 | nop | |
846 | ba 3b ! store next char in pkt | |
847 | nop | |
848 | 4: | |
849 | mov %g6, %o1 ! return / store chars copied | |
850 | stb %g6, [%g4 + LDC_CONS_SIZE] | |
851 | ||
852 | ldub [ %g2 + LDC_TARGET_TYPE ], %g5 | |
853 | cmp %g5, LDC_GUEST_ENDPOINT | |
854 | be %xcc, 3f | |
855 | nop | |
856 | ||
857 | ! %g1 = new tail value | |
858 | ! %g2 = sender's endpoint | |
859 | ||
860 | HVCALL(guest_to_sp_tx_set_tail) ! clobbers all %g1,%g3-%g7 | |
861 | ba 4f | |
862 | nop | |
863 | 3: | |
864 | HVCALL(guest_to_guest_tx_set_tail) ! clobbers all %g1,%g3-%g7 | |
865 | 4: | |
866 | ! %g2 = sender's endpoint | |
867 | ||
868 | HCALL_RET(EOK) | |
869 | ||
870 | SET_SIZE(hcall_cons_write) | |
871 | ||
872 |