* ========== Copyright Header Begin ==========================================
* Hypervisor Software File: res_guest.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_guest.c 1.6 07/06/07 SMI"
* (re)-configuration code to handle HV guest resources
static hvctl_status_t
res_guest_parse_1(bin_md_t
*mdp
, md_element_t
*guestnodep
,
hvctl_res_error_t
*fail_codep
, int *fail_res_idp
);
static void res_guest_commit_config(guest_t
*guestp
);
static void res_guest_commit_unconfig(guest_t
*guestp
);
static void res_guest_commit_modify(guest_t
*guestp
);
void config_a_guest_device_vino(guest_t
*guestp
, int ino
, uint8_t type
);
void unconfig_a_guest_device_vino(guest_t
*guestp
, int ino
, uint8_t type
);
void config_guest_virtual_device(guest_t
*guestp
, uint64_t cfg_handle
);
void unconfig_guest_virtual_device(guest_t
*guestp
);
void config_guest_channel_device(guest_t
*guestp
, uint64_t cfg_handle
);
void unconfig_guest_channel_device(guest_t
*guestp
);
gp
= (guest_t
*)config
.guests
;
/* clear out everything ! */
c_bzero(gp
, sizeof (*gp
));
gp
->state
= GUEST_STATE_UNCONFIGURED
;
/* FIXME: remove the configp pointer */
gp
->pip
.vdev_cfghandle
= INVALID_CFGHANDLE
;
gp
->pip
.cdev_cfghandle
= INVALID_CFGHANDLE
;
for (j
= 0; j
< NUM_RA2PA_SEGMENTS
; j
++) {
init_ra2pa_segment(&(gp
->ra2pa_segment
[j
]));
asmsg
= (hvctl_msg_t
*)gp
->async_buf
;
asmsg
->hdr
.op
= HVctl_op_new_res_stat
;
/* Everything else gets whacked when we (re)config the guest */
* Guest MD support functions.
for (i
= 0; i
< NGUESTS
; i
++, gp
++) {
gp
->pip
.res
.flags
= (gp
->state
== GUEST_STATE_UNCONFIGURED
) ?
RESF_Noop
: RESF_Unconfig
;
res_guest_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
;
DBG(c_printf("\nGuest configuration:\n"));
mdep
= md_find_node(mdp
, NULL
, MDNAME(guests
));
DBG(c_printf("Missing guests node in HVMD\n"));
arc_token
= MDARC(MDNAME(fwd
));
node_token
= MDNODE(MDNAME(guest
));
while (NULL
!= (mdep
= md_find_node_by_arc(mdp
, mdep
, arc_token
,
node_token
, &guest_nodep
))) {
status
= res_guest_parse_1(mdp
, guest_nodep
, fail_codep
,
if (status
!= HVctl_st_ok
) {
*failnodepp
= guest_nodep
;
res_guest_parse_1(bin_md_t
*mdp
, md_element_t
*guest_nodep
,
hvctl_res_error_t
*fail_codep
, int *fail_res_idp
)
uint64_t resource_id
, unbind
, cfg_handle
, base_memsize
;
uint64_t arc_token
, node_token
;
md_element_t
*mblock_nodep
, *mdep
, *base_mblock
;
md_element_t
*devices_nodep
;
DBG(c_printf("res_guest_parse_1\n"));
DBG(md_dump_node(mdp
, guest_nodep
));
if (!md_node_get_val(mdp
, guest_nodep
, MDNAME(resource_id
),
DBG(c_printf("Missing resource_id in guest node\n"));
*fail_codep
= HVctl_e_guest_missing_id
;
if (resource_id
>= NGUESTS
) {
DBG(c_printf("Invalid resource_id in guest node\n"));
*fail_codep
= HVctl_e_guest_invalid_id
;
guestp
= &(guestp
[resource_id
]);
DBG(c_printf("Guest 0x%x @ 0x%x\n", resource_id
, (uint64_t)guestp
));
*fail_res_idp
= resource_id
;
* If guest is being unbound check that it is stopped.
if (!md_node_get_val(mdp
, guest_nodep
, MDNAME(unbind
), &unbind
))
if (guestp
->state
!= GUEST_STATE_STOPPED
) {
*fail_codep
= HVctl_e_guest_active
;
return (HVctl_st_eillegal
);
* Now fill in basic properties for this guest ...
* FIXME: These should be available is the guest is
#define GET_PROPERTY(_g_val, _md_name) \
if (!md_node_get_val(mdp, guest_nodep, \
MDNAME(_md_name), &_x)) { \
DBG(c_printf("Missing "#_md_name" in " \
/* A guest can't change ID, so just push it in */
GET_PROPERTY(guestp
->guestid
, resource_id
);
GET_PROPERTY(guestp
->pip
.rom_base
, rombase
);
GET_PROPERTY(guestp
->pip
.rom_size
, romsize
);
GET_PROPERTY(guestp
->pip
.md_pa
, mdpa
);
if (!md_node_get_val(mdp
, guest_nodep
, MDNAME(uartbase
),
&guestp
->pip
.uartbase
)) {
guestp
->pip
.uartbase
= -1LL;
if (!md_node_get_val(mdp
, guest_nodep
, MDNAME(diskpa
),
guestp
->pip
.diskpa
= -1LL;
* Check for a reset-reason property
* FIXME: How sensible is this in an LDoms world?
* This is fine unless master start gets called somehow as
* part of the guest reconfig - so dont do that unless
* FIXME: Again - why re-do if guest configured already
if (!md_node_get_val(mdp
, guest_nodep
, MDNAME(reset_reason
),
&guestp
->pip
.reset_reason
)) {
guestp
->pip
.reset_reason
= RESET_REASON_POR
;
* Determine guest realbase.
guestp
->pip
.real_base
= UINT64_MAX
;
guestp
->pip
.real_limit
= 0;
guestp
->pip
.mem_offset
= 0;
arc_token
= MDARC(MDNAME(fwd
));
node_token
= MDNODE(MDNAME(mblock
));
while (NULL
!= (mdep
= md_find_node_by_arc(mdp
, mdep
,
arc_token
, node_token
, &mblock_nodep
))) {
uint64_t realbase
, membase
;
if (!md_node_get_val(mdp
, mblock_nodep
, MDNAME(realbase
),
DBG(c_printf("Missing realbase in mblock node\n"));
* Initialise guest real_base, real_limit and mem_offset
* Note: real_limit/mem_offset are required for N2 MMu HWTW
* FIXME: real_limit will not work for segmented memory
if (realbase
< guestp
->pip
.real_base
) {
guestp
->pip
.real_base
= realbase
;
base_mblock
= mblock_nodep
;
if (!md_node_get_val(mdp
, mblock_nodep
, MDNAME(membase
),
membase
= guestp
->pip
.real_base
;
guestp
->pip
.mem_offset
= membase
-
if (!md_node_get_val(mdp
, base_mblock
, MDNAME(memsize
),
if (guestp
->pip
.real_limit
< (realbase
+ base_memsize
))
guestp
->pip
.real_limit
= (realbase
+ base_memsize
);
if (base_mblock
== NULL
) {
DBG(c_printf("Missing mblock node in guest node\n"));
if (!md_node_get_val(mdp
, base_mblock
, MDNAME(memsize
),
DBG(c_printf("Missing memsize in mblock node\n"));
if (guestp
->pip
.rom_size
> base_memsize
) {
*fail_codep
= HVctl_e_guest_base_mblock_too_small
;
* Look for the "perfctraccess" property. This property
* must be present and set to a non-zero value for the
* guest to have access to the JBUS/DRAM perf counters
* FIXME; These probably need to be their own resource !
if (!md_node_get_val(mdp
, guest_nodep
, MDNAME(perfctraccess
),
&guestp
->pip
.perfreg_accessible
)) {
guestp
->pip
.perfreg_accessible
= 0;
* Look for "diagpriv" property. This property enables
* the guest to execute arbitrary hyperprivileged code.
if (!md_node_get_val(mdp
, guest_nodep
, MDNAME(diagpriv
),
&guestp
->pip
.diagpriv
)) {
guestp
->pip
.diagpriv
= -1;
guestp
->pip
.diagpriv
= 0;
* Look for "rngctlaccessible" property. This property enables
* the guest to access the N2 RNG if available.
if (!md_node_get_val(mdp
, guest_nodep
, MDNAME(rngctlaccessible
),
&guestp
->rng_ctl_accessible
)) {
guestp
->rng_ctl_accessible
= 0;
* Look for "perfctrhtaccess" property. This property enables
* the guest to access the N2 hyper-privileged events if available.
if (!md_node_get_val(mdp
, guest_nodep
, MDNAME(perfctrhtaccess
),
&guestp
->perfreght_accessible
)) {
guestp
->perfreght_accessible
= 0;
* Per guest TOD offset ...
* This property doesn't trigger a modify op if the guest is already
* live. So changes are only visible after a reconfig when the
if (!md_node_get_val(mdp
, guest_nodep
, MDNAME(todoffset
),
&guestp
->pip
.tod_offset
)) {
guestp
->pip
.tod_offset
= 0;
* Now look for the guest devices ...
* FIXME: Needs updating so devices can be DR'd in.
if (NULL
!= md_find_node_by_arc(mdp
, guest_nodep
, MDARC(MDNAME(fwd
)),
MDNODE(MDNAME(virtual_devices
)), &devices_nodep
)) {
if (!md_node_get_val(mdp
, devices_nodep
, MDNAME(cfghandle
),
DBG(c_printf("Missing cfg_handle in device node\n"));
DBG(md_dump_node(mdp
, devices_nodep
));
guestp
->pip
.vdev_cfghandle
= cfg_handle
;
if (NULL
!= md_find_node_by_arc(mdp
, guest_nodep
, MDARC(MDNAME(fwd
)),
MDNODE(MDNAME(channel_devices
)), &devices_nodep
)) {
if (!md_node_get_val(mdp
, devices_nodep
, MDNAME(cfghandle
),
DBG(c_printf("Missing cfg_handle in device node\n"));
DBG(md_dump_node(mdp
, devices_nodep
));
guestp
->pip
.cdev_cfghandle
= cfg_handle
;
DBG(c_printf("End of guest parse 1\n"));
* Now we go and figureout what we need to do to the
* guest to update or configure its state.
* Memory was dealt with earlier.
if (guestp
->state
== GUEST_STATE_UNCONFIGURED
) {
DBG(c_printf("\t\tElected to config guest\n"));
guestp
->pip
.res
.flags
= RESF_Config
;
* what kind of a re-configure is this ?
* Since guest structures dont really bind to anything, stuff
* binds to them, this is a modify IMHO, but only if stuff
* actually got modified ..
* Note: it is implicit in this test that an MD update
* *requires* that the mdpa changes. Since the prospect of
* and MD update inplace while the guest is running is
* frightening and likely to break the guest this is a
* suffient condion. If Zeus updates the MD in place we
* have really big problems on our hands .. not sure we
* can detect this easily.
* We ignore dynamic updates of uartbase and diskpa
* since they should be detected above and denied if the
if (guestp
->pip
.rom_base
!= guestp
->rom_base
||
guestp
->pip
.rom_size
!= guestp
->rom_size
||
guestp
->pip
.md_pa
!= guestp
->md_pa
||
guestp
->pip
.reset_reason
!= guestp
->reset_reason
||
guestp
->pip
.perfreg_accessible
!=
guestp
->perfreg_accessible
||
guestp
->pip
.diagpriv
!= guestp
->diagpriv
||
((guestp
->pip
.vdev_cfghandle
!= INVALID_CFGHANDLE
) &&
(guestp
->pip
.vdev_cfghandle
!= guestp
->vdev_cfghandle
)) ||
((guestp
->pip
.cdev_cfghandle
!= INVALID_CFGHANDLE
) &&
(guestp
->pip
.cdev_cfghandle
!= guestp
->cdev_cfghandle
)) ||
guestp
->pip
.tod_offset
!= guestp
->tod_offset
) {
DBG(c_printf("\t\tElected to modify guest\n"));
guestp
->pip
.res
.flags
= RESF_Modify
;
guestp
->pip
.res
.flags
= RESF_Noop
;
DBG(c_printf("\t\tElected to ignore guest\n"));
*fail_codep
= HVctl_e_guest_missing_property
;
* Simple suite of checks based on the flags for this resource
res_guest_postparse(hvctl_res_error_t
*fail_codep
, int *fail_res_idp
)
for (i
= 0; i
< NGUESTS
; i
++, gp
++) {
switch (gp
->pip
.res
.flags
) {
if (gp
->state
!= GUEST_STATE_STOPPED
) {
*fail_codep
= HVctl_e_guest_active
;
ASSERT(0); /* not supported */
res_guest_commit(int flag
)
for (i
= 0; i
< NGUESTS
; i
++, gp
++) {
/* if not this ops turn move on */
if (gp
->pip
.res
.flags
!= flag
) continue;
switch (gp
->pip
.res
.flags
) {
DBG(c_printf("guest 0x%x : noop\n", i
));
res_guest_commit_unconfig(gp
);
res_guest_commit_config(gp
);
DBG(c_printf("guest 0x%x : rebind\n", i
));
ASSERT(0); /* not supported */
res_guest_commit_modify(gp
);
gp
->pip
.res
.flags
= RESF_Noop
; /* cleanup */
reset_guest_state(guest_t
*guestp
)
ASSERT(guestp
->state
== GUEST_STATE_STOPPED
);
reset_api_hcall_table(guestp
);
DBG(c_printf("\tguest hcall table @ 0x%x\n", guestp
->hcall_table
));
reset_guest_perm_mappings(guestp
);
reset_guest_ldc_mapins(guestp
);
* reconfigure common path - also shared with commit
res_guest_commit_modify(guest_t
*guestp
)
DBG(c_printf("modify guest 0x%x\n", guestp
->guestid
));
ASSERT(guestp
->state
!= GUEST_STATE_UNCONFIGURED
);
guestp
->rom_base
= guestp
->pip
.rom_base
;
guestp
->rom_size
= guestp
->pip
.rom_size
;
guestp
->real_base
= guestp
->pip
.real_base
;
guestp
->real_limit
= guestp
->pip
.real_limit
;
guestp
->mem_offset
= guestp
->pip
.mem_offset
;
guestp
->md_pa
= guestp
->pip
.md_pa
;
* Compute the Guests MD size
guestp
->disk
.pa
= guestp
->pip
.diskpa
;
guestp
->snet
.ino
= guestp
->pip
.snet_ino
;
guestp
->snet
.pa
= guestp
->pip
.snet_pa
;
* Look for the "perfctraccess" property. This property
* must be present and set to a non-zero value for the
* guest to have access to the JBUS/DRAM perf counters
guestp
->perfreg_accessible
= guestp
->pip
.perfreg_accessible
;
* Look for "diagpriv" property. This property enables
* the guest to execute arbitrary hyperprivileged code.
guestp
->diagpriv
= guestp
->pip
.diagpriv
;
* Assume entry point is at base of real memory
guestp
->entry
= guestp
->real_base
;
guestp
->reset_reason
= guestp
->pip
.reset_reason
;
unconfig_guest_virtual_device(guestp
);
unconfig_guest_channel_device(guestp
);
config_guest_virtual_device(guestp
, guestp
->pip
.vdev_cfghandle
);
config_guest_channel_device(guestp
, guestp
->pip
.cdev_cfghandle
);
res_guest_commit_config(guest_t
*guestp
)
DBG(c_printf("commit config guest 0x%x : config\n", guestp
->guestid
));
ASSERT(guestp
->state
== GUEST_STATE_UNCONFIGURED
);
* Now fill in basic properties for this guest ...
* FIXME: These should be available if the guest is
guestp
->ldc_mapin_basera
= LDC_MAPIN_BASERA
;
guestp
->ldc_mapin_size
= LDC_MAPIN_RASIZE
;
guestp
->state
= GUEST_STATE_STOPPED
;
res_guest_commit_modify(guestp
);
* TOD is configured once at the begining of time.
* we don't allow external modifies as that would warp
* the time value each time the guest gets and update
* after the guest had modified itself.
guestp
->tod_offset
= guestp
->pip
.tod_offset
;
* At the time of first guest config mark all INO2LDC
for (x
= 0; x
< MAX_LDC_INOS
; x
++) {
guestp
->ldc_ino2endpoint
[x
].endpointp
= NULL
;
guestp
->ldc_ino2endpoint
[x
].mapregp
= NULL
;
* FIXME: Should we really care to track this - why not
* just use the constant; MAX_LDC_CHANNELS
guestp
->ldc_max_channel_idx
= MAX_LDC_CHANNELS
;
/* we might check that all the LDC endpoints are !live */
/* kind of pointless since we are moving them to a global */
* clear out the devops table.
for (int y
= 0; y
< NVINOS
; y
++) {
guestp
->vino2inst
.vino
[y
] = DEVOPS_RESERVED
;
for (int x
= 0; x
< NDEVIDS
; x
++) {
guestp
->dev2inst
[x
] = DEVOPS_RESERVED
;
config_guest_virtual_device(guestp
, guestp
->pip
.vdev_cfghandle
);
config_guest_channel_device(guestp
, guestp
->pip
.cdev_cfghandle
);
/* until we boot it ... */
ASSERT(guestp
->state
== GUEST_STATE_STOPPED
);
reset_guest_state(guestp
);
DBG(c_printf("End of guest setup\n"));
res_guest_commit_unconfig(guest_t
*guestp
)
/* preserve the guest ID ... err thats about it */
DBG(c_printf("guest 0x%x : unconfig\n", guestp
->guestid
));
ASSERT(guestp
->state
== GUEST_STATE_STOPPED
);
unconfig_guest_virtual_device(guestp
);
unconfig_guest_channel_device(guestp
);
* clear out the devops table.
for (int y
= 0; y
< NVINOS
; y
++) {
guestp
->vino2inst
.vino
[y
] = DEVOPS_RESERVED
;
for (int x
= 0; x
< NDEVIDS
; x
++) {
guestp
->dev2inst
[x
] = DEVOPS_RESERVED
;
init_guest(guestp
->guestid
);
/* Just incase we add more phases */
guestp
->pip
.res
.flags
= RESF_Noop
;
config_a_guest_device_vino(guest_t
*guestp
, int ino
, uint8_t type
)
uint64_t cfg_handle
= INVALID_CFGHANDLE
;
cfg_handle
= guestp
->vdev_cfghandle
;
cfg_handle
= guestp
->cdev_cfghandle
;
DBG(c_printf("guest device:\n\tcfg handle 0x%x 0x%x\n",
ASSERT(cfg_handle
!= INVALID_CFGHANDLE
);
guestp
->vino2inst
.vino
[cfg_handle
+ ino
] = type
;
unconfig_a_guest_device_vino(guest_t
*guestp
, int ino
, uint8_t type
)
uint64_t cfg_handle
= INVALID_CFGHANDLE
;
cfg_handle
= guestp
->vdev_cfghandle
;
cfg_handle
= guestp
->cdev_cfghandle
;
ASSERT(cfg_handle
!= INVALID_CFGHANDLE
);
guestp
->vino2inst
.vino
[cfg_handle
+ ino
] = DEVOPS_RESERVED
;
config_guest_virtual_device(guest_t
*guestp
, uint64_t cfg_handle
)
ASSERT(cfg_handle
!= INVALID_CFGHANDLE
);
if (cfg_handle
== INVALID_CFGHANDLE
)
guestp
->vdev_cfghandle
= cfg_handle
;
devid
= guestp
->vdev_cfghandle
>> DEVCFGPA_SHIFT
;
guestp
->dev2inst
[devid
] = DEVOPS_VDEV
;
unconfig_guest_virtual_device(guest_t
*guestp
)
devid
= guestp
->vdev_cfghandle
>> DEVCFGPA_SHIFT
;
guestp
->dev2inst
[devid
] = DEVOPS_RESERVED
;
guestp
->vdev_cfghandle
= INVALID_CFGHANDLE
;
config_guest_channel_device(guest_t
*guestp
, uint64_t cfg_handle
)
ASSERT(cfg_handle
!= INVALID_CFGHANDLE
);
if (cfg_handle
== INVALID_CFGHANDLE
)
guestp
->cdev_cfghandle
= cfg_handle
;
devid
= guestp
->cdev_cfghandle
>> DEVCFGPA_SHIFT
;
edevid
= (guestp
->cdev_cfghandle
+ MAX_LDC_INOS
) >> DEVCFGPA_SHIFT
;
for (x
= devid
; x
< edevid
; x
++)
guestp
->dev2inst
[x
] = DEVOPS_CDEV
;
unconfig_guest_channel_device(guest_t
*guestp
)
devid
= guestp
->cdev_cfghandle
>> DEVCFGPA_SHIFT
;
edevid
= (guestp
->cdev_cfghandle
+ MAX_LDC_INOS
) >> DEVCFGPA_SHIFT
;
for (x
= devid
; x
< edevid
; x
++)
guestp
->dev2inst
[x
] = DEVOPS_RESERVED
;
guestp
->cdev_cfghandle
= INVALID_CFGHANDLE
;