]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[librm] Allow interrupts in protected mode
authorMichael Brown <mcb30@ipxe.org>
Mon, 28 Apr 2014 19:17:15 +0000 (20:17 +0100)
committerMichael Brown <mcb30@ipxe.org>
Tue, 29 Apr 2014 17:24:04 +0000 (18:24 +0100)
When running in a virtual machine, switching to real mode may be
expensive.  Allow interrupts to be enabled while in protected mode and
reflected down to the real-mode interrupt handlers.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/i386/core/gdbidt.S
src/arch/i386/core/gdbmach.c
src/arch/i386/core/virtaddr.S
src/arch/i386/include/gdbmach.h
src/arch/i386/include/librm.h
src/arch/i386/transitions/librm.S
src/arch/i386/transitions/librm_mgmt.c
src/arch/x86_64/include/gdbmach.h
src/core/gdbstub.c

index cd8b38a9edc6cf821980515ea059b52f3e9615cd..a1e309d7c88554880a2c28641676d967ced20901 100644 (file)
 /*
- * Interrupt Descriptor Table (IDT) setup and interrupt handlers for GDB stub.
+ * Interrupt handlers for GDB stub
  */
 
-#include <librm.h>
-
 #define SIZEOF_I386_REGS       32
 #define SIZEOF_I386_FLAGS      4
 
-/****************************************************************************
- * Interrupt Descriptor Table
- ****************************************************************************
- */
-       .section ".data16", "aw", @progbits
-       .globl idtr
-idtr:
-idt_limit:
-       .word   idt_length - 1
-idt_base:
-       .long   0
-
-/* IDT entries have the following format:
- * offset_lo, segment selector, flags, offset_hi
- *
- * Since it is not possible to specify relocations in arbitrary
- * expressions like (int_overflow & 0xffff), we initialise the
- * IDT with entries in an incorrect format.
- *
- * The entries are shuffled into the correct format in init_librm().
- */
-#define IDT_ENTRY_EMPTY(name) .word 0, 0, 0, 0
-#define IDT_ENTRY_PRESENT(name) \
-       .long   int_##name; \
-       .word   0x8e00, VIRTUAL_CS
-
-.align 16
-idt:
-       IDT_ENTRY_PRESENT(divide_error)
-       IDT_ENTRY_PRESENT(debug_trap)
-       IDT_ENTRY_EMPTY(non_maskable_interrupt)
-       IDT_ENTRY_PRESENT(breakpoint)
-       IDT_ENTRY_PRESENT(overflow)
-       IDT_ENTRY_PRESENT(bound_range_exceeded)
-       IDT_ENTRY_PRESENT(invalid_opcode)
-       IDT_ENTRY_EMPTY(device_not_available)
-       IDT_ENTRY_PRESENT(double_fault)
-       IDT_ENTRY_EMPTY(coprocessor_segment_overrun)
-       IDT_ENTRY_PRESENT(invalid_tss)
-       IDT_ENTRY_PRESENT(segment_not_present)
-       IDT_ENTRY_PRESENT(stack_segment_fault)
-       IDT_ENTRY_PRESENT(general_protection)
-       IDT_ENTRY_PRESENT(page_fault)
-idt_end:
-       .equ    idt_length, idt_end - idt
-
-/* The IDT entries are fixed up (once) in init_librm() */
-idt_fixed:
-       .byte   0
-
-/****************************************************************************
- * idt_init (real-mode near call, 16-bit real-mode near return address)
- *
- * Initialise the IDT, called from init_librm.
- *
- * Parameters:
- *   %eax : IDT base address
- *
- * Destroys %ax, %bx, and %di.
- ****************************************************************************
- */
-       .section ".text16", "ax", @progbits
-       .code16
-       .globl idt_init
-idt_init:
-       movl    %eax, idt_base
-       addl    $idt, idt_base
-
-       /* IDT entries are only fixed up once */
-       movb    idt_fixed, %al
-       orb     %al, %al
-       jnz     2f
-       movb    $1, idt_fixed
-
-       /* Shuffle IDT entries into the correct format */
-       movb    $(idt_length / 8), %al
-       movw    $idt, %bx
-       or      %al, %al
-       jz      2f
-1:
-       movw    2(%bx), %di
-       xchg    %di, 6(%bx)
-       movw    %di, 2(%bx)
-       addw    $8, %bx
-       dec     %al
-       jnz     1b
-2:
-       ret
-
 /****************************************************************************
  * Interrupt handlers
  ****************************************************************************
@@ -111,35 +20,35 @@ idt_init:
 #define SIGSEGV 11
 #define SIGSTKFLT 16
 
-int_divide_error:
+       .globl  gdbmach_nocode_sigfpe
+gdbmach_nocode_sigfpe:
        pushl   $SIGFPE
-       jmp     do_interrupt
+       jmp     gdbmach_interrupt
 
-int_debug_trap:
-int_breakpoint:
+       .globl  gdbmach_nocode_sigtrap
+gdbmach_nocode_sigtrap:
        pushl   $SIGTRAP
-       jmp     do_interrupt
+       jmp     gdbmach_interrupt
 
-int_overflow:
-int_bound_range_exceeded:
+       .globl  gdbmach_nocode_sigstkflt
+gdbmach_nocode_sigstkflt:
        pushl   $SIGSTKFLT
-       jmp     do_interrupt
+       jmp     gdbmach_interrupt
 
-int_invalid_opcode:
+       .globl  gdbmach_nocode_sigill
+gdbmach_nocode_sigill:
        pushl   $SIGILL
-       jmp     do_interrupt
+       jmp     gdbmach_interrupt
 
-int_double_fault:
+       .globl  gdbmach_withcode_sigbus
+gdbmach_withcode_sigbus:
        movl    $SIGBUS, (%esp)
-       jmp     do_interrupt
+       jmp     gdbmach_interrupt
 
-int_invalid_tss:
-int_segment_not_present:
-int_stack_segment_fault:
-int_general_protection:
-int_page_fault:
+       .globl  gdbmach_withcode_sigsegv
+gdbmach_withcode_sigsegv:
        movl    $SIGSEGV, (%esp)
-       jmp     do_interrupt
+       jmp     gdbmach_interrupt
 
 /* When invoked, the stack contains: eflags, cs, eip, signo. */
 #define IH_OFFSET_GDB_REGS ( 0 )
