The “Unix support” library provides the substrate to debug and run certain OSKit components in user-mode on Unix-like systems such as Linux and FreeBSD. In particular, you can use the networking stack with a second network interface, run the Linux file system code using a regular file instead of a raw partition, and use general OSKit timers.
By default, programs to be run in this environment are compiled against the OSKit headers and linked against the OSKit C library, rather than using the native system headers and libraries. This allows more complete OSKit kernels to be run in user mode, but requires linking with an extra “native OS” glue library (see section 18.1.2) in addition to the Unix support library.
The difference between a standard OSKit kernel and one running under Unix is shown in Figure 18.1. When running under Unix, the low-level kernel and driver support is replaced with glue code that provides the same interfaces, but does so by using Unix system calls instead of accessing the hardware directly. The FS and Net components can optionally be replaced; they can either be Unix support glue, or the original unmodified code.
Support is provided for Linux (ELF with glibc headers), along with FreeBSD 2.x (a.out) and 3.x and above (a.out or ELF). A number of example programs that use this code can be built in the examples/unix directory.
To run on Linux it’s recommended that you use a 2.2.x kernel. The 2.0.x kernels don’t support sigaltstack or the SO_SNDLOWAT, SO_RECVLOWAT, SO_SNDTIMEO or SO_RCVTIMEO socket options which are needed for general OSKit support. If you do run on a 2.0.x kernel, make sure you disable the STACKGUARD code in the pthreads library (currently the only thing that uses sigaltstack) and make sure that your OSKit program can tolerate those socket options being undefined.
Linux doesn’t allow the above socket options to be set: they can only be queried, which may be a problem since the OSKit sets them on occasion. None of the example programs should rely on this behavior however.
Two problems arise since applications are compiled with the OSKit header files and linked against the OSKit libraries. First, is the issue of binary-level compatibility between the OSKit and any particular Unix system. Since the OSKit definitions of system data structures and flags (e.g., the layout of the stat structure, or the value of error codes) are likely to differ from those of the native OS, there is a need for glue code to map between the two. Additionally, the Unix glue layer needs a way to call the underlying Unix system’s syscalls. It cannot invoke them directly by name (e.g., read) since those names are redefined by the OSKit. The Native OS library solves both of these problems, converting syscall arguments, structures and error return codes as well as providing syscall stubs for the native system calls. The Native OS library for Linux can be found in oskit/examples/unix/linux; the FreeBSD version is in oskit/examples/unix/freebsd.
Unless you have a simple OSKit kernel (e.g., one that doesn’t access the disk or network), you’ll need to modify it to run with the Unix support code. The changes aren’t pervasive and mostly apply to the initialization code. For example, all of the changes to get the example kernels to run are #ifdef’ed with OSKIT_UNIX, so it should be easy to see what needs to be changed. To avoid compile-time dependencies you can check the global variable oskit_usermode_simulation. This variable is used, for example, by the libstartup libraries to determine which environment they should initialize for.
There are two different ways to access the network with the Unix library. You can either use an emulated network, where socket calls are mapped directly to system socket calls, or you can talk to an interface directly. The emulated network needs to be initialized before it can be used, so there is a convenience function in liboskit_startup.a to do just that. You should call start_network_native (start_network_native_pthreads if your kernel uses the pthreads library) where you would otherwise initialize the network device. See socket_bsd, http_proxy and disknet as some examples of how to do this.
To talk to an interface directly you don’t have to make any changes to your OSKit kernel, you need to call oskit_linux_init_net to initialize the network devices. When you subsequently do an osenv_device_lookup for an ethernet device, you’ll get a special one from the Unix support library that talks directly to an interface. This allows you to run your own TCP/IP stack on an interface. See section 18.4.2 for more detailed information on how to run your OSKit kernel in this mode.
As with the network, you can access the disk directly or through an emulation layer. To be able to access the standard Unix filesystem space, you’ll need to initialize it by calling start_fs_native which takes a pathname specifying where your OSKit filesystem should be rooted in the Unix filesystem (threaded applications use start_fs_native_pthreads). The current working directory is currently set to the same as the root directory.
To access the disk directly oskit_linux_block_open is overridden by the Unix library so that takes a filename instead of a disk name. You can specify a regular file or a special file (so as to access the raw device).
The socket_bsd example kernel can use either method and so is a good one to look at to see the changes. The linux_fs_com and netbsd_fs_com kernels are good examples of using a regular file under Unix for your filesystem.
Your link line will also need to change to build with the Unix support code. The first thing that will need to change are the start files (crt0.o for a.out and crt*.o for ELF). The Unix support specific start files are installed in the lib/unix subdirectory of your installed OSKit tree.
The second step is to put unix_support.o (unix_support_pthreads.o if you’re using pthreads) and the appropriate Native OS library (liblinuxsys.a or libfreebsdsys.a) first in the link line and add -loskit_unix to the list of libraries.
Getting all of this correct is messy and a pain. Look at the changes that were made in the example kernel makefiles (examples/x86/GNUmakerules vs. examples/unix/GNUmakerules).
You can also use the installed GCC driver script (bin/ix86-oskit-gcc). This turns your native compiler into a cross-compiler for the OSKit by causing it to use a different “specs” file (the specs file can be found in the OSKit tree at unsupported/scripts/gcc-driver-script-unix.specs). Currently the driver script will only work on an a.out system.
What you get at the end of the build process is a regular Unix executable, which you can run as normal. Any arguments that you would normally pass to your OSKit kernel at the boot prompt can be passed on the command line.
If you are using the emulated filesystem, then your OSKit program will see the same filesystem space as any other regular Unix executable It’s root and current working directory (“cwd”) are the same as the parameter you passed to start_fs_native. Thus, unlike a standard Unix program, your cwd is N OT the same as where you ran it.
If you’re using the emulated network then there’s nothing you have to do, things should work the same as a Unix executable. If you’re accessing an interface directly you’ll have to do some more work. Since OSKit kernels assume that they’re the only thing that can access the net, the best way to do networking is to dedicate one or more interface for use by the OSKit on Unix.
There are a couple environment variables that you’ll need to set:
There are also some differences between running on Linux and FreeBSD.
First off, you’re going to have to run as root to be able to take over an ethernet interface under Linux. This makes sense, since if any user could hijack an interface you’d be in big trouble.
You’re going to want to turn arp off on the interface you’ll be using. Also, set the IP address to be a local address (see rfc1918, http://www.faqs.org/rfcs/rfc1918.html). The following commands assume that you’re using eth1.
ifconfig eth1 -arp up 10.1.1.1 |
You can also use ipfwadm to make sure the Linux network stack never sees the packets. The following commands will add a rule to deny packets for all protocol types for both input and output.
ipfwadm -I -a deny -P all -W eth1 ipfwadm -O -a deny -P all -W eth1 |
The Unix support library networking code uses the Berkeley Packet Filter (“BPF”) to get it’s packets. If your kernel isn’t compiled with BPF support you’ll have to build a new kernel. To do this, put options bpfilter 2 in your kernel config file and rebuild. You should set the number of packet filter instances to at least 2 so that you can run tcpdump along with your OSKit kernel. Normally you’ll have to be root to use the packet filter, but access is only determined by the permissions on /dev/bpfn, so you don’t necessarily have to. You’ll have to determine for yourself if the risk of having an exploitable packet filter is greater than the risk of running your OSKit kernels as root.
As with Linux, you should take measures to “isolate” the interfaces you are working with. First use a local address and disable arp:
ifconfig de1 inet 10.1.1.1 netmask 255.0.0.0 -arp |
If your kernel is configured with IPFIREWALL, you can keep packets out of the BSD network stack with:
ipfw add deny all from any to any via de1 |