From ccd06c407cea0a65f8b54a2fac30a79b6eb55c95 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Tue, 3 May 2005 08:52:41 +0000 Subject: [PATCH] boot odds and ends. unfinished cd-detection work. --- boot/Makefile | 9 +- boot/boot.c | 96 +++ boot/bootcdhead.s | 1535 +++++++++++++++++++++++++++++++++++++++++++++ boot/boothead.s | 38 ++ 4 files changed, 1676 insertions(+), 2 deletions(-) create mode 100644 boot/bootcdhead.s diff --git a/boot/Makefile b/boot/Makefile index 39cd5e3ed..6d73afa6a 100755 --- a/boot/Makefile +++ b/boot/Makefile @@ -11,7 +11,7 @@ LD86 = $(CC86) -.o BIN = /usr/bin MDEC = /usr/mdec -all: bootblock boot edparams masterboot jumpboot installboot addaout +all: bootblock boot edparams masterboot jumpboot installboot addaout bootcd dos: boot.com mkfile.com bootblock: bootblock.s @@ -40,6 +40,11 @@ boot: boothead.s boot.o bootimage.o rawfs86.o boothead.s boot.o bootimage.o rawfs86.o $(LIBS) install -S 16kb boot +bootcd: bootcdhead.s boot.o bootimage.o rawfs86.o + $(LD86) -o $@ \ + boothead.s boot.o bootimage.o rawfs86.o $(LIBS) + install -S 16kb bootcd + edparams.o: boot.c ln -f boot.c edparams.c $(CC) $(CFLAGS) -DUNIX -c edparams.c @@ -112,5 +117,5 @@ $(BIN)/edparams: edparams clean: rm -f *.bak *.o - rm -f bootblock addaout installboot boot masterboot jumpboot edparams + rm -f bootblock addaout installboot boot masterboot jumpboot edparams bootcd rm -f dosboot boot.com mkfile mkfile.com diff --git a/boot/boot.c b/boot/boot.c index 63b40d26c..c948f2926 100755 --- a/boot/boot.c +++ b/boot/boot.c @@ -49,6 +49,13 @@ int fsok= -1; /* File system state. Initially unknown. */ static int block_size; #if BIOS + +/* this data is reserved for BIOS int 0x13 to put the 'specification packet' + * in. It has a structure of course, but we don't define a struct because + * of compiler padding. We fiddle out the bytes ourselves later. + */ +unsigned char boot_spec[24]; + char *bios_err(int err) /* Translate BIOS error code to a readable string. (This is a rare trait * known as error checking and reporting. Take a good look at it, you won't @@ -491,9 +498,11 @@ void initialize(void) #if !DOS u32_t dma64k= (memend - 1) & ~0x0FFFFL; + /* Check if data segment crosses a 64K boundary. */ if (newaddr + (daddr - caddr) < dma64k) newaddr= dma64k - runsize; #endif + /* Set the new caddr for relocate. */ caddr= newaddr; @@ -1828,19 +1837,106 @@ void monitor(void) #if BIOS +unsigned char cdspec[25]; +void bootcdinfo(u32_t, int *, int drive); + +void fixcdroot(void) +{ + int i, d; + int ret; + char name[20]; + u32_t addr; + char *bootcd; + /* Booting from CD? If so, add the cdroot in.. */ + int driveno, termno; + char *drive, *term, *rid; + + if((rid = b_value("ramimagedev"))) { + static int f = 0; + int i, j; + char ram[15]; + ram[0] = 'c'; + ram[1] = '0'; + if(rid[0] == 'c' || rid[0] == 'C') j = 2; + else j = 0; + f++; + for(i = 2; i < sizeof(ram)-1; i++, j++) { + ram[i] = rid[j]; + if(ram[i] == 'p') ram[i] = '\0'; + if(ram[i] == '\0') break; + } + ram[sizeof(ram)-1] = '\0'; + + b_setvar(E_SPECIAL|E_VAR, "ramname", ram); + } + + /* the rest of this code is.. not finished */ + return; + + if(!(bootcd = b_value("bootcd")) || bootcd[0] != '1') return; + if(!(drive = b_value("drive"))) return; + if(!(term = b_value("term"))) termno = 0; + else termno = term[0] - '0'; + driveno = 100 * (drive[0] - '0') + 10 * (drive[1] - '0') + drive[2] - '0'; + + /* cdroot has to be derived from what the BIOS + * says the actual CD was. + */ + memset(cdspec, 0, sizeof(cdspec)); + + addr = mon2abs(cdspec); + ret = 0x38; + printf("bootcdinfo (%lx, drive %d, term %d)..\n", addr, driveno, termno); + bootcdinfo(addr, &ret, (termno << 8) | driveno); + + i = 0; + printf("drive %02x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + driveno, + cdspec[i], cdspec[i+1], cdspec[i+2], cdspec[i+3], + cdspec[i+4], cdspec[i+5], cdspec[i+6], cdspec[i+7], + cdspec[i+8], cdspec[i+9], cdspec[i+10], cdspec[i+11]); + + /* CD's are faked to be booting from partition 1 + * (more to the point, that is their appropriate + * root partition to mount). + */ + strcpy(bootdev.name, "d0p1"); + + /* boot_spec[3] is the controller number (0 or 1), + * boot_spec[8] is the device specification. The + * below only works for IDE CD drives. boot_spec is + * filled in by BIOS after an int 0x13 call in boothead.s. + */ + bootdev.name[1] += boot_spec[3] * 2 + boot_spec[8]; + bootdev.primary = 1; /* p1 */ + b_setvar(E_SPECIAL|E_VAR, "cdroot", bootdev.name); /* dXp1 */ + strcpy(name, "c0"); + strcat(name, bootdev.name); + name[4] = '\0'; + b_setvar(E_SPECIAL|E_VAR, "cddrive", name); /* c0dX */ + + return; +} + void boot(void) /* Load Minix and start it, among other things. */ { + /* Initialize tables. */ initialize(); /* Get environment variables from the parameter sector. */ get_parameters(); + fixcdroot(); + while (1) { /* While there are commands, execute them! */ + while (cmds != nil) execute(); + fixcdroot(); + /* The "monitor" is just a "read one command" thing. */ monitor(); } diff --git a/boot/bootcdhead.s b/boot/bootcdhead.s new file mode 100644 index 000000000..42927985e --- /dev/null +++ b/boot/bootcdhead.s @@ -0,0 +1,1535 @@ +! Boothead.s - BIOS support for boot.c Author: Kees J. Bot +! +! +! This file contains the startup and low level support for the secondary +! boot program. It contains functions for disk, tty and keyboard I/O, +! copying memory to arbitrary locations, etc. +! +! The primary bootstrap code supplies the following parameters in registers: +! dl = Boot-device. +! es:si = Partition table entry if hard disk. +! +.text + + o32 = 0x66 ! This assembler doesn't know 386 extensions + BOOTOFF = 0x7C00 ! 0x0000:BOOTOFF load a bootstrap here + LOADSEG = 0x1000 ! Where this code is loaded. + BUFFER = 0x0600 ! First free memory + PENTRYSIZE = 16 ! Partition table entry size. + a_flags = 2 ! From a.out.h, struct exec + a_text = 8 + a_data = 12 + a_bss = 16 + a_total = 24 + A_SEP = 0x20 ! Separate I&D flag + K_I386 = 0x0001 ! Call Minix in 386 mode + K_RET = 0x0020 ! Returns to the monitor on reboot + K_INT86 = 0x0040 ! Requires generic INT support + K_MEML = 0x0080 ! Pass a list of free memory + + DS_SELECTOR = 3*8 ! Kernel data selector + ES_SELECTOR = 4*8 ! Flat 4 Gb + SS_SELECTOR = 5*8 ! Monitor stack + CS_SELECTOR = 6*8 ! Kernel code + MCS_SELECTOR= 7*8 ! Monitor code + + ESC = 0x1B ! Escape character + +! Imported variables and functions: +.extern _caddr, _daddr, _runsize, _edata, _end ! Runtime environment +.extern _device ! BIOS device number +.extern _rem_part ! To pass partition info +.extern _k_flags ! Special kernel flags +.extern _mem ! Free memory list + +.text + +! Set segment registers and stack pointer using the programs own header! +! The header is either 32 bytes (short form) or 48 bytes (long form). The +! bootblock will jump to address 0x10030 in both cases, calling one of the +! two jmpf instructions below. + + jmpf boot, LOADSEG+3 ! Set cs right (skipping long a.out header) + .space 11 ! jmpf + 11 = 16 bytes + jmpf boot, LOADSEG+2 ! Set cs right (skipping short a.out header) +boot: + mov ax, #LOADSEG + mov ds, ax ! ds = header + + movb al, a_flags + testb al, #A_SEP ! Separate I&D? + jnz sepID +comID: xor ax, ax + xchg ax, a_text ! No text + add a_data, ax ! Treat all text as data +sepID: + mov ax, a_total ! Total nontext memory usage + and ax, #0xFFFE ! Round down to even + mov a_total, ax ! total - text = data + bss + heap + stack + cli ! Ignore interrupts while stack in limbo + mov sp, ax ! Set sp at the top of all that + + mov ax, a_text ! Determine offset of ds above cs + movb cl, #4 + shr ax, cl + mov cx, cs + add ax, cx + mov ds, ax ! ds = cs + text / 16 + mov ss, ax + sti ! Stack ok now + push es ! Save es, we need it for the partition table + mov es, ax + cld ! C compiler wants UP + +! Clear bss + xor ax, ax ! Zero + mov di, #_edata ! Start of bss is at end of data + mov cx, #_end ! End of bss (begin of heap) + sub cx, di ! Number of bss bytes + shr cx, #1 ! Number of words + rep + stos ! Clear bss + +! Copy primary boot parameters to variables. (Can do this now that bss is +! cleared and may be written into). + xorb dh, dh + mov _device, dx ! Boot device (probably 0x00 or 0x80) + mov _rem_part+0, si ! Remote partition table offset + pop _rem_part+2 ! and segment (saved es) + +! Remember the current video mode for restoration on exit. + movb ah, #0x0F ! Get current video mode + int 0x10 + andb al, #0x7F ! Mask off bit 7 (no blanking) + movb old_vid_mode, al + movb cur_vid_mode, al + +! Give C code access to the code segment, data segment and the size of this +! process. + xor ax, ax + mov dx, cs + call seg2abs + mov _caddr+0, ax + mov _caddr+2, dx + xor ax, ax + mov dx, ds + call seg2abs + mov _daddr+0, ax + mov _daddr+2, dx + push ds + mov ax, #LOADSEG + mov ds, ax ! Back to the header once more + mov ax, a_total+0 + mov dx, a_total+2 ! dx:ax = data + bss + heap + stack + add ax, a_text+0 + adc dx, a_text+2 ! dx:ax = text + data + bss + heap + stack + pop ds + mov _runsize+0, ax + mov _runsize+2, dx ! 32 bit size of this process + +! Determine available memory as a list of (base,size) pairs as follows: +! mem[0] = low memory, mem[1] = memory between 1M and 16M, mem[2] = memory +! above 16M. Last two coalesced into mem[1] if adjacent. + mov di, #_mem ! di = memory list + int 0x12 ! Returns low memory size (in K) in ax + mul c1024 + mov 4(di), ax ! mem[0].size = low memory size in bytes + mov 6(di), dx + call _getprocessor + cmp ax, #286 ! Only 286s and above have extended memory + jb no_ext + cmp ax, #486 ! Assume 486s were the first to have >64M + jb small_ext ! (It helps to be paranoid when using the BIOS) +big_ext: + mov ax, #0xE801 ! Code for get memory size for >64M + int 0x15 ! ax = mem at 1M per 1K, bx = mem at 16M per 64K + jnc got_ext +small_ext: + movb ah, #0x88 ! Code for get extended memory size + clc ! Carry will stay clear if call exists + int 0x15 ! Returns size (in K) in ax for AT's + jc no_ext + test ax, ax ! An AT with no extended memory? + jz no_ext + xor bx, bx ! bx = mem above 16M per 64K = 0 +got_ext: + mov cx, ax ! cx = copy of ext mem at 1M + mov 10(di), #0x0010 ! mem[1].base = 0x00100000 (1M) + mul c1024 + mov 12(di), ax ! mem[1].size = "ext mem at 1M" * 1024 + mov 14(di), dx + test bx, bx + jz no_ext ! No more ext mem above 16M? + cmp cx, #15*1024 ! Chunks adjacent? (precisely 15M at 1M?) + je adj_ext + mov 18(di), #0x0100 ! mem[2].base = 0x01000000 (16M) + mov 22(di), bx ! mem[2].size = "ext mem at 16M" * 64K + jmp no_ext +adj_ext: + add 14(di), bx ! Add ext mem above 16M to mem below 16M +no_ext: + +! Time to switch to a higher level language (not much higher) + call _boot + +! void ..exit(int status) +! Exit the monitor by rebooting the system. +.define _exit, __exit, ___exit ! Make various compilers happy +_exit: +__exit: +___exit: + mov bx, sp + cmp 2(bx), #0 ! Good exit status? + jz reboot +quit: mov ax, #any_key + push ax + call _printf + xorb ah, ah ! Read character from keyboard + int 0x16 +reboot: call dev_reset + call restore_video + int 0x19 ! Reboot the system +.data +any_key: + .ascii "\nHit any key to reboot\n\0" +.text + +! u32_t mon2abs(void *ptr) +! Address in monitor data to absolute address. +.define _mon2abs +_mon2abs: + mov bx, sp + mov ax, 2(bx) ! ptr + mov dx, ds ! Monitor data segment + jmp seg2abs + +! u32_t vec2abs(vector *vec) +! 8086 interrupt vector to absolute address. +.define _vec2abs +_vec2abs: + mov bx, sp + mov bx, 2(bx) + mov ax, (bx) + mov dx, 2(bx) ! dx:ax vector + !jmp seg2abs ! Translate + +seg2abs: ! Translate dx:ax to the 32 bit address dx-ax + push cx + movb ch, dh + movb cl, #4 + shl dx, cl + shrb ch, cl ! ch-dx = dx << 4 + add ax, dx + adcb ch, #0 ! ch-ax = ch-dx + ax + movb dl, ch + xorb dh, dh ! dx-ax = ch-ax + pop cx + ret + +abs2seg: ! Translate the 32 bit address dx-ax to dx:ax + push cx + movb ch, dl + mov dx, ax ! ch-dx = dx-ax + and ax, #0x000F ! Offset in ax + movb cl, #4 + shr dx, cl + shlb ch, cl + orb dh, ch ! dx = ch-dx >> 4 + pop cx + ret + +! void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count) +! Copy count bytes from srcaddr to dstaddr. Don't do overlaps. +! Also handles copying words to or from extended memory. +.define _raw_copy +_raw_copy: + push bp + mov bp, sp + push si + push di ! Save C variable registers +copy: + cmp 14(bp), #0 + jnz bigcopy + mov cx, 12(bp) + jcxz copydone ! Count is zero, end copy + cmp cx, #0xFFF0 + jb smallcopy +bigcopy:mov cx, #0xFFF0 ! Don't copy more than about 64K at once +smallcopy: + push cx ! Save copying count + mov ax, 4(bp) + mov dx, 6(bp) + cmp dx, #0x0010 ! Copy to extended memory? + jae ext_copy + cmp 10(bp), #0x0010 ! Copy from extended memory? + jae ext_copy + call abs2seg + mov di, ax + mov es, dx ! es:di = dstaddr + mov ax, 8(bp) + mov dx, 10(bp) + call abs2seg + mov si, ax + mov ds, dx ! ds:si = srcaddr + shr cx, #1 ! Words to move + rep + movs ! Do the word copy + adc cx, cx ! One more byte? + rep + movsb ! Do the byte copy + mov ax, ss ! Restore ds and es from the remaining ss + mov ds, ax + mov es, ax + jmp copyadjust +ext_copy: + mov x_dst_desc+2, ax + movb x_dst_desc+4, dl ! Set base of destination segment + mov ax, 8(bp) + mov dx, 10(bp) + mov x_src_desc+2, ax + movb x_src_desc+4, dl ! Set base of source segment + mov si, #x_gdt ! es:si = global descriptor table + shr cx, #1 ! Words to move + movb ah, #0x87 ! Code for extended memory move + int 0x15 +copyadjust: + pop cx ! Restore count + add 4(bp), cx + adc 6(bp), #0 ! srcaddr += copycount + add 8(bp), cx + adc 10(bp), #0 ! dstaddr += copycount + sub 12(bp), cx + sbb 14(bp), #0 ! count -= copycount + jmp copy ! and repeat +copydone: + pop di + pop si ! Restore C variable registers + pop bp + ret + +! u16_t get_word(u32_t addr); +! void put_word(u32_t addr, u16_t word); +! Read or write a 16 bits word at an arbitrary location. +.define _get_word, _put_word +_get_word: + mov bx, sp + call gp_getaddr + mov ax, (bx) ! Word to get from addr + jmp gp_ret +_put_word: + mov bx, sp + push 6(bx) ! Word to store at addr + call gp_getaddr + pop (bx) ! Store the word + jmp gp_ret +gp_getaddr: + mov ax, 2(bx) + mov dx, 4(bx) + call abs2seg + mov bx, ax + mov ds, dx ! ds:bx = addr + ret +gp_ret: + push es + pop ds ! Restore ds + ret + +! void relocate(void); +! After the program has copied itself to a safer place, it needs to change +! the segment registers. Caddr has already been set to the new location. +.define _relocate +_relocate: + pop bx ! Return address + mov ax, _caddr+0 + mov dx, _caddr+2 + call abs2seg + mov cx, dx ! cx = new code segment + mov ax, cs ! Old code segment + sub ax, cx ! ax = -(new - old) = -Moving offset + mov dx, ds + sub dx, ax + mov ds, dx ! ds += (new - old) + mov es, dx + mov ss, dx + xor ax, ax + call seg2abs + mov _daddr+0, ax + mov _daddr+2, dx ! New data address + push cx ! New text segment + push bx ! Return offset of this function + retf ! Relocate + +! void *brk(void *addr) +! void *sbrk(size_t incr) +! Cannot fail implementations of brk(2) and sbrk(3), so we can use +! malloc(3). They reboot on stack collision instead of returning -1. +.data + .align 2 +break: .data2 _end ! A fake heap pointer +.text +.define _brk, __brk, _sbrk, __sbrk +_brk: +__brk: ! __brk is for the standard C compiler + xor ax, ax + jmp sbrk ! break= 0; return sbrk(addr); +_sbrk: +__sbrk: + mov ax, break ! ax= current break +sbrk: push ax ! save it as future return value + mov bx, sp ! Stack is now: (retval, retaddr, incr, ...) + add ax, 4(bx) ! ax= break + increment + mov break, ax ! Set new break + lea dx, -1024(bx) ! sp minus a bit of breathing space + cmp dx, ax ! Compare with the new break + jb heaperr ! Suffocating noises + lea dx, -4096(bx) ! A warning when heap+stack goes < 4K + cmp dx, ax + jae plenty ! No reason to complain + mov ax, #memwarn + push ax + call _printf ! Warn about memory running low + pop ax + movb memwarn, #0 ! No more warnings +plenty: pop ax ! Return old break (0 for brk) + ret +heaperr:mov ax, #chmem + push ax + mov ax, #nomem + push ax + call _printf + jmp quit +.data +nomem: .ascii "\nOut of%s\0" +memwarn:.ascii "\nLow on" +chmem: .ascii " memory, use chmem to increase the heap\n\0" +.text + +! int dev_open(void); +! Given the device "_device" figure out if it exists and what its number +! of heads and sectors may be. Return the BIOS error code on error, +! otherwise 0. +.define _dev_open +_dev_open: + call dev_reset ! Optionally reset the disks + movb dev_state, #0 ! State is "closed" + push es + push di ! Save registers used by BIOS calls + movb dl, _device ! The default device + cmpb dl, #0x80 ! Floppy < 0x80, winchester >= 0x80 + jae winchester +floppy: + mov di, #3 ! Three tries to init drive by reading sector 0 +finit0: xor ax, ax + mov es, ax + mov bx, #BUFFER ! es:bx = scratch buffer + mov ax, #0x0201 ! Read sector, #sectors = 1 + mov cx, #0x0001 ! Track 0, first sector + xorb dh, dh ! Drive dl, head 0 + int 0x13 + jnc finit0ok ! Sector 0 read ok? + cmpb ah, #0x80 ! Disk timed out? (Floppy drive empty) + je geoerr + dec di + jz geoerr + xorb ah, ah ! Reset drive + int 0x13 + jc geoerr + jmp finit0 ! Retry once more, it may need to spin up +finit0ok: + mov di, #seclist ! List of per floppy type sectors/track +flast: movb cl, (di) ! Sectors per track to test + cmpb cl, #9 ! No need to do the last 720K/360K test + je ftestok + xor ax, ax + mov es, ax + mov bx, #BUFFER ! es:bx = scratch buffer + mov ax, #0x0201 ! Read sector, #sectors = 1 + xorb ch, ch ! Track 0, last sector + xorb dh, dh ! Drive dl, head 0 + int 0x13 + jnc ftestok ! Sector cl read ok? + xorb ah, ah ! Reset drive + int 0x13 + jc geoerr + inc di ! Try next sec/track number + jmp flast +ftestok: + movb dh, #2 ! Floppies have two sides + jmp geoboth +winchester: + movb ah, #0x08 ! Code for drive parameters + int 0x13 ! dl still contains drive + jc geoerr ! No such drive? + andb cl, #0x3F ! cl = max sector number (1-origin) + incb dh ! dh = 1 + max head number (0-origin) +geoboth: + movb sectors, cl ! Sectors per track + movb al, cl ! al = sectors per track + mulb dh ! ax = heads * sectors + mov secspcyl, ax ! Sectors per cylinder = heads * sectors + movb dev_state, #1 ! Device state is "open" + xor ax, ax ! Code for success +geodone: + pop di + pop es ! Restore di and es registers + ret +geoerr: movb al, ah + xorb ah, ah ! ax = BIOS error code + jmp geodone +.data +seclist: + .data1 18, 15, 9 ! 1.44M, 1.2M, and 360K/720K floppy sec/track +.text + +! int dev_close(void); +! Close the current device. Under the BIOS this does nothing much. +.define _dev_close +_dev_close: + xor ax, ax + movb dev_state, al ! State is "closed" + ret + +! Reset the disks if needed. Minix may have messed things up. +dev_reset: + cmpb dev_state, #0 ! Need reset if dev_state < 0 + jge 0f + xorb ah, ah ! Reset (ah = 0) + movb dl, #0x80 ! All disks + int 0x13 + movb dev_state, #0 ! State is "closed" +0: ret + +! int dev_boundary(u32_t sector); +! True if a sector is on a boundary, i.e. sector % sectors == 0. +.define _dev_boundary +_dev_boundary: + mov bx, sp + xor dx, dx + mov ax, 4(bx) ! divide high half of sector number + div sectors + mov ax, 2(bx) ! divide low half of sector number + div sectors ! dx = sector % sectors + sub dx, #1 ! CF = dx == 0 + sbb ax, ax ! ax = -CF + neg ax ! ax = (sector % sectors) == 0 + ret + +! int readsectors(u32_t bufaddr, u32_t sector, u8_t count) +! int writesectors(u32_t bufaddr, u32_t sector, u8_t count) +! Read/write several sectors from/to disk or floppy. The buffer must +! be between 64K boundaries! Count must fit in a byte. The external +! variables _device, sectors and secspcyl describe the disk and its +! geometry. Returns 0 for success, otherwise the BIOS error code. +! +.define _readsectors, _writesectors +_writesectors: + push bp + mov bp, sp + movb 13(bp), #0x03 ! Code for a disk write + jmp rwsec +_readsectors: + push bp + mov bp, sp + movb 13(bp), #0x02 ! Code for a disk read +rwsec: push si + push di + push es + cmpb dev_state, #0 ! Device state? + jg 0f ! >0 if open + call _dev_open ! Initialize + test ax, ax + jnz badopen +0: mov ax, 4(bp) + mov dx, 6(bp) + call abs2seg + mov bx, ax + mov es, dx ! es:bx = bufaddr + mov di, #3 ! Execute 3 resets on floppy error + cmpb _device, #0x80 + jb nohd + mov di, #1 ! But only 1 reset on hard disk error +nohd: cmpb 12(bp), #0 ! count equals zero? + jz done +more: mov ax, 8(bp) + mov dx, 10(bp) ! dx:ax = abs sector. Divide it by sectors/cyl + cmp dx, #[1024*255*63-255]>>16 ! Near 8G limit? + jae bigdisk + div secspcyl ! ax = cylinder, dx = sector within cylinder + xchg ax, dx ! ax = sector within cylinder, dx = cylinder + movb ch, dl ! ch = low 8 bits of cylinder + divb sectors ! al = head, ah = sector (0-origin) + xorb dl, dl ! About to shift bits 8-9 of cylinder into dl + shr dx, #1 + shr dx, #1 ! dl[6..7] = high cylinder + orb dl, ah ! dl[0..5] = sector (0-origin) + movb cl, dl ! cl[0..5] = sector, cl[6..7] = high cyl + incb cl ! cl[0..5] = sector (1-origin) + movb dh, al ! dh = head + movb dl, _device ! dl = device to use + movb al, sectors ! Sectors per track - Sector number (0-origin) + subb al, ah ! = Sectors left on this track + cmpb al, 12(bp) ! Compare with # sectors to transfer + jbe doit ! Can't go past the end of a cylinder? + movb al, 12(bp) ! 12(bp) < sectors left on this track +doit: movb ah, 13(bp) ! Code for disk read (0x02) or write (0x03) + push ax ! Save al = sectors to read + int 0x13 ! call the BIOS to do the transfer + pop cx ! Restore al in cl + jmp rdeval +bigdisk: + mov si, #ext_rw ! si = extended read/write parameter packet + movb cl, 12(bp) + movb 2(si), cl ! Fill in # blocks to transfer + mov 4(si), bx ! Buffer address = es:bx + mov 6(si), es + mov 8(si), ax ! Starting block number = dx:ax + mov 10(si), dx + movb dl, _device ! dl = device to use + mov ax, #0x4000 ! This, or-ed with 0x02 or 0x03 becomes + orb ah, 13(bp) ! extended read (0x4200) or write (0x4300) + int 0x13 + !jmp rdeval +rdeval: + jc ioerr ! I/O error + movb al, cl ! Restore al = sectors read + addb bh, al ! bx += 2 * al * 256 (add bytes transferred) + addb bh, al ! es:bx = where next sector is located + add 8(bp), ax ! Update address by sectors transferred + adc 10(bp), #0 ! Don't forget high word + subb 12(bp), al ! Decrement sector count by sectors transferred + jnz more ! Not all sectors have been transferred +done: xorb ah, ah ! No error here! + jmp finish +ioerr: cmpb ah, #0x80 ! Disk timed out? (Floppy drive empty) + je finish + cmpb ah, #0x03 ! Disk write protected? + je finish + dec di ! Do we allow another reset? + jl finish ! No, report the error + xorb ah, ah ! Code for a reset (0) + int 0x13 + jnc more ! Succesful reset, try request again +finish: movb al, ah + xorb ah, ah ! ax = error number +badopen:pop es + pop di + pop si + pop bp + ret +.data + .align 4 +! Extended read/write commands require a parameter packet. +ext_rw: + .data1 0x10 ! Length of extended r/w packet + .data1 0 ! Reserved + .data2 0 ! Blocks to transfer (to be filled in) + .data2 0 ! Buffer address offset (tbfi) + .data2 0 ! Buffer address segment (tbfi) + .data4 0 ! Starting block number low 32 bits (tbfi) + .data4 0 ! Starting block number high 32 bits +.text + +! int getch(void); +! Read a character from the keyboard, and check for an expired timer. +! A carriage return is changed into a linefeed for UNIX compatibility. +.define _getch +_getch: + xor ax, ax + xchg ax, unchar ! Ungotten character? + test ax, ax + jnz gotch +getch: + hlt ! Play dead until interrupted (see pause()) + movb ah, #0x01 ! Keyboard status + int 0x16 + jz 0f ! Nothing typed + xorb ah, ah ! Read character from keyboard + int 0x16 + jmp press ! Keypress +0: mov dx, line ! Serial line? + test dx, dx + jz 0f + add dx, #5 ! Line Status Register + inb dx + testb al, #0x01 ! Data Ready? + jz 0f + mov dx, line + !add dx, 0 ! Receive Buffer Register + inb dx ! Get character + jmp press +0: call _expired ! Timer expired? + test ax, ax + jz getch + mov ax, #ESC ! Return ESC + ret +press: + cmpb al, #0x0D ! Carriage return? + jnz nocr + movb al, #0x0A ! Change to linefeed +nocr: cmpb al, #ESC ! Escape typed? + jne noesc + inc escape ! Set flag +noesc: xorb ah, ah ! ax = al +gotch: ret + +! int ungetch(void); +! Return a character to undo a getch(). +.define _ungetch +_ungetch: + mov bx, sp + mov ax, 2(bx) + mov unchar, ax + ret + +! int escape(void); +! True if ESC has been typed. +.define _escape +_escape: + movb ah, #0x01 ! Keyboard status + int 0x16 + jz escflg ! Keypress? + cmpb al, #ESC ! Escape typed? + jne escflg + xorb ah, ah ! Discard the escape + int 0x16 + inc escape ! Set flag +escflg: xor ax, ax + xchg ax, escape ! Escape typed flag + ret + +! int putch(int c); +! Write a character in teletype mode. The putk synonym is +! for the kernel printf function that uses it. +! Newlines are automatically preceded by a carriage return. +! +.define _putch, _putk +_putch: +_putk: mov bx, sp + movb al, 2(bx) ! al = character to be printed + testb al, al ! Kernel printf adds a null char to flush queue + jz nulch + cmpb al, #0x0A ! al = newline? + jnz putc + movb al, #0x0D + call putc ! putc('\r') + movb al, #0x0A ! Restore the '\n' and print it +putc: movb ah, #0x0E ! Print character in teletype mode + mov bx, #0x0001 ! Page 0, foreground color + int 0x10 + mov bx, line ! Serial line? + test bx, bx + jz nulch + push ax ! Save character to print + call _get_tick ! Current clock tick counter + mov cx, ax + add cx, #2 ! Don't want to see it count twice +1: lea dx, 5(bx) ! Line Status Register + inb dx + testb al, #0x20 ! Transmitter Holding Register Empty? + jnz 0f + call _get_tick + cmp ax, cx ! Clock ticked more than once? + jne 1b +0: pop ax ! Restore character to print + mov dx, bx ! Transmit Holding Register + outb dx ! Send character down the serial line +nulch: ret + +! void pause(void); +! Wait for an interrupt using the HLT instruction. This either saves +! power, or tells an x86 emulator that nothing is happening right now. +.define _pause +_pause: + hlt + ret + +! void set_mode(unsigned mode); +! void clear_screen(void); +! Set video mode / clear the screen. +.define _set_mode, _clear_screen +_set_mode: + mov bx, sp + mov ax, 2(bx) ! Video mode + cmp ax, cur_vid_mode + je modeok ! Mode already as requested? + mov cur_vid_mode, ax +_clear_screen: + xor ax, ax + mov es, ax ! es = Vector segment + mov ax, cur_vid_mode + movb ch, ah ! Copy of the special flags + andb ah, #0x0F ! Test bits 8-11, clear special flags + jnz xvesa ! VESA extended mode? + int 0x10 ! Reset video (ah = 0) + jmp md_480 +xvesa: mov bx, ax ! bx = extended mode + mov ax, #0x4F02 ! Reset video + int 0x10 +md_480: ! Basic video mode is set, now build on it + testb ch, #0x20 ! 480 scan lines requested? + jz md_14pt + mov dx, #0x3CC ! Get CRTC port + inb dx + movb dl, #0xD4 + testb al, #1 ! Mono or color? + jnz 0f + movb dl, #0xB4 +0: mov ax, #0x110C ! Vertical sync end (also unlocks CR0-7) + call out2 + mov ax, #0x060B ! Vertical total + call out2 + mov ax, #0x073E ! (Vertical) overflow + call out2 + mov ax, #0x10EA ! Vertical sync start + call out2 + mov ax, #0x12DF ! Vertical display end + call out2 + mov ax, #0x15E7 ! Vertical blank start + call out2 + mov ax, #0x1604 ! Vertical blank end + call out2 + push dx + movb dl, #0xCC ! Misc output register (read) + inb dx + movb dl, #0xC2 ! (write) + andb al, #0x0D ! Preserve clock select bits and color bit + orb al, #0xE2 ! Set correct sync polarity + outb dx + pop dx ! Index register still in dx +md_14pt: + testb ch, #0x40 ! 9x14 point font requested? + jz md_8pt + mov ax, #0x1111 ! Load ROM 9 by 14 font + xorb bl, bl ! Load block 0 + int 0x10 + testb ch, #0x20 ! 480 scan lines? + jz md_8pt + mov ax, #0x12DB ! VGA vertical display end + call out2 + eseg movb 0x0484, #33 ! Tell BIOS the last line number +md_8pt: + testb ch, #0x80 ! 8x8 point font requested? + jz setcur + mov ax, #0x1112 ! Load ROM 8 by 8 font + xorb bl, bl ! Load block 0 + int 0x10 + testb ch, #0x20 ! 480 scan lines? + jz setcur + mov ax, #0x12DF ! VGA vertical display end + call out2 + eseg movb 0x0484, #59 ! Tell BIOS the last line number +setcur: + xor dx, dx ! dl = column = 0, dh = row = 0 + xorb bh, bh ! Page 0 + movb ah, #0x02 ! Set cursor position + int 0x10 + push ss + pop es ! Restore es +modeok: ret + +! Out to the usual [index, data] port pair that are common for VGA devices +! dx = port, ah = index, al = data. +out2: + push dx + push ax + movb al, ah + outb dx ! Set index + inc dx + pop ax + outb dx ! Send data + pop dx + ret + +restore_video: ! To restore the video mode on exit + mov ax, old_vid_mode + push ax + call _set_mode + pop ax + ret + +! void serial_init(int line) +! Initialize copying console I/O to a serial line. +.define _serial_init +_serial_init: + mov bx, sp + mov dx, 2(bx) ! Line number + push ds + xor ax, ax + mov ds, ax ! Vector and BIOS data segment + mov bx, dx ! Line number + shl bx, #1 ! Word offset + mov bx, 0x0400(bx) ! I/O port for the given line + pop ds + mov line, bx ! Remember I/O port +serial_init: + mov bx, line + test bx, bx ! I/O port must be nonzero + jz 0f + mov ax, #0x00E3 ! 9600 N-8-1 + int 0x14 ! Initialize serial line dx + lea dx, 4(bx) ! Modem Control Register + movb al, #0x0B ! DTR, RTS, OUT2 + outb dx +0: ret + +! u32_t get_tick(void); +! Return the current value of the clock tick counter. This counter +! increments 18.2 times per second. Poll it to do delays. Does not +! work on the original PC, but works on the PC/XT. +.define _get_tick +_get_tick: + push cx + xorb ah, ah ! Code for get tick count + int 0x1A + mov ax, dx + mov dx, cx ! dx:ax = cx:dx = tick count + pop cx + ret + + +! Functions used to obtain info about the hardware. Boot uses this information +! itself, but will also pass them on to a pure 386 kernel, because one can't +! make BIOS calls from protected mode. The video type could probably be +! determined by the kernel too by looking at the hardware, but there is a small +! chance on errors that the monitor allows you to correct by setting variables. + +.define _get_bus ! returns type of system bus +.define _get_video ! returns type of display + +! u16_t get_bus(void) +! Return type of system bus, in order: XT, AT, MCA. +_get_bus: + call _getprocessor + xor dx, dx ! Assume XT + cmp ax, #286 ! An AT has at least a 286 + jb got_bus + inc dx ! Assume AT + movb ah, #0xC0 ! Code for get configuration + int 0x15 + jc got_bus ! Carry clear and ah = 00 if supported + testb ah, ah + jne got_bus + eseg + movb al, 5(bx) ! Load feature byte #1 + inc dx ! Assume MCA + testb al, #0x02 ! Test bit 1 - "bus is Micro Channel" + jnz got_bus + dec dx ! Assume AT + testb al, #0x40 ! Test bit 6 - "2nd 8259 installed" + jnz got_bus + dec dx ! It is an XT +got_bus: + push ds + pop es ! Restore es + mov ax, dx ! Return bus code + mov bus, ax ! Keep bus code, A20 handler likes to know + ret + +! u16_t get_video(void) +! Return type of display, in order: MDA, CGA, mono EGA, color EGA, +! mono VGA, color VGA. +_get_video: + mov ax, #0x1A00 ! Function 1A returns display code + int 0x10 ! al = 1A if supported + cmpb al, #0x1A + jnz no_dc ! No display code function supported + + mov ax, #2 + cmpb bl, #5 ! Is it a monochrome EGA? + jz got_video + inc ax + cmpb bl, #4 ! Is it a color EGA? + jz got_video + inc ax + cmpb bl, #7 ! Is it a monochrome VGA? + jz got_video + inc ax + cmpb bl, #8 ! Is it a color VGA? + jz got_video + +no_dc: movb ah, #0x12 ! Get information about the EGA + movb bl, #0x10 + int 0x10 + cmpb bl, #0x10 ! Did it come back as 0x10? (No EGA) + jz no_ega + + mov ax, #2 + cmpb bh, #1 ! Is it monochrome? + jz got_video + inc ax + jmp got_video + +no_ega: int 0x11 ! Get bit pattern for equipment + and ax, #0x30 ! Isolate color/mono field + sub ax, #0x30 + jz got_video ! Is it an MDA? + mov ax, #1 ! No it's CGA + +got_video: + ret + + +! Functions to leave the boot monitor. +.define _bootstrap ! Call another bootstrap +.define _minix ! Call Minix + +! void _bootstrap(int device, struct part_entry *entry) +! Call another bootstrap routine to boot MS-DOS for instance. (No real +! need for that anymore, now that you can format floppies under Minix). +! The bootstrap must have been loaded at BOOTSEG from "device". +_bootstrap: + call restore_video + mov bx, sp + movb dl, 2(bx) ! Device to boot from + mov si, 4(bx) ! ds:si = partition table entry + xor ax, ax + mov es, ax ! Vector segment + mov di, #BUFFER ! es:di = buffer in low core + mov cx, #PENTRYSIZE ! cx = size of partition table entry + rep movsb ! Copy the entry to low core + mov si, #BUFFER ! es:si = partition table entry + mov ds, ax ! Some bootstraps need zero segment registers + cli + mov ss, ax + mov sp, #BOOTOFF ! This should do it + sti + jmpf BOOTOFF, 0 ! Back to where the BIOS loads the boot code + +! void minix(u32_t koff, u32_t kcs, u32_t kds, +! char *bootparams, size_t paramsize, u32_t aout); +! Call Minix. +_minix: + push bp + mov bp, sp ! Pointer to arguments + + mov dx, #0x03F2 ! Floppy motor drive control bits + movb al, #0x0C ! Bits 4-7 for floppy 0-3 are off + outb dx ! Kill the motors + push ds + xor ax, ax ! Vector & BIOS data segments + mov ds, ax + andb 0x043F, #0xF0 ! Clear diskette motor status bits of BIOS + pop ds + cli ! No more interruptions + + test _k_flags, #K_I386 ! Switch to 386 mode? + jnz minix386 + +! Call Minix in real mode. +minix86: + test _k_flags, #K_MEML ! New memory arrangements? + jz 0f + push 22(bp) ! Address of a.out headers + push 20(bp) +0: + push 18(bp) ! # bytes of boot parameters + push 16(bp) ! Address of boot parameters + + test _k_flags, #K_RET ! Can the kernel return? + jz noret86 + xor dx, dx ! If little ext mem then monitor not preserved + xor ax, ax + cmp _mon_return, ax ! Minix can return to the monitor? + jz 0f + mov dx, cs ! Monitor far return address + mov ax, #ret86 +0: push dx ! Push monitor far return address or zero + push ax +noret86: + + mov ax, 8(bp) + mov dx, 10(bp) + call abs2seg + push dx ! Kernel code segment + push 4(bp) ! Kernel code offset + mov ax, 12(bp) + mov dx, 14(bp) + call abs2seg + mov ds, dx ! Kernel data segment + mov es, dx ! Set es to kernel data too + retf ! Make a far call to the kernel + +! Call Minix in 386 mode. +minix386: + cseg mov cs_real-2, cs ! Patch CS and DS into the instructions that + cseg mov ds_real-2, ds ! reload them when switching back to real mode + .data1 0x0F,0x20,0xC0 ! mov eax, cr0 + orb al, #0x01 ! Set PE (protection enable) bit + .data1 o32 + mov msw, ax ! Save as protected mode machine status word + + mov dx, ds ! Monitor ds + mov ax, #p_gdt ! dx:ax = Global descriptor table + call seg2abs + mov p_gdt_desc+2, ax + movb p_gdt_desc+4, dl ! Set base of global descriptor table + + mov ax, 12(bp) + mov dx, 14(bp) ! Kernel ds (absolute address) + mov p_ds_desc+2, ax + movb p_ds_desc+4, dl ! Set base of kernel data segment + + mov dx, ss ! Monitor ss + xor ax, ax ! dx:ax = Monitor stack segment + call seg2abs ! Minix starts with the stack of the monitor + mov p_ss_desc+2, ax + movb p_ss_desc+4, dl + + mov ax, 8(bp) + mov dx, 10(bp) ! Kernel cs (absolute address) + mov p_cs_desc+2, ax + movb p_cs_desc+4, dl + + mov dx, cs ! Monitor cs + xor ax, ax ! dx:ax = Monitor code segment + call seg2abs + mov p_mcs_desc+2, ax + movb p_mcs_desc+4, dl + + push #MCS_SELECTOR + test _k_flags, #K_INT86 ! Generic INT86 support? + jz 0f + push #int86 ! Far address to INT86 support + jmp 1f +0: push #bios13 ! Far address to BIOS int 13 support +1: + test _k_flags, #K_MEML ! New memory arrangements? + jz 0f + .data1 o32 + push 20(bp) ! Address of a.out headers +0: + push #0 + push 18(bp) ! 32 bit size of parameters on stack + push #0 + push 16(bp) ! 32 bit address of parameters (ss relative) + + test _k_flags, #K_RET ! Can the kernel return? + jz noret386 + push #MCS_SELECTOR + push #ret386 ! Monitor far return address +noret386: + + push #0 + push #CS_SELECTOR + push 6(bp) + push 4(bp) ! 32 bit far address to kernel entry point + + call real2prot ! Switch to protected mode + mov ax, #DS_SELECTOR ! Kernel data + mov ds, ax + mov ax, #ES_SELECTOR ! Flat 4 Gb + mov es, ax + .data1 o32 ! Make a far call to the kernel + retf + +! Minix-86 returns here on a halt or reboot. +ret86: + mov _reboot_code+0, ax + mov _reboot_code+2, dx ! Return value (obsolete method) + jmp return + +! Minix-386 returns here on a halt or reboot. +ret386: + .data1 o32 + mov _reboot_code, ax ! Return value (obsolete method) + call prot2real ! Switch to real mode + +return: + mov sp, bp ! Pop parameters + sti ! Can take interrupts again + + call _get_video ! MDA, CGA, EGA, ... + movb dh, #24 ! dh = row 24 + cmp ax, #2 ! At least EGA? + jb is25 ! Otherwise 25 rows + push ds + xor ax, ax ! Vector & BIOS data segments + mov ds, ax + movb dh, 0x0484 ! Number of rows on display minus one + pop ds +is25: + xorb dl, dl ! dl = column 0 + xorb bh, bh ! Page 0 + movb ah, #0x02 ! Set cursor position + int 0x10 + + movb dev_state, #-1 ! Minix may have upset the disks, must reset. + call serial_init ! Likewise with our serial console + + call _getprocessor + cmp ax, #286 + jb noclock + xorb al, al +tryclk: decb al + jz noclock + movb ah, #0x02 ! Get real-time clock time (from CMOS clock) + int 0x1A + jc tryclk ! Carry set, not running or being updated + movb al, ch ! ch = hour in BCD + call bcd ! al = (al >> 4) * 10 + (al & 0x0F) + mulb c60 ! 60 minutes in an hour + mov bx, ax ! bx = hour * 60 + movb al, cl ! cl = minutes in BCD + call bcd + add bx, ax ! bx = hour * 60 + minutes + movb al, dh ! dh = seconds in BCD + call bcd + xchg ax, bx ! ax = hour * 60 + minutes, bx = seconds + mul c60 ! dx-ax = (hour * 60 + minutes) * 60 + add bx, ax + adc dx, #0 ! dx-bx = seconds since midnight + mov ax, dx + mul c19663 + xchg ax, bx + mul c19663 + add dx, bx ! dx-ax = dx-bx * (0x1800B0 / (2*2*2*2*5)) + mov cx, ax ! (0x1800B0 = ticks per day of BIOS clock) + mov ax, dx + xor dx, dx + div c1080 + xchg ax, cx + div c1080 ! cx-ax = dx-ax / (24*60*60 / (2*2*2*2*5)) + mov dx, ax ! cx-dx = ticks since midnight + movb ah, #0x01 ! Set system time + int 0x1A +noclock: + + pop bp + ret ! Return to monitor as if nothing much happened + +! Transform BCD number in al to a regular value in ax. +bcd: movb ah, al + shrb ah, #4 + andb al, #0x0F + .data1 0xD5,10 ! aad ! ax = (al >> 4) * 10 + (al & 0x0F) + ret ! (BUG: assembler messes up aad & aam!) + + +! void bootcdinfo(u32_t bufaddr, int *ret, int drive) +! If booted from CD, do BIOS int 0x13 call to obtain boot CD device. +.define _bootcdinfo +_bootcdinfo: + push bp + mov bp, sp + push ax + push bx + push cx + push dx + push si + push ds + mov bx, 10(bp) ! drive number + mov cx, 8(bp) + mov ax, 4(bp) ! buffer address from stack + mov dx, 6(bp) + call abs2seg + mov si, ax ! bios will put data in ds:si + mov ds, dx +! movb dl, #0x00 + movb dh, #0x00 + movb dl, bl +! mov ax, #0x4b01 ! command 0x4b, subcommand 0x01 + movb ah, #0x4b + movb al, bh + int 0x13 + mov bp, cx + mov (bp), ax + pop ds + pop si + pop dx + pop cx + pop bx + pop ax + pop bp + ret + +! Support function for Minix-386 to make a BIOS int 13 call (disk I/O). +bios13: + mov bp, sp + call prot2real + sti ! Enable interrupts + + mov ax, 8(bp) ! Load parameters + mov bx, 10(bp) + mov cx, 12(bp) + mov dx, 14(bp) + mov es, 16(bp) + int 0x13 ! Make the BIOS call + mov 8(bp), ax ! Save results + mov 10(bp), bx + mov 12(bp), cx + mov 14(bp), dx + mov 16(bp), es + + cli ! Disable interrupts + call real2prot + mov ax, #DS_SELECTOR ! Kernel data + mov ds, ax + .data1 o32 + retf ! Return to the kernel + +! Support function for Minix-386 to make an 8086 interrupt call. +int86: + mov bp, sp + call prot2real + + .data1 o32 + xor ax, ax + mov es, ax ! Vector & BIOS data segments + .data1 o32 + eseg mov 0x046C, ax ! Clear BIOS clock tick counter + + sti ! Enable interrupts + + movb al, #0xCD ! INT instruction + movb ah, 8(bp) ! Interrupt number? + testb ah, ah + jnz 0f ! Nonzero if INT, otherwise far call + push cs + push #intret+2 ! Far return address + .data1 o32 + push 12(bp) ! Far driver address + mov ax, #0x90CB ! RETF; NOP +0: + cseg cmp ax, intret ! Needs to be changed? + je 0f ! If not then avoid a huge I-cache stall + cseg mov intret, ax ! Patch `INT n' or `RETF; NOP' into code + jmp .+2 ! Clear instruction queue +0: + mov ds, 16(bp) ! Load parameters + mov es, 18(bp) + .data1 o32 + mov ax, 20(bp) + .data1 o32 + mov bx, 24(bp) + .data1 o32 + mov cx, 28(bp) + .data1 o32 + mov dx, 32(bp) + .data1 o32 + mov si, 36(bp) + .data1 o32 + mov di, 40(bp) + .data1 o32 + mov bp, 44(bp) + +intret: int 0xFF ! Do the interrupt or far call + + .data1 o32 ! Save results + push bp + .data1 o32 + pushf + mov bp, sp + .data1 o32 + pop 8+8(bp) ! eflags + mov 8+16(bp), ds + mov 8+18(bp), es + .data1 o32 + mov 8+20(bp), ax + .data1 o32 + mov 8+24(bp), bx + .data1 o32 + mov 8+28(bp), cx + .data1 o32 + mov 8+32(bp), dx + .data1 o32 + mov 8+36(bp), si + .data1 o32 + mov 8+40(bp), di + .data1 o32 + pop 8+44(bp) ! ebp + + cli ! Disable interrupts + + xor ax, ax + mov ds, ax ! Vector & BIOS data segments + .data1 o32 + mov cx, 0x046C ! Collect lost clock ticks in ecx + + mov ax, ss + mov ds, ax ! Restore monitor ds + call real2prot + mov ax, #DS_SELECTOR ! Kernel data + mov ds, ax + .data1 o32 + retf ! Return to the kernel + +! Switch from real to protected mode. +real2prot: + movb ah, #0x02 ! Code for A20 enable + call gate_A20 + + lgdt p_gdt_desc ! Global descriptor table + .data1 o32 + mov ax, pdbr ! Load page directory base register + .data1 0x0F,0x22,0xD8 ! mov cr3, eax + .data1 0x0F,0x20,0xC0 ! mov eax, cr0 + .data1 o32 + xchg ax, msw ! Exchange real mode msw for protected mode msw + .data1 0x0F,0x22,0xC0 ! mov cr0, eax + jmpf cs_prot, MCS_SELECTOR ! Set code segment selector +cs_prot: + mov ax, #SS_SELECTOR ! Set data selectors + mov ds, ax + mov es, ax + mov ss, ax + ret + +! Switch from protected to real mode. +prot2real: + lidt p_idt_desc ! Real mode interrupt vectors + .data1 0x0F,0x20,0xD8 ! mov eax, cr3 + .data1 o32 + mov pdbr, ax ! Save page directory base register + .data1 0x0F,0x20,0xC0 ! mov eax, cr0 + .data1 o32 + xchg ax, msw ! Exchange protected mode msw for real mode msw + .data1 0x0F,0x22,0xC0 ! mov cr0, eax + jmpf cs_real, 0xDEAD ! Reload cs register +cs_real: + mov ax, #0xBEEF +ds_real: + mov ds, ax ! Reload data segment registers + mov es, ax + mov ss, ax + + xorb ah, ah ! Code for A20 disable + !jmp gate_A20 + +! Enable (ah = 0x02) or disable (ah = 0x00) the A20 address line. +gate_A20: + cmp bus, #2 ! PS/2 bus? + je gate_PS_A20 + call kb_wait + movb al, #0xD1 ! Tell keyboard that a command is coming + outb 0x64 + call kb_wait + movb al, #0xDD ! 0xDD = A20 disable code if ah = 0x00 + orb al, ah ! 0xDF = A20 enable code if ah = 0x02 + outb 0x60 + call kb_wait + movb al, #0xFF ! Pulse output port + outb 0x64 + call kb_wait ! Wait for the A20 line to settle down + ret +kb_wait: + inb 0x64 + testb al, #0x02 ! Keyboard input buffer full? + jnz kb_wait ! If so, wait + ret + +gate_PS_A20: ! The PS/2 can twiddle A20 using port A + inb 0x92 ! Read port A + andb al, #0xFD + orb al, ah ! Set A20 bit to the required state + outb 0x92 ! Write port A + jmp .+2 ! Small delay +A20ok: inb 0x92 ! Check port A + andb al, #0x02 + cmpb al, ah ! A20 line settled down to the new state? + jne A20ok ! If not then wait + ret + +! void int15(bios_env_t *ep) +! Do an "INT 15" call, primarily for APM (Power Management). +.define _int15 +_int15: + push si ! Save callee-save register si + mov si, sp + mov si, 4(si) ! ep + mov ax, (si) ! ep->ax + mov bx, 2(si) ! ep->bx + mov cx, 4(si) ! ep->cx + int 0x15 ! INT 0x15 BIOS call + pushf ! Save flags + mov (si), ax ! ep->ax + mov 2(si), bx ! ep->bx + mov 4(si), cx ! ep->cx + pop 6(si) ! ep->flags + pop si ! Restore + ret + +.data + .ascii "(null)\0" ! Just in case someone follows a null pointer + .align 2 +c60: .data2 60 ! Constants for MUL and DIV +c1024: .data2 1024 +c1080: .data2 1080 +c19663: .data2 19663 + +! Global descriptor tables. + UNSET = 0 ! Must be computed + +! For "Extended Memory Block Move". +x_gdt: +x_null_desc: + ! Null descriptor + .data2 0x0000, 0x0000 + .data1 0x00, 0x00, 0x00, 0x00 +x_gdt_desc: + ! Descriptor for this descriptor table + .data2 6*8-1, UNSET + .data1 UNSET, 0x00, 0x00, 0x00 +x_src_desc: + ! Source segment descriptor + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0x00, 0x00 +x_dst_desc: + ! Destination segment descriptor + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0x00, 0x00 +x_bios_desc: + ! BIOS segment descriptor (scratch for int 0x15) + .data2 UNSET, UNSET + .data1 UNSET, UNSET, UNSET, UNSET +x_ss_desc: + ! BIOS stack segment descriptor (scratch for int 0x15) + .data2 UNSET, UNSET + .data1 UNSET, UNSET, UNSET, UNSET + +! Protected mode descriptor table. +p_gdt: +p_null_desc: + ! Null descriptor + .data2 0x0000, 0x0000 + .data1 0x00, 0x00, 0x00, 0x00 +p_gdt_desc: + ! Descriptor for this descriptor table + .data2 8*8-1, UNSET + .data1 UNSET, 0x00, 0x00, 0x00 +p_idt_desc: + ! Real mode interrupt descriptor table descriptor + .data2 0x03FF, 0x0000 + .data1 0x00, 0x00, 0x00, 0x00 +p_ds_desc: + ! Kernel data segment descriptor (4 Gb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0xCF, 0x00 +p_es_desc: + ! Physical memory descriptor (4 Gb flat) + .data2 0xFFFF, 0x0000 + .data1 0x00, 0x92, 0xCF, 0x00 +p_ss_desc: + ! Monitor data segment descriptor (64 kb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0x00, 0x00 +p_cs_desc: + ! Kernel code segment descriptor (4 Gb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x9A, 0xCF, 0x00 +p_mcs_desc: + ! Monitor code segment descriptor (64 kb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x9A, 0x00, 0x00 + +.bss + .comm old_vid_mode, 2 ! Video mode at startup + .comm cur_vid_mode, 2 ! Current video mode + .comm dev_state, 2 ! Device state: reset (-1), closed (0), open (1) + .comm sectors, 2 ! # sectors of current device + .comm secspcyl, 2 ! (Sectors * heads) of current device + .comm msw, 4 ! Saved machine status word (cr0) + .comm pdbr, 4 ! Saved page directory base register (cr3) + .comm escape, 2 ! Escape typed? + .comm bus, 2 ! Saved return value of _get_bus + .comm unchar, 2 ! Char returned by ungetch(c) + .comm line, 2 ! Serial line I/O port to copy console I/O to. diff --git a/boot/boothead.s b/boot/boothead.s index e212c867d..42927985e 100755 --- a/boot/boothead.s +++ b/boot/boothead.s @@ -1204,6 +1204,44 @@ bcd: movb ah, al .data1 0xD5,10 ! aad ! ax = (al >> 4) * 10 + (al & 0x0F) ret ! (BUG: assembler messes up aad & aam!) + +! void bootcdinfo(u32_t bufaddr, int *ret, int drive) +! If booted from CD, do BIOS int 0x13 call to obtain boot CD device. +.define _bootcdinfo +_bootcdinfo: + push bp + mov bp, sp + push ax + push bx + push cx + push dx + push si + push ds + mov bx, 10(bp) ! drive number + mov cx, 8(bp) + mov ax, 4(bp) ! buffer address from stack + mov dx, 6(bp) + call abs2seg + mov si, ax ! bios will put data in ds:si + mov ds, dx +! movb dl, #0x00 + movb dh, #0x00 + movb dl, bl +! mov ax, #0x4b01 ! command 0x4b, subcommand 0x01 + movb ah, #0x4b + movb al, bh + int 0x13 + mov bp, cx + mov (bp), ax + pop ds + pop si + pop dx + pop cx + pop bx + pop ax + pop bp + ret + ! Support function for Minix-386 to make a BIOS int 13 call (disk I/O). bios13: mov bp, sp