@@ -161,7 +70,7 @@ int_page_fault:
 #define IH_OFFSET_FLUX_OLD_EFLAGS ( IH_OFFSET_OLD_EFLAGS - 40 )
 #define IH_OFFSET_FLUX_OLD_EIP ( IH_OFFSET_OLD_EIP - 36 )
 #define IH_OFFSET_FLUX_END ( IH_OFFSET_END - 20 )
-do_interrupt:
+gdbmach_interrupt:
        /* Store CPU state in GDB register snapshot */
        pushw   $0
        pushw   %gs
@@ -187,25 +96,41 @@ do_interrupt:
        pushl   %ecx
        pushl   %eax
 
+       /* Switch to virtual addressing */
+       call    _intr_to_virt
+
        /* Call GDB stub exception handler */
        pushl   %esp
        pushl   (IH_OFFSET_SIGNO + 4)(%esp)
        call    gdbmach_handler
        addl    $8, %esp
 
+       /* Copy register snapshot to new stack and switch to new stack */
+       movl    %esp, %esi
+       movl    (IH_OFFSET_GDB_SEG_REGS + 4)(%esp), %eax
+       movl    %eax, %es
+       movl    (IH_OFFSET_GDB_REGS + 16)(%esp), %edi
+       subl    $IH_OFFSET_END, %edi
+       movl    $(IH_OFFSET_END / 4), %ecx
+       pushl   %edi
+       ss rep movsl
+       popl    %edi
+       movl    %eax, %ss
+       movl    %edi, %esp
+
        /* Restore CPU state from GDB register snapshot */
        popl    %eax
        popl    %ecx
        popl    %edx
        popl    %ebx
