Server Building


I know this is the question that's been on all your minds: "How do I build myself a wonderful happy Mach server?" Well, I could say that here I provide all the answers, but I'd be lying through my teeth. Instead, I'll provide the small number of answers I've got:


Index


How do I compile my server?

I've found it convenient to do things like this. When building each object file, compile like so:

	gcc -fno-builtin -pedantic -Wall -nostdinc -nostartfiles -g -I- -I. -I/powermac/include -I/powermac/include/sa_mach

Then, when linking, I use something like this:

	gcc -fno-builtin -pedantic -Wall -static -nostartfiles -nostdlib -L/powermac/lib -e __start_mach -u __start_mach -lmach -lsa_mach -lmach

You might want to adjust the libraries (The things with a '-l' in front of them)for your own evil plans, but the mach/sa_mach/mach seems to be the basic set. The '-e __start_mach -u __start_mach' flags basically set up entry points, I believe, so that cthreads and the mach libs get initialized properly. This way you don't need to go about making those ioperm() calls and such from within your code.

How do install my server to load at boot time?

After you compile your server, you can go into /mach_servers/, then move your vmlinux file to something else, like /mach_servers/vmlinux.real, and then cp your server into that directory, under the name "myserver". This will cause Mach to stop after booting, and it will spit out an error, saying that it can't find the vmlinux server (unless you've changed the bootstrap.conf file, I believe). It will then give you the chance to use a different file as a server. All you have to do is erase the vmlinux at the end of the path that it has there, and replace it with "myserver" and hit return. Your server will now load (hopefully...see the problem entry on this page if something goes wrong). To use the regular linux server, just type in vmlinux.real instead of myserver, and the real vmlinux will load.


Mach starts up, but just sits there, what's wrong?

I think that some older versions of gcc, or perhaps just older versions of shared-library gcc have problems building working servers. I have no problem using gcc-2.7.2-2H and the associated glibc*K packages as found at ftp.linuxppc.org.


What do I do first?

The first thing you might want your server to do is to find its bootstrap ports as well as find out a bit about the environment it runs in.


How do I find the bootstrap ports?

To do this, you might want to look at the function bootstrap_ports(), which resides in the mach kernel. It takes the main bootstrap port as its first parameter, which can be found by using task_get_special_port(), which is also a mach service function. bootstrap_ports() returns a number of special ports used for various purposes (ie: I'm clueless as to what they're actually for). To see the procedure in action, please examine the following snippet of code:

	
	mach_port_t privileged_host_port;
	mach_port_t device_server_port;
	mach_port_t root_ledger_wired;
	mach_port_t root_ledger_paged;
	mach_port_t security_port;
	mach_port_t bootstrap_port;

	{
		kern_return_t kr;
		kr = task_get_special_port(mach_task_self(),
					TASK_BOOTSTRAP_PORT,
					&bootstrap_port );
		if ( kr != KERN_SUCCESS ) 
			panic( "get bootstrap port %d", kr );
		
		kr = bootstrap_ports ( bootstrap_port, 
				&privileged_host_port,
				&device_server_port,
				&root_ledger_wired,
				&root_ledger_paged,
				&security_port );
		if ( kr != KERN_SUCCESS ) 
			panic( "bootstrap_ports %d", kr );
	}


How do I find out about the environment I'm running in?

The mach kernel provides a number of functions which can be used to determine various things about the machine you're running on. One such function is host_info(), which can return a number of different types of structures, one of which is the host_basic_info structure. This structure provides you with all sorts of info regarding the cpu(s) of the machine you're using as well as the memory size. To see this bad boy in action, take a look at the following code snippet:

	kern_return_t kr;
	int host_buff[HOST_BASIC_INFO_COUNT];
		/* I'm not sure if this would be better being
			a struct host_basic_info... */
	mach_msg_type_number_t host_buff_size = HOST_BASIC_INFO_COUNT;
	struct host_basic_info *p;

	kr = host_info(mach_host_self(),
			HOST_BASIC_INFO,
			host_buff,
			&host_buff_size );
	if ( kr != KERN_SUCCESS ) 
		panic("host_info failed!");

	p = (struct host_basic_info*)host_buff;	
	printf ( "cpu_type = %d\n", p->cpu_type );
	printf ( "cpu_subtype = %d\n", p->cpu_subtype );
	printf ( "max_cpus = %d\n", p->max_cpus );
	printf ( "avail_cpus = %d\n", p->avail_cpus );
	printf ( "memsize = %d\n", p->memory_size );
	printf ( "\n" );


