Path: utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!apple!ames!lll-winken!uunet!munnari!murtoa.cs.mu.oz.au!ditmela!worsley
From: wors...@ditmela.oz (Andrew Worsley)
Newsgroups: comp.os.minix
Subject: Protected mode MINIX for 80286 Info - By Bruce Evans
Keywords: Protected Mode
Message-ID: <5262@ditmela.oz>
Date: 18 May 89 01:17:27 GMT
Organization: CSIRO Division of Information Technology, Australia
Lines: 204
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 - ev...@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: wors...@ditmela.oz.au
|