-       addl    $4, %esp /* Changing ESP currently not supported */
+       popl    %ebp /* Skip %esp: already loaded */
        popl    %ebp
        popl    %esi
        popl    %edi
        popl    IH_OFFSET_FLUX_OLD_EIP(%esp)
        popl    IH_OFFSET_FLUX_OLD_EFLAGS(%esp)
        popl    IH_OFFSET_FLUX_OLD_CS(%esp)
-       popl    %ss
+       popl    %ds /* Skip %ss: already loaded */
        popl    %ds
        popl    %es
        popl    %fs
index 4232c7553b28791413469b6a7b1861a8b518a0d1..4d6897f7d3d79abea70b09064c68c6f80e154efd 100644 (file)
@@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <assert.h>
 #include <ipxe/uaccess.h>
 #include <ipxe/gdbstub.h>
+#include <librm.h>
 #include <gdbmach.h>
 
 /** @file
@@ -150,3 +151,30 @@ __asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
        gdbstub_handler ( signo, regs );
        gdbmach_enable_hwbps();
 }
+
+static void * gdbmach_interrupt_vectors[] = {
+       gdbmach_nocode_sigfpe,          /* Divide by zero */
+       gdbmach_nocode_sigtrap,         /* Debug trap */
+       NULL,                           /* Non-maskable interrupt */
+       gdbmach_nocode_sigtrap,         /* Breakpoint */
+       gdbmach_nocode_sigstkflt,       /* Overflow */
+       gdbmach_nocode_sigstkflt,       /* Bound range exceeded */
+       gdbmach_nocode_sigill,          /* Invalid opcode */
+       NULL,                           /* Device not available */
+       gdbmach_withcode_sigbus,        /* Double fault */
+       NULL,                           /* Coprocessor segment overrun */
+       gdbmach_withcode_sigsegv,       /* Invalid TSS */
+       gdbmach_withcode_sigsegv,       /* Segment not present */
+       gdbmach_withcode_sigsegv,       /* Stack segment fault */
+       gdbmach_withcode_sigsegv,       /* General protection fault */
+       gdbmach_withcode_sigsegv,       /* Page fault */
+};
+
+void gdbmach_init ( void ) {
+       unsigned int i;
+
+       for ( i = 0 ; i < ( sizeof ( gdbmach_interrupt_vectors ) /
+                           sizeof ( gdbmach_interrupt_vectors[0] ) ) ; i++ ) {
+               set_interrupt_vector ( i, gdbmach_interrupt_vectors[i] );
+       }
+}
index aae1e1edd70c1dfda9f17ccc29c44fe68b5ce0ec..5e5d7735247530743a6df3b8646670e604f1b096 100644 (file)
@@ -36,6 +36,7 @@ _virt_to_phys:
        addl    %ebp, 12(%esp)
 
        /* Switch to physical code segment */
+       cli
        pushl   $PHYSICAL_CS
        leal    1f(%ebp), %eax
        pushl   %eax
@@ -44,10 +45,10 @@ _virt_to_phys:
        /* Reload other segment registers and adjust %esp */
        movl    $PHYSICAL_DS, %eax
        movl    %eax, %ds
-       movl    %eax, %es       
-       movl    %eax, %fs       
+       movl    %eax, %es
+       movl    %eax, %fs
        movl    %eax, %gs
-       movl    %eax, %ss       
+       movl    %eax, %ss
        addl    %ebp, %esp
 
        /* Restore registers and flags, and return */
@@ -64,9 +65,6 @@ _virt_to_phys:
  * selectors.  All other registers are preserved.  Flags are
  * preserved.
  *
- * Note that this depends on the GDT already being correctly set up
- * (e.g. by a call to run_here()).
- *
  * Parameters: none
  * Returns: none
  ****************************************************************************
@@ -79,18 +77,19 @@ _phys_to_virt:
        pushl   %ebp
 
        /* Switch to virtual code segment */
+       cli
        ljmp    $VIRTUAL_CS, $1f
-1:     
+1:
        /* Reload data segment registers */
        movl    $VIRTUAL_DS, %eax
        movl    %eax, %ds
-       movl    %eax, %es       
-       movl    %eax, %fs       
+       movl    %eax, %es
+       movl    %eax, %fs
        movl    %eax, %gs
 
        /* Reload stack segment and adjust %esp */
        movl    virt_offset, %ebp
