Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * Hypervisor Software File: res_ldc.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_ldc.c 1.7 07/06/07 SMI" | |
50 | ||
51 | #include <stdarg.h> | |
52 | #include <sys/htypes.h> | |
53 | #include <traps.h> | |
54 | #include <mmu.h> | |
55 | #include <vdev_intr.h> | |
56 | #include <ncs.h> | |
57 | #include <cyclic.h> | |
58 | #include <support.h> | |
59 | #include <strand.h> | |
60 | #include <vcpu.h> | |
61 | #include <guest.h> | |
62 | #include <pcie.h> | |
63 | #include <vdev_ops.h> | |
64 | #include <fpga.h> | |
65 | #include <ldc.h> | |
66 | #include <config.h> | |
67 | #include <offsets.h> | |
68 | #include <hvctl.h> | |
69 | #include <md.h> | |
70 | #include <abort.h> | |
71 | #include <hypervisor.h> | |
72 | #include <proto.h> | |
73 | #include <debug.h> | |
74 | ||
75 | ||
76 | /* | |
77 | * (re)-configuration code to handle HV LDC resources | |
78 | */ | |
79 | static void res_ldc_commit_config(guest_t *gp, ldc_endpoint_t *ldcp); | |
80 | static void res_ldc_commit_unconfig(guest_t *gp, ldc_endpoint_t *ldcp); | |
81 | ||
82 | static hvctl_status_t res_ldc_parse_1(bin_md_t *mdp, md_element_t *guest_nodep, | |
83 | hvctl_res_error_t *fail_codep, | |
84 | md_element_t **failnodepp, int *fail_res_idp); | |
85 | ||
86 | static hvctl_status_t res_ldc_parse_2(bin_md_t *mdp, int guest_id, | |
87 | md_element_t *ldce_nodep, | |
88 | hvctl_res_error_t *fail_codep, int *fail_res_idp); | |
89 | ||
90 | static void res_hv_ldc_commit_config(int ch_id); | |
91 | static void res_hv_ldc_commit_unconfig(int ch_id); | |
92 | ||
93 | static hvctl_status_t res_hv_ldc_parse_1(bin_md_t *mdp, | |
94 | md_element_t *hvldc_nodep, | |
95 | hvctl_res_error_t *fail_codep, int *fail_res_idp); | |
96 | ||
97 | ||
98 | ||
99 | /* | |
100 | * LDC support functions | |
101 | * | |
102 | * FIXME: This changes when LDC endpoints have their own | |
103 | * global IDs. This means for now the uniq ID for an endpoints | |
104 | * is the tuple: (guest_id, channel_id) | |
105 | * | |
106 | * We'll fix this later... | |
107 | */ | |
108 | void | |
109 | res_ldc_prep() | |
110 | { | |
111 | guest_t *gp; | |
112 | int i; | |
113 | ||
114 | gp = config.guests; | |
115 | ||
116 | for (i = 0; i < NGUESTS; i++, gp++) { | |
117 | int j; | |
118 | ||
119 | /* | |
120 | * If the guest is already unconfigured, then | |
121 | * we've nothing to potentially unconfigure | |
122 | * So we could optimise by skipping all the | |
123 | * channels, but it's pointless because the | |
124 | * per-guest endpoints are going away. | |
125 | * So for now we brute force all endpoints. | |
126 | */ | |
127 | for (j = 0; j < MAX_LDC_CHANNELS; j++) { | |
128 | ldc_endpoint_t *ep; | |
129 | ||
130 | ep = &(gp->ldc_endpoint[j]); | |
131 | ||
132 | /* unconfigd guest cannot have live channel */ | |
133 | ASSERT(!(gp->state == GUEST_STATE_UNCONFIGURED && | |
134 | ep->is_live)); | |
135 | ||
136 | /* if live potentially unconfig it */ | |
137 | ep->pip.res.flags = ep->is_live ? | |
138 | RESF_Unconfig : RESF_Noop; | |
139 | } | |
140 | } | |
141 | } | |
142 | ||
143 | ||
144 | hvctl_status_t | |
145 | res_ldc_parse(bin_md_t *mdp, hvctl_res_error_t *fail_codep, | |
146 | md_element_t **failnodepp, int *fail_res_idp) | |
147 | { | |
148 | md_element_t *mdep; | |
149 | uint64_t arc_token; | |
150 | uint64_t node_token; | |
151 | md_element_t *guest_nodep; | |
152 | hvctl_status_t status; | |
153 | ||
154 | mdp = (bin_md_t *)config.parse_hvmd; | |
155 | ||
156 | DBGL(c_printf("\nLDC configuration:\n")); | |
157 | ||
158 | mdep = md_find_node(mdp, NULL, MDNAME(guests)); | |
159 | if (mdep == NULL) { | |
160 | DBG(c_printf("Missing guests node in HVMD\n")); | |
161 | *failnodepp = NULL; | |
162 | *fail_res_idp = 0; | |
163 | return (HVctl_st_badmd); | |
164 | } | |
165 | ||
166 | /* | |
167 | * FIXME: this multistep process goes away once the top-level | |
168 | * ldc-endpoints aggregator includes all endpoints. | |
169 | */ | |
170 | arc_token = MDARC(MDNAME(fwd)); | |
171 | node_token = MDNODE(MDNAME(guest)); | |
172 | ||
173 | status = HVctl_st_ok; | |
174 | while (status == HVctl_st_ok && | |
175 | NULL != (mdep = md_find_node_by_arc(mdp, mdep, arc_token, | |
176 | node_token, &guest_nodep))) { | |
177 | status = res_ldc_parse_1(mdp, guest_nodep, fail_codep, | |
178 | failnodepp, fail_res_idp); | |
179 | } | |
180 | return (status); | |
181 | } | |
182 | ||
183 | ||
184 | hvctl_status_t | |
185 | res_ldc_parse_1(bin_md_t *mdp, md_element_t *guest_nodep, | |
186 | hvctl_res_error_t *fail_codep, | |
187 | md_element_t **failnodepp, int *fail_res_idp) | |
188 | { | |
189 | md_element_t *elemp; | |
190 | md_element_t *ldce_nodep; | |
191 | int dummy; | |
192 | uint64_t guest_id; | |
193 | uint64_t arc_token; | |
194 | uint64_t node_token; | |
195 | ||
196 | /* | |
197 | * by now we know its here because we passed the guest parse | |
198 | * which is higher priority than this parse function. | |
199 | */ | |
200 | dummy = md_node_get_val(mdp, guest_nodep, MDNAME(resource_id), | |
201 | &guest_id); | |
202 | ASSERT(dummy); | |
203 | ||
204 | #if defined(lint) | |
205 | dummy = dummy; | |
206 | #endif | |
207 | ||
208 | DBGL(c_printf("\tGuest 0x%x for ldcs:\n", guest_id)); | |
209 | ||
210 | arc_token = MDARC(MDNAME(fwd)); | |
211 | node_token = MDNODE(MDNAME(ldc_endpoint)); | |
212 | ||
213 | /* | |
214 | * Spin through the "ldc_endpoint" arcs in the | |
215 | * ldc_endpoints node and config each endpoint ! | |
216 | * FIXME; what if already configured ! | |
217 | */ | |
218 | elemp = guest_nodep; | |
219 | while (NULL != (elemp = md_find_node_by_arc(mdp, elemp, arc_token, | |
220 | node_token, &ldce_nodep))) { | |
221 | hvctl_status_t status; | |
222 | ||
223 | status = res_ldc_parse_2(mdp, guest_id, ldce_nodep, | |
224 | fail_codep, fail_res_idp); | |
225 | if (status != HVctl_st_ok) { | |
226 | *failnodepp = ldce_nodep; | |
227 | return (HVctl_st_badmd); | |
228 | } | |
229 | } | |
230 | return (HVctl_st_ok); | |
231 | } | |
232 | ||
233 | ||
234 | hvctl_status_t | |
235 | res_ldc_parse_2(bin_md_t *mdp, int guest_id, md_element_t *ldce_nodep, | |
236 | hvctl_res_error_t *fail_codep, int *fail_res_idp) | |
237 | { | |
238 | uint64_t endpt_id; | |
239 | ldc_endpoint_t *ldc_ep; | |
240 | uint64_t target_type; | |
241 | uint64_t target_channel; | |
242 | uint64_t tx_ino; | |
243 | uint64_t rx_ino; | |
244 | guest_t *guestp; | |
245 | bool_t check_guest; | |
246 | guest_t *target_guestp; | |
247 | uint64_t pvt_svc; | |
248 | ||
249 | guestp = config.guests; | |
250 | guestp = &(guestp[guest_id]); | |
251 | ||
252 | if (!md_node_get_val(mdp, ldce_nodep, MDNAME(channel), | |
253 | &endpt_id)) { | |
254 | DBGL(c_printf("Missing channel (endpoint number) in" | |
255 | " ldc_endpoint node\n")); | |
256 | *fail_res_idp = 0; | |
257 | miss_prop: | |
258 | *fail_codep = HVctl_e_ldc_missing_prop; | |
259 | return (HVctl_st_badmd); | |
260 | } | |
261 | ||
262 | if (endpt_id >= MAX_LDC_CHANNELS) { | |
263 | DBGL(c_printf("Illegal channel (endpoint number) in" | |
264 | " ldc_endpoint node\n")); | |
265 | *fail_res_idp = 0; | |
266 | ill_prop: | |
267 | *fail_codep = HVctl_e_ldc_illegal_prop; | |
268 | return (HVctl_st_badmd); | |
269 | } | |
270 | ||
271 | DBGL(c_printf("\t\tGuest 0x%x endpoint 0x%x\n", guest_id, endpt_id)); | |
272 | ||
273 | ldc_ep = &(guestp->ldc_endpoint[endpt_id]); | |
274 | ||
275 | ldc_ep->pip.channel = endpt_id; | |
276 | ||
277 | *fail_res_idp = endpt_id; | |
278 | ||
279 | if (!md_node_get_val(mdp, ldce_nodep, MDNAME(target_type), | |
280 | &target_type)) { | |
281 | DBGL(c_printf("Missing target_type in ldc_endpoint node\n")); | |
282 | goto miss_prop; | |
283 | } | |
284 | if (!md_node_get_val(mdp, ldce_nodep, MDNAME(target_channel), | |
285 | &target_channel)) { | |
286 | DBGL(c_printf("Missing target_channel in " | |
287 | "ldc_endpoint node\n")); | |
288 | goto ill_prop; | |
289 | } | |
290 | ||
291 | ldc_ep->pip.target_type = target_type; | |
292 | ldc_ep->pip.target_channel = target_channel; | |
293 | ||
294 | check_guest = false; /* a flag for later change check */ | |
295 | ||
296 | switch (target_type) { | |
297 | uint64_t target_guest_id; | |
298 | case LDC_HV_ENDPOINT: | |
299 | /* nothing more to do */ | |
300 | DBGL(c_printf("\t\tConnected to HV endpoint 0x%x\n", | |
301 | target_channel)); | |
302 | break; | |
303 | ||
304 | case LDC_GUEST_ENDPOINT: | |
305 | if (!md_node_get_val(mdp, ldce_nodep, MDNAME(target_guest), | |
306 | &target_guest_id)) { | |
307 | DBGL(c_printf("Missing target_guest in " | |
308 | "ldc_endpoint node\n")); | |
309 | goto miss_prop; | |
310 | } | |
311 | ||
312 | DBGL(c_printf("\t\t\tConnected to guest 0x%x endpoint 0x%x\n", | |
313 | target_guest_id, target_channel)); | |
314 | ||
315 | /* This goes away with single target ID */ | |
316 | target_guestp = config.guests; | |
317 | target_guestp = &(target_guestp[target_guest_id]); | |
318 | ldc_ep->pip.target_guestp = target_guestp; | |
319 | check_guest = true; | |
320 | break; | |
321 | ||
322 | #ifdef CONFIG_FPGA /* { */ | |
323 | case LDC_SP_ENDPOINT: | |
324 | DBGL(c_printf("\t\t\tConnected to SP endpoint 0x%x\n", | |
325 | target_channel)); | |
326 | break; | |
327 | #endif /* } */ | |
328 | ||
329 | default: | |
330 | DBGL(c_printf("Invalid target_type in ldc-endpoint node\n")); | |
331 | goto ill_prop; | |
332 | } | |
333 | ||
334 | ||
335 | if (md_node_get_val(mdp, ldce_nodep, MDNAME(private_svc), &pvt_svc)) { | |
336 | ||
337 | ldc_ep->pip.is_private = 1; | |
338 | ldc_ep->pip.svc_id = pvt_svc; | |
339 | ||
340 | switch (ldc_ep->pip.svc_id) { | |
341 | case LDC_CONSOLE_SVC: | |
342 | ||
343 | if (target_type == LDC_HV_ENDPOINT) { | |
344 | DBG(c_printf("Console cannot use HV " | |
345 | "endpoint\n")); | |
346 | goto ill_prop; | |
347 | } | |
348 | ||
349 | if (!md_node_get_val(mdp, ldce_nodep, MDNAME(rx_ino), | |
350 | &rx_ino)) { | |
351 | DBG(c_printf("Missing rx_ino in ldc_endpoint " | |
352 | "node\n")); | |
353 | goto miss_prop; | |
354 | } | |
355 | ||
356 | ldc_ep->pip.tx_ino = ldc_ep->pip.rx_ino = tx_ino = | |
357 | rx_ino; | |
358 | ||
359 | break; | |
360 | ||
361 | default: | |
362 | DBG(c_printf("Invalid private service type\n")); | |
363 | goto ill_prop; | |
364 | } | |
365 | } else { | |
366 | ldc_ep->pip.is_private = 0; | |
367 | ||
368 | if (!md_node_get_val(mdp, ldce_nodep, MDNAME(tx_ino), | |
369 | &tx_ino)) { | |
370 | DBGL(c_printf("Missing tx-ino in ldc_endpoint node\n")); | |
371 | goto miss_prop; | |
372 | } | |
373 | if (!md_node_get_val(mdp, ldce_nodep, MDNAME(rx_ino), | |
374 | &rx_ino)) { | |
375 | DBGL(c_printf("Missing rx-ino in ldc_endpoint node\n")); | |
376 | goto miss_prop; | |
377 | } | |
378 | ||
379 | DBGL(c_printf("\t\t\t\ttx-ino 0x%x rx-ino 0x%x\n", tx_ino, | |
380 | rx_ino)); | |
381 | ||
382 | ldc_ep->pip.tx_ino = tx_ino; | |
383 | ldc_ep->pip.rx_ino = rx_ino; | |
384 | } | |
385 | ||
386 | /* | |
387 | * OK figure out if something changed, or if this is a | |
388 | * noop or a config. | |
389 | */ | |
390 | if (!ldc_ep->is_live) { | |
391 | ldc_ep->pip.res.flags = RESF_Config; | |
392 | DBGL(c_printf("\t\tElected to config LDC endpoint\n")); | |
393 | } else | |
394 | if (ldc_ep->target_type != target_type || | |
395 | ldc_ep->target_channel != target_channel || | |
396 | (!(ldc_ep->pip.is_private) && (ldc_ep->tx_mapreg.ino != tx_ino)) || | |
397 | (!(ldc_ep->pip.is_private) && (ldc_ep->rx_mapreg.ino != rx_ino)) || | |
398 | (check_guest && (ldc_ep->target_guest != target_guestp))) { | |
399 | ||
400 | #define E(_e) DBGL(if (_e) c_printf("\t\t\t%s\n", #_e)); | |
401 | E(ldc_ep->target_type != target_type); | |
402 | E(ldc_ep->target_channel != target_channel); | |
403 | E(ldc_ep->tx_mapreg.ino != tx_ino); | |
404 | E(ldc_ep->rx_mapreg.ino != rx_ino); | |
405 | E((check_guest && (ldc_ep->target_guest != target_guestp))); | |
406 | #undef E | |
407 | DBGL(c_printf("\t\tModify not supported on LDC channels\n")); | |
408 | *fail_codep = HVctl_e_ldc_rebind_na; | |
409 | return (HVctl_st_badmd); | |
410 | } else { | |
411 | DBGL(c_printf("\t\tElected to ignore LDC endpoint\n")); | |
412 | ldc_ep->pip.res.flags = RESF_Noop; | |
413 | } | |
414 | ||
415 | return (HVctl_st_ok); | |
416 | } | |
417 | ||
418 | ||
419 | hvctl_status_t | |
420 | res_ldc_postparse(hvctl_res_error_t *res_error, int *fail_res_id) | |
421 | { | |
422 | return (HVctl_st_ok); | |
423 | } | |
424 | ||
425 | ||
426 | void | |
427 | res_ldc_commit(int flag) | |
428 | { | |
429 | guest_t *gp; | |
430 | int i; | |
431 | ||
432 | gp = config.guests; | |
433 | ||
434 | for (i = 0; i < NGUESTS; i++, gp++) { | |
435 | int j; | |
436 | ||
437 | /* | |
438 | * FIXME:again can optimise around guest configdness | |
439 | * and to skip the unsupported flags | |
440 | */ | |
441 | for (j = 0; j < MAX_LDC_CHANNELS; j++) { | |
442 | ldc_endpoint_t *ep; | |
443 | ||
444 | ep = &(gp->ldc_endpoint[j]); | |
445 | ||
446 | /* if not this ops turn move on */ | |
447 | if (ep->pip.res.flags != flag) continue; | |
448 | ||
449 | switch (ep->pip.res.flags) { | |
450 | case RESF_Noop: | |
451 | DBG(c_printf("guest 0x%x ldc 0x%x : noop\n", | |
452 | gp->guestid, j)); | |
453 | ASSERT(0); /* not supported */ | |
454 | break; | |
455 | case RESF_Unconfig: | |
456 | res_ldc_commit_unconfig(gp, ep); | |
457 | break; | |
458 | case RESF_Config: | |
459 | res_ldc_commit_config(gp, ep); | |
460 | break; | |
461 | case RESF_Rebind: | |
462 | DBG(c_printf("guest 0x%x ldc 0x%x : rebind\n", | |
463 | gp->guestid, j)); | |
464 | ASSERT(0); /* not supported */ | |
465 | break; | |
466 | case RESF_Modify: | |
467 | DBG(c_printf("guest 0x%x ldc 0x%x : rebind\n", | |
468 | gp->guestid, j)); | |
469 | ASSERT(0); /* not supported */ | |
470 | break; | |
471 | default: | |
472 | ASSERT(0); | |
473 | } | |
474 | ||
475 | ep->pip.res.flags = RESF_Noop; /* cleanup */ | |
476 | } | |
477 | } | |
478 | } | |
479 | ||
480 | ||
481 | void | |
482 | reset_ldc_endpoint(ldc_endpoint_t *ldc_ep) | |
483 | { | |
484 | ldc_ep->tx_qbase_ra = 0; | |
485 | ldc_ep->tx_qhead = 0; | |
486 | ldc_ep->tx_qtail = 0; | |
487 | ||
488 | ldc_ep->rx_qbase_ra = 0; | |
489 | ldc_ep->rx_qhead = 0; | |
490 | ldc_ep->rx_qtail = 0; | |
491 | } | |
492 | ||
493 | ||
494 | void | |
495 | res_ldc_commit_config(guest_t *guestp, ldc_endpoint_t *ldc_ep) | |
496 | { | |
497 | int tx_ino; | |
498 | int rx_ino; | |
499 | ||
500 | DBG(c_printf("res_ldc_commit_config guest 0x%x ldce 0x%x\n", | |
501 | guestp->guestid, ldc_ep - guestp->ldc_endpoint)); | |
502 | ||
503 | ldc_ep->target_type = ldc_ep->pip.target_type; | |
504 | ldc_ep->target_channel = ldc_ep->pip.target_channel; | |
505 | ldc_ep->is_private = ldc_ep->pip.is_private; | |
506 | ldc_ep->svc_id = ldc_ep->pip.svc_id; | |
507 | ||
508 | switch (ldc_ep->target_type) { | |
509 | case LDC_HV_ENDPOINT: | |
510 | DBG(c_printf("\tHV")); | |
511 | break; | |
512 | case LDC_GUEST_ENDPOINT: | |
513 | ldc_ep->target_guest = ldc_ep->pip.target_guestp; | |
514 | DBG(c_printf("\tguest 0x%x", ldc_ep->target_guest->guestid)); | |
515 | break; | |
516 | case LDC_SP_ENDPOINT: | |
517 | DBG(c_printf("\tSP")); | |
518 | break; | |
519 | default: | |
520 | ASSERT(0); | |
521 | } | |
522 | ||
523 | DBG(c_printf(" endpoint 0x%x\n", ldc_ep->target_channel)); | |
524 | ||
525 | ||
526 | if (!ldc_ep->is_private) { | |
527 | tx_ino = ldc_ep->pip.tx_ino; | |
528 | ||
529 | ldc_ep->tx_mapreg.ino = tx_ino; | |
530 | guestp->ldc_ino2endpoint[tx_ino].endpointp = ldc_ep; | |
531 | guestp->ldc_ino2endpoint[tx_ino].mapregp = &(ldc_ep->tx_mapreg); | |
532 | ||
533 | ASSERT(guestp->cdev_cfghandle != 0); | |
534 | config_a_guest_device_vino(guestp, tx_ino, DEVOPS_CDEV); | |
535 | rx_ino = ldc_ep->pip.rx_ino; | |
536 | ||
537 | ldc_ep->rx_mapreg.ino = rx_ino; | |
538 | guestp->ldc_ino2endpoint[rx_ino].endpointp = ldc_ep; | |
539 | guestp->ldc_ino2endpoint[rx_ino].mapregp = &(ldc_ep->rx_mapreg); | |
540 | ||
541 | config_a_guest_device_vino(guestp, rx_ino, DEVOPS_CDEV); | |
542 | ||
543 | ldc_ep->tx_qbase_pa = 0; | |
544 | ldc_ep->tx_qsize = 0; | |
545 | ||
546 | ldc_ep->rx_qbase_pa = 0; | |
547 | ldc_ep->rx_qsize = 0; | |
548 | ldc_ep->rx_updated = 1; | |
549 | } | |
550 | ||
551 | reset_ldc_endpoint(ldc_ep); | |
552 | ||
553 | ldc_ep->is_live = true; | |
554 | } | |
555 | ||
556 | ||
557 | void | |
558 | res_ldc_commit_unconfig(guest_t *guestp, ldc_endpoint_t *ldc_ep) | |
559 | { | |
560 | int tx_ino; | |
561 | int rx_ino; | |
562 | ||
563 | DBG(c_printf("res_ldc_commit_unconfig guest 0x%x ldce 0x%x\n", | |
564 | guestp->guestid, ldc_ep - guestp->ldc_endpoint)); | |
565 | ||
566 | ASSERT(ldc_ep->is_live); | |
567 | ||
568 | /* | |
569 | * Undo the interrupt back maps | |
570 | */ | |
571 | if (ldc_ep->is_private == 0) { | |
572 | tx_ino = ldc_ep->tx_mapreg.ino; | |
573 | guestp->ldc_ino2endpoint[tx_ino].endpointp = NULL; | |
574 | guestp->ldc_ino2endpoint[tx_ino].mapregp = NULL; | |
575 | ||
576 | unconfig_a_guest_device_vino(guestp, tx_ino, DEVOPS_CDEV); | |
577 | ||
578 | rx_ino = ldc_ep->rx_mapreg.ino; | |
579 | guestp->ldc_ino2endpoint[rx_ino].endpointp = NULL; | |
580 | guestp->ldc_ino2endpoint[rx_ino].mapregp = NULL; | |
581 | ||
582 | unconfig_a_guest_device_vino(guestp, rx_ino, DEVOPS_CDEV); | |
583 | } | |
584 | ||
585 | /* This is about it for an unconfigure */ | |
586 | ldc_ep->is_live = false; | |
587 | } | |
588 | ||
589 | ||
590 | /* | |
591 | * Now the HV LDCs ... which should be vanishing into | |
592 | * becomming regular LDCs sometime in the very newar future. | |
593 | */ | |
594 | void | |
595 | res_hv_ldc_prep() | |
596 | { | |
597 | int i; | |
598 | ||
599 | for (i = 0; i < MAX_HV_LDC_CHANNELS; i++) { | |
600 | ldc_endpoint_t *hvep; | |
601 | ||
602 | hvep = config.hv_ldcs; | |
603 | hvep = &(hvep[i]); | |
604 | ||
605 | hvep->pip.res.flags = (!hvep->is_live) ? | |
606 | RESF_Noop : RESF_Unconfig; | |
607 | } | |
608 | } | |
609 | ||
610 | ||
611 | hvctl_status_t | |
612 | res_hv_ldc_parse(bin_md_t *mdp, hvctl_res_error_t *fail_codep, | |
613 | md_element_t **failnodepp, int *fail_res_idp) | |
614 | { | |
615 | md_element_t *mdep, *hvldc_nodep, *rootnodep; | |
616 | uint64_t arc_token; | |
617 | uint64_t name_token; | |
618 | ||
619 | mdp = (bin_md_t *)config.parse_hvmd; | |
620 | ||
621 | DBGHL(c_printf("HV LDC configuration:\n")); | |
622 | ||
623 | /* | |
624 | * First find the root node | |
625 | */ | |
626 | rootnodep = md_find_node(mdp, NULL, MDNAME(root)); | |
627 | if (rootnodep == NULL) { | |
628 | DBGHL(c_printf("Missing root node in HVMD\n")); | |
629 | *failnodepp = NULL; | |
630 | *fail_res_idp = 0; | |
631 | return (HVctl_st_badmd); | |
632 | } | |
633 | ||
634 | /* if no ldc_endpoints node under root nothing to parse */ | |
635 | if (md_find_node_by_arc(mdp, rootnodep, MDARC(MDNAME(fwd)), | |
636 | MDNODE(MDNAME(ldc_endpoints)), &mdep) == NULL) { | |
637 | return (HVctl_st_ok); | |
638 | } | |
639 | ||
640 | arc_token = MDARC(MDNAME(fwd)); | |
641 | name_token = MDNODE(MDNAME(ldc_endpoint)); | |
642 | ||
643 | while (NULL != (mdep = md_find_node_by_arc(mdp, mdep, arc_token, | |
644 | name_token, &hvldc_nodep))) { | |
645 | hvctl_status_t status; | |
646 | ||
647 | status = res_hv_ldc_parse_1(mdp, hvldc_nodep, fail_codep, | |
648 | fail_res_idp); | |
649 | if (status != HVctl_st_ok) { | |
650 | *failnodepp = hvldc_nodep; | |
651 | return (status); | |
652 | } | |
653 | } | |
654 | return (HVctl_st_ok); | |
655 | } | |
656 | ||
657 | ||
658 | hvctl_status_t | |
659 | res_hv_ldc_parse_1(bin_md_t *mdp, md_element_t *hvldc_nodep, | |
660 | hvctl_res_error_t *fail_codep, int *fail_res_idp) | |
661 | { | |
662 | uint64_t chid; | |
663 | uint64_t type; | |
664 | uint64_t guestid; | |
665 | uint64_t svc_id; | |
666 | uint64_t tchan_id; | |
667 | ldc_endpoint_t *hvep; | |
668 | ||
669 | if (!md_node_get_val(mdp, hvldc_nodep, MDNAME(svc_id), &svc_id)) { | |
670 | /* Not a HV endpoint - skip it */ | |
671 | return (HVctl_st_ok); | |
672 | } | |
673 | ||
674 | DBGHL(c_printf("Parse HV LDC endpoints\n")); | |
675 | ||
676 | if (!md_node_get_val(mdp, hvldc_nodep, MDNAME(channel), &chid)) { | |
677 | DBGHL(c_printf("Missing channel id in HV LDC node\n")); | |
678 | *fail_res_idp = 0; | |
679 | miss_prop: | |
680 | *fail_codep = HVctl_e_hv_ldc_missing_prop; | |
681 | return (HVctl_st_badmd); | |
682 | } | |
683 | if (chid >= MAX_HV_LDC_CHANNELS) { | |
684 | DBGHL(c_printf("Invalid channel id 0x%x in HV LDC node\n", | |
685 | chid)); | |
686 | *fail_res_idp = 0; | |
687 | ill_prop: | |
688 | *fail_codep = HVctl_e_hv_ldc_illegal_prop; | |
689 | return (HVctl_st_badmd); | |
690 | } | |
691 | ||
692 | DBGHL(c_printf("\tHV endpoint 0x%x :", chid)); | |
693 | ||
694 | if (!md_node_get_val(mdp, hvldc_nodep, MDNAME(target_type), &type)) { | |
695 | DBG(c_printf("Missing target_type in HV LDC node\n")); | |
696 | goto miss_prop; | |
697 | } | |
698 | ||
699 | hvep = config.hv_ldcs; | |
700 | hvep = &(hvep[chid]); | |
701 | ||
702 | *fail_res_idp = chid; | |
703 | ||
704 | hvep->pip.target_type = type; | |
705 | ||
706 | switch (type) { | |
707 | case LDC_GUEST_ENDPOINT: /* guest<->HV LDC */ | |
708 | if (!md_node_get_val(mdp, hvldc_nodep, MDNAME(target_guest), | |
709 | &guestid)) { | |
710 | DBGHL(c_printf("Missing target_guest in HV " | |
711 | "LDC node\n")); | |
712 | goto miss_prop; | |
713 | } | |
714 | ||
715 | /* point to target guest */ | |
716 | hvep->pip.target_guestp = | |
717 | &(((guest_t *)config.guests)[guestid]); | |
718 | ||
719 | DBGHL(c_printf("\tConnected to endpoint in guest 0x%x\n", | |
720 | guestid)); | |
721 | break; | |
722 | ||
723 | case LDC_SP_ENDPOINT: /* HV<->SP LDC */ | |
724 | DBGHL(c_printf("\tConnected to SP endpoint\n")); | |
725 | break; | |
726 | ||
727 | default: | |
728 | DBGHL(c_printf("Illegal target_type 0x%x\n", type)); | |
729 | goto ill_prop; | |
730 | } | |
731 | ||
732 | ||
733 | if (!md_node_get_val(mdp, hvldc_nodep, MDNAME(target_channel), | |
734 | &tchan_id)) { | |
735 | DBGHL(c_printf("Missing target channel id in HV LDC node\n")); | |
736 | goto miss_prop; | |
737 | } | |
738 | if (tchan_id >= MAX_LDC_CHANNELS) { | |
739 | DBGHL(c_printf("Invalid target channel id 0x%x in HV " | |
740 | "LDC node\n", tchan_id)); | |
741 | goto ill_prop; | |
742 | } | |
743 | ||
744 | DBGHL(c_printf("\t\tTarget channel = 0x%x ", tchan_id)); | |
745 | ||
746 | hvep->pip.target_channel = tchan_id; | |
747 | ||
748 | hvep->pip.svc_id = svc_id; | |
749 | ||
750 | switch (svc_id) { | |
751 | case LDC_HVCTL_SVC: | |
752 | /* | |
753 | * We don't yet allow for HVCTL channel between the | |
754 | * hypervisor and SP. Maybe one day we will have a | |
755 | * Zeus or Zeus-lite running on the SP and at that | |
756 | * point we can remove this check. | |
757 | */ | |
758 | if (hvep->pip.target_type == LDC_SP_ENDPOINT) { | |
759 | DBGHL(c_printf("No HVCTL LDC to the SP " | |
760 | "allowed yet\n")); | |
761 | goto ill_prop; | |
762 | } | |
763 | ||
764 | DBGHL(c_printf(" for HVCTL service\n")); | |
765 | ||
766 | break; | |
767 | ||
768 | default: | |
769 | DBGHL(c_printf("Unknown service type 0x%x\n", svc_id)); | |
770 | goto miss_prop; | |
771 | } | |
772 | ||
773 | /* | |
774 | * Now figure if this is a config or a modify | |
775 | */ | |
776 | if (!hvep->is_live) { | |
777 | hvep->pip.res.flags = RESF_Config; | |
778 | DBGHL(c_printf("Elected to Configure")); | |
779 | } else { | |
780 | if (hvep->pip.target_type != hvep->target_type || | |
781 | (hvep->target_type == LDC_GUEST_ENDPOINT && | |
782 | hvep->pip.target_guestp != hvep->target_guest) || | |
783 | hvep->pip.target_channel != hvep->target_channel) { | |
784 | DBGHL(c_printf("A HV LDC channel must be unconfiged " | |
785 | "before it can be re-bound\n")); | |
786 | *fail_codep = HVctl_e_hv_ldc_rebind_na; | |
787 | return (HVctl_st_eillegal); | |
788 | } else { | |
789 | hvep->pip.res.flags = RESF_Noop; | |
790 | DBGHL(c_printf("Elected to Ignore")); | |
791 | } | |
792 | } | |
793 | ||
794 | return (HVctl_st_ok); | |
795 | } | |
796 | ||
797 | ||
798 | hvctl_status_t | |
799 | res_hv_ldc_postparse(hvctl_res_error_t *res_error, int *fail_res_id) | |
800 | { | |
801 | return (HVctl_st_ok); | |
802 | } | |
803 | ||
804 | ||
805 | void | |
806 | res_hv_ldc_commit(int flag) | |
807 | { | |
808 | int i; | |
809 | ||
810 | for (i = 0; i < MAX_HV_LDC_CHANNELS; i++) { | |
811 | ldc_endpoint_t *hvep; | |
812 | ||
813 | hvep = config.hv_ldcs; | |
814 | hvep = &(hvep[i]); | |
815 | ||
816 | /* if not this ops turn move on */ | |
817 | if (hvep->pip.res.flags != flag) continue; | |
818 | ||
819 | switch (hvep->pip.res.flags) { | |
820 | case RESF_Noop: | |
821 | DBG(c_printf("hv_ldc 0x%x : noop\n", i)); | |
822 | break; | |
823 | case RESF_Unconfig: | |
824 | res_hv_ldc_commit_unconfig(i); | |
825 | break; | |
826 | case RESF_Config: | |
827 | res_hv_ldc_commit_config(i); | |
828 | break; | |
829 | case RESF_Rebind: | |
830 | DBG(c_printf("hv_ldc 0x%x : rebind\n", i)); | |
831 | ASSERT(0); /* not supported */ | |
832 | break; | |
833 | case RESF_Modify: | |
834 | DBG(c_printf("hv_ldc 0x%x : modify\n", i)); | |
835 | ASSERT(0); /* not supported */ | |
836 | break; | |
837 | default: | |
838 | ASSERT(0); | |
839 | } | |
840 | ||
841 | hvep->pip.res.flags = RESF_Noop; /* cleanup */ | |
842 | } | |
843 | } | |
844 | ||
845 | ||
846 | void | |
847 | res_hv_ldc_commit_config(int ch_id) | |
848 | { | |
849 | ldc_endpoint_t *hvep; | |
850 | extern void hvctl_svc_callback(); /* FIXME: in a header */ | |
851 | ||
852 | hvep = config.hv_ldcs; | |
853 | hvep = &(hvep[ch_id]); | |
854 | ||
855 | DBGHL(c_printf("\t\tConfig endpoint\n")); | |
856 | ASSERT(!hvep->is_live); | |
857 | ||
858 | hvep->target_type = hvep->pip.target_type; | |
859 | ||
860 | switch (hvep->target_type) { | |
861 | case LDC_GUEST_ENDPOINT: /* guest<->HV LDC */ | |
862 | hvep->target_guest = hvep->pip.target_guestp; | |
863 | break; | |
864 | ||
865 | case LDC_SP_ENDPOINT: /* HV<->SP LDC */ | |
866 | break; | |
867 | default: | |
868 | ASSERT(0); | |
869 | } | |
870 | ||
871 | hvep->target_channel = hvep->pip.target_channel; | |
872 | ||
873 | /* svc id determines the callback setup */ | |
874 | switch (hvep->pip.svc_id) { | |
875 | ||
876 | case LDC_HVCTL_SVC: | |
877 | /* | |
878 | * FIXME: Why did we save the endpoint number | |
879 | * instead of a pointer to the endpoint ? | |
880 | */ | |
881 | config.hvctl_ldc = ch_id; /* save the HVCTL channel id */ | |
882 | ||
883 | hvep->rx_cb = (uint64_t)&hvctl_svc_callback; | |
884 | hvep->rx_cbarg = (uint64_t)&config; | |
885 | break; | |
886 | ||
887 | default: | |
888 | DBG(c_printf("Unknown service type 0x%x\n", hvep->pip.svc_id)); | |
889 | ASSERT(0); | |
890 | } | |
891 | ||
892 | /* Mark channel as live */ | |
893 | ASSERT(!hvep->is_live); | |
894 | hvep->is_live = true; | |
895 | } | |
896 | ||
897 | ||
898 | void | |
899 | res_hv_ldc_commit_unconfig(int ch_id) | |
900 | { | |
901 | ldc_endpoint_t *hvep; | |
902 | ||
903 | hvep = config.hv_ldcs; | |
904 | hvep = &(hvep[ch_id]); | |
905 | ||
906 | DBGHL(c_printf("\t\tUnconfig endpoint %d\n", ch_id)); | |
907 | ASSERT(hvep->is_live); | |
908 | ||
909 | hvep->is_live = false; | |
910 | } |