eesrajm@cc.brunel.ac.uk (Andrew J Michael) (01/13/91)
This article has been written in response to the continued questions about the use of MINIX on 680X0 processors where X > 0. I finally got my act together and put something in writing. I have ported MINIX to three different 68020 boxes over the last few years, using MINIX-ST as a starting point. I hope that the knowledge gained in the process will be useful to others. Not all of the material is new, but several undocumented features of MINIX-ST are discussed. The article is aimed at those porting MINIX to 680X0 machines which have not previously run MINIX, but also contains the information needed for converting from a 68000 to a 680X0. There are probably some errors or omissions, so if anyone spots any, please let me know. Note that the distinction between general principles and 680X0 specifics is somewhat blurred. If in doubt, read both sections. You may use this article for any purpose that you think fit, but I'd be grateful if you acknowledged its authorship. GENERAL PRINCIPLES This article is NOT intended as a general purpose recipe for porting MINIX, but some idea of the issues involved may be relevant. When the author carried out his first 68020 port, an Atari ST running a very early version of MINIX-ST was used as the host system. Such an approach has several advantages - the host already has a known working MINIX C compiler, and is directly capable of producing boot disks for the target system. Now that MINIX is available for the Amiga, it would make an equally suitable platform. I don't consider the Macintosh suitable due to its use of non-standard floppy disks, although since most later Macs can read and write MSDOS floppies this notion may be incorrect. There is no absolute reason why the target machine should use MINIX floppy disks, but all the ports undertaken so far have booted from a traditional MINIX boot floppy. Since I consider it highly desirable to allow the 680X0 to read and write MINIX-ST floppies once the port is complete, a 720k 3.5" floppy has always been made part of the target hardware and used to boot the system. The first thing required is a bootstrap to load the MINIX image from disk into memory. Examination of the code in tools/boot.s will show that the MINIX-ST bootstrap loads the first sector of the boot floppy into memory and looks at the location nsect to determine the total number of sectors required to load. The boot floppy is then read sequentially into a buffer located at some suitable address in memory. Once the required number of sectors have been loaded, the image is copied from the buffer into memory starting at address 0. The code then jumps to the restart vector at address 0. On the ST it is important not to overwrite some TOS system variables in the area 0x0400 to 0x0600, so the bootstrap skips over this region. Although this might be unnecessary for other hardware, leaving this feature in saves possible confusion later. As a general rule, the less you change, the less there is to go wrong ! In particular, if you remove the skip, remember to alter the code in kernel/mpx.s accordingly. The "reserved" area left by the skip may also be useful for pseudo DMA, as explained later. You should also note that the code of both tools/boot.s and tools/build.c changed at MINIX-1.5, and the bss segment is now handled differently to earlier versions of MINIX-ST. To save space, instead of loading a pre-cleared bss from the boot disk, the bss is cleared by the bootstrap. Make sure that your build and bootstrap are compatible, or some very strange things can happen. The bootstrap code needs to be written for the target machine before anything else can proceed, unless the transfer of code to the target will be by a serial link or similar. Two of the authors ports were to bare hardware, so the bootstrap was written in assembler (under SK*DOS/68k) and blown into ROM. On the third, which originally ran OS9/68k, a small C OS9 program was used to load MINIX initially. The bootstrap can be tested by attempting to load a disk containing a "Hello World" program or similar. Once the bootstrap is in place, development proper can proceed. So far it has been assumed that the host system runs MINIX/68k and its own C compiler will be used for development. There is of course no reason why MINIX cannot be cross-compiled on a host using gcc or some other C compiler producing 68k output. In this case make sure that your cross-compiler produces known working MINIX code; you don't want to be actually debugging the C library when you think you are debugging device drivers ! Also beware endian problems when using a host machine which does not itself use a 680X0 processor. If you want to use gcc, excellent ports of gcc up to version 1.36 were provided by Jhawar Bammi and are still available from various archive sites. These provide both cross and native gcc compilers, and I have used the cross- compiler running on a Sun-3 to compile many utility programs for MINIX-ST. You should be aware that the libraries provided are pre MINIX-1.5, and this may cause some problems. I have produced a version of gcc-1.37.1 which uses MINIX-1.5 libraries. By default, all these versions of gcc will produce code using 32-bit ints in order to solve difficulties with the large amount of public domain code which assumes (sizeof (int) == sizeof (char *)). The MINIX-ST kernel needs to be compiled with 16-bit ints, and gcc supports this through the -mshort compile-time flag. All the author's 680X0 ports run versions of MINIX-1.5 with gcc compiled kernels. A gcc compiled dhrystone on a gcc compiled kernel is nearly twice the speed of the ack compiled originals. Throughout this article, "gcc" refers to Bammi's port, or derivatives thereof. Because of the twin 16 and 32-bit nature of gcc, some snags can arise when compiling the MINIX source. In general, be very careful of sizeof's, which need casting to int. A more annoying feature is that gcc uses the gnu assembler gas, which only understands MIT 68k assembler syntax. This means that the kernel assembler files need to be converted from Motorola syntax to MIT syntax before using gcc. Unless you are very sure (or have no choice), it may be better to use the ack C compiler to get your port running and convert to gcc later. I will assume that you have gathered enough information about the target machine to be able to write device drivers for it. You may be able to borrow some code from existing versions of MINIX. One important point to realise is that you do not need to have DMA hardware to run MINIX, although all the official versions use it. On two of the author's 68020 ports, the floppy accesses are carried out by "pseudo DMA". The floppy controller is a WD1772, set up to produce a level 7 interrupt from its DRQ pin and a level 2 interrupt from the IRQ pin. The code in kernel/floppy.c is very close to that of the MINIX-ST original except that the commands are issued directly to the 1772 command register. A dummy DMA device is also programmed with the direction of the transfer, the address of the data and the number of bytes required. The registers of the dummy DMA device are actually memory locations in the "reserved area" mentioned above. Code in kernel/mpx.s handles the DRQ interrupts generated as data is presented, using the dummy DMA registers to determine the address of the data and the direction in which to copy it. These level 7 interrupts are completely hidden from MINIX and simply dealt with by a few lines of assembler. On completion of a transfer, the 1772 produces an IRQ signal, which then calls the MINIX fdcint() routine. It is perhaps worth commenting on the way in which MINIX-ST determines the amount of ram present in the machine. On the Atari ST, there is a TOS variable at 0x0436 which contains the highest ram memory address. (This is one of the variables in the "reserved area" which the bootstrap doesn't overwrite). This variable is read by MINIX-ST during initialisation. To avoid changing any more MINIX source than necessary, I have a line of code in kernel/mpx.s which puts the appropriate address in 0x0436. If the amount of memory on your machine is liable to change, then a memory test in mpx.s (or preferably the bootstrap) might be a good idea. You should also note that MINIX-ST blocks interrupts below level 2 with code in kernel/main.c and kernel/mpx.s. These will need modifying if you want to use all the interrupt levels on your hardware. Once you have a hopefully complete system compiled, you can try to boot it. The TRACE routines in the kernel drivers are a great help here if things go wrong. The most important thing is to get console output working. Debugging is a lot easier once the patient can tell the doctor what is wrong ! If you can't even get that far to start with, try some simple assembler routines in mpx.s to output progress numbers to the screen or something similar. At one stage I had a port which was falling over in kernel/main.c, so I used some code in mpx.s to beep at various points in the initialisation. 680X0 SPECIFICS The official code for MINIX-ST and AmigaMINIX (I havn't seen MacMINIX) makes several assumptions about the use of a 68000 processor. Luckily these are almost all contained in the file kernel/mpx.s, written in assembler. The first thing to note is that mpx.s uses a 68000 dependant trick in its exception code handling. Most exceptions are dealt with by the common handlers trp, int and non. Since these handlers can be called by a wide number of exceptions, MINIX needs to know which exception actually called the handler. On a 68000, only the lower 24 bits of the address bus are present in hardware, so the contents of the upper byte are irrelevant. On other words, the address appearing on the address bus will be the same regardless of the contents of the upper byte of the program counter. MINIX-ST uses this to its advantage by placing the exception number in the high byte of each exception vector. The processor will jump to the (in)correct address, and the stacked program counter can be examined to determine the exception responsible. Unfortunately of course, this will have grave consequences on a 68020 where the upper address lines are present and decoded. The processor will jump to some strange location and (hopefully) cause a bus error. However, this will in turn call the bus error exception, and eventually the processor will halt with a double bus error. The author was originally blissfully unaware of this problem because quite by accident my first 68020 did not use the upper 8 bits of the address bus. If your hardware doesn't either, then you need not worry. If it does, then replace the upper byte of each exception vector with zero. My preferred method is to write the vectors as longs, one per line, with a comment. This takes up more space in the source code, but is much easier to read than a pile of meaningless data statements. Removing the exception number does of course upset MINIX's exception identification, but the kernel will run in this state although the vector numbers reported will be meaningless. We will deal with this later. The next area of difficulty is that of stack frames. The restart routine in mpx.s works by setting up a dummy stack frame and then executing an rte. This is again processor dependant and will only work reliably on a 68000. The 680X0 machines have a format word in the stack, but the 68000 doesn't. If the existing code is run on a 68020, the processor will examine the format word of the dummy stack frame when executing the rte. This has to be zero for the code to work correctly, otherwise the processor will take the format exception trap. The presence of the format word on a 680X0 also upsets the mpx.s save routine. Thus in its simplest form, to get the stack frame right for a 680X0, change the save and restart routines in mpx.s so that they read: move.l d0-d7/a0-a5,(a6) move.l (sp)+,savea6(a6) ! a6 > lea 12(sp),a1 ! 12 because of format word btst #5,4(sp) ! test old S-bit .................. L6:move.l a0,sp > L7:move.w #0,-(sp) ! format word move.l savpc(a6),-(sp) ! pc move.w savsr(a6),-(sp) ! sr The two lines marked with ">" are the minimum that need to be changed to get MINIX running on a 68020 (and presumably 68010). I have recently had a report that this modification will allow AmigaMINIX to run on a machine with a 68020 processor card. Although this modification will work successfully, it is rather a hack than a permanent solution, because the 680X0 can produce differing stack frames depending on the cause of the exception. We can tie the solution of this difficulty together with the problem encountered earlier of determining the cause of the exception when the upper byte of the exception vector is zero. Here are some example code fragments, based on original code by Inigo Cyliax. They are shown here in gcc (gas) MIT syntax. .text | | Trap vectors | .long 0 | Reset stack .long start | Reset PC .long err | Bus error .long err | Address error .long trp | Illegal instruction .long trp | Zero divide .long trp | CHK, CHK2 .long trp | cpTRAPcc, TRAPcc, TRAPV .long trp | Privilege violation .long trc | Trace .long trp | Line 1010 .long trp | Line 1111 .long trp | Unassigned .long trp | Coprocessor protocol violation .long trp | Format error .long trp | Uninitialised interrupt ................................... | | Perform task switch by save and restart | save: movw #0x2700,sr | ? movl a6,sp@- movl _proc_ptr,a6 moveml d0-d7/a0-a5,a6@ movl sp@+,a6@(sava6) | a6 lea sp@(10),a1 | Point to format word btst #5,sp@(4) | test old S-bit beq L45 | jump if usp movw a1@+, d2 | Get format word in d2 andw #0xf000, d2 | Mask to give format type cmpw #0x0000, d2 | Format type 0 ? beq L50 | Yes cmpw #0x1000, d2 | Format type 1 ? beq L50 | Yes cmpw #0x2000, d2 | Format type 2 ? bne L41 | Jump if not addl #8, a1 | Else add two words bra L50 L41: cmpw #0x9000, d2 | Format type 9 ? bne L42 | No addl #12, a1 | Yes - add six words bra L50 L42: cmpw #0xa000, d2 | Format type a ? bne L43 | No addl #24, a1 | Yes - add twelve words bra L50 L43: cmpw #0xb000, d2 | Format type b ? bne L50 | No addl #84, a1 | Yes - add forty-two words bra L50 L45: movl usp, a1 | Load a1 with old usp L50: movl a1, a6@(savsp) | old sp: usp or ksp movb sp@, a6@(savtt) | trap type clrb sp@ | cleanup movl sp@+, a1 | return address movw sp@+, a6@(savsr) | sr movl sp@+, a6@(savpc) | pc movw sp@+, d2 | Get format word lsrw #2, d2 | Convert it to vector number movb d2, a6@(savtt) | Save it as trap type addb #1, _k_reenter | from -1 if not reentering jmp a1@ Note that I have made no attempt to determine the processor type on a run-time basis. Since my ports have always been to specific hardware, there hasn't been any advantage in doing so. For general purpose versions such as AmigaMINIX there is an obvious case for detecting the processor type when booting and acting accordingly. Hopefully future official versions of MINIX will implement this. OTHER ISSUES Astute readers will have noticed that even when running on a 68020 I have retained the existing 16-bit int and 32-bit pointer nature of the original MINIX-68k kernel. Although there are obvious advantages in using 32-bit ints on a 68020, I decided that compatibility with MINIX-ST was more important. To use 32-bit ints would require changes to the C library as well as the kernel, and I prefer to use a common C library for both 68000 and 680X0. This is of particular importance to me since I use a 68020 to develop software for the ST, and I want them to run common binaries. You may have other ideas; if anyone does produce a 32-bit kernel I'd be interested to know. For the same reasons I havn't used any 68020 specific or 68881 code, even though my current 68020 machine has a 68881 on board. Bammi's port of gcc was intended for the Atari ST, so the 68881 and 68851 generating code was removed from gas. When I ported gas-1.37 I put this code back, but havn't yet tested it. Also remember that if you use the 68881 then alterations will have to be made to the kernel to handle the FPU registers on a context switch. I also havn't bothered to flush the cache on a context switch, since this doesn't seem to cause me any problems. It probably ought to be done for the 68020, and will almost certainly need to be done for the 68030. I have had a report that AmigaMINIX with the above mods to mpx.s won't run on a 68030, so this may well be an area to investigate. I don't have a 68030 to experiment with, so I'd appreciate any feedback in this area. Several people have told me that mm does some strange things with the upper byte of memory pointers. I havn't investigated, but if this is true then it is another area that may cause trouble if your hardware decodes the upper byte of the address bus, and has ram at these addresses. Best of luck, and happy porting ! Andy Michael 13 January 1991. -- Andy Michael (eesrajm@cc.brunel.ac.uk) "You might think that. I 85 Hawthorne Crescent couldn't possibly comment." West Drayton Middlesex UB7 9PA