/*
* 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
*/
/*
*/
#include <sys/machbrand.h>
#include <sys/pathname.h>
#if defined(__sparcv9)
/* sparcv9 uses system wide brand interposition hooks */
static void brand_plat_interposition_enable(void);
static void brand_plat_interposition_disable(void);
};
#else /* !__sparcv9 */
};
#endif /* !__sparcv9 */
"native",
NULL,
};
/*
* Used to maintain a list of all the brands currently loaded into the
* kernel.
*/
struct brand_list {
int bl_refcnt;
};
/*
* This lock protects the integrity of the brand list.
*/
void
{
}
int
{
return (EINVAL);
"brand '%s' was built to run on older versions "
"of Solaris.",
} else {
"brand '%s' was built to run on a newer version "
"of Solaris.",
}
return (EINVAL);
}
/* Sanity checks */
return (EINVAL);
}
/* Add the brand to the list of loaded brands. */
/*
* Check to be sure we haven't already registered this brand.
*/
"Invalid attempt to load a second instance of "
return (EINVAL);
}
}
#if defined(__sparcv9)
/* sparcv9 uses system wide brand interposition hooks */
if (brand_list == NULL)
#endif /* __sparcv9 */
brand_list = list;
return (0);
}
/*
* The kernel module implementing this brand is being unloaded, so remove
* it from the list of active brands.
*/
int
{
/* Sanity checks */
return (EINVAL);
}
break;
}
return (EINVAL);
}
return (EBUSY);
}
/* Remove brand from the list */
else
#if defined(__sparcv9)
/* sparcv9 uses system wide brand interposition hooks */
if (brand_list == NULL)
#endif /* __sparcv9 */
return (0);
}
/*
* Record that a zone of this brand has been instantiated. If the kernel
* module implementing this brand's functionality is not present, this
* routine attempts to load the module as a side effect.
*/
brand_t *
{
struct brand_list *l = NULL;
char *modname;
int err = 0;
if (is_system_labeled()) {
"Branded zones are not allowed on labeled systems.");
return (NULL);
}
/*
* We make at most two passes through this loop. The first time
* through, we're looking to see if this is a new user of an
* already loaded brand. If the brand hasn't been loaded, we
* call ddi_modopen() to force it to be loaded and then make a
* second pass through the list of brands. If we don't find the
* brand the second time through it means that the modname
* specified in the brand_attr structure doesn't provide the brand
* specified in the brandname field. This would suggest a bug in
* the brand's config.xml file. We close the module and return
* 'NULL' to the caller.
*/
for (;;) {
/*
* Search list of loaded brands
*/
break;
break;
/*
* We didn't find that the requested brand has been loaded
* yet, so we trigger the load of the appropriate kernel
* module and search the list again.
*/
if (err != 0)
return (NULL);
}
/*
* If we found the matching brand, bump its reference count.
*/
if (l != NULL)
l->bl_refcnt++;
(void) ddi_modclose(hdl);
}
/*
* Return the number of zones currently using this brand.
*/
int
{
struct brand_list *l;
int cnt = 0;
break;
}
return (cnt);
}
void
{
break;
}
}
}
void
{
/*
* We should only be called from exec(), when we know the process
* is single-threaded.
*/
ASSERT(PROC_IS_BRANDED(p));
BROP(p)->b_setbrand(p);
}
void
{
/*
* If called from exec_common() or proc_exit(),
* we know the process is single-threaded.
* If called from fork_fail, p_tlist is NULL.
*/
if (!no_lwps) {
}
ASSERT(PROC_IS_BRANDED(p));
p->p_brand = &native_brand;
}
#if defined(__sparcv9)
/*
* Currently, only sparc has system level brand syscall interposition.
* On x86 we're able to enable syscall interposition on a per-cpu basis
* when a branded thread is scheduled to run on a cpu.
*/
/* Local variables needed for dynamic syscall interposition support */
/* Trap Table syscall entry hot patch points */
extern void syscall_trap_patch_point(void);
extern void syscall_trap32_patch_point(void);
/* Alternate syscall entry handlers used when branded zones are running */
extern void syscall_wrapper(void);
extern void syscall_wrapper32(void);
/* Macros used to facilitate sparcv9 instruction generation */
/*ARGSUSED*/
static void
{
/*
* Before we hot patch the kernel save the current instructions
* so that we can restore them later.
*/
/*
* Modify the trap table at the patch points.
*
* We basically replace the first instruction at the patch
* point with a ba,a instruction that will transfer control
* to syscall_wrapper or syscall_wrapper32 for 64-bit and
* 32-bit syscalls respectively. It's important to note that
* the annul bit is set in the branch so we don't execute
* the instruction directly following the one we're patching
* during the branch's delay slot.
*
* It also doesn't matter that we're not atomically updating both
* the 64 and 32 bit syscall paths at the same time since there's
* no actual branded processes running on the system yet.
*/
4);
4);
}
/*ARGSUSED*/
static void
{
/*
* Restore the original instructions at the trap table syscall
* patch points to disable the brand syscall interposition
* mechanism.
*/
}
#endif /* __sparcv9 */
/*
* The following functions can be shared among kernel brand modules which
* implement Solaris-derived brands, all of which need to do similar tasks
* to manage the brand.
*/
#if defined(_LP64)
static void
{
}
#endif /* _LP64 */
/*
* Return -1 if the cmd was not handled by this function.
*/
/*ARGSUSED*/
int
{
int err;
/*
* There is one operation that is supported for a native
* process; B_EXEC_BRAND. This brand operaion is redundant
* since the kernel assumes a native process doing an exec
* in a branded zone is going to run a branded processes.
* hence we don't support this operation.
*/
if (cmd == B_EXEC_BRAND)
return (ENOSYS);
/* For all other operations this must be a branded process. */
if (p->p_brand == &native_brand)
return (ENOSYS);
switch ((cmd)) {
case B_EXEC_NATIVE:
(const char **)arg3, EBA_NATIVE);
return (err);
/*
* Get the address of the user-space system call handler from
* the user process and attach it to the proc structure.
*/
case B_REGISTER:
if (p->p_model == DATAMODEL_NATIVE) {
return (EFAULT);
}
#if defined(_LP64)
else {
return (EFAULT);
}
#endif /* _LP64 */
return (ENOTSUP);
return (0);
case B_ELFDATA:
if (p->p_model == DATAMODEL_NATIVE) {
sizeof (brand_elf_data_t)) != 0)
return (EFAULT);
}
#if defined(_LP64)
else {
!= 0)
return (EFAULT);
}
#endif /* _LP64 */
return (0);
/*
* The B_TRUSS_POINT subcommand exists so that we can see
* truss output from interposed system calls that return
* without first calling any other system call, meaning they
* would be invisible to truss(1).
* If the second argument is set non-zero, set errno to that
* value as well.
*
* Common arguments seen with truss are:
*
* arg1: syscall number
* arg2: errno
*/
case B_TRUSS_POINT:
}
return (-1);
}
/*ARGSUSED*/
void
{
/*
* Just duplicate all the proc data of the parent for the
* child
*/
}
static void
{
setexecenv(ep);
}
/*ARGSUSED*/
int
{
int interp;
int i, err;
char *linker;
/*
* We will exec the brand library and then map in the target
* application and (optionally) the brand's default linker.
*/
}
#if defined(_LP64)
else {
}
#endif /* _LP64 */
return (err);
}
/*
* The following elf{32}exec call changes the execenv in the proc
* struct which includes changing the p_exec member to be the vnode
* for the brand library (e.g. /.SUNWnative/usr/lib/s10_brand.so.1).
* We will eventually set the p_exec member to be the vnode for the new
* executable when we call setexecenv(). However, if we get an error
* before that call we need to restore the execenv to its original
* values so that when we return to the caller fop_close() works
* properly while cleaning up from the failed exec(). Restoring the
* original value will also properly decrement the 2nd VN_RELE that we
* took on the brand library.
*/
}
#if defined(_LP64)
else {
}
#endif /* _LP64 */
if (err != 0) {
return (err);
}
/*
* The u_auxv veCTors are set up by elfexec to point to the
* brand emulation library and linker. Save these so they can
* be copied to the specific brand aux vectors.
*/
for (i = 0; i < __KERN_NAUXV_IMPL; i++) {
case AT_SUN_LDDATA:
break;
case AT_BASE:
break;
case AT_ENTRY:
break;
case AT_PHDR:
break;
case AT_PHENT:
break;
case AT_PHNUM:
break;
default:
break;
}
}
/* Make sure the emulator has an entry point */
}
#if defined(_LP64)
else {
else
}
#endif /* _LP64 */
if (err != 0) {
return (err);
}
/*
* Save off the important properties of the executable. The
* brand library will ask us for this data later, when it is
* initializing and getting ready to transfer control to the
* brand application.
*/
else
if (interp) {
/*
* This is a shared object executable, so we
* need to pick a reasonable place to put the
* heap. Just don't use the first page.
*/
}
/*
* If the program needs an interpreter (most do), map
* it in and store relevant information about it in the
* aux vector, where the brand library can find it.
*/
return (err);
}
}
#if defined(_LP64)
else {
else
}
#endif /* _LP64 */
if (err != 0) {
return (err);
}
/*
* Now that we know the base address of the brand's
* linker, place it in the aux vector.
*/
} else {
/*
* This program has no interpreter. The brand library
* will jump to the address in the AT_SUN_BRAND_LDENTRY
* aux vector, so in this case, put the entry point of
* the main executable there.
*/
/*
* An executable with no interpreter, this must
* be a statically linked executable, which
* means we loaded it at the address specified
* in the elf header, in which case the e_entry
* field of the elf header is an absolute
* address.
*/
} else {
/*
* A shared object with no interpreter, we use
* the calculated address from above.
*/
/*
* Delay setting the brkbase until the
* first call to brk(); see elfexec()
* for details.
*/
env.ex_brksize = 0;
}
}
}
setexecenv(&env);
/*
* It's time to manipulate the process aux vectors. First
* we need to update the AT_SUN_AUXFLAGS aux vector to set
* the AF_SUN_NOPLM flag.
*/
sizeof (auxflags_auxv)) != 0)
return (EFAULT);
sizeof (auxflags_auxv)) != 0)
return (EFAULT);
}
#if defined(_LP64)
else {
sizeof (auxflags_auxv32)) != 0)
return (EFAULT);
sizeof (auxflags_auxv32)) != 0)
return (EFAULT);
}
#endif /* _LP64 */
/* Second, copy out the brand specific aux vectors. */
{ AT_SUN_BRAND_AUX1, 0 },
{ AT_SUN_BRAND_AUX2, 0 },
{ AT_SUN_BRAND_AUX3, 0 }
};
sizeof (brand_auxv)) != 0)
return (EFAULT);
}
#if defined(_LP64)
else {
{ AT_SUN_BRAND_AUX1, 0 },
{ AT_SUN_BRAND_AUX2, 0 },
{ AT_SUN_BRAND_AUX3, 0 }
};
sizeof (brand_auxv32)) != 0)
return (EFAULT);
}
#endif /* _LP64 */
/*
* Third, the /proc aux vectors set up by elfexec() point to
* brand emulation library and it's linker. Copy these to the
* /proc brand specific aux vector, and update the regular
* /proc aux vectors to point to the executable (and it's
* linker). This will enable debuggers to access the
* executable via the usual /proc or elf notes aux vectors.
*
* The brand emulation library's linker will get it's aux
* vectors off the stack, and then update the stack with the
* executable's aux vectors before jumping to the executable's
* linker.
*
* Debugging the brand emulation library must be done from
* the global zone, where the librtld_db module knows how to
* fetch the brand specific aux vectors to access the brand
* emulation libraries linker.
*/
for (i = 0; i < __KERN_NAUXV_IMPL; i++) {
continue;
case AT_BASE:
break;
case AT_ENTRY:
break;
case AT_PHDR:
break;
case AT_PHENT:
break;
case AT_PHNUM:
break;
case AT_SUN_LDDATA:
break;
default:
continue;
}
/* Hide the entry for static binaries */
}
}
/*
* The last thing we do here is clear spd->spd_handler. This
* is important because if we're already a branded process and
* if this exec succeeds, there is a window between when the
* exec() first returns to the userland of the new process and
* when our brand library get's initialized, during which we
* don't want system calls to be re-directed to our brand
* library since it hasn't been initialized yet.
*/
return (0);
}
void
{
/*
* We should only be called from exec(), when we know the process
* is single-threaded.
*/
/* Upon exec, reset our lwp brand data. */
/*
* Upon exec, reset all the proc brand data, except for the elf
* data associated with the executable we are exec'ing.
*/
}
int
{
int err;
/*
* If there are any zones using this brand, we can't allow it
* to be unloaded.
*/
if (brand_zone_count(pbrand))
return (EBUSY);
*emul_table = NULL;
if (err)
return (err);
}
/*ARGSUSED*/
void
{
/*
* Both LWPs have already had been initialized via
* brand_solaris_initlwp().
*/
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
int
{
l->lwp_brand = (void *)-1;
return (0);
}
/*ARGSUSED*/
void
{
/*
* We should never be called for the last thread in a process.
* (That case is handled by brand_solaris_proc_exit().)
* Therefore this lwp must be exiting from a multi-threaded
* process.
*/
}
/*ARGSUSED*/
void
{
/*
* When called from proc_exit(), we know that process is
* single-threaded and free our lwp brand data.
* otherwise just free p_brand_data and return.
*/
if (l != NULL) {
(void) brand_solaris_freelwp(l, pbrand);
}
/* upon exit, free our proc brand data */
p->p_brand_data = NULL;
}
void
{
/*
* We should only be called from exec(), when we know the process
* is single-threaded.
*/
}