worsley@ditmela.oz (Andrew Worsley) (05/18/89)
I am posting this on behalf of Bruce Evans there is a problem with news postings from the site he uses. I have gotten it running on my 386 machine after some initial hassles with a few files which weren't exactly the 1.3 version. Make sure your diffs work with no rejections or there are no guarentees. As there is only one line of context diffs supplied patch will often find some where to apply the patch with a some offset. All queries/comments to Bruce Evans - evans@ditsyda.oz Andrew Worsley The README file: Here are patches to Minix 1.3d to provide: o use of 286 protected mode on 286's and 386's. o overhaul of interrupt handling to reduce latency by a huge factor. o changes to FS and boot programs to allow the root device and RAM disk size to be specified at boot time. o several minor bug fixes. The 286 system binaries remain capable of running on 8088 systems, and can be limited at boot time to 8088 mode on 286's to provide full backward compatibility. Most testing was done on a 386-AT, some on a real PC, and a little on a clone-AT and a Toshiba 1100 (8088 portable). I tried to avoid breaking nonstandard configurations but didn't touch Amoeba (it can no longer work) and may have messed up the PS/2 Model 30. Protected mode. --------------- The protection is of of system processes from wild user processes. Protected mode also provides a 16MB address space, limited mainly by available RAM and by the small model to 64K code + 64K data per process. I am usually greeted by a startup message like: Memory size = 1024K MINIX = 127K RAM disk = 0K Available = 897K Protected mode has some drawbacks. Interrupt handling is 5 to 10% slower because loading segment registers takes a long time. It is imprudent to allow users easy access to the i/o ports and another project to allow access to memory outside the data segment. Badly-behaved programs try to do these things and will cause an exception in protected mode. Other pitfalls are writing to the code segment, and assuming code segment == data segment because the program is common I&D. It is possible for a program to almost lock up the system if it ignores SIGSEGV and then causes one (many exceptions resume at the faulty PC since the signal handlers are too simple). CTRL-F9 is needed to break out of that. Fortunately most Minix programs are well-behaved. This kernel provides /dev/port to go with /dev/mem so ports can be accessed (slowly) by suitably privileged users. Only readclock uses this now. Things like EGA graphics will require immediate access to both ports and screen memory. It is easy to provide the memory access by expanding the per-process local descriptor table, but not clear how to interface the capability. Ports are more difficult since the 286 allows access to all or none, depending on the IOPL bits in the PSW. The 386 allows a more flexible permissions bitmap. Still, port-mapped i/o is awful for software. My 386 protected mode code is not included here since it offers few capabilities beyond what the 286 can do, so I'm reserving it for a proper 32 bit 386 version. The changes for it are simple but bulky. All the assembler code uses 386 instructions where appropriate. This is not essential (below 16M) but is cleaner - until you ask asld to assemble it. The interrupt handlers have to save and restore 32 bit registers - this involves little more than putting 'e' in front of register names and 'd' after some instructions. The process table structure has to be larger to hold the larger registers. "protect.c" needs a few more lines to initialize the top 2 bytes in segment descriptors and to build 32 bit TSS's. Everywhere that a saved register is accessed, the code needs to test the processor variable to decide whether to use an r16 (8088-286) register or r32 (386) register. It is practical to store 8088 r16's in the r32's (for an 8088/386 kernel like I started with) but not 80286 r16's since that requires too much rebuilding of the stack frame. And all this is worthless without a 386 compiler and a hacked library to interface to the 16 bit kernel. Interrupt handling. ------------------- The 1.3 kernel leaves interrupts off while it does context-switching and hardware interrupt handling. This takes a few hundred instructions, well over 1ms on a 5MHz 8088, and sometimes an iteration causes an even longer delay. My TTY driver with this kernel will go no faster than 2400 baud, which suggests that the normal worst case latency is about 4ms. The 1.3 TTY driver has internal inefficiencies which makes it another 4 to 8 times slower. This kernel improves the interrupt latency by a factor of about 12 in two steps. The first step is to run the main part of the context-switcher (sys_call()) with interrupts enabled, and rewrite the interrupt handler to task interface (interrupt()) to be more efficient. This, together with other small changes to proc.c, mainly using pointers instead of arrays and arrays instead of pointer arithmetic, gives about a factor of 4 improvement. The second step is to enable interrupts while most interrupt handlers are running. Ideally, only save() and restart() and some *small* critical regions should have interrupts disabled. In practice, fast asynchronous input devices like RS232 need interrupts disabled throughout their interrupt handler. Several methods are used to prevent reentry to critical regions without locking everything. The variable 'k_reenter' is the most important. It counts reentries into the kernel (context-switching and interrupt handling). Interrupt() may not reenter unless k_reenter is 0 (when there is exactly one interrupt handler in the kernel, and no sys_call()). Calls from nested handlers are delayed until the first handler finishes, using a little queue. This is cheap because now only task numbers have to be queued. Calls to context-switching functions are also critical. The main one, sys_call(), needs no special treatment since interrupt handlers may not call it. Complications are caused by calls from tasks, e.g. sched(), when k_reenter is -1. These are protected by the flag 'switching', since k_reenter is used to tell when to switch stacks as well as for locking, so cannot be adjusted. Curiously, interrupt() may be called when k_reenter is -1 (e.g. for TTY_O_DONE), but this causes no trouble. Hardware locking is still used for individual interrupt lines to ensure that no interrupt handler is ever reentered. This bounds the amount of kernel stack required, and eliminates most competition for shared variables. See mpx88.x and klib88.x. I think the low level is now correct for level- sensitive interrupts on PS/2's, but haven't tested it. Access to shared variables is now very tricky. The clock handler locks everything for short regions. The printer driver uses a test-and-set on its hardware mask bit. My TTY driver uses a test-and-set on a normal variable - see tty_wakeup(). This is beginning to look like another operating system beneath the message passing. Select root device etc. at boot time. ------------------------------------- The boot program (standalone fsck) has several more options on the menu, to select the system configuration: o root device o RAM image device o RAM disk size o processor limit Not all combinations are valid. The keyboard type was already specified by the scan code. The processor limit is the way of handicapping 286 and 386 processors so they run in 8088 mode. Fsck fills in a structure with the options and passes it to kernel and FS. This lets me test lots of variants of the system without recompiling. In particular I can keep my setup with the hard disk as root when testing the version to be posted! The changes in FS mainly involve replacing BOOT_DEV and ROOT_DEV by variables and eliminating unwarranted messages about the RAM disk being root. The "h/boot.h" file is kept separate and so needs including all over because it is needed by the bootstrap program (fsck.c here). Supporting code is in fsck.c, fsck1.s, mpx88.x, com.h and system.c. Adding a system call to support copying the parameters from kernel to FS was a lot of work but FS can't sensibly call sys_copy() since it doesn't know where they are. Reorganized assembler files. ---------------------------- It was too hard to maintain constants across several files without being able to include header files, and asld lacked the ability to handle 286 code even via macros. So all the '.s' files are renamed '.x' files and passed through the C preprocessor (automatically with make). Most of the constants defined in the C header files become available in the assembler code. One problem is that asld wants "[]" for parentheses, so many derived constants have to be defined as if they were fundamental. TTY. ---- The TTY driver I posted in January should have been a prerequisite to these changes, but I forgot to say so at the time. I didn't want to post it all again since this is already too long, so made minimal changes to get the 1.3 driver working. Diffs for both versions are included. I don't trust the 1.3 version but used it to upload this posting at 2400 baud with no trouble. My version now works at 19.2K baud on a 4.77MHz 8088 (1700 chars/sec throughput) and on 2 lines at 115.2K baud on a 20MHz 386 (2 x 8000 chars/sec throughput). The 1.3 version works at 2400 baud on the PC and 2 x 9600 baud on the 386. Running RS232 at a high speed is a good test of interrupt handling. I ran the dual lines test by logging into tty2 using term on tty1. This requires 2 serial ports but no fast external devices. Debugger. --------- The debugger I posted last December needs upgrading to work with this kernel. Even in real mode, it needs small changes. I have a version for protected mode, but it only runs on 386's since it needs to switch to real mode to run. I consider a debugger essential for serious work on the system, but mine no longer compiles with the usual tools: Asld problems. -------------- Asld may be close to running out of space when linking the kernel. It failed ungracefully when trying to link the kernel + debugger. Yet a core dump showed it had 10K of zeros between heap and stack. -- Division of Information Technology (Melbourne), Phone +61 3 347 8644 C.S.I.R.O. Fax +61 3 347 8987 55 Barry St. Telex AA 152914 Carlton, Vic, 3053, Australia E-mail: worsley@ditmela.oz.au
worsley@ditmela.oz (Andrew Worsley) (05/25/89)
More Info from Bruce.... These installation hints were added as a postscript in my original posting but didn't get into the repost by Andrew. 1) Rebuild everything except libc.a from scratch. 2) You must use the 1.3 version of make (chmem = 20000). I may have used a version compiled with a larger line length, but don't think this matters. 3) Leave port_in() and port_out() in libc.a, though I said they no longer belong there. 4) Run chmem =64000 on asld and perhaps some of the other compliler passes. If this causes space problems, do the usual juggling. I run a 0K RAM disk with root on the hard disk to avoid most such problems. 5) Starting from files not matching the 1.3 crc list is only for experts and adventurers. Bruce Evans evans@ditsyda.oz.au -- Division of Information Technology (Melbourne), Phone +61 3 347 8644 C.S.I.R.O. Fax +61 3 347 8987 55 Barry St. Telex AA 152914 Carlton, Vic, 3053, Australia E-mail: worsley@ditmela.oz.au