At first, I was convinced that such a driver had never been written. But in fact, there was one; although it was never merged into upstream Linux. Thanks to @Joshua for pointing me to it.
The driver is part of Yggdrasil Linux/GNU/X, a LiveCD widely recognised as the first commercial Linux distribution. The source code for the driver is found for example within the directory /usr/src/linux/drivers/bios/
on the Fall 1994 release of the disc (and remains included in a patch form on the Winter 1997 software compilation CD), along with a driver that delegates CD-ROM accesses to MSCDEX. The README file included with the patch containing the driver makes it very clear how stable the driver was considered:
This driver is still experimental, it has worked for numerous people, but
it has failed for others. Use at your own risk. I only recommend this
to people who are interested in using it as a boot strap for developing
new device drivers or for acessing some bios routines which need only
be used rarely such as laptop power down functions.
The mechanism employed is pretty simple: each time the driver needs to perform a BIOS call, it switches into real mode, performs the call, then switches back into protected mode. This has all the drawbacks you would expect: any I/O operation would suspend every other thread of execution, the kernel had to perform extra copies between kernel memory and conventional memory, and if the BIOS invocation went off the rails, it took down the whole system. (Concurrency? SMP? Locking? Memory protection? What are you talking about? Ah, simpler times.)
Given the architecture of the kernel, this was the best one could do. The kernel has never had any facility to invoke 16-bit code from kernel mode during normal operation (after early boot); even the APM driver only ever supported the 32-bit protected-mode entry point. While support for 16-bit protected mode and virtual 8086 mode has been added, it was only ever driven by userspace, i.e. by DOSEMU, Wine and (userspace) VBE video drivers. The closest Linux got to a mechanism to enter virtual 8086 mode from kernel mode is in a patch for the vesafb
driver in Linux 2.6.20 that was never merged into the upstream kernel; the patch’s successor, uvesafb
, likewise invoked the video BIOS from userspace.
Although the BIOS and MSCDEX drivers were never merged upstream, the block device numbers they used were listed as reserved in the upstream kernel from version 1.3.22 (which was the first to even have a file listing all block device numbers: both the BIOS driver’s and the MSCDEX driver’s numbers are present) until their removal in 2.6.30.
As for “how it worked” in the other sense… the hard disk driver works surprisingly well, actually: no mysterious “tables” seem to be missing. I did some light testing in 86Box: after choosing a period-appropriate configuration, booting the system from an included LILO floppy image and activating the driver with bios_hd=1
, it would access the raw disk, partitions and file systems just fine.
As for the MSCDEX CD driver, trying that one was… less successful. This is what I got when I booted into MS-DOS 6.22 in 86Box 3.5, then launched Yggdrasil from the CD with a provided batch file:
MSCDEX CD-Rom Driver Copyright (C) 1994 Yggdrasil Computing, Inc.
MSCDEX CD-Rom Driver found 1 Drive
drive letter found: D
— [irrelevant kernel logs cut] —
MSCDEX error: AX=0x15
MSCDEX: Invalid Drive or other error
MSCDEX: Checking drive D
MSCDEX Drive Status: 0x00000000 return status: 0x8102
MSCDEX Reset: status: 0x8102
MSCDEX error: AX=0x15
MSCDEX: Invalid Drive or other error
MSCDEX: Checking drive D
MSCDEX Drive Status: 0x00000000 return status: 0x8102
MSCDEX Reset: status: 0x8102
MSCDEX error: AX=0x15
MSCDEX: Invalid Drive or other error
MSCDEX: Checking drive D
MSCDEX Drive Status: 0x00000000 return status: 0x8102
MSCDEX Reset: status: 0x8102
And it just went on like that. And this was the only configuration where booting into Linux didn’t outright crash the emulated machine; I have not investigated if the emulator is to blame for this. Still, it was better than PCem, where it crashed the emulator itself. QEMU didn’t do better: it hung even earlier, while testing the hlt
opcode.
If you want to run a more modern kernel, your best bet may be to write a driver for this device yourself: either via reverse-engineering or by creating a virtual 8086 mode monitor/emulator to run the BIOS-based driver in, like userspace VBE drivers do. (You may even attempt to port the Yggdrasil driver to modern kernels, but by no means will it be trivial, if possible at all: kernel APIs have changed significantly in the meantime.) In the general case, I expect the latter approach to be very fragile, as the ROM BIOS code may assume that it has the entirety of the hardware at its disposal, make all sorts of assumptions about its state, and attempt to perform operations that are difficult to emulate, especially from userspace code. In particular it may try to:
- raise a System Management Interrupt (the BIOS on a laptop I am writing this on does that),
- turn off interrupts to guarantee atomicity,
- reconfigure the Programmable Interrupt Controller and other hardware,
- perform DMA transfers,
And many other things. Most of these considerations usually do not apply to video BIOSes, as those usually confine themselves to operating on the video hardware itself. As such, they only require access to I/O ports and memory, and those are rather easy to provide.
Also note that the BIOS interrupt calls were not designed to be reentrant or execute under the supervision of a multitasking operating system (they were designed as drivers for DOS, after all). Given that, environments which do provide BIOS-based disk drivers are either single-tasking systems like DOS anyway or take some pains to ensure that BIOS calls have exclusive access to all the hardware (including the CPU) and do not interfere with anything else:
All of the above told, things are not hopeless: if you stick to a narrow goal of writing a driver that works with a specific BIOS whose behaviour is known so that you can apply workarounds specific to your firmware (as opposed to creating a fully general solution), there is a good chance it might actually work quite reliably. Especially if the BIOS comes from an option ROM on an extension card, as those are much less free to assume things about other hardware that may be present.
vesafb
only calls the video BIOS once at boot, before the kernel is properly started (mode-switching is impossible afterwards), whileuvesafb
delegates BIOS calls to userspace.