/*
* @OSF_FREE_FREE_COPYRIGHT@
*
*/
/*
* HISTORY
* $Log: serv_init.c,v $
* Revision 1.1.2.7 1997/02/27 11:11:06 bruel
* HPPA support
* [1997/02/27 11:08:08 bruel]
*
* Revision 1.1.2.6 1996/11/18 18:16:15 barbou
* Initialize the notification module.
* [1996/11/18 18:00:59 barbou]
*
* Revision 1.1.2.5 1996/11/05 12:20:29 barbou
* Added "cthread_red_zone_size" (1 page) for DEBUG configurations.
* [96/11/05 barbou]
*
* Revision 1.1.2.4 1996/10/09 09:35:44 barbou
* Added osfmach3_halt_system.
* Added support for the "-s" boot flag.
* [96/10/04 barbou]
*
* Revision 1.1.2.3 1996/09/27 23:04:14 barbou
* Merged up to linux-2.0.21.
* [1996/09/27 22:14:04 barbou]
*
* Revision 1.1.2.2 1996/09/18 14:25:22 barbou
* Don't call parse_root_device in osfmach3_start_init: the devices haven't
* been initialized yet.
* [96/09/17 barbou]
*
* Revision 1.1.2.1 1996/09/09 17:19:00 barbou
* Call sys_setup in the context of the "init" task. Do the
* appropriate segment registers incantations to tell it to
* copy data from our own address space.
* [96/09/07 barbou]
*
* Use task[smp_num_cpus] for init's task.
* [1996/09/06 23:07:52 barbou]
*
* Call sys_setup after forking init, so that init gets pid 1.
* [96/09/07 barbou]
*
* Terminate the startup thread after start_kernel().
* [96/09/06 barbou]
*
* Adapted for clone().
* [1996/09/05 17:26:56 barbou]
*
* Created.
* [1996/09/02 10:14:00 barbou]
*
* $EndLog$
*/
#include <linux/autoconf.h>
#include <mach/exc_server.h>
#include <mach/std_types.h>
#include <mach/port.h>
#include <mach/mach_traps.h>
#include <mach/host_info.h>
#include <mach/host_reboot.h>
#include <mach/mach_host.h>
#include <mach/mach_interface.h>
#include <mach/bootstrap.h>
#include <osfmach3/mach_init.h>
#include <osfmach3/mach3_debug.h>
#include <osfmach3/uniproc.h>
#include <osfmach3/console.h>
#include <osfmach3/parent_server.h>
#include <osfmach3/serv_port.h>
#include <osfmach3/server_thread.h>
#include <osfmach3/block_dev.h>
#include <asm/segment.h>
#include <asm/page.h>
#include <asm/param.h>
#include <asm/processor.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <asm/pgtable.h>
extern void uniproc_preemption_init(void);
extern char *getenv(char *var);
extern void start_kernel(void);
extern void subsystem_init(void);
extern void mach_trap_init(void);
extern void server_thread_init(void);
extern void osfmach3_notify_init(void);
extern void ux_server_init(void);
extern void dev_to_object_init(void);
extern void device_reply_hdlr(void);
extern void init_mapped_time(void);
extern void jiffies_thread_init(void);
extern void user_memory_init(void);
extern void inode_pager_init(void);
extern void fake_interrupt_init(void);
extern void osfmach3_console_init(void);
void setup_server(int argc, char **argv);
void ports_init(void);
void print_versions(void);
void gen_init(int argc, char **argv);
boolean_t use_activations = FALSE;
extern boolean_t in_kernel; /* exported by libcthreads */
extern boolean_t map_user_mem_to_copy;
mach_port_t privileged_host_port;
mach_port_t host_port;
mach_port_t security_port;
mach_port_t root_ledger_wired;
mach_port_t root_ledger_paged;
mach_port_t device_server_port;
mach_port_t default_pager_port;
mach_port_t default_processor_set;
mach_port_t default_processor_set_name;
mach_port_t exc_subsystem_port;
security_token_t server_security_token;
char *rootdev_name = "boot_device";
vm_size_t osfmach3_mem_size;
vm_size_t page_size;
vm_size_t page_mask;
int page_shift;
int parent_server = 0; /* Running under another server ? */
char osfmach3_kernel_version[KERNEL_VERSION_MAX];
/*
* Size of the server threads' stacks size.
* This variable is imported and used by the cthreads library.
*
* IMPORTANT NOTE: one needs to use the -maxonstack MIG option when
* building libmach. Otherwise, stacks of 64K or over are necessary !
*/
#ifdef i386
vm_size_t cthread_stack_size = 16 * 1024;
#endif /* i386 */
#ifdef PPC
vm_size_t cthread_stack_size = 16 * 1024;
#endif /* PPC */
#ifdef hp_pa
vm_size_t cthread_stack_size = 32 * 1024;
#endif /* hp_pa */
#ifdef CONFIG_OSFMACH3_DEBUG
vm_size_t cthread_red_zone_size = PAGE_SIZE;
#endif /* CONFIG_OSFMACH3_DEBUG */
host_priority_info_data_t host_pri_info;
void
main(
int argc,
char **argv)
{
struct server_thread_priv_data priv_data;
server_thread_set_priv_data(cthread_self(), &priv_data);
set_current_task(&init_task);
uniproc_enter();
uniproc_preemption_init();
/*
* Initialize the server.
*/
setup_server(argc, argv);
/*
* Linux-specific code starts here.
*/
start_kernel();
uniproc_exit();
cthread_detach(cthread_self());
cthread_exit((void *) 0);
}
void
setup_server(
int argc,
char **argv)
{
kern_return_t kr;
mach_msg_type_number_t count;
ports_init();
count = HOST_PRIORITY_INFO_COUNT;
kr= host_info(mach_host_self(), HOST_PRIORITY_INFO,
(host_info_t) &host_pri_info, &count);
if (kr != KERN_SUCCESS)
panic("host_info HOST_PRIORITY_INFO");
gen_init(argc, argv); /* generic "machine-dependent" initialization */
print_versions();
if (use_activations)
subsystem_init();
/*
* Make the server unswappable (but it's still pageable).
*/
kr = task_swappable(privileged_host_port, mach_task_self(), FALSE);
if (kr != KERN_SUCCESS) {
MACH3_DEBUG(1, kr, ("setup_server: task_swappable"));
}
/*
* Initialize module for handling server threads. Must be
* done very early, because even our wired threads are managed
* by this module.
*/
server_thread_init();
ux_server_init();
mach_trap_init();
osfmach3_notify_init();
if (parent_server) {
/*
* Catch signals if we're a regular application
* of a parent server.
*/
parent_server_catchall_signals();
}
fake_interrupt_init();
init_mapped_time();
jiffies_thread_init();
dev_to_object_init();
device_reply_hdlr();
user_memory_init();
inode_pager_init();
}
/*
* Print Mach kernel version and server version.
*/
void
print_versions(void)
{
if (host_kernel_version(mach_host_self(),&osfmach3_kernel_version[0])
!= KERN_SUCCESS) {
printk("Warning: print_versions: can't get micro-kernel version.\n");
} else {
printk("%s", osfmach3_kernel_version); /* MK version */
}
}
void
ports_init(void)
{
mach_port_t bootstrap_port;
kern_return_t result;
u_int security_token_size;
/*
* Get our bootstrap port
*/
result = task_get_special_port(mach_task_self(),
TASK_BOOTSTRAP_PORT,
&bootstrap_port);
if (result != KERN_SUCCESS)
panic("get bootstrap port %d", result);
/*
* Ask for the privileged ports
*/
result = bootstrap_ports(bootstrap_port,
&privileged_host_port,
&device_server_port,
&root_ledger_wired,
&root_ledger_paged,
&security_port);
if (result != KERN_SUCCESS)
panic("bootstrap privileged ports %d", result);
/*
* Get default_pager port
*/
result = vm_set_default_memory_manager(privileged_host_port,
&default_pager_port);
if (result != KERN_SUCCESS)
panic("getting default pager port");
host_port = mach_host_self();
/*
* Lookup our default processor-set name/control ports.
*/
(void) processor_set_default(mach_host_self(),
&default_processor_set_name);
(void) host_processor_set_priv(privileged_host_port,
default_processor_set_name,
&default_processor_set);
/*
* Get the security token for device operations
*/
security_token_size = TASK_SECURITY_TOKEN_COUNT;
result = task_info(mach_task_self(), TASK_SECURITY_TOKEN,
(task_info_t) &server_security_token,
&security_token_size);
if (result != KERN_SUCCESS)
panic("server security token %d", result);
}
void
subsystem_init(void)
{
kern_return_t result;
rpc_subsystem_t serv_subsystem;
mach_msg_type_number_t serv_subsystem_size;
extern struct rpc_subsystem serv_callback_subsystem;
/*
* Combine the exception_raise subsystem with the callback
* subsystem, so that a single port can be used for RPC to
* both of them.
*/
serv_subsystem =
mach_subsystem_join((rpc_subsystem_t) &catch_exc_subsystem,
(rpc_subsystem_t) &serv_callback_subsystem,
&serv_subsystem_size,
(void *(*)(int)) cthread_malloc);
/*
* Register subsystems.
*/
result = mach_subsystem_create(mach_task_self(),
(user_subsystem_t) serv_subsystem,
serv_subsystem_size,
&exc_subsystem_port);
if (result != KERN_SUCCESS) {
MACH3_DEBUG(0, result, ("ports_init: mach_subsystem_create"));
printk("subsystem_init: continuing without activations\n");
use_activations = FALSE;
}
}
void
insert_args(
char *args,
int *argcp,
char ***argvp)
{
int pass, nargs, argc = 0, i;
char *p, **argv;
kern_return_t kr;
/* Two passes over the argument string. The first counts the
arguments so we know how big an argv[] to allocate; the second
fills in the pointers in this argv[]. */
for (pass = 1; pass <= 2; pass++) {
nargs = 0;
p = args;
while (1) {
while (*p == ' ') p++;
if (*p == '\0')
break;
if (pass == 2)
argv[nargs] = p;
nargs++;
while (*p != ' ' && *p != '\0') p++;
if (pass == 2 && *p == ' ')
*p++ = '\0';
}
if (pass == 1) {
/* Allocate a new argv[] that will contain the
inserted arguments followed by the remaining
original arguments. After this argv[], we will
put a copy of `args' so that we can replace
spaces by '\0's in this copy. We can't use
MALLOC yet so vm_allocate directly. Since
pointers to these args can be stashed away
(e.g. in bootparam), we never deallocate this
memory. */
argc = *argcp + nargs;
i = (argc + 1) * sizeof *argv + p - args + sizeof '\0';
kr = vm_allocate(mach_task_self(),
(vm_address_t *) &argv, i, TRUE);
if (kr != KERN_SUCCESS) {
MACH3_DEBUG(1, kr, ("insert_args: allocate"));
panic("insert_args");
}
for (i = 0; i < *argcp; i++)
argv[i + nargs] = (*argvp)[i];
argv[argc] = NULL;
p = (char *) &argv[argc + 1];
args = strcpy(p, args);
}
}
*argcp = argc;
*argvp = argv;
}
extern char **server_command_line_p;
boolean_t single_user = FALSE;
void
old_get_config_info(
int argc,
char **argv)
{
char *args;
if (in_kernel) {
use_activations = TRUE;
}
/*
* Parse the arguments.
*/
/*
* Arg 0 is program name
*/
argv++, argc--;
/*
* There may be additional args in our environment
*/
args = getenv("startup_flags");
/*
* Process flag arguments:
*/
while (1) {
register char *cp;
register char c;
if (argc == 0 || argv[0][0] != '-') {
if (args != NULL) {
insert_args(args, &argc, &argv);
args = NULL;
continue;
}
break;
}
cp = argv[0];
while ((c = *++cp) != '\0') {
switch (c) {
case 'h':
osfmach3_console_init(); /* XXX */
Debugger("inline");
break;
case 'l':
parent_server_set_type(PARENT_SERVER_LINUX);
goto flag_2;
case 'o':
parent_server_set_type(PARENT_SERVER_OSF1);
goto flag_2;
case '2':
flag_2:
parent_server_get_mach_privilege();
if (parent_server != 1) {
parent_server = 1;
printk("The server is not running standalone. PID = %d.\n",
parent_server_getpid());
}
break;
case 'A':
/* Set option for using short-circuited
* RPC in syscalls. Check for {0,1}
* argument (use 1, if not specified):
*/
if (argv[1] && argv[1][0] != '-') {
use_activations =
(argv[1][0] != '0');
argv++; argc--;
} else
use_activations = TRUE;
if (use_activations) {
if (! in_kernel) {
printk("\nWARNING: user mode: "
"-A ignored\n");
use_activations = FALSE;
}
}
break;
case 'k':
case 'S':
if (!in_kernel)
printk("WARNING: Server not "
"kernel-loaded but -%c found\n",
c);
if (argv[1] && argv[1][0] != '-')
argv++, argc--;
/* Skip over the submap base/size arg */
break;
case 's':
single_user = TRUE;
break;
case 'c': /* Linux-like command line */
if (argv[1]) {
server_command_line_p = &argv[1];
argv++; argc--;
}
break;
default:
printk("Warning: invalid flag `-%c'\n", c);
break;
}
}
argv++, argc--;
}
/*
* First non-flag arg is root device name. If this is a first
* server the microkernel will pass us this even if we say -a,
* so we skip it even in that case.
*/
if (argc > 0) {
rootdev_name = argv[0];
argv++, argc--;
}
if (in_kernel) {
printk("*** SERVER IS KERNEL-LOADED ***\n");
}
if (use_activations) {
printk("*** USING ACTIVATIONS ***\n");
}
}
kernel_boot_info_t kernel_boot_info;
void
get_config_info(
int argc,
char **argv)
{
boolean_t found_name_arg;
char *bp;
int len;
kern_return_t kr;
char *(local_argv[50]);
if (argc > 1) {
old_get_config_info(argc, argv);
return;
}
kr = host_get_boot_info(privileged_host_port,
kernel_boot_info);
if (kr != KERN_SUCCESS || *kernel_boot_info == 0) {
old_get_config_info(argc, argv);
return;
}
printk("Boot info: %s\n", kernel_boot_info);
/*
* Process the arguments: the format of the boot info is
* rootdev [-howto] [kernel_name[:server_name]] [server args]
*
* We translate this into the syntax expected by old_get_config_info,
* i.e. the syntax that is used to invoke a child server as a
* process under the main server:
*
* server_name [-howto] [server args] rootdev
*/
/* We will construct argc and argv as for a child server: */
argc = 1; /* Leave room for argv[0], to be filled in later */
argv = local_argv;
bp = kernel_boot_info;
/* Remove any trailing new-line from kernel_boot_info: */
len = strlen(bp);
if (bp[len - 1] == '\n')
bp[len - 1] = '\0';
rootdev_name = bp; /* This will be last entry in argv */
found_name_arg = FALSE;
/* Process the blank-separated words of the string into null-terminated
* argv entries. At top of each iteration, we are positioned at
* start of the previously processed word (or at the end of string).
*/
do {
/* Find the end of the previous word and null terminate it: */
while (*bp && *bp != ' ' && *bp != '\n')
bp++;
if (*bp == '\n') {
*bp++ = 0;
break;
}
else if (*bp)
*bp++ = 0;
else
break;
/* Find the start of the next word, or end-of-line: */
while (*bp == ' ')
bp++;
if (*bp == '\n') {
*bp++ = 0;
break;
}
else if (!*bp)
break;
/* Is it the kernel:server argument? */
if (*bp != '-' && !found_name_arg) {
found_name_arg = TRUE;
/* Skip over kernel name to server name: */
while (*bp && *bp != ' ' && *bp != ':')
bp++;
if (*bp == ':')
argv[0] = ++bp;
else
argv[0] = "[No Server Name Found]";
} else {
argv[argc++] = bp;
}
} while (1);
argv[argc++] = rootdev_name;
argv[argc] = 0;
old_get_config_info(argc, argv);
}
void
set_rootdev(void)
{
}
void
parse_root_device(const char *rootdev_name)
{
static char name[GETS_MAX];
int kr;
if (ROOT_DEV != 0) {
/*
* root device has already been specified via
* a "root=..." argument in the command line.
*/
return;
}
while (1) {
if (rootdev_name == NULL) {
printk("root device? ");
rootdev_name = gets(name);
if (rootdev_name == NULL)
panic("set_rootdev: gets");
}
kr = gen_disk_name_to_dev(rootdev_name, &ROOT_DEV);
if (kr == 0 && ROOT_DEV != NODEV)
break;
printk("Bad Mach root device name: %s\n", rootdev_name);
rootdev_name = NULL;
}
printk("Mach root device %s: major=%d minor=%d\n", rootdev_name,
MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
}
void
size_memory(void)
{
kern_return_t rc;
int host_buff[HOST_BASIC_INFO_COUNT];
mach_msg_type_number_t host_buff_size = HOST_BASIC_INFO_COUNT;
vm_statistics_data_t vm_stat;
rc = host_info(mach_host_self(), HOST_BASIC_INFO,
host_buff, &host_buff_size);
if (rc != KERN_SUCCESS) {
printk("host_info() returned 0x%x.\n", rc);
panic("size_memory: host_info failed");
}
osfmach3_mem_size = ((struct host_basic_info *) host_buff)->memory_size;
host_buff_size = HOST_VM_INFO_COUNT;
rc = host_statistics(privileged_host_port, HOST_VM_INFO,
(host_info_t) &vm_stat, &host_buff_size);
if (rc != KERN_SUCCESS) {
printk("vm_statistics() returned 0x%x.\n", rc);
panic("size_memory: vm_statistics failed");
}
(void) host_page_size(mach_host_self(), &page_size);
page_mask = page_size - 1;
if ((page_mask & page_size) != 0)
panic("size_memory: page size not a power of two");
for (page_shift = 0; ; page_shift++)
if ((1 << page_shift) == page_size)
break;
}
void
gen_init(
int argc,
char **argv)
{
extern boolean_t in_kernel;
boolean_t page_0_was_allocated;
kern_return_t rc;
size_memory();
serv_port_register(mach_task_self(), &init_task);
init_task.osfmach3.task->mach_task_port = mach_task_self();
/*
* Protect our page 0, so that all null pointers references
* will fail. We may have to undo the existing protection...
*/
if (!in_kernel &&
((rc = vm_protect(mach_task_self(), 0, PAGE_SIZE, TRUE,
VM_PROT_READ)) != KERN_SUCCESS ||
(rc = vm_protect(mach_task_self(), 0, PAGE_SIZE, FALSE,
VM_PROT_NONE)) != KERN_SUCCESS)) {
vm_offset_t page_0 = (vm_offset_t)0; /* page 0 in the map */
/*
* vm_deallocate returns success even for non-mapped storage.
* Trust the vm_protect rc instead.
*/
(void)vm_deallocate(mach_task_self(), 0, PAGE_SIZE);
page_0_was_allocated = (rc != KERN_INVALID_ADDRESS); /* (known non-SUCCESS) */
rc = vm_map(mach_task_self(), &page_0, PAGE_SIZE,
0, FALSE, MEMORY_OBJECT_NULL, 0, FALSE,
VM_PROT_NONE, VM_PROT_READ, VM_INHERIT_NONE);
} else {
rc = KERN_SUCCESS;
page_0_was_allocated = FALSE;
}
/*
* Get arguments and config info
*/
get_config_info(argc, argv);
if (rc != KERN_SUCCESS)
printk("\aWARNING: failed to reserve page 0. vm_map() returned 0x%x\n", rc);
if (page_0_was_allocated) {
printk("\aWARNING: existing page 0 deallocated.\n");
}
osfmach3_console_init();
}
void
osfmach3_start_init(
char **argv_init,
char **envp_init)
{
extern int sys_setup(void);
int ret;
kern_return_t kr;
mach_port_t child_thread;
struct vm_area_struct *mpnt;
unsigned long p1, p2, argv, envp;
int argc, envc, i;
char *cp;
struct pt_regs *user_regs;
extern char *rootdev_name;
unsigned long old_fs;
/*
* Create a new empty task.
*/
ret = do_fork(CLONING_INIT, 0, NULL);
if (ret != 1) {
if (ret > 0)
panic("fork init: got pid %d instead of 1.\n", ret);
panic("can't fork init: error %d", -ret);
}
/*
* Act as "init" now.
*/
uniproc_switch_to(current, task[smp_num_cpus]);
/*
* This will mount the root file system, amongst other things.
*/
old_fs = get_fs();
set_fs(get_ds());
sys_setup();
set_fs(old_fs);
/*
* Load /mach_servers/mach_init into the new task.
*/
child_thread = current->osfmach3.thread->mach_thread_port;
/*
* Put the arguments in the empty user task.
*/
mpnt = (struct vm_area_struct *)kmalloc(sizeof (*mpnt), GFP_KERNEL);
if (mpnt == NULL) {
panic("can't allocate vm_area for init args");
}
mpnt->vm_mm = current->mm;
mpnt->vm_start = PAGE_SIZE;
mpnt->vm_end = PAGE_SIZE + ARG_MAX;
mpnt->vm_page_prot = PAGE_COPY;
mpnt->vm_flags = VM_READ|VM_WRITE;
mpnt->vm_inode = NULL;
mpnt->vm_offset = 0;
mpnt->vm_ops = NULL;
insert_vm_struct(current->mm, mpnt);
p1 = mpnt->vm_start;
for (argc = 0; argv_init[argc] != NULL; argc++);
for (envc = 0; envp_init[envc] != NULL; envc++);
p2 = p1 + ((argc+1 + envc+1) * sizeof (char *));
argv = p1;
for (i = 0; i < argc; i++) {
put_fs_long(p2, (long *) p1);
for (cp = argv_init[i]; *cp; cp++) {
put_fs_byte(*cp, (char *) p2);
p2 += sizeof (char);
}
put_fs_byte('\0', (char *) p2);
p2 += sizeof (char);
p1 += sizeof (char *);
}
put_fs_long(0, (long *) p1);
p1 += sizeof (char *);
envp = p1;
for (i = 0; i < envc; i++) {
put_fs_long(p2, (long *) p1);
for (cp = envp_init[i]; *cp; cp++) {
put_fs_byte(*cp, (char *) p2);
p2 += sizeof (char);
}
put_fs_byte('\0', (char *) p2);
p2 += sizeof (char);
p1 += sizeof (char *);
}
put_fs_long(0, (long *) p1);
p1 += sizeof (char *);
/*
* The fake exec.
*/
ret = do_execve("/mach_servers/mach_init",
(char **) argv, (char **) envp,
current->osfmach3.thread->regs_ptr);
if (ret) {
panic("can't exec /mach_servers/mach_init: error %d", -ret);
}
user_regs = current->osfmach3.thread->regs_ptr;
uniproc_switch_to(current, task[0]);
/*
* Launch /mach_servers/mach_init.
*/
kr = osfmach3_thread_set_state(child_thread,
user_regs);
if (kr != KERN_SUCCESS) {
MACH3_DEBUG(0, kr, ("osfmach3_start_init: "
"osfmach3_thread_set_state(init=0x%x)",
child_thread));
panic("can't set init threads's state");
}
kr = thread_resume(child_thread);
if (kr != KERN_SUCCESS) {
MACH3_DEBUG(0, kr, ("osfmach3_start_init: "
"thread_resume(init=0x%x)",
child_thread));
panic("can't resume init's thread");
}
}
boolean_t server_halted = FALSE;
void
osfmach3_halt_system(
boolean_t reset)
{
kern_return_t kr;
server_halted = TRUE; /* for the jiffies thread */
if (parent_server) {
parent_server_release_console();
parent_server_exit(0);
} else {
kr = host_reboot(privileged_host_port,
reset ? 0 : HOST_REBOOT_HALT);
if (kr != KERN_SUCCESS) {
MACH3_DEBUG(0, kr,
("osfmach3_halt_system(%d): host_reboot",
reset));
}
}
/* in case it didn't work ... */
printk("osfmach3_halt_system: entering endless loop...\n");
for (;;);
}