* ========== Copyright Header Begin ==========================================
* Hypervisor Software File: res_ldc.c
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* - Do no alter or remove copyright notices
* - Redistribution and use of this software in source and binary forms, with
* or without modification, are permitted provided that the following
* - Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of Sun Microsystems, Inc. or the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* This software is provided "AS IS," without a warranty of any kind.
* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
* OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
* FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or maintenance of
* ========== Copyright Header End ============================================
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
#pragma ident "@(#)res_ldc.c 1.7 07/06/07 SMI"
* (re)-configuration code to handle HV LDC resources
static void res_ldc_commit_config(guest_t
*gp
, ldc_endpoint_t
*ldcp
);
static void res_ldc_commit_unconfig(guest_t
*gp
, ldc_endpoint_t
*ldcp
);
static hvctl_status_t
res_ldc_parse_1(bin_md_t
*mdp
, md_element_t
*guest_nodep
,
hvctl_res_error_t
*fail_codep
,
md_element_t
**failnodepp
, int *fail_res_idp
);
static hvctl_status_t
res_ldc_parse_2(bin_md_t
*mdp
, int guest_id
,
md_element_t
*ldce_nodep
,
hvctl_res_error_t
*fail_codep
, int *fail_res_idp
);
static void res_hv_ldc_commit_config(int ch_id
);
static void res_hv_ldc_commit_unconfig(int ch_id
);
static hvctl_status_t
res_hv_ldc_parse_1(bin_md_t
*mdp
,
md_element_t
*hvldc_nodep
,
hvctl_res_error_t
*fail_codep
, int *fail_res_idp
);
* FIXME: This changes when LDC endpoints have their own
* global IDs. This means for now the uniq ID for an endpoints
* is the tuple: (guest_id, channel_id)
* We'll fix this later...
for (i
= 0; i
< NGUESTS
; i
++, gp
++) {
* If the guest is already unconfigured, then
* we've nothing to potentially unconfigure
* So we could optimise by skipping all the
* channels, but it's pointless because the
* per-guest endpoints are going away.
* So for now we brute force all endpoints.
for (j
= 0; j
< MAX_LDC_CHANNELS
; j
++) {
ep
= &(gp
->ldc_endpoint
[j
]);
/* unconfigd guest cannot have live channel */
ASSERT(!(gp
->state
== GUEST_STATE_UNCONFIGURED
&&
/* if live potentially unconfig it */
ep
->pip
.res
.flags
= ep
->is_live
?
RESF_Unconfig
: RESF_Noop
;
res_ldc_parse(bin_md_t
*mdp
, hvctl_res_error_t
*fail_codep
,
md_element_t
**failnodepp
, int *fail_res_idp
)
md_element_t
*guest_nodep
;
mdp
= (bin_md_t
*)config
.parse_hvmd
;
DBGL(c_printf("\nLDC configuration:\n"));
mdep
= md_find_node(mdp
, NULL
, MDNAME(guests
));
DBG(c_printf("Missing guests node in HVMD\n"));
* FIXME: this multistep process goes away once the top-level
* ldc-endpoints aggregator includes all endpoints.
arc_token
= MDARC(MDNAME(fwd
));
node_token
= MDNODE(MDNAME(guest
));
while (status
== HVctl_st_ok
&&
NULL
!= (mdep
= md_find_node_by_arc(mdp
, mdep
, arc_token
,
node_token
, &guest_nodep
))) {
status
= res_ldc_parse_1(mdp
, guest_nodep
, fail_codep
,
failnodepp
, fail_res_idp
);
res_ldc_parse_1(bin_md_t
*mdp
, md_element_t
*guest_nodep
,
hvctl_res_error_t
*fail_codep
,
md_element_t
**failnodepp
, int *fail_res_idp
)
md_element_t
*ldce_nodep
;
* by now we know its here because we passed the guest parse
* which is higher priority than this parse function.
dummy
= md_node_get_val(mdp
, guest_nodep
, MDNAME(resource_id
),
DBGL(c_printf("\tGuest 0x%x for ldcs:\n", guest_id
));
arc_token
= MDARC(MDNAME(fwd
));
node_token
= MDNODE(MDNAME(ldc_endpoint
));
* Spin through the "ldc_endpoint" arcs in the
* ldc_endpoints node and config each endpoint !
* FIXME; what if already configured !
while (NULL
!= (elemp
= md_find_node_by_arc(mdp
, elemp
, arc_token
,
node_token
, &ldce_nodep
))) {
status
= res_ldc_parse_2(mdp
, guest_id
, ldce_nodep
,
fail_codep
, fail_res_idp
);
if (status
!= HVctl_st_ok
) {
*failnodepp
= ldce_nodep
;
res_ldc_parse_2(bin_md_t
*mdp
, int guest_id
, md_element_t
*ldce_nodep
,
hvctl_res_error_t
*fail_codep
, int *fail_res_idp
)
guestp
= &(guestp
[guest_id
]);
if (!md_node_get_val(mdp
, ldce_nodep
, MDNAME(channel
),
DBGL(c_printf("Missing channel (endpoint number) in"
" ldc_endpoint node\n"));
*fail_codep
= HVctl_e_ldc_missing_prop
;
if (endpt_id
>= MAX_LDC_CHANNELS
) {
DBGL(c_printf("Illegal channel (endpoint number) in"
" ldc_endpoint node\n"));
*fail_codep
= HVctl_e_ldc_illegal_prop
;
DBGL(c_printf("\t\tGuest 0x%x endpoint 0x%x\n", guest_id
, endpt_id
));
ldc_ep
= &(guestp
->ldc_endpoint
[endpt_id
]);
ldc_ep
->pip
.channel
= endpt_id
;
*fail_res_idp
= endpt_id
;
if (!md_node_get_val(mdp
, ldce_nodep
, MDNAME(target_type
),
DBGL(c_printf("Missing target_type in ldc_endpoint node\n"));
if (!md_node_get_val(mdp
, ldce_nodep
, MDNAME(target_channel
),
DBGL(c_printf("Missing target_channel in "
ldc_ep
->pip
.target_type
= target_type
;
ldc_ep
->pip
.target_channel
= target_channel
;
check_guest
= false; /* a flag for later change check */
uint64_t target_guest_id
;
DBGL(c_printf("\t\tConnected to HV endpoint 0x%x\n",
if (!md_node_get_val(mdp
, ldce_nodep
, MDNAME(target_guest
),
DBGL(c_printf("Missing target_guest in "
DBGL(c_printf("\t\t\tConnected to guest 0x%x endpoint 0x%x\n",
target_guest_id
, target_channel
));
/* This goes away with single target ID */
target_guestp
= config
.guests
;
target_guestp
= &(target_guestp
[target_guest_id
]);
ldc_ep
->pip
.target_guestp
= target_guestp
;
#ifdef CONFIG_FPGA /* { */
DBGL(c_printf("\t\t\tConnected to SP endpoint 0x%x\n",
DBGL(c_printf("Invalid target_type in ldc-endpoint node\n"));
if (md_node_get_val(mdp
, ldce_nodep
, MDNAME(private_svc
), &pvt_svc
)) {
ldc_ep
->pip
.is_private
= 1;
ldc_ep
->pip
.svc_id
= pvt_svc
;
switch (ldc_ep
->pip
.svc_id
) {
if (target_type
== LDC_HV_ENDPOINT
) {
DBG(c_printf("Console cannot use HV "
if (!md_node_get_val(mdp
, ldce_nodep
, MDNAME(rx_ino
),
DBG(c_printf("Missing rx_ino in ldc_endpoint "
ldc_ep
->pip
.tx_ino
= ldc_ep
->pip
.rx_ino
= tx_ino
=
DBG(c_printf("Invalid private service type\n"));
ldc_ep
->pip
.is_private
= 0;
if (!md_node_get_val(mdp
, ldce_nodep
, MDNAME(tx_ino
),
DBGL(c_printf("Missing tx-ino in ldc_endpoint node\n"));
if (!md_node_get_val(mdp
, ldce_nodep
, MDNAME(rx_ino
),
DBGL(c_printf("Missing rx-ino in ldc_endpoint node\n"));
DBGL(c_printf("\t\t\t\ttx-ino 0x%x rx-ino 0x%x\n", tx_ino
,
ldc_ep
->pip
.tx_ino
= tx_ino
;
ldc_ep
->pip
.rx_ino
= rx_ino
;
* OK figure out if something changed, or if this is a
ldc_ep
->pip
.res
.flags
= RESF_Config
;
DBGL(c_printf("\t\tElected to config LDC endpoint\n"));
if (ldc_ep
->target_type
!= target_type
||
ldc_ep
->target_channel
!= target_channel
||
(!(ldc_ep
->pip
.is_private
) && (ldc_ep
->tx_mapreg
.ino
!= tx_ino
)) ||
(!(ldc_ep
->pip
.is_private
) && (ldc_ep
->rx_mapreg
.ino
!= rx_ino
)) ||
(check_guest
&& (ldc_ep
->target_guest
!= target_guestp
))) {
#define E(_e) DBGL(if (_e) c_printf("\t\t\t%s\n", #_e));
E(ldc_ep
->target_type
!= target_type
);
E(ldc_ep
->target_channel
!= target_channel
);
E(ldc_ep
->tx_mapreg
.ino
!= tx_ino
);
E(ldc_ep
->rx_mapreg
.ino
!= rx_ino
);
E((check_guest
&& (ldc_ep
->target_guest
!= target_guestp
)));
DBGL(c_printf("\t\tModify not supported on LDC channels\n"));
*fail_codep
= HVctl_e_ldc_rebind_na
;
DBGL(c_printf("\t\tElected to ignore LDC endpoint\n"));
ldc_ep
->pip
.res
.flags
= RESF_Noop
;
res_ldc_postparse(hvctl_res_error_t
*res_error
, int *fail_res_id
)
for (i
= 0; i
< NGUESTS
; i
++, gp
++) {
* FIXME:again can optimise around guest configdness
* and to skip the unsupported flags
for (j
= 0; j
< MAX_LDC_CHANNELS
; j
++) {
ep
= &(gp
->ldc_endpoint
[j
]);
/* if not this ops turn move on */
if (ep
->pip
.res
.flags
!= flag
) continue;
switch (ep
->pip
.res
.flags
) {
DBG(c_printf("guest 0x%x ldc 0x%x : noop\n",
ASSERT(0); /* not supported */
res_ldc_commit_unconfig(gp
, ep
);
res_ldc_commit_config(gp
, ep
);
DBG(c_printf("guest 0x%x ldc 0x%x : rebind\n",
ASSERT(0); /* not supported */
DBG(c_printf("guest 0x%x ldc 0x%x : rebind\n",
ASSERT(0); /* not supported */
ep
->pip
.res
.flags
= RESF_Noop
; /* cleanup */
reset_ldc_endpoint(ldc_endpoint_t
*ldc_ep
)
res_ldc_commit_config(guest_t
*guestp
, ldc_endpoint_t
*ldc_ep
)
DBG(c_printf("res_ldc_commit_config guest 0x%x ldce 0x%x\n",
guestp
->guestid
, ldc_ep
- guestp
->ldc_endpoint
));
ldc_ep
->target_type
= ldc_ep
->pip
.target_type
;
ldc_ep
->target_channel
= ldc_ep
->pip
.target_channel
;
ldc_ep
->is_private
= ldc_ep
->pip
.is_private
;
ldc_ep
->svc_id
= ldc_ep
->pip
.svc_id
;
switch (ldc_ep
->target_type
) {
ldc_ep
->target_guest
= ldc_ep
->pip
.target_guestp
;
DBG(c_printf("\tguest 0x%x", ldc_ep
->target_guest
->guestid
));
DBG(c_printf(" endpoint 0x%x\n", ldc_ep
->target_channel
));
if (!ldc_ep
->is_private
) {
tx_ino
= ldc_ep
->pip
.tx_ino
;
ldc_ep
->tx_mapreg
.ino
= tx_ino
;
guestp
->ldc_ino2endpoint
[tx_ino
].endpointp
= ldc_ep
;
guestp
->ldc_ino2endpoint
[tx_ino
].mapregp
= &(ldc_ep
->tx_mapreg
);
ASSERT(guestp
->cdev_cfghandle
!= 0);
config_a_guest_device_vino(guestp
, tx_ino
, DEVOPS_CDEV
);
rx_ino
= ldc_ep
->pip
.rx_ino
;
ldc_ep
->rx_mapreg
.ino
= rx_ino
;
guestp
->ldc_ino2endpoint
[rx_ino
].endpointp
= ldc_ep
;
guestp
->ldc_ino2endpoint
[rx_ino
].mapregp
= &(ldc_ep
->rx_mapreg
);
config_a_guest_device_vino(guestp
, rx_ino
, DEVOPS_CDEV
);
reset_ldc_endpoint(ldc_ep
);
res_ldc_commit_unconfig(guest_t
*guestp
, ldc_endpoint_t
*ldc_ep
)
DBG(c_printf("res_ldc_commit_unconfig guest 0x%x ldce 0x%x\n",
guestp
->guestid
, ldc_ep
- guestp
->ldc_endpoint
));
* Undo the interrupt back maps
if (ldc_ep
->is_private
== 0) {
tx_ino
= ldc_ep
->tx_mapreg
.ino
;
guestp
->ldc_ino2endpoint
[tx_ino
].endpointp
= NULL
;
guestp
->ldc_ino2endpoint
[tx_ino
].mapregp
= NULL
;
unconfig_a_guest_device_vino(guestp
, tx_ino
, DEVOPS_CDEV
);
rx_ino
= ldc_ep
->rx_mapreg
.ino
;
guestp
->ldc_ino2endpoint
[rx_ino
].endpointp
= NULL
;
guestp
->ldc_ino2endpoint
[rx_ino
].mapregp
= NULL
;
unconfig_a_guest_device_vino(guestp
, rx_ino
, DEVOPS_CDEV
);
/* This is about it for an unconfigure */
* Now the HV LDCs ... which should be vanishing into
* becomming regular LDCs sometime in the very newar future.
for (i
= 0; i
< MAX_HV_LDC_CHANNELS
; i
++) {
hvep
->pip
.res
.flags
= (!hvep
->is_live
) ?
RESF_Noop
: RESF_Unconfig
;
res_hv_ldc_parse(bin_md_t
*mdp
, hvctl_res_error_t
*fail_codep
,
md_element_t
**failnodepp
, int *fail_res_idp
)
md_element_t
*mdep
, *hvldc_nodep
, *rootnodep
;
mdp
= (bin_md_t
*)config
.parse_hvmd
;
DBGHL(c_printf("HV LDC configuration:\n"));
* First find the root node
rootnodep
= md_find_node(mdp
, NULL
, MDNAME(root
));
DBGHL(c_printf("Missing root node in HVMD\n"));
/* if no ldc_endpoints node under root nothing to parse */
if (md_find_node_by_arc(mdp
, rootnodep
, MDARC(MDNAME(fwd
)),
MDNODE(MDNAME(ldc_endpoints
)), &mdep
) == NULL
) {
arc_token
= MDARC(MDNAME(fwd
));
name_token
= MDNODE(MDNAME(ldc_endpoint
));
while (NULL
!= (mdep
= md_find_node_by_arc(mdp
, mdep
, arc_token
,
name_token
, &hvldc_nodep
))) {
status
= res_hv_ldc_parse_1(mdp
, hvldc_nodep
, fail_codep
,
if (status
!= HVctl_st_ok
) {
*failnodepp
= hvldc_nodep
;
res_hv_ldc_parse_1(bin_md_t
*mdp
, md_element_t
*hvldc_nodep
,
hvctl_res_error_t
*fail_codep
, int *fail_res_idp
)
if (!md_node_get_val(mdp
, hvldc_nodep
, MDNAME(svc_id
), &svc_id
)) {
/* Not a HV endpoint - skip it */
DBGHL(c_printf("Parse HV LDC endpoints\n"));
if (!md_node_get_val(mdp
, hvldc_nodep
, MDNAME(channel
), &chid
)) {
DBGHL(c_printf("Missing channel id in HV LDC node\n"));
*fail_codep
= HVctl_e_hv_ldc_missing_prop
;
if (chid
>= MAX_HV_LDC_CHANNELS
) {
DBGHL(c_printf("Invalid channel id 0x%x in HV LDC node\n",
*fail_codep
= HVctl_e_hv_ldc_illegal_prop
;
DBGHL(c_printf("\tHV endpoint 0x%x :", chid
));
if (!md_node_get_val(mdp
, hvldc_nodep
, MDNAME(target_type
), &type
)) {
DBG(c_printf("Missing target_type in HV LDC node\n"));
hvep
->pip
.target_type
= type
;
case LDC_GUEST_ENDPOINT
: /* guest<->HV LDC */
if (!md_node_get_val(mdp
, hvldc_nodep
, MDNAME(target_guest
),
DBGHL(c_printf("Missing target_guest in HV "
/* point to target guest */
hvep
->pip
.target_guestp
=
&(((guest_t
*)config
.guests
)[guestid
]);
DBGHL(c_printf("\tConnected to endpoint in guest 0x%x\n",
case LDC_SP_ENDPOINT
: /* HV<->SP LDC */
DBGHL(c_printf("\tConnected to SP endpoint\n"));
DBGHL(c_printf("Illegal target_type 0x%x\n", type
));
if (!md_node_get_val(mdp
, hvldc_nodep
, MDNAME(target_channel
),
DBGHL(c_printf("Missing target channel id in HV LDC node\n"));
if (tchan_id
>= MAX_LDC_CHANNELS
) {
DBGHL(c_printf("Invalid target channel id 0x%x in HV "
"LDC node\n", tchan_id
));
DBGHL(c_printf("\t\tTarget channel = 0x%x ", tchan_id
));
hvep
->pip
.target_channel
= tchan_id
;
hvep
->pip
.svc_id
= svc_id
;
* We don't yet allow for HVCTL channel between the
* hypervisor and SP. Maybe one day we will have a
* Zeus or Zeus-lite running on the SP and at that
* point we can remove this check.
if (hvep
->pip
.target_type
== LDC_SP_ENDPOINT
) {
DBGHL(c_printf("No HVCTL LDC to the SP "
DBGHL(c_printf(" for HVCTL service\n"));
DBGHL(c_printf("Unknown service type 0x%x\n", svc_id
));
* Now figure if this is a config or a modify
hvep
->pip
.res
.flags
= RESF_Config
;
DBGHL(c_printf("Elected to Configure"));
if (hvep
->pip
.target_type
!= hvep
->target_type
||
(hvep
->target_type
== LDC_GUEST_ENDPOINT
&&
hvep
->pip
.target_guestp
!= hvep
->target_guest
) ||
hvep
->pip
.target_channel
!= hvep
->target_channel
) {
DBGHL(c_printf("A HV LDC channel must be unconfiged "
"before it can be re-bound\n"));
*fail_codep
= HVctl_e_hv_ldc_rebind_na
;
return (HVctl_st_eillegal
);
hvep
->pip
.res
.flags
= RESF_Noop
;
DBGHL(c_printf("Elected to Ignore"));
res_hv_ldc_postparse(hvctl_res_error_t
*res_error
, int *fail_res_id
)
res_hv_ldc_commit(int flag
)
for (i
= 0; i
< MAX_HV_LDC_CHANNELS
; i
++) {
/* if not this ops turn move on */
if (hvep
->pip
.res
.flags
!= flag
) continue;
switch (hvep
->pip
.res
.flags
) {
DBG(c_printf("hv_ldc 0x%x : noop\n", i
));
res_hv_ldc_commit_unconfig(i
);
res_hv_ldc_commit_config(i
);
DBG(c_printf("hv_ldc 0x%x : rebind\n", i
));
ASSERT(0); /* not supported */
DBG(c_printf("hv_ldc 0x%x : modify\n", i
));
ASSERT(0); /* not supported */
hvep
->pip
.res
.flags
= RESF_Noop
; /* cleanup */
res_hv_ldc_commit_config(int ch_id
)
extern void hvctl_svc_callback(); /* FIXME: in a header */
DBGHL(c_printf("\t\tConfig endpoint\n"));
hvep
->target_type
= hvep
->pip
.target_type
;
switch (hvep
->target_type
) {
case LDC_GUEST_ENDPOINT
: /* guest<->HV LDC */
hvep
->target_guest
= hvep
->pip
.target_guestp
;
case LDC_SP_ENDPOINT
: /* HV<->SP LDC */
hvep
->target_channel
= hvep
->pip
.target_channel
;
/* svc id determines the callback setup */
switch (hvep
->pip
.svc_id
) {
* FIXME: Why did we save the endpoint number
* instead of a pointer to the endpoint ?
config
.hvctl_ldc
= ch_id
; /* save the HVCTL channel id */
hvep
->rx_cb
= (uint64_t)&hvctl_svc_callback
;
hvep
->rx_cbarg
= (uint64_t)&config
;
DBG(c_printf("Unknown service type 0x%x\n", hvep
->pip
.svc_id
));
/* Mark channel as live */
res_hv_ldc_commit_unconfig(int ch_id
)
DBGHL(c_printf("\t\tUnconfig endpoint %d\n", ch_id
));