-       movl    %eax, %ss       
+       movl    %eax, %ss
        subl    %ebp, %esp
 
        /* Change the return address to a virtual address */
@@ -101,3 +100,46 @@ _phys_to_virt:
        popl    %eax
        popfl
        ret
+
+/****************************************************************************
+ * _intr_to_virt (virtual code segment, virtual or physical stack segment)
+ *
+ * Switch from virtual code segment with either a virtual or physical
+ * stack segment to using virtual addressing.  %esp is adjusted if
+ * necessary to a virtual value.  Segment registers are set to virtual
+ * selectors.  All other registers are preserved.  Flags are
+ * preserved.
+ *
+ * Parameters: none
+ * Returns: none
+ ****************************************************************************
+ */
+       .globl _intr_to_virt
+_intr_to_virt:
+       /* Preserve registers and flags */
+       pushfl
+       pushl   %eax
+       pushl   %ebp
+
+       /* Check whether stack segment is physical or virtual */
+       movl    %ss, %eax
+       cmpw    $VIRTUAL_DS, %ax
+       movl    $VIRTUAL_DS, %eax
+
+       /* Reload data segment registers */
+       movl    %eax, %ds
+       movl    %eax, %es
+       movl    %eax, %fs
+       movl    %eax, %gs
+
+       /* Reload stack segment and adjust %esp if necessary */
+       je      1f
+       movl    virt_offset, %ebp
+       movl    %eax, %ss
+       subl    %ebp, %esp
+1:
+       /* Restore registers and flags, and return */
+       popl    %ebp
+       popl    %eax
+       popfl
+       ret
index 794dab193c820af6243fe08302c66b47800fa16e..416ae341a82c1a902d44af3b30a80111302e2e81 100644 (file)
@@ -46,6 +46,14 @@ enum {
        GDBMACH_AWATCH,
 };
 
+/* Interrupt vectors */
+extern void gdbmach_nocode_sigfpe ( void );
+extern void gdbmach_nocode_sigtrap ( void );
+extern void gdbmach_nocode_sigstkflt ( void );
+extern void gdbmach_nocode_sigill ( void );
+extern void gdbmach_withcode_sigbus ( void );
+extern void gdbmach_withcode_sigsegv ( void );
+
 static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) {
        regs [ GDBMACH_EIP ] = pc;
 }
@@ -61,4 +69,6 @@ static inline void gdbmach_breakpoint ( void ) {
 
 extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable );
 
+extern void gdbmach_init ( void );
+
 #endif /* GDBMACH_H */
index fc5598ebacd14e03799d9011ae2a364c33149324..4a4e61aa1e79b03616e2efa69dbcf426d46ca0e8 100644 (file)
@@ -209,6 +209,71 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size );
        asm_code_str                                    \
        "call _phys_to_virt\n\t"
 
+/** Number of interrupts */
+#define NUM_INT 256
+
+/** An interrupt descriptor table register */
+struct idtr {
+       /** Limit */
+       uint16_t limit;
+       /** Base */
+       uint32_t base;
+} __attribute__ (( packed ));
+
+/** An interrupt descriptor table entry */
+struct interrupt_descriptor {
+       /** Low 16 bits of address */
+       uint16_t low;
+       /** Code segment */
+       uint16_t segment;
+       /** Unused */
+       uint8_t unused;
+       /** Type and attributes */
+       uint8_t attr;
+       /** High 16 bits of address */
+       uint16_t high;
+} __attribute__ (( packed ));
+
+/** Interrupt descriptor is present */
+#define IDTE_PRESENT 0x80
+
+/** Interrupt descriptor 32-bit interrupt gate type */
+#define IDTE_TYPE_IRQ32 0x0e
+
+/** An interrupt vector
+ *
+ * Each interrupt vector comprises an eight-byte fragment of code:
+ *
+ *   60                        pushal
+ *   b0 xx             movb $INT, %al
+ *   e9 xx xx xx xx    jmp interrupt_wrapper
+ */
+struct interrupt_vector {
+       /** "pushal" instruction */
+       uint8_t pushal;
+       /** "movb" instruction */
+       uint8_t movb;
+       /** Interrupt number */
+       uint8_t intr;
+       /** "jmp" instruction */
+       uint8_t jmp;
+       /** Interrupt wrapper address offset */
+       uint32_t offset;
+       /** Next instruction after jump */
+       uint8_t next[0];
+} __attribute__ (( packed ));
+
+/** "pushal" instruction */
+#define PUSHAL_INSN 0x60
+
+/** "movb" instruction */
+#define MOVB_INSN 0xb0
+
+/** "jmp" instruction */
+#define JMP_INSN 0xe9
+
+extern void set_interrupt_vector ( unsigned int intr, void *vector );
+
 #endif /* ASSEMBLY */
 
 #endif /* LIBRM_H */
