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).