How do I output stuff?

You use printf(), silly! :-) Either libmach or libsa_mach provides you with an implementation of printf() that works pretty much as is expected when yours is the only server that wants control of the console. If another server, such as the Linux server, already has control of the console, it seems as if trying to use that printf() doesn't work, and you'd probably have to devise your own (perhaps writing to the console_port would work?) method of handling this.


How do I get input?

If you'll settle for simple console input, I've got you an answer. What you want to do is first get the security token for the kernel (the semantics might be wrong here...), and then use it to open a device over the device server port, which you've found with bootstrap_ports(). Then, you simply want to read some data from it. Here's how I've gotten it to work:


	server_security_token_t server_security_token;
	mach_port_t console_port;

	void console_init ( void ) {
		unsigned int security_token_size; 
		kern_return_t kr;
		
		security_token_size = TASK_SECURITY_TOKEN_COUNT;
		kr = task_info(mach_task_self(), 
				TASK_SECURITY_TOKEN,
				(task_info_t) &server_security_token,
				&security_token_size );
		/* do we want to verify the value of security_token_size here?*/
		if ( kr != KERN_SUCCESS ) 
			panic( "server security token: %d", kr );
		kr = device_open ( device_server_port, /* this guy must be initialized already */
				MACH_PORT_NULL,
				D_READ | D_WRITE, 
				server_security_token,
				"console", 
				&console_port );
		if ( kr != KERN_SUCCESS );
			panic("console_init");
	}
	int console_getc ( void ) { 
		int n;
		char c;
		kern_return_t kr;
		
		kr = device_read_inband ( console_port, 
					0, 0, sizeof c, &c, &n );
		if ( kr != D_SUCCESS || n <= 0 ) 
			panic("console_getc");
		return ((int)c) &0xff ;
	}
Then I use console_getc() to get some input...


How do I read the realtime clock?

Here's a quick way to read the amount of time since the machine has started up (presuming you're on a PowerMacintosh?). It basically just goes through a number of mach kernel calls, which return a time structure reminiscent of a UNIX timeval structure that's used by select(), consisting of a seconds member and a nanoseconds member, called a tvalspec_t in mach lingo. Here's an example on how to read it:

	mach_port_t rt_clock;
	
	void clock_init ( void ) { 
		kern_return_t kr;
		kr = host_get_clock_service ( mach_host_self(),
					REALTIME_CLOCK,
					&rt_clock );
		if ( kr != KERN_SUCCESS )
			panic( "Can't get realtime clock" );
	}
	tvalspec_t read_clock ( void ) { 
		kern_return_t kr;
		tvalspec_t ntime;
		kr = clock_get_time ( rt_clock,
					&ntime );
		if ( kr != KERN_SUCCESS ) 
			panic("can't get realtime clock" );
		return ntime;
	}
	void print_clock ( void ) { 
		static int initted = 0;
		tvalspec_t time;
		if ( !initted ) clock_init();
		initted = 1;
		
		time = read_clock();
		printf("seconds: %d\n", time.tv_sec );
		printf("nanoseconds: %d\n", time.tv_nsec );
	}


How do I emulate myself some syscalls?

Woah! Slow down there! I'm currently trying to figure that one out. I think that it can be done a number of ways...MkLinux, for instance, makes all kinds of use of catch_exception_raise() and other similar functions ( I believe catch_exception_raise() responds to "signals" sent via exc_server() ) to do this (which is VERY messy), whereas Lites-1.1 and other BSD-like servers on Mach make use of a built-in syscall emulation system. If anybody can give a clear description of how these things are happening, I'd be willing to name my first-born after ye! :-)


How do I get the netname server functioning?

Follow these instructions


Are there any simple Mach server examples available?

I've whipped up a few servers for testing purposes. You can get at them here:


This page is copyrighted by David T. McWherter in 1997.