index b5affdb89f2d02e9627a237a83c15cf1784d1cd2..0e550def11ffde9b2ed273b9f50e87715efd4059 100644 (file)
@@ -128,10 +128,15 @@ init_librm:
        addr32 leal     (%eax, %edi), %ebx
        movl    %ebx, rm_data16
 
-       /* Set GDT and IDT base */
+       /* Set GDT base */
        movl    %eax, gdt_base
        addl    $gdt, gdt_base
-       call    idt_init
+
+       /* Initialise IDT */
+       pushl   $init_idt
+       pushw   %cs
+       call    prot_call
+       popl    %eax /* discard */
 
        /* Restore registers */
        negl    %edi
@@ -141,14 +146,12 @@ init_librm:
 
        .section ".text16", "ax", @progbits
        .code16
-       .weak idt_init
 set_seg_base:
 1:     movw    %ax, 2(%bx)
        rorl    $16, %eax
        movb    %al, 4(%bx)
        movb    %ah, 7(%bx)
        roll    $16, %eax
-idt_init: /* Reuse the return opcode here */
        ret
 
 /****************************************************************************
@@ -237,10 +240,8 @@ real_to_prot:
        /* Return to virtual address */
        ret
 
-       /* Default IDTR with no interrupts */
+       /* Default real-mode interrupt descriptor table */
        .section ".data16", "aw", @progbits
-       .weak idtr
-idtr:
 rm_idtr:
        .word 0xffff /* limit */
        .long 0 /* base */
@@ -536,6 +537,46 @@ flatten_real_mode:
 flatten_dummy:
        ret
 
+/****************************************************************************
+ * Interrupt wrapper
+ *
+ * Used by the protected-mode interrupt vectors to call the
+ * interrupt() function.
+ *
+ * May be entered with either physical or virtual stack segment.
+ ****************************************************************************
+ */
+       .globl interrupt_wrapper
+interrupt_wrapper:
+       /* Preserve segment registers and original %esp */
+       pushl   %ds
+       pushl   %es
+       pushl   %fs
+       pushl   %gs
+       pushl   %ss
+       pushl   %esp
+
+       /* Switch to virtual addressing */
+       call    _intr_to_virt
+
+       /* Expand IRQ number to whole %eax register */
+       movzbl  %al, %eax
+
+       /* Call interrupt handler */
+       call    interrupt
+
+       /* Restore original stack and segment registers */
+       lss     (%esp), %esp
+       popl    %ss
+       popl    %gs
+       popl    %fs
+       popl    %es
+       popl    %ds
+
+       /* Restore registers and return */
+       popal
+       iret
+
 /****************************************************************************
  * Stored real-mode and protected-mode stack pointers
  *
index f00be8117021363faafc22aeafdf037578ccf9fc..dee14357221586a07fe8f49abf4df633bc56701a 100644 (file)
@@ -9,19 +9,35 @@ FILE_LICENCE ( GPL2_OR_LATER );
 
 #include <stdint.h>
 #include <realmode.h>
+#include <pic8259.h>
 
 /*
  * This file provides functions for managing librm.
  *
  */
 
