*BSD News Article 8633


Return to BSD News archive

Newsgroups: comp.unix.bsd
Path: sserve!manuel.anu.edu.au!munnari.oz.au!spool.mu.edu!sdd.hp.com!usc!zaphod.mps.ohio-state.edu!darwin.sura.net!wupost!uunet!mcsun!news.funet.fi!hydra!klaava!torvalds
From: torvalds@klaava.Helsinki.FI (Linus Torvalds)
Subject: Re: MS-DOS emulator? [386BSD]
Message-ID: <1992Dec8.131240.16747@klaava.Helsinki.FI>
Organization: University of Helsinki
References: <1992Dec5.164135.4077@robkaos.GUN.de> <1992Dec7.232615.2854@tinman.mke.ab.com>
Date: Tue, 8 Dec 1992 13:12:40 GMT
Lines: 71

In article <1992Dec7.232615.2854@tinman.mke.ab.com> tdphette@mke.ab.com (Thad Phetteplace x4461) writes:
>
>I am in favor of using virtual 8086 modes for the performance gain it would
>have over a fully emulated version.  If the Linux DOS emulator uses a 386 
>dependent implementation we might be able to borow from its design.  I don't
>know much about linux but I get the feeling it has a different approach for
>memory management and CPU scheduling.  This would make a direct port
>difficult.  Nevertheless, it could be worth taking a look at as a starting
>point.  Any Linux users out there care to comment about this?

The problem with using the linux v86 mode code isn't so much the memory
management: that probably needs only minor mods.  The thing that
probably leads to problems is the different handling of kernel traps in
the two systems. 

When linux gets a kernel trap (be it a system call or a interrupt) it
essentially does nothing special: it just saves some needed registers on
the kernel stack, does the appropriate handler and returns (slightly
simplified: if the return is to user mode, the return code also checks
for signals, time slices etc).  Linux never saves any state anywhere
else (well, the task-switching saves the registers, but that's done by
the 386 hardware). 

So when linux gets a request to move the process into v86 mode, all it
does is move the tss->esp0 value below the current stack frame, which
automatically means that the current process state is completely saved
as far as the kernel is concerned.  After that, it just builds a new
stack frame with the v86 info, and does an IRET.  All the old
information is automatically protected on the stack, and any new
interrupts automatically use a new stack frame below the saved values. 

When a signal occurs and the process has to be moved back into protected
mode again to handle it, the current (vm86 mode) stack frame is saved
for later reference by the DOS emulator, and then discarded.  This
automatically makes the stack frame below (the one that was used before
entering v86 mode) the current stack frame, and a normal IRET will again
restore the wanted mode (as well as returning to the emulator at the
point the vm86() system call was done after handling the signal). 

With this rather elegant v86 mode setup, the actual kernel code in linux
to handle v86 mode is truly minimal: I think it's less than 100 lines of
C and assembly in all (and most of that is actually setting up and
saving the stack frames).  The reason this works is because linux never
does any non-local jumps in the kernel (very much by design: I consider
longjmp's very ugly), and never saves any process state anywhere but on
the kernel stack (*). 

With bsd, the above isn't true, I think, so you can't just simply
protect the stack by changing the default esp0 pointer and assume that
all state is correctly saved.  I might be wrong, but I think the kernel
plays tricks with the kernel stack when handling signals and/or some
sleep/wakeup code, so you have to save state somewhere else.  That makes
it harder to implement transparently to the rest of the system. 

So essentially: feel free to check out how the linux kernel does it, and
using the same user-level interface might be worth it if only because
you could then port the linux dos-emulator without any problems instead
of starting from scratch.  But the actual switch into protected mode
will almost certainly have to be handled differently, and there might be
many places that need some minor tweaking to work correctly due the
different kernel trap stack layout from vm86 mode. 

		Linus

(*) As mentioned, the task-switching obviously has to save state. 
However, the linux task-switch is so simple that you never have to worry
about it: it's essentially invisible to the kernel on a very low level,
and is handled by one simple __asm__-statement in sched.c (and even that
is hidden behind a macro).  No weird and complex transitions: all kernel
functions always return to the place they were called from (except, of
course, do_exit(), which kills the process and never returns anywhere).