mach_descrip.c revision d1a9c4c143aaef0b57cf9531ab060cc86dddaf09
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Kernel Machine Description (MD)
*
* The Kernel maintains a global copy of the machine description for
* the system. This is for use by all kernel subsystems and is exported
* to user applications through the the 'mdesc' device driver. It is
* initially copied in from the Hypervisor at boot time, but can be
* updated dynamically on demand. The Kernel provides an interface
* for consumers to obtain a handle to the global MD. Consumers of the
* MD must use the specified interfaces. An update interface is provided
* for platform services to intiate an MD update on notification by a
* service entity.
*
* Locks
* The current global MD is protected by the curr_mach_descrip_lock.
* Each Machine description has a lock to synchornize its ref count.
* The Obsolete MD list is protected by the obs_list_lock.
*/
#include <sys/machsystm.h>
#include <sys/machcpuvar.h>
#include <sys/machparam.h>
#include <vm/hat_sfmmu.h>
#include <vm/seg_kmem.h>
#include <sys/hypervisor_api.h>
#include <sys/sysmacros.h>
#include <sys/mdesc_impl.h>
#include <sys/mach_descrip.h>
#include <sys/prom_plat.h>
static void init_md_params(void);
/*
* Global ptr of the current generation Machine Description
*/
static machine_descrip_t *curr_mach_descrip;
/*
* Initialized by machine_descrip_startup_init in startup.
* machine_descript_init will reintialize the structure with
* the vmem allocators once the vmem is available in the boot up
* process.
*/
static machine_descrip_memops_t startup_memops = {
};
static machine_descrip_memops_t mach_descrip_memops = {
};
static kmutex_t curr_mach_descrip_lock;
/*
* List of obsolete Machine Descriptions
* Machine descriptions that have users are put on this list
* and freed after the last user has called md_fini_handle.
*/
static kmutex_t obs_list_lock;
static const char alloc_fail_msg[] =
"MD: cannot allocate MD buffer of size %ld bytes\n";
/*
* Global flags that indicate what domaining features are
* available, if any. The value is set at boot time based on
* the value of the 'domaining-enabled' property in the MD
* and the global override flag below. Updates to this
* variable after boot are not supported.
*/
/*
* Global override for the 'domaining_capailities' flags. If this
* ignoring the value of the 'domaining-enabled' property in
* the MD.
*/
#define META_ALLOC_ALIGN 8
#define HAS_GEN(x) (x != MDESC_INVAL_GEN)
#ifdef DEBUG
static int mach_descrip_debug = 0;
#ifdef MACH_DESC_DEBUG
static void
{
int i;
for (i = 0; i < size; i += 16) {
int j;
prom_printf("0x%04x :", i);
for (j = 0; j < 16 && (i+j) < size; j++)
prom_printf("\n");
}
}
#endif /* MACH_DESC_DEBUG */
static void
print_obs_list(void)
{
prom_printf("MD_obs_list->");
prom_printf("->");
}
prom_printf("NULL\n");
}
#else
#define PRINT_LIST()
#endif /* DEBUG */
/*
* MD obsolete list managment functions
*/
static machine_descrip_t *
{
return (mdescp);
}
}
return (mdescp);
}
static void
{
if (obs_machine_descrip_list == mdescp) {
} else {
break;
}
}
}
PRINT_LIST();
}
static void
{
PRINT_LIST();
}
/*
* Allocate a machine_descrip meta structure and intitialize it.
*/
static machine_descrip_t *
new_mach_descrip(void)
{
(sizeof (machine_descrip_t));
}
return (mdescp);
}
/*
* Free a machine_descrip meta structure and intitialize it.
* Also free the MD buffer.
*/
static void
{
panic("destroy_machine_descrip: memops NULL\n");
}
/*
* Call into the Hypervisor to retrieve the most recent copy of the
* machine description. If references to the current MD are active
* stow it in the obsolete MD list and update the current MD reference
* with the new one.
* The obsolete list contains one MD per generation. If the firmware
* doesn't support MD generation fail the call.
*/
int
mach_descrip_update(void)
{
int ret = 0;
MDP(("MD: Requesting buffer size\n"));
/*
* If the required MD size changes between our first call
* to hv_mach_desc (to find the required buf size) and the
* second call (to get the actual MD) and our allocated
* memory is insufficient, loop until we have allocated
* sufficient space.
*/
do {
/*
* Align allocated space to nearest page.
* contig_mem_alloc_align() requires a power of 2 alignment.
*/
ret = -1;
goto done;
}
/*
* We get H_EINVAL if our buffer size is too small. In
* that case stay in the loop, reallocate the buffer
* and try again.
*/
ret = -1;
goto done;
}
#ifdef DEBUG
MDP(("MD: generation number not found\n"));
} else
#endif /* DEBUG */
/* check for the same generation number */
#ifdef DEBUG
/*
* Pedantic Check for generation number. If the
* generation number is the same, make sure the
* MDs are really identical.
*/
"with the same generation (%ld) are not "
"identical", tgen);
ret = -1;
goto done;
}
#endif
"the same generation (%ld) as the old MD", tgen);
ret = 0;
goto done;
}
/* check for generations moving backwards */
" older generation (%ld) than current MD (%ld)",
ret = -1;
goto done;
}
if (curr_mach_descrip->refcnt == 0) {
MDP(("MD: freeing old md buffer gen %ld\n",
curr_mach_descrip->gen));
/* Free old space */
} else {
/*
* No update support if FW
* doesn't have MD generation id
* feature.
*/
prom_printf("WARNING: F/W does not support MD "
"generation count, MD update failed\n");
ret = -1;
goto done;
}
MDP(("MD: adding to obs list %ld\n",
curr_mach_descrip->gen));
if (curr_mach_descrip == NULL) {
panic("Allocation for machine description"
" failed\n");
}
}
}
#ifdef MACH_DESC_DEBUG
#endif /* MACH_DESC_DEBUG */
return (ret);
done:
return (ret);
}
static void *
{
void *p;
return (p);
}
static void *
{
}
static void
{
}
static void *
{
if (p == NULL)
return (p);
}
static void
{
}
static void *
{
}
/*
* Initialize the kernel's Machine Description(MD) framework
* early on in startup during mlsetup() so consumers
* can get to the MD before the VM system has been initialized.
*
* Also get the most recent version of the MD.
*/
void
{
if (curr_mach_descrip == NULL)
panic("Allocation for machine description failed\n");
if (mach_descrip_update())
panic("Machine description initialization failed\n");
}
/*
* Counterpart to the above init function. Free up resources
* allocated at startup by mach_descrip_startup_setup().
* And reset machine description framework state.
*
* All consumers must have fini'ed their handles at this point.
*/
void
{
}
/*
* Initialize the kernel's Machine Description(MD) framework
* after the the VM system has been initialized.
*
* Also get the most recent version of the MD.
* Assumes that the machine description frame work is in a clean
* state and the machine description intialized during startup
* has been cleaned up and resources deallocated.
*/
void
mach_descrip_init(void)
{
curr_mach_descrip_memops == NULL));
if (curr_mach_descrip == NULL)
panic("Allocation for machine description failed\n");
if (mach_descrip_update())
panic("Machine description intialization failed\n");
/* read in global params */
}
/*
* Client interface to get a handle to the current MD.
* The md_fini_handle() interface should be used to
* clean up the refernce to the MD returned by this function.
*/
md_t *
md_get_handle(void)
{
if (curr_mach_descrip != NULL) {
}
return (mdp);
}
/*
* Client interface to clean up the refernce to the MD returned
* by md_get_handle().
*/
int
{
return (-1);
/*
* Check if mdp is current MD gen
*/
goto fini;
}
/*
* MD is in the obsolete list
*/
return (-1);
goto fini;
}
fini:
}
/*
* General purpose initialization function used to extract parameters
* from the MD during the boot process. This is called immediately after
* the in kernel copy of the MD has been initialized so that global
* flags are available to various subsystems as they get initialized.
*/
static void
init_md_params(void)
{
int num_nodes;
int listsz;
mdp = md_get_handle();
listp = (mde_cookie_t *)
/*
* Import various parameters from the MD. For now,
* the only parameter of interest is whether or not
* domaining features are supported.
*/
(void) md_fini_handle(mdp);
}
static void
{
int num_nodes;
/* should only be one platform node */
/*
* The property is not present. This implies
* that the firmware does not support domaining
* features.
*/
MDP(("'domaining-enabled' property not present\n"));
return;
}
if (val == 1) {
if (force_domaining_disabled) {
MDP(("domaining manually disabled\n"));
} else {
}
}
}
/*
* Client interface to get a pointer to the raw MD buffer
* Private to kernel and mdesc driver.
*/
{
return (NULL);
}
/*
* This is called before an MD structure is intialized, so
* it walks the raw MD looking for the generation property.
*/
static uint64_t
{
char *namep;
int idx;
/*
* Very basic check for alignment to avoid
* bus error issues.
*/
return (MDESC_INVAL_GEN);
return (MDESC_INVAL_GEN);
}
/*
* Search for the root node. Perform the walk manually
* since the MD structure is not set up yet.
*/
case MDET_LIST_END:
break;
case MDET_NODE:
/* found root node */
break;
}
break;
default:
/* ignore */
idx++;
}
}
/* root not found */
return (MDESC_INVAL_GEN);
}
/* search the rootnode for the generation property */
char *prop_name;
/* generation field is a prop_val */
continue;
return (MDE_PROP_VALUE(elem));
}
}
return (MDESC_INVAL_GEN);
}
/*
* Failed to allocate the list : Return value -1
* md_scan_dag API failed : Return the result from md_scan_dag API
*/
int
char *node_name,
char *dag,
mde_cookie_t **list)
{
int res;
mdp->node_count);
return (-1);
/*
* If md_scan_dag API returned 0 or -1 then free the buffer
* and return -1 to indicate the error from this API.
*/
if (res < 1) {
}
return (res);
}
void
mde_cookie_t **list)
{
}