+/** The interrupt wrapper */
+extern char interrupt_wrapper[];
+
+/** The interrupt vectors */
+static struct interrupt_vector intr_vec[ IRQ_MAX + 1 ];
+
+/** The interrupt descriptor table */
+struct interrupt_descriptor idt[NUM_INT] __attribute__ (( aligned ( 16 ) ));
+
+/** The interrupt descriptor table register */
+struct idtr __data16 ( idtr ) = {
+       .limit = ( sizeof ( idt ) - 1 ),
+};
+#define idtr __use_data16 ( idtr )
+
 /**
  * Allocate space on the real-mode stack and copy data there from a
  * user buffer
  *
- * @v data                     User buffer
- * @v size                     Size of stack data
- * @ret sp                     New value of real-mode stack pointer
+ * @v data             User buffer
+ * @v size             Size of stack data
+ * @ret sp             New value of real-mode stack pointer
  */
 uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
        userptr_t rm_stack;
@@ -35,8 +51,8 @@ uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
  * Deallocate space on the real-mode stack, optionally copying back
  * data to a user buffer.
  *
- * @v data                     User buffer
- * @v size                     Size of stack data
+ * @v data             User buffer
+ * @v size             Size of stack data
  */
 void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
        if ( data ) {
@@ -46,6 +62,63 @@ void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
        rm_sp += size;
 };
 
+/**
+ * Set interrupt vector
+ *
+ * @v intr             Interrupt number
+ * @v vector           Interrupt vector, or NULL to disable
+ */
+void set_interrupt_vector ( unsigned int intr, void *vector ) {
+       struct interrupt_descriptor *idte;
+
+       idte = &idt[intr];
+       idte->segment = VIRTUAL_CS;
+       idte->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 );
+       idte->low = ( ( ( uint32_t ) vector ) & 0xffff );
+       idte->high = ( ( ( uint32_t ) vector ) >> 16 );
+}
+
+/**
+ * Initialise interrupt descriptor table
+ *
+ */
+void init_idt ( void ) {
+       struct interrupt_vector *vec;
+       unsigned int irq;
+       unsigned int intr;
+
+       /* Initialise the interrupt descriptor table and interrupt vectors */
+       for ( irq = 0 ; irq <= IRQ_MAX ; irq++ ) {
+               intr = IRQ_INT ( irq );
+               vec = &intr_vec[irq];
+               vec->pushal = PUSHAL_INSN;
+               vec->movb = MOVB_INSN;
+               vec->intr = intr;
+               vec->jmp = JMP_INSN;
+               vec->offset = ( ( uint32_t ) interrupt_wrapper -
+                               ( uint32_t ) vec->next );
+               set_interrupt_vector ( intr, vec );
+       }
+
+       /* Initialise the interrupt descriptor table register */
+       idtr.base = virt_to_phys ( idt );
+}
+
+/**
+ * Interrupt handler
+ *
+ * @v irq              Interrupt number
+ */
+void __attribute__ (( cdecl, regparm ( 1 ) )) interrupt ( int irq ) {
+       uint32_t discard_eax;
+
+       /* Reissue interrupt in real mode */
+       __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"
+                                          "\n1:\n\t"
+                                          "int $0x00\n\t" )
+                              : "=a" ( discard_eax ) : "0" ( irq ) );
+}
+
 PROVIDE_UACCESS_INLINE ( librm, phys_to_user );
 PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
 PROVIDE_UACCESS_INLINE ( librm, virt_to_user );
index fcf8e94e6ddb693d0573df8803a81a75a6ff6e4a..6dadbbdd3252c8e8dc8adb76596a2294879e2e2b 100644 (file)
@@ -48,4 +48,6 @@ static inline void gdbmach_breakpoint ( void ) {
 
 extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable );
 
+extern void gdbmach_init ( void );
+
 #endif /* GDBMACH_H */
index cbe328f9f46253b75a9905b94e5c11f76f5db888..af06118b2efa51af2f7acabc44ec70f3638756d1 100644 (file)
@@ -396,5 +396,6 @@ struct gdb_transport *find_gdb_transport ( const char *name ) {
 void gdbstub_start ( struct gdb_transport *trans ) {
        stub.trans = trans;
        stub.payload = &stub.buf [ 1 ];
+       gdbmach_init();
        gdbmach_breakpoint();
 }