Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * Hypervisor Software File: res_console.c | |
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 | #pragma ident "@(#)res_console.c 1.6 07/06/07 SMI" | |
50 | ||
51 | #include <stdarg.h> | |
52 | ||
53 | #include <sys/htypes.h> | |
54 | #include <traps.h> | |
55 | #include <cache.h> | |
56 | #include <mmu.h> | |
57 | #include <sun4v/asi.h> | |
58 | #include <vdev_intr.h> | |
59 | #include <ncs.h> | |
60 | #include <cyclic.h> | |
61 | #include <support.h> | |
62 | #include <strand.h> | |
63 | #include <vcpu.h> | |
64 | #include <guest.h> | |
65 | #include <pcie.h> | |
66 | #include <vdev_ops.h> | |
67 | #include <fpga.h> | |
68 | #include <ldc.h> | |
69 | #include <config.h> | |
70 | #include <offsets.h> | |
71 | #include <hvctl.h> | |
72 | #include <md.h> | |
73 | #include <abort.h> | |
74 | #include <hypervisor.h> | |
75 | #include <proto.h> | |
76 | #include <debug.h> | |
77 | ||
78 | /* | |
79 | * (re)-configuration code to handle HV guest consoles | |
80 | */ | |
81 | static hvctl_status_t res_console_parse_1(bin_md_t *mdp, | |
82 | md_element_t *cons_nodep, | |
83 | hvctl_res_error_t *fail_codep, int *fail_res_idp); | |
84 | ||
85 | static void res_console_commit_config(guest_t *guestp); | |
86 | static void res_console_commit_unconfig(guest_t *guestp); | |
87 | static void res_console_commit_modify(guest_t *guestp); | |
88 | ||
89 | ||
90 | void | |
91 | init_consoles() | |
92 | { | |
93 | guest_t *gp; | |
94 | int i; | |
95 | ||
96 | gp = (guest_t *)config.guests; | |
97 | ||
98 | for (i = 0; i < NGUESTS; i++, gp++) { | |
99 | gp->console.type = CONS_TYPE_UNCONFIG; | |
100 | } | |
101 | } | |
102 | ||
103 | /* | |
104 | * Console MD support functions. | |
105 | */ | |
106 | void | |
107 | res_console_prep() | |
108 | { | |
109 | guest_t *gp; | |
110 | int i; | |
111 | ||
112 | gp = config.guests; | |
113 | ||
114 | for (i = 0; i < NGUESTS; i++, gp++) { | |
115 | gp->console.pip.res.flags = | |
116 | (gp->console.type == CONS_TYPE_UNCONFIG) ? | |
117 | RESF_Noop : RESF_Unconfig; | |
118 | } | |
119 | } | |
120 | ||
121 | ||
122 | hvctl_status_t | |
123 | res_console_parse(bin_md_t *mdp, hvctl_res_error_t *fail_codep, | |
124 | md_element_t **failnodepp, int *fail_res_idp) | |
125 | { | |
126 | md_element_t *mdep; | |
127 | uint64_t arc_token; | |
128 | uint64_t node_token; | |
129 | md_element_t *cons_nodep; | |
130 | ||
131 | mdp = (bin_md_t *)config.parse_hvmd; | |
132 | ||
133 | DBG(c_printf("\nConsole configuration:\n")); | |
134 | ||
135 | mdep = md_find_node(mdp, NULL, MDNAME(consoles)); | |
136 | if (mdep == NULL) { | |
137 | DBG(c_printf("Missing consoles node in HVMD\n")); | |
138 | *failnodepp = NULL; | |
139 | *fail_res_idp = 0; | |
140 | return (HVctl_st_badmd); | |
141 | } | |
142 | ||
143 | arc_token = MDARC(MDNAME(fwd)); | |
144 | node_token = MDNODE(MDNAME(console)); | |
145 | ||
146 | while (NULL != (mdep = md_find_node_by_arc(mdp, mdep, arc_token, | |
147 | node_token, &cons_nodep))) { | |
148 | hvctl_status_t status; | |
149 | status = res_console_parse_1(mdp, cons_nodep, fail_codep, | |
150 | fail_res_idp); | |
151 | if (status != HVctl_st_ok) { | |
152 | *failnodepp = cons_nodep; | |
153 | return (status); | |
154 | } | |
155 | } | |
156 | DBG(c_printf("\nConsole configuration: OK \n")); | |
157 | return (HVctl_st_ok); | |
158 | } | |
159 | ||
160 | ||
161 | hvctl_status_t | |
162 | res_console_parse_1(bin_md_t *mdp, md_element_t *cons_nodep, | |
163 | hvctl_res_error_t *fail_codep, int *fail_res_idp) | |
164 | { | |
165 | uint64_t resource_id, guest_id, channel_id, ino; | |
166 | guest_t *guestp; | |
167 | md_element_t *guest_nodep, *ldc_nodep, *mdep; | |
168 | ||
169 | DBG(md_dump_node(mdp, cons_nodep)); | |
170 | ||
171 | if (!md_node_get_val(mdp, cons_nodep, MDNAME(resource_id), | |
172 | &resource_id)) { | |
173 | DBG(c_printf("Missing resource_id in console node\n")); | |
174 | *fail_codep = HVctl_e_cons_missing_id; | |
175 | *fail_res_idp = 0; | |
176 | return (HVctl_st_badmd); | |
177 | } | |
178 | ||
179 | *fail_res_idp = resource_id; | |
180 | ||
181 | /* | |
182 | * This console node needs to point back to the guest | |
183 | * that owns it. | |
184 | */ | |
185 | ||
186 | mdep = md_find_node_by_arc(mdp, cons_nodep, MDARC(MDNAME(back)), | |
187 | MDNODE(MDNAME(guest)), &guest_nodep); | |
188 | if (mdep == NULL) { | |
189 | DBG(c_printf("Missing guest for console 0x%x", resource_id)); | |
190 | *fail_codep = HVctl_e_cons_missing_guest; | |
191 | goto fail; | |
192 | } | |
193 | ||
194 | if (!md_node_get_val(mdp, guest_nodep, MDNAME(resource_id), | |
195 | &guest_id)) { | |
196 | DBG(c_printf("Missing resource_id in console's guest node\n")); | |
197 | *fail_codep = HVctl_e_cons_missing_guest_id; | |
198 | *fail_res_idp = 0; | |
199 | return (HVctl_st_badmd); | |
200 | } | |
201 | if (guest_id >= NGUESTS) { | |
202 | DBG(c_printf("Invalid resource_id in console's guest node\n")); | |
203 | *fail_codep = HVctl_e_cons_invalid_guest_id; | |
204 | *fail_res_idp = 0; | |
205 | return (HVctl_st_badmd); | |
206 | } | |
207 | ||
208 | guestp = config.guests; | |
209 | guestp = &(guestp[guest_id]); | |
210 | ||
211 | DBG(c_printf("\tconsole 0x%x for guest 0x%x @ 0x%x\n", | |
212 | resource_id, guest_id, (uint64_t)guestp)); | |
213 | ||
214 | /* | |
215 | * This console node points fwd to the LDC node | |
216 | * that it needs. Failing that there should be a | |
217 | * UART address in the node for this specific | |
218 | * console. | |
219 | */ | |
220 | ||
221 | mdep = md_find_node_by_arc(mdp, cons_nodep, MDARC(MDNAME(fwd)), | |
222 | MDNODE(MDNAME(ldc_endpoint)), &ldc_nodep); | |
223 | if (mdep == NULL) { | |
224 | #ifdef CONFIG_CN_UART /* { */ | |
225 | uint64_t uartbase; | |
226 | if (!md_node_get_val(mdp, cons_nodep, MDNAME(uartbase), | |
227 | &uartbase)) { | |
228 | DBG(c_printf("Missing ldc arc or uartbase in console" | |
229 | " node\n")); | |
230 | *fail_codep = HVctl_e_cons_missing_uartbase; | |
231 | goto fail; | |
232 | } | |
233 | guestp->console.pip.type = CONS_TYPE_UART; | |
234 | guestp->console.pip.uartbase = uartbase; | |
235 | DBG(c_printf("UART specified for console\n")); | |
236 | #else /* } { */ | |
237 | DBG(c_printf("No ldc endpoint specified for console\n")); | |
238 | *fail_codep = HVctl_e_cons_missing_ldc_id; | |
239 | goto fail; | |
240 | #endif /* } */ | |
241 | } else { | |
242 | ||
243 | /* | |
244 | * FIXME: LDC errors shouldn't really be possible here | |
245 | * we should have already parsed all the LDC endpoints | |
246 | * so if properties were wrong or missing by this point | |
247 | * we would already have found out. | |
248 | * We leave the checks in here for sanity for now. | |
249 | */ | |
250 | ||
251 | if (!md_node_get_val(mdp, ldc_nodep, MDNAME(channel), | |
252 | &channel_id)) { | |
253 | DBG(c_printf("Missing channel_id in console's" | |
254 | " ldc node\n")); | |
255 | *fail_codep = HVctl_e_cons_missing_ldc_id; | |
256 | *fail_res_idp = 0; | |
257 | goto fail; | |
258 | } | |
259 | if (channel_id >= MAX_LDC_CHANNELS) { | |
260 | DBG(c_printf("Invalid channel id in console's ldc" | |
261 | " node\n")); | |
262 | *fail_codep = HVctl_e_cons_invalid_ldc_id; | |
263 | *fail_res_idp = 0; | |
264 | goto fail; | |
265 | } | |
266 | ||
267 | DBG(c_printf("\tconsole 0x%x for guest 0x%x [@ 0x%x]" | |
268 | " uses LDC channel 0x%x\n", | |
269 | resource_id, guest_id, (uint64_t)guestp, channel_id)); | |
270 | ||
271 | guestp->console.pip.type = CONS_TYPE_LDC; | |
272 | guestp->console.pip.ldc_channel = channel_id; | |
273 | } | |
274 | ||
275 | ||
276 | /* Determine the interrupt the console is bound to */ | |
277 | if (!md_node_get_val(mdp, cons_nodep, MDNAME(ino), &ino)) { | |
278 | DBG(c_printf("Missing ino in console node\n")); | |
279 | *fail_codep = HVctl_e_cons_missing_ino; | |
280 | goto fail; | |
281 | } | |
282 | if (ino >= NINOSPERDEV) { | |
283 | DBG(c_printf("Invalid ino in console node\n")); | |
284 | *fail_codep = HVctl_e_cons_invalid_ino; | |
285 | goto fail; | |
286 | } | |
287 | guestp->console.pip.ino = ino; | |
288 | ||
289 | /* | |
290 | * Now we go and figureout what we need to do to the | |
291 | * guest console to update or configure its state. | |
292 | */ | |
293 | if (guestp->console.type == CONS_TYPE_UNCONFIG) { | |
294 | DBG(c_printf("\t\tElected to config console of " | |
295 | "guest 0x%x\n", guest_id)); | |
296 | guestp->console.pip.res.flags = RESF_Config; | |
297 | } else { | |
298 | /* BEGIN CSTYLED */ | |
299 | if (guestp->console.type != guestp->console.pip.type || | |
300 | (guestp->console.type == CONS_TYPE_LDC && | |
301 | guestp->console.endpt != | |
302 | guestp->console.pip.ldc_channel) || | |
303 | (guestp->console.vintr_mapreg->ino != | |
304 | guestp->console.pip.ino) | |
305 | #ifdef CONFIG_CN_UART /* { */ | |
306 | || ((guestp->console.type == CONS_TYPE_UART) && | |
307 | (guestp->console.uartbase != | |
308 | guestp->console.pip.uartbase)) | |
309 | #endif /* } */ | |
310 | ) { | |
311 | ||
312 | DBG(c_printf("\t\tElected to modify console of " | |
313 | "guest 0x%x\n", guest_id)); | |
314 | guestp->console.pip.res.flags = RESF_Modify; | |
315 | } else { | |
316 | DBG(c_printf("\t\tElected to ignore console of " | |
317 | "guest 0x%x\n", guest_id)); | |
318 | guestp->console.pip.res.flags = RESF_Noop; | |
319 | } | |
320 | /* END CSTYLED */ | |
321 | } | |
322 | ||
323 | return (HVctl_st_ok); | |
324 | ||
325 | fail:; | |
326 | return (HVctl_st_badmd); | |
327 | } | |
328 | ||
329 | ||
330 | hvctl_status_t | |
331 | res_console_postparse(hvctl_res_error_t *res_error, int *fail_res_id) | |
332 | { | |
333 | return (HVctl_st_ok); | |
334 | } | |
335 | ||
336 | ||
337 | void | |
338 | res_console_commit(int flag) | |
339 | { | |
340 | guest_t *gp; | |
341 | int i; | |
342 | ||
343 | gp = config.guests; | |
344 | ||
345 | for (i = 0; i < NGUESTS; i++, gp++) { | |
346 | /* if not this ops turn move on */ | |
347 | if (gp->console.pip.res.flags != flag) continue; | |
348 | ||
349 | switch (gp->console.pip.res.flags) { | |
350 | case RESF_Noop: | |
351 | DBG(c_printf("console for guest 0x%x : noop\n", i)); | |
352 | break; | |
353 | case RESF_Unconfig: | |
354 | res_console_commit_unconfig(gp); | |
355 | break; | |
356 | case RESF_Config: | |
357 | DBG(c_printf("console for guest 0x%x : config\n", i)); | |
358 | res_console_commit_config(gp); | |
359 | break; | |
360 | case RESF_Rebind: | |
361 | DBG(c_printf("console for guest 0x%x : rebind\n", i)); | |
362 | ASSERT(0); /* not supported */ | |
363 | break; | |
364 | case RESF_Modify: | |
365 | res_console_commit_modify(gp); | |
366 | break; | |
367 | default: | |
368 | ASSERT(0); | |
369 | } | |
370 | ||
371 | gp->console.pip.res.flags = RESF_Noop; /* cleanup */ | |
372 | } | |
373 | } | |
374 | ||
375 | ||
376 | void | |
377 | res_console_commit_modify(guest_t *guestp) | |
378 | { | |
379 | DBG(c_printf("commit modify for guest 0x%x console\n", | |
380 | guestp->guestid)); | |
381 | ASSERT(guestp->console.type != CONS_TYPE_UNCONFIG); | |
382 | res_console_commit_unconfig(guestp); | |
383 | res_console_commit_config(guestp); | |
384 | } | |
385 | ||
386 | ||
387 | void | |
388 | res_console_commit_config(guest_t *guestp) | |
389 | { | |
390 | int channelid; | |
391 | int ino; | |
392 | ldc_endpoint_t *ldc_ep; | |
393 | vdev_mapreg_t *mapregp; | |
394 | ||
395 | DBG(c_printf("commit config for guest 0x%x console\n", | |
396 | guestp->guestid)); | |
397 | ASSERT(guestp->console.type == CONS_TYPE_UNCONFIG); | |
398 | ||
399 | guestp->console.type = guestp->console.pip.type; | |
400 | ||
401 | #ifdef CONFIG_CN_UART /* { */ | |
402 | if (guestp->console.type == CONS_TYPE_UART) { | |
403 | guestp->console.uartbase = guestp->console.pip.uartbase; | |
404 | return; | |
405 | } | |
406 | #endif /* } */ | |
407 | ||
408 | ASSERT(guestp->console.type == CONS_TYPE_LDC); | |
409 | ||
410 | channelid = guestp->console.pip.ldc_channel; | |
411 | ||
412 | guestp->console.endpt = channelid; | |
413 | ||
414 | guestp->console.in_head = 0; | |
415 | guestp->console.in_tail = 0; | |
416 | guestp->console.status = LDC_CONS_READY; | |
417 | ||
418 | ldc_ep = &(guestp->ldc_endpoint[channelid]); | |
419 | ||
420 | /* Quick check that the LDC endpoint was configured */ | |
421 | ASSERT(ldc_ep->is_live); | |
422 | ASSERT(ldc_ep->is_private); | |
423 | ASSERT(ldc_ep->svc_id == LDC_CONSOLE_SVC); | |
424 | ||
425 | ino = guestp->console.pip.ino; | |
426 | DBG(c_printf("\tconsole - ino 0x%x\n", ino)); | |
427 | ||
428 | mapregp = &(guestp->vdev_state.mapreg[(ino & DEVINOMASK)]); | |
429 | ||
430 | guestp->console.vintr_mapreg = mapregp; | |
431 | ldc_ep->rx_vintr_cookie = mapregp; | |
432 | mapregp->ino = ino; | |
433 | ||
434 | config_a_guest_device_vino(guestp, ino, DEVOPS_VDEV); | |
435 | ||
436 | ldc_ep->tx_qbase_pa = | |
437 | (uint64_t)&cons_queues[guestp->guestid].cons_txq; | |
438 | ||
439 | ldc_ep->rx_qbase_pa = | |
440 | (uint64_t)&cons_queues[guestp->guestid].cons_rxq; | |
441 | ||
442 | ldc_ep->tx_qsize = Q_EL_SIZE * LDC_CONS_QSIZE; | |
443 | ldc_ep->rx_qsize = Q_EL_SIZE * LDC_CONS_QSIZE; | |
444 | ||
445 | #if defined(CONFIG_FPGA) /* { */ | |
446 | if (ldc_ep->target_type == LDC_SP_ENDPOINT) { | |
447 | sp_ldc_endpoint_t *sp_ept; | |
448 | sram_ldc_qd_t *sram_qd_pa; | |
449 | ||
450 | sp_ept = config.sp_ldcs; | |
451 | sp_ept = &(sp_ept[ldc_ep->target_channel]); | |
452 | sram_qd_pa = sp_ept->rx_qd_pa; | |
453 | ||
454 | sram_qd_pa->state_updated = 1; | |
455 | sram_qd_pa->state = 1; | |
456 | sram_qd_pa->state_notify = 1; | |
457 | c_ldc_send_sp_intr(sp_ept, SP_LDC_STATE_CHG); | |
458 | } | |
459 | #endif /* } CONFIG_FPGA */ | |
460 | ||
461 | DBG(c_printf("\tguest has LDC based console.\n")); | |
462 | } | |
463 | ||
464 | ||
465 | void | |
466 | res_console_commit_unconfig(guest_t *guestp) | |
467 | { | |
468 | ldc_endpoint_t *ldc_ep; | |
469 | int ino; | |
470 | ||
471 | DBG(c_printf("commit unconfig for guest 0x%x console\n", | |
472 | guestp->guestid)); | |
473 | ASSERT(guestp->console.type != CONS_TYPE_UNCONFIG); | |
474 | ||
475 | #ifdef CONFIG_CN_UART /* { */ | |
476 | if (guestp->console.type == CONS_TYPE_UART) { | |
477 | guestp->console.type = CONS_TYPE_UNCONFIG; | |
478 | return; | |
479 | } | |
480 | #endif /* } */ | |
481 | ||
482 | ldc_ep = &(guestp->ldc_endpoint[guestp->console.endpt]); | |
483 | ||
484 | /* Quick check that the LDC endpoint was configured */ | |
485 | ASSERT(ldc_ep->is_live); | |
486 | ASSERT(ldc_ep->is_private); | |
487 | ASSERT(ldc_ep->svc_id == LDC_CONSOLE_SVC); | |
488 | ||
489 | ino = guestp->console.vintr_mapreg->ino; | |
490 | unconfig_a_guest_device_vino(guestp, ino, DEVOPS_VDEV); | |
491 | ||
492 | guestp->console.vintr_mapreg = NULL; | |
493 | ldc_ep->rx_vintr_cookie = NULL; | |
494 | ldc_ep->rx_qsize = 0; | |
495 | ldc_ep->tx_qsize = 0; | |
496 | /* Leave the Q bases incase packets are in flight */ | |
497 | guestp->console.type = CONS_TYPE_UNCONFIG; | |
498 | } |