Jump into the extra stack for all hardware irq handlers. This reduces the overall stack requirements of SeaBIOS.
Replace all users of call16_simpint with call16_int. Only the hardware irq handlers used the old call, and they need to use the new call to ensure the extra stack is properly re-entrant.
Also, pass in a 'struct bregs' to the hardware irq handlers now. It was not done previously to save stack space. Now that the extra stack is used, that is no longer an issue.
Note that should an old OS invoke a hardware irq in 16bit protected mode, then this patch could break that OS. However, the chances of this causing a regression seem small as several existing hardware irq handlers already do not work in 16bit protected mode.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/asm-offsets.c | 1 + src/clock.c | 20 ++++++++++------ src/disk.c | 4 +- src/floppy.c | 4 +- src/kbd.c | 14 ++++++----- src/misc.c | 14 ++++++----- src/mouse.c | 57 ++++++++++++++++++++++++++---------------------- src/output.c | 9 ------- src/ps2port.c | 8 +++--- src/romlayout.S | 60 +++++++++++++++++++++++++++++++++++++++++++++----- src/stacks.c | 4 +- src/util.h | 25 +------------------- tools/checkstack.py | 2 +- 13 files changed, 127 insertions(+), 95 deletions(-)
diff --git a/src/asm-offsets.c b/src/asm-offsets.c index b98f3b5..576bf34 100644 --- a/src/asm-offsets.c +++ b/src/asm-offsets.c @@ -20,4 +20,5 @@ void foo(void) OFFSET(BREGS_edi, bregs, edi); OFFSET(BREGS_flags, bregs, flags); OFFSET(BREGS_code, bregs, code); + DEFINE(BREGS_size, sizeof(struct bregs)); } diff --git a/src/clock.c b/src/clock.c index 97d5301..55dde2e 100644 --- a/src/clock.c +++ b/src/clock.c @@ -516,9 +516,9 @@ handle_1a(struct bregs *regs)
// INT 08h System Timer ISR Entry Point void VISIBLE16 -handle_08(void) +handle_08(struct bregs *regs) { - debug_isr(DEBUG_ISR_08); + debug_enter(regs, DEBUG_ISR_08);
floppy_tick();
@@ -536,8 +536,10 @@ handle_08(void) usb_check_event();
// chain to user timer tick INT #0x1c - u32 eax=0, flags; - call16_simpint(0x1c, &eax, &flags); + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x1c, &br);
eoi_pic1(); } @@ -657,9 +659,9 @@ handle_1583(struct bregs *regs)
// int70h: IRQ8 - CMOS RTC void VISIBLE16 -handle_70(void) +handle_70(struct bregs *regs) { - debug_isr(DEBUG_ISR_70); + debug_enter(regs, DEBUG_ISR_70);
// Check which modes are enabled and have occurred. u8 registerB = inb_cmos(CMOS_STATUS_B); @@ -669,8 +671,10 @@ handle_70(void) goto done; if (registerC & RTC_B_AIE) { // Handle Alarm Interrupt. - u32 eax=0, flags; - call16_simpint(0x4a, &eax, &flags); + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x4a, &br); } if (!(registerC & RTC_B_PIE)) goto done; diff --git a/src/disk.c b/src/disk.c index 080d6cd..ed54e97 100644 --- a/src/disk.c +++ b/src/disk.c @@ -883,9 +883,9 @@ handle_13(struct bregs *regs)
// record completion in BIOS task complete flag void VISIBLE16 -handle_76(void) +handle_76(struct bregs *regs) { - debug_isr(DEBUG_ISR_76); + debug_enter(regs, DEBUG_ISR_76); SET_BDA(disk_interrupt_flag, 0xff); eoi_pic2(); } diff --git a/src/floppy.c b/src/floppy.c index 72bc79b..5400bb0 100644 --- a/src/floppy.c +++ b/src/floppy.c @@ -582,9 +582,9 @@ process_floppy_op(struct disk_op_s *op)
// INT 0Eh Diskette Hardware ISR Entry Point void VISIBLE16 -handle_0e(void) +handle_0e(struct bregs *regs) { - debug_isr(DEBUG_ISR_0e); + debug_enter(regs, DEBUG_ISR_0e); if (! CONFIG_FLOPPY) goto done;
diff --git a/src/kbd.c b/src/kbd.c index fdb61d4..586d57e 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -379,7 +379,7 @@ static struct scaninfo { };
// Handle a scancode read from the ps2 port. Note that "noinline" is -// used to make sure the call to call16_simpint in process_key doesn't +// used to make sure the call to call16_int in process_key doesn't // have the overhead of this function's stack. static void noinline __process_key(u8 scancode) @@ -562,12 +562,14 @@ process_key(u8 key)
if (CONFIG_KBD_CALL_INT15_4F) { // allow for keyboard intercept - u32 eax = (0x4f << 8) | key; - u32 flags; - call16_simpint(0x15, &eax, &flags); - if (!(flags & F_CF)) + struct bregs br; + memset(&br, 0, sizeof(br)); + br.eax = (0x4f << 8) | key; + br.flags = F_IF|F_CF; + call16_int(0x15, &br); + if (!(br.flags & F_CF)) return; - key = eax; + key = br.eax; } __process_key(key); } diff --git a/src/misc.c b/src/misc.c index 9db49e3..d0d6665 100644 --- a/src/misc.c +++ b/src/misc.c @@ -55,9 +55,9 @@ handle_10(struct bregs *regs)
// NMI handler void VISIBLE16 -handle_02(void) +handle_02(struct bregs *regs) { - debug_isr(DEBUG_ISR_02); + debug_enter(regs, DEBUG_ISR_02); }
void @@ -71,17 +71,19 @@ mathcp_setup(void)
// INT 75 - IRQ13 - MATH COPROCESSOR EXCEPTION void VISIBLE16 -handle_75(void) +handle_75(struct bregs *regs) { - debug_isr(DEBUG_ISR_75); + debug_enter(regs, DEBUG_ISR_75);
// clear irq13 outb(0, PORT_MATH_CLEAR); // clear interrupt eoi_pic2(); // legacy nmi call - u32 eax=0, flags; - call16_simpint(0x02, &eax, &flags); + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x02, &br); }
diff --git a/src/mouse.c b/src/mouse.c index 237c8ff..93e4ed2 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -6,8 +6,7 @@ // This file may be distributed under the terms of the GNU LGPLv3 license.
#include "biosvar.h" // GET_EBDA -#include "util.h" // debug_isr -#include "pic.h" // eoi_pic2 +#include "util.h" // dprintf #include "bregs.h" // struct bregs #include "ps2port.h" // ps2_mouse_command #include "usb-hid.h" // usb_mouse_command @@ -273,34 +272,12 @@ handle_15c2(struct bregs *regs) } }
-void noinline -process_mouse(u8 data) +static void +invoke_mouse_handler(u16 ebda_seg) { - if (!CONFIG_MOUSE) - return; - - u16 ebda_seg = get_ebda_seg(); - u8 mouse_flags_1 = GET_EBDA(ebda_seg, mouse_flag1); - u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2); - - if (! (mouse_flags_2 & 0x80)) - // far call handler not installed - return; - - u8 package_count = mouse_flags_2 & 0x07; - u8 index = mouse_flags_1 & 0x07; - SET_EBDA(ebda_seg, mouse_data[index], data); - - if ((index+1) < package_count) { - mouse_flags_1++; - SET_EBDA(ebda_seg, mouse_flag1, mouse_flags_1); - return; - } - u16 status = GET_EBDA(ebda_seg, mouse_data[0]); u16 X = GET_EBDA(ebda_seg, mouse_data[1]); u16 Y = GET_EBDA(ebda_seg, mouse_data[2]); - SET_EBDA(ebda_seg, mouse_flag1, 0);
struct segoff_s func = GET_EBDA(ebda_seg, far_call_pointer); dprintf(16, "mouse farcall s=%04x x=%04x y=%04x func=%04x:%04x\n" @@ -325,3 +302,31 @@ process_mouse(u8 data) : : "edi", "esi", "cc", "memory"); } + +void noinline +process_mouse(u8 data) +{ + if (!CONFIG_MOUSE) + return; + + u16 ebda_seg = get_ebda_seg(); + u8 mouse_flags_1 = GET_EBDA(ebda_seg, mouse_flag1); + u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2); + + if (! (mouse_flags_2 & 0x80)) + // far call handler not installed + return; + + u8 package_count = mouse_flags_2 & 0x07; + u8 index = mouse_flags_1 & 0x07; + SET_EBDA(ebda_seg, mouse_data[index], data); + + if ((index+1) < package_count) { + mouse_flags_1++; + SET_EBDA(ebda_seg, mouse_flag1, mouse_flags_1); + return; + } + + SET_EBDA(ebda_seg, mouse_flag1, 0); + stack_hop_back(ebda_seg, 0, invoke_mouse_handler); +} diff --git a/src/output.c b/src/output.c index 37c4942..1fe5d91 100644 --- a/src/output.c +++ b/src/output.c @@ -487,15 +487,6 @@ dump_regs(struct bregs *regs) , regs->code.seg, regs->code.offset, regs->flags); }
-// Report entry to an Interrupt Service Routine (ISR). -void -__debug_isr(const char *fname) -{ - puts_cs(&debuginfo, fname); - putc(&debuginfo, '\n'); - debug_serial_flush(); -} - // Function called on handler startup. void __debug_enter(struct bregs *regs, const char *fname) diff --git a/src/ps2port.c b/src/ps2port.c index 15bce8e..c835e14 100644 --- a/src/ps2port.c +++ b/src/ps2port.c @@ -357,12 +357,12 @@ ps2_mouse_command(int command, u8 *param)
// INT74h : PS/2 mouse hardware interrupt void VISIBLE16 -handle_74(void) +handle_74(struct bregs *regs) { if (! CONFIG_PS2PORT) return;
- debug_isr(DEBUG_ISR_74); + debug_enter(regs, DEBUG_ISR_74);
u8 v = inb(PORT_PS2_STATUS); if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA)) @@ -384,12 +384,12 @@ done:
// INT09h : Keyboard Hardware Service Entry Point void VISIBLE16 -handle_09(void) +handle_09(struct bregs *regs) { if (! CONFIG_PS2PORT) return;
- debug_isr(DEBUG_ISR_09); + debug_enter(regs, DEBUG_ISR_09);
// read key from keyboard controller u8 v = inb(PORT_PS2_STATUS); diff --git a/src/romlayout.S b/src/romlayout.S index 147cd3b..82be760 100644 --- a/src/romlayout.S +++ b/src/romlayout.S @@ -219,12 +219,15 @@ __call16big: lretw .endm
+ IRQ_TRAMPOLINE 02 IRQ_TRAMPOLINE 10 IRQ_TRAMPOLINE 13 IRQ_TRAMPOLINE 15 IRQ_TRAMPOLINE 16 IRQ_TRAMPOLINE 18 IRQ_TRAMPOLINE 19 + IRQ_TRAMPOLINE 1c + IRQ_TRAMPOLINE 4a
/**************************************************************** @@ -386,10 +389,55 @@ entry_elf: * Interrupt entry points ****************************************************************/
- // Main entry point for interrupts without args - DECLFUNC irqentry -irqentry: - ENTRY_ST + // Main entry point for interrupts handled on extra stack + DECLFUNC hwirqentry +irqentry_extrastack: + cli + cld + pushw %ds + pushl %eax + movl $_datalow_seg, %eax + movl %eax, %ds + movl StackPos, %eax + subl $BREGS_size+12, %eax + popl BREGS_eax(%eax) + popw BREGS_ds(%eax) + movl %edi, BREGS_edi(%eax) + movl %esi, BREGS_esi(%eax) + movl %ebp, BREGS_ebp(%eax) + movl %ebx, BREGS_ebx(%eax) + movl %edx, BREGS_edx(%eax) + movl %ecx, BREGS_ecx(%eax) + movw %es, BREGS_es(%eax) + popl %ecx + popl BREGS_code(%eax) + popw BREGS_flags(%eax) + + movw %ss, BREGS_size+8(%eax) + movzwl %sp, %edx + movl %edx, BREGS_size+4(%eax) + movl %esp, BREGS_size+0(%eax) + movw %ds, %dx + movw %dx, %ss + movl %eax, %esp + calll *%ecx + + movl %esp, %eax + movw BREGS_size+8(%eax), %ss + movl BREGS_size+0(%eax), %esp + movl BREGS_edi(%eax), %edi + movl BREGS_esi(%eax), %esi + movl BREGS_ebp(%eax), %ebp + movl BREGS_ebx(%eax), %ebx + movl BREGS_edx(%eax), %edx + movl BREGS_ecx(%eax), %ecx + movw BREGS_es(%eax), %es + pushw BREGS_flags(%eax) + pushl BREGS_code(%eax) + pushw BREGS_ds(%eax) + pushl BREGS_eax(%eax) + popl %eax + popw %ds iretw
// Main entry point for interrupts with args @@ -398,12 +446,12 @@ irqentryarg: ENTRY_ARG_ST iretw
- // Define an entry point for an interrupt (no args passed). + // Define an entry point for hardware interrupts. .macro IRQ_ENTRY num .global entry_\num entry_\num : pushl $ handle_\num - jmp irqentry + jmp irqentry_extrastack .endm
.macro DECL_IRQ_ENTRY num diff --git a/src/stacks.c b/src/stacks.c index cfdd68d..2804e47 100644 --- a/src/stacks.c +++ b/src/stacks.c @@ -25,7 +25,7 @@ on_extra_stack(void) }
// Switch to the extra stack and call a function. -inline u32 +u32 stack_hop(u32 eax, u32 edx, void *func) { if (on_extra_stack()) @@ -58,7 +58,7 @@ stack_hop(u32 eax, u32 edx, void *func) }
// Switch back to original caller's stack and call a function. -static u32 +u32 stack_hop_back(u32 eax, u32 edx, void *func) { if (!on_extra_stack()) diff --git a/src/util.h b/src/util.h index a4fabd5..0d41785 100644 --- a/src/util.h +++ b/src/util.h @@ -159,23 +159,6 @@ static inline u8 readb(const void *addr) { return *(volatile const u8 *)addr; }
-#define call16_simpint(nr, peax, pflags) do { \ - ASSERT16(); \ - asm volatile( \ - "pushl %%ebp\n" \ - "sti\n" \ - "stc\n" \ - "int %2\n" \ - "pushfl\n" \ - "popl %1\n" \ - "cli\n" \ - "cld\n" \ - "popl %%ebp" \ - : "+a"(*peax), "=c"(*pflags) \ - : "i"(nr) \ - : "ebx", "edx", "esi", "edi", "cc", "memory"); \ - } while (0) - // GDT bits #define GDT_CODE (0x9bULL << 40) // Code segment - P,R,A bits also set #define GDT_DATA (0x93ULL << 40) // Data segment - W,A bits also set @@ -222,7 +205,8 @@ int get_keystroke(int msec);
// stacks.c extern u8 ExtraStack[], *StackPos; -inline u32 stack_hop(u32 eax, u32 edx, void *func); +u32 stack_hop(u32 eax, u32 edx, void *func); +u32 stack_hop_back(u32 eax, u32 edx, void *func); u32 call32(void *func, u32 eax, u32 errret); struct bregs; inline void farcall16(struct bregs *callregs); @@ -260,7 +244,6 @@ char * znprintf(size_t size, const char *fmt, ...) void __dprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); void __debug_enter(struct bregs *regs, const char *fname); -void __debug_isr(const char *fname); void __debug_stub(struct bregs *regs, int lineno, const char *fname); void __warn_invalid(struct bregs *regs, int lineno, const char *fname); void __warn_unimplemented(struct bregs *regs, int lineno, const char *fname); @@ -282,10 +265,6 @@ void hexdump(const void *d, int len); if ((lvl) && (lvl) <= CONFIG_DEBUG_LEVEL) \ __debug_enter((regs), __func__); \ } while (0) -#define debug_isr(lvl) do { \ - if ((lvl) && (lvl) <= CONFIG_DEBUG_LEVEL) \ - __debug_isr(__func__); \ - } while (0) #define debug_stub(regs) \ __debug_stub((regs), __LINE__, __func__) #define warn_invalid(regs) \ diff --git a/tools/checkstack.py b/tools/checkstack.py index 717de2d..23b7c8e 100755 --- a/tools/checkstack.py +++ b/tools/checkstack.py @@ -13,7 +13,7 @@ import sys import re
# Functions that change stacks -STACKHOP = ['__send_disk_op'] +STACKHOP = ['stack_hop', 'stack_hop_back'] # List of functions we can assume are never called. #IGNORE = ['panic', '__dprintf'] IGNORE = ['panic']