From e253f9a0cad14f636a51f6211308491ab19dfbe5 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Sat, 25 May 2019 23:36:30 +0300 Subject: [PATCH] backported fixes from 256boss: - moved early serial console from 1st to 2nd stage loader, to leave space for the partition table if needed. - saving boot_drive_number for later use. - fixed floppy motor turnoff after 2nd stage loader is done. - saving of carry flag before returning from int86. - added functions in intr.h/intr.c to manipulate the PIC IRQ masks. - restoring irq masks after returning from int86. - keyboard controller voodoo: do a single read after returning from int86 to clear any pending inputs that would otherwise clog the buffer and we'd never get an interrupt again from the keyboard. --- src/boot/boot.s | 70 +--------------------------------- src/boot/boot2.s | 110 +++++++++++++++++++++++++++++++++++++++++++++++++----- src/intr.c | 53 +++++++++++++++++++++++--- src/intr.h | 5 +++ 4 files changed, 154 insertions(+), 84 deletions(-) diff --git a/src/boot/boot.s b/src/boot/boot.s index 50817a3..4f0023a 100644 --- a/src/boot/boot.s +++ b/src/boot/boot.s @@ -1,5 +1,5 @@ # pcboot - bootable PC demo/game kernel -# Copyright (C) 2018 John Tsiombikas +# Copyright (C) 2018-2019 John Tsiombikas # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -37,8 +37,6 @@ boot: mov %dl, drive_number - call setup_serial - call get_drive_chs mov $loading_msg, %si @@ -244,7 +242,6 @@ end: popa print_char: push %es - push %ax cmp $10, %ax jnz 0f mov $32, %ax @@ -258,8 +255,6 @@ print_char: movb $7, %es:1(%di) incw cursor_x - pop %ax - call ser_putchar pop %es ret @@ -287,69 +282,6 @@ convloop: popa ret - .set UART_DATA, 0x3f8 - .set UART_DIVLO, 0x3f8 - .set UART_DIVHI, 0x3f9 - .set UART_FIFO, 0x3fa - .set UART_LCTL, 0x3fb - .set UART_MCTL, 0x3fc - .set UART_LSTAT, 0x3fd - .set DIV_9600, 115200 / 9600 - .set LCTL_8N1, 0x03 - .set LCTL_DLAB, 0x80 - .set FIFO_ENABLE_CLEAR, 0x07 - .set MCTL_DTR_RTS_OUT2, 0x0b - .set LST_TREG_EMPTY, 0x20 - -setup_serial: - # set clock divisor - mov $LCTL_DLAB, %al - mov $UART_LCTL, %dx - out %al, %dx - mov $DIV_9600, %ax - mov $UART_DIVLO, %dx - out %al, %dx - shr $8, %ax - mov $UART_DIVHI, %dx - out %al, %dx - # set format 8n1 - mov $LCTL_8N1, %al - mov $UART_LCTL, %dx - out %al, %dx - # clear and enable fifo - mov $FIFO_ENABLE_CLEAR, %al - mov $UART_FIFO, %dx - out %al, %dx - # assert RTS and DTR - mov $MCTL_DTR_RTS_OUT2, %al - mov $UART_MCTL, %dx - out %al, %dx - ret - - # expects a character in al -ser_putchar: - push %dx - - cmp $10, %al - jnz 0f - push %ax - mov $13, %al - call ser_putchar - pop %ax - -0: mov %al, %ah - # wait until the transmit register is empty - mov $UART_LSTAT, %dx -wait: in %dx, %al - and $LST_TREG_EMPTY, %al - jz wait - mov $UART_DATA, %dx - mov %ah, %al - out %al, %dx - - pop %dx - ret - .org 510 .byte 0x55 .byte 0xaa diff --git a/src/boot/boot2.s b/src/boot/boot2.s index 5aeebaa..894c7fa 100644 --- a/src/boot/boot2.s +++ b/src/boot/boot2.s @@ -1,5 +1,5 @@ # pcboot - bootable PC demo/game kernel -# Copyright (C) 2018 John Tsiombikas +# Copyright (C) 2018-2019 John Tsiombikas # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,10 +21,17 @@ .section .boot2,"ax" .set main_load_addr, 0x100000 + .set drive_number, 0x7bec # make sure any BIOS call didn't re-enable interrupts cli + xor %eax, %eax + mov drive_number, %al + mov %eax, boot_drive_number + + call setup_serial + # enter unreal mode call unreal @@ -42,9 +49,6 @@ # load the whole program into memory starting at 1MB call load_main - #mov $0x13, %ax - #int $0x10 - # load initial GDT lgdt (gdt_lim) # load initial IDT @@ -227,11 +231,15 @@ ldloop: # the BIOS might have enabled interrupts cli - # just in case we were loaded from floppy, turn all floppy motors off + # if we were loaded from floppy, turn all floppy motors off + movb drive_number, %bl + and $0x80, %bl + jnz 0f mov $0x3f2, %dx in %dx, %al and $0xf, %al out %al, %dx +0: mov $10, %ax call putchar @@ -248,7 +256,6 @@ rdfail_msg: .asciz "failed\n" read_retries: .short 0 - .set drive_number, 0x7bec read_track: # set es to the start of the destination buffer to allow reading in # full 64k chunks if necessary @@ -451,15 +458,52 @@ scrollup: clearscr: mov $0xb8000, %edi - xor %eax, %eax + # clear with white-on-black spaces + mov $0x07200720, %eax mov $1000, %ecx addr32 rep stosl ret .set UART_DATA, 0x3f8 + .set UART_DIVLO, 0x3f8 + .set UART_DIVHI, 0x3f9 + .set UART_FIFO, 0x3fa + .set UART_LCTL, 0x3fb + .set UART_MCTL, 0x3fc .set UART_LSTAT, 0x3fd + .set DIV_9600, 115200 / 9600 + .set LCTL_8N1, 0x03 + .set LCTL_DLAB, 0x80 + .set FIFO_ENABLE_CLEAR, 0x07 + .set MCTL_DTR_RTS_OUT2, 0x0b .set LST_TREG_EMPTY, 0x20 +setup_serial: + # set clock divisor + mov $LCTL_DLAB, %al + mov $UART_LCTL, %dx + out %al, %dx + mov $DIV_9600, %ax + mov $UART_DIVLO, %dx + out %al, %dx + shr $8, %ax + mov $UART_DIVHI, %dx + out %al, %dx + # set format 8n1 + mov $LCTL_8N1, %al + mov $UART_LCTL, %dx + out %al, %dx + # clear and enable fifo + mov $FIFO_ENABLE_CLEAR, %al + mov $UART_FIFO, %dx + out %al, %dx + # assert RTS and DTR + mov $MCTL_DTR_RTS_OUT2, %al + mov $UART_MCTL, %dx + out %al, %dx + ret + + ser_putchar: push %dx @@ -767,6 +811,8 @@ saved_eax: .long 0 saved_es: .word 0 saved_ds: .word 0 saved_flags: .word 0 +saved_pic1_mask: .byte 0 +saved_pic2_mask: .byte 0 # drop back to unreal mode to call 16bit interrupt .global int86 @@ -779,6 +825,16 @@ int86: sidt (saved_idtr) lidt (rmidt) + # save PIC masks + pushl $0 + call get_pic_mask + add $4, %esp + mov %al, saved_pic1_mask + pushl $1 + call get_pic_mask + add $4, %esp + mov %al, saved_pic2_mask + # modify the int instruction. do this here before the # cs-load jumps, to let them flush the instruction cache mov $int_op, %ebx @@ -812,7 +868,6 @@ int86: pop %es pop %ds # ignore fs and gs for now, don't think I'm going to need them - mov saved_esp, %esp # move to the real-mode stack, accessible from ss=0 # just in case the BIOS call screws up our unreal mode @@ -829,7 +884,7 @@ int_op: int $0 mov %ds, saved_ds mov %es, saved_es pushfw - pop %ax + popw %ax mov %ax, saved_flags # re-enable protection @@ -855,7 +910,11 @@ int_op: int $0 pushw %ax mov saved_es, %ax pushw %ax - mov saved_flags, %ax + # grab the flags and replace the carry bit from the saved flags + pushfw + popw %ax + and $0xfffe, %ax + or saved_flags, %ax pushw %ax mov saved_eax, %eax pushal @@ -863,12 +922,43 @@ int_op: int $0 # restore 32bit interrupt descriptor table lidt (saved_idtr) + + # restore PIC configuration + call init_pic + + # restore IRQ masks + movzbl saved_pic1_mask, %eax + push %eax + pushl $0 + call set_pic_mask + add $8, %esp + + movzbl saved_pic2_mask, %eax + push %eax + pushl $1 + call set_pic_mask + add $8, %esp + + # keyboard voodoo: with some BIOS implementations, after returning from + # int13, there's (I guess) leftover data in the keyboard port and we + # can't receive any more keyboard interrupts afterwards. Reading from + # the keyboard data port (60h) once, seems to resolve this. And it's + # cheap enough, so why not... I give up. + push %eax + in $0x60, %al + pop %eax + sti popal pop %ebp ret + .align 4 + .global boot_drive_number +boot_drive_number: + .long 0 + # buffer used by the track loader ... to load tracks. .align 16 buffer: diff --git a/src/intr.c b/src/intr.c index e04a177..1946708 100644 --- a/src/intr.c +++ b/src/intr.c @@ -54,7 +54,7 @@ along with this program. If not, see . #define OCW2_EOI (1 << 5) -static void init_pic(int offset); +void init_pic(void); static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type); /* defined in intr_asm.S */ @@ -101,7 +101,7 @@ void init_intr(void) /* initialize the programmable interrupt controller * setting up the maping of IRQs [0, 15] to interrupts [32, 47] */ - init_pic(IRQ_OFFSET); + init_pic(); eoi_pending = 0; } @@ -149,14 +149,14 @@ void dispatch_intr(struct intr_frame frm) } } -static void init_pic(int offset) +void init_pic(void) { /* send ICW1 saying we'll follow with ICW4 later on */ outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC1_CMD); outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC2_CMD); /* send ICW2 with IRQ remapping */ - outb(offset, PIC1_DATA); - outb(offset + 8, PIC2_DATA); + outb(IRQ_OFFSET, PIC1_DATA); + outb(IRQ_OFFSET + 8, PIC2_DATA); /* send ICW3 to setup the master/slave relationship */ /* ... set bit3 = 3rd interrupt input has a slave */ outb(4, PIC1_DATA); @@ -197,6 +197,49 @@ void set_intr_entry(int num, void (*handler)(void)) gate_desc(idt + num, selector(SEGM_KCODE, 0), (uint32_t)handler, dpl, type); } +void set_pic_mask(int pic, unsigned char mask) +{ + outb(mask, pic > 0 ? PIC2_DATA : PIC1_DATA); +} + +unsigned char get_pic_mask(int pic) +{ + return inb(pic > 0 ? PIC2_DATA : PIC1_DATA); +} + +void mask_irq(int irq) +{ + int port; + unsigned char mask; + + if(irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + + mask = inb(port) | (1 << irq); + outb(mask, port); +} + +void unmask_irq(int irq) +{ + int port; + unsigned char mask; + + if(irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + + mask = inb(port) & ~(1 << irq); + outb(mask, port); +} + + void end_of_irq(int irq) { int intr_state = get_intr_flag(); diff --git a/src/intr.h b/src/intr.h index 0c6e8bf..fe6ed44 100644 --- a/src/intr.h +++ b/src/intr.h @@ -72,6 +72,11 @@ void interrupt(int intr_num, intr_func_t func); */ void set_intr_entry(int num, void (*handler)(void)); +void set_pic_mask(int pic, unsigned char mask); +unsigned char get_pic_mask(int pic); +void mask_irq(int irq); +void unmask_irq(int irq); + /* defined in intr_asm.S */ int get_intr_flag(void); void set_intr_flag(int onoff); -- 1.7.10.4