iPXE
Defines | Functions | Variables
librm_mgmt.c File Reference
#include <stdint.h>
#include <strings.h>
#include <assert.h>
#include <ipxe/profile.h>
#include <realmode.h>
#include <pic8259.h>
#include <ipxe/shell.h>

Go to the source code of this file.

Defines

#define STACK_DUMP_LEN   128
 Length of stack dump.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
uint16_t copy_user_to_rm_stack (userptr_t data, size_t size)
 Allocate space on the real-mode stack and copy data there from a user buffer.
void remove_user_from_rm_stack (userptr_t data, size_t size)
 Deallocate space on the real-mode stack, optionally copying back data to a user buffer.
void set_interrupt_vector (unsigned int intr, void *vector)
 Set interrupt vector.
void init_idt (void)
 Initialise interrupt descriptor table.
static struct profilerinterrupt_profiler (int intr)
 Determine interrupt profiler (for debugging)
static void interrupt_dump (int intr, struct interrupt_frame32 *frame32, struct interrupt_frame64 *frame64)
 Display interrupt stack dump (for debugging)
void interrupt (int intr, struct interrupt_frame32 *frame32, struct interrupt_frame64 *frame64)
 Interrupt handler.
static void * ioremap_pages (unsigned long bus_addr, size_t len)
 Map pages for I/O.
static void iounmap_pages (volatile const void *io_addr)
 Unmap pages for I/O.
 PROVIDE_UACCESS_INLINE (librm, phys_to_user)
 PROVIDE_UACCESS_INLINE (librm, user_to_phys)
 PROVIDE_UACCESS_INLINE (librm, virt_to_user)
 PROVIDE_UACCESS_INLINE (librm, user_to_virt)
 PROVIDE_UACCESS_INLINE (librm, userptr_add)
 PROVIDE_UACCESS_INLINE (librm, memcpy_user)
 PROVIDE_UACCESS_INLINE (librm, memmove_user)
 PROVIDE_UACCESS_INLINE (librm, memset_user)
 PROVIDE_UACCESS_INLINE (librm, strlen_user)
 PROVIDE_UACCESS_INLINE (librm, memchr_user)
 PROVIDE_IOMAP_INLINE (pages, io_to_bus)
 PROVIDE_IOMAP (pages, ioremap, ioremap_pages)
 PROVIDE_IOMAP (pages, iounmap, iounmap_pages)

Variables

char interrupt_wrapper []
 The interrupt wrapper.
static struct interrupt_vector intr_vec [NUM_INT]
 The interrupt vectors.
static struct
interrupt32_descriptor 
idt32 [NUM_INT]
 The 32-bit interrupt descriptor table.
struct idtr32 idtr32
 The 32-bit interrupt descriptor table register.
static struct
interrupt64_descriptor 
idt64 [NUM_INT]
 The 64-bit interrupt descriptor table.
struct idtr64 idtr64
 The interrupt descriptor table register.
static struct profiler
timer_irq_profiler 
__profiler = { .name = "irq.timer" }
 Timer interrupt profiler.

Define Documentation

#define STACK_DUMP_LEN   128

Length of stack dump.

Definition at line 48 of file librm_mgmt.c.

Referenced by interrupt_dump().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )

Allocate space on the real-mode stack and copy data there from a user buffer.

Parameters:
dataUser buffer
sizeSize of stack data
Return values:
spNew value of real-mode stack pointer

Definition at line 64 of file librm_mgmt.c.

References memcpy_user(), real_to_user(), rm_sp, rm_ss, and size.

Referenced by com32_cfarcall().

                                                               {
        userptr_t rm_stack;
        rm_sp -= size;
        rm_stack = real_to_user ( rm_ss, rm_sp );
        memcpy_user ( rm_stack, 0, data, 0, size );
        return rm_sp;
};
void remove_user_from_rm_stack ( userptr_t  data,
size_t  size 
)

Deallocate space on the real-mode stack, optionally copying back data to a user buffer.

Parameters:
dataUser buffer
sizeSize of stack data

Definition at line 79 of file librm_mgmt.c.

References memcpy_user(), real_to_user(), rm_sp, rm_ss, and size.

Referenced by com32_cfarcall().

                                                               {
        if ( data ) {
                userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
                memcpy_user ( rm_stack, 0, data, 0, size );
        }
        rm_sp += size;
};
void set_interrupt_vector ( unsigned int  intr,
void *  vector 
)

Set interrupt vector.

Parameters:
intrInterrupt number
vectorInterrupt vector, or NULL to disable

Definition at line 93 of file librm_mgmt.c.

References addr, interrupt32_descriptor::attr, interrupt64_descriptor::attr, interrupt32_descriptor::high, interrupt64_descriptor::high, idt32, idt64, IDTE_PRESENT, IDTE_TYPE_IRQ32, IDTE_TYPE_IRQ64, intr, LONG_CS, interrupt32_descriptor::low, interrupt64_descriptor::low, interrupt64_descriptor::mid, interrupt32_descriptor::segment, interrupt64_descriptor::segment, and VIRTUAL_CS.

Referenced by gdbmach_init(), and init_idt().

                                                              {
        struct interrupt32_descriptor *idte32;
        struct interrupt64_descriptor *idte64;
        intptr_t addr = ( ( intptr_t ) vector );

        /* Populate 32-bit interrupt descriptor */
        idte32 = &idt32[intr];
        idte32->segment = VIRTUAL_CS;
        idte32->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 );
        idte32->low = ( addr >> 0 );
        idte32->high = ( addr >> 16 );

        /* Populate 64-bit interrupt descriptor, if applicable */
        if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) {
                idte64 = &idt64[intr];
                idte64->segment = LONG_CS;
                idte64->attr = ( vector ?
                                 ( IDTE_PRESENT | IDTE_TYPE_IRQ64 ) : 0 );
                idte64->low = ( addr >> 0 );
                idte64->mid = ( addr >> 16 );
                idte64->high = ( ( ( uint64_t ) addr ) >> 32 );
        }
}
void init_idt ( void  )

Initialise interrupt descriptor table.

Definition at line 121 of file librm_mgmt.c.

References idtr32::base, idtr64::base, DBGC, idt32, idt64, interrupt_wrapper, intr, interrupt_vector::intr, intr_vec, interrupt_vector::jmp, JMP_INSN, interrupt_vector::movb, MOVB_INSN, interrupt_vector::next, NUM_INT, interrupt_vector::offset, interrupt_vector::push, PUSH_INSN, set_interrupt_vector(), and virt_to_phys().

                       {
        struct interrupt_vector *vec;
        unsigned int intr;

        /* Initialise the interrupt descriptor table and interrupt vectors */
        for ( intr = 0 ; intr < NUM_INT ; intr++ ) {
                vec = &intr_vec[intr];
                vec->push = PUSH_INSN;
                vec->movb = MOVB_INSN;
                vec->intr = intr;
                vec->jmp = JMP_INSN;
                vec->offset = ( ( intptr_t ) interrupt_wrapper -
                                ( intptr_t ) vec->next );
                set_interrupt_vector ( intr, vec );
        }
        DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n",
               intr_vec, sizeof ( intr_vec[0] ),
               virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) );

        /* Initialise the 32-bit interrupt descriptor table register */
        idtr32.base = virt_to_phys ( idt32 );

        /* Initialise the 64-bit interrupt descriptor table register,
         * if applicable.
         */
        if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) )
                idtr64.base = virt_to_phys ( idt64 );
}
static struct profiler* interrupt_profiler ( int  intr) [static, read]

Determine interrupt profiler (for debugging)

Parameters:
intrInterrupt number
Return values:
profilerProfiler

Definition at line 156 of file librm_mgmt.c.

Referenced by interrupt().

                                                         {

        switch ( intr ) {
        case IRQ_INT ( 0 ) :
                return &timer_irq_profiler;
        default:
                return &other_irq_profiler;
        }
}
static void interrupt_dump ( int  intr,
struct interrupt_frame32 frame32,
struct interrupt_frame64 frame64 
) [static]

Display interrupt stack dump (for debugging)

Parameters:
intrInterrupt number
frame3232-bit interrupt wrapper stack frame (or NULL)
frame6464-bit interrupt wrapper stack frame (or NULL)

Definition at line 174 of file librm_mgmt.c.

References DBG_LOG, DBGC, DBGC_HDA, esp, offsetof, phys_to_virt(), sp, and STACK_DUMP_LEN.

Referenced by interrupt().

                                                     {
        unsigned long sp;
        void *stack;

        /* Do nothing unless debugging is enabled */
        if ( ! DBG_LOG )
                return;

        /* Print register dump */
        if ( ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) || frame32 ) {
                sp = ( frame32->esp + sizeof ( *frame32 ) -
                       offsetof ( typeof ( *frame32 ), esp ) );
                DBGC ( &intr, "INT%d at %04x:%08x (stack %04x:%08lx):\n",
                       intr, frame32->cs, frame32->eip, frame32->ss, sp );
                DBGC ( &intr, "cs = %04x  ds = %04x  es = %04x  fs = %04x  "
                       "gs = %04x  ss = %04x\n", frame32->cs, frame32->ds,
                       frame32->es, frame32->fs, frame32->gs, frame32->ss );
                DBGC ( &intr, "eax = %08x  ebx = %08x  ecx = %08x  "
                       "edx = %08x  flg = %08x\n", frame32->eax, frame32->ebx,
                       frame32->ecx, frame32->edx, frame32->eflags );
                DBGC ( &intr, "esi = %08x  edi = %08x  ebp = %08x  "
                       "esp = %08lx  eip = %08x\n", frame32->esi, frame32->edi,
                       frame32->ebp, sp, frame32->eip );
                stack = ( ( ( void * ) frame32 ) + sizeof ( *frame32 ) );
        } else {
                DBGC ( &intr, "INT%d at %04llx:%016llx (stack "
                       "%04llx:%016llx):\n", intr,
                       ( ( unsigned long long ) frame64->cs ),
                       ( ( unsigned long long ) frame64->rip ),
                       ( ( unsigned long long ) frame64->ss ),
                       ( ( unsigned long long ) frame64->rsp ) );
                DBGC ( &intr, "rax = %016llx  rbx = %016llx  rcx = %016llx\n",
                       ( ( unsigned long long ) frame64->rax ),
                       ( ( unsigned long long ) frame64->rbx ),
                       ( ( unsigned long long ) frame64->rcx ) );
                DBGC ( &intr, "rdx = %016llx  rsi = %016llx  rdi = %016llx\n",
                       ( ( unsigned long long ) frame64->rdx ),
                       ( ( unsigned long long ) frame64->rsi ),
                       ( ( unsigned long long ) frame64->rdi ) );
                DBGC ( &intr, "rbp = %016llx  rsp = %016llx  flg = %016llx\n",
                       ( ( unsigned long long ) frame64->rbp ),
                       ( ( unsigned long long ) frame64->rsp ),
                       ( ( unsigned long long ) frame64->rflags ) );
                DBGC ( &intr, "r8  = %016llx  r9  = %016llx  r10 = %016llx\n",
                       ( ( unsigned long long ) frame64->r8 ),
                       ( ( unsigned long long ) frame64->r9 ),
                       ( ( unsigned long long ) frame64->r10 ) );
                DBGC ( &intr, "r11 = %016llx  r12 = %016llx  r13 = %016llx\n",
                       ( ( unsigned long long ) frame64->r11 ),
                       ( ( unsigned long long ) frame64->r12 ),
                       ( ( unsigned long long ) frame64->r13 ) );
                DBGC ( &intr, "r14 = %016llx  r15 = %016llx\n",
                       ( ( unsigned long long ) frame64->r14 ),
                       ( ( unsigned long long ) frame64->r15 ) );
                sp = frame64->rsp;
                stack = phys_to_virt ( sp );
        }

        /* Print stack dump */
        DBGC_HDA ( &intr, sp, stack, STACK_DUMP_LEN );
}
void interrupt ( int  intr,
struct interrupt_frame32 frame32,
struct interrupt_frame64 frame64 
)

Interrupt handler.

Parameters:
intrInterrupt number
frame3232-bit interrupt wrapper stack frame (or NULL)
frame6464-bit interrupt wrapper stack frame (or NULL)
frameInterrupt wrapper stack frame

Definition at line 246 of file librm_mgmt.c.

References __asm__(), DBG, DBG_LOG, interrupt_dump(), interrupt_profiler(), profile_exclude(), profile_start(), profile_stop(), REAL_CODE, and shell().

Referenced by com32_intcall().

                                                {
        struct profiler *profiler = interrupt_profiler ( intr );
        uint32_t discard_eax;

        /* Trap CPU exceptions if debugging is enabled.  Note that we
         * cannot treat INT8+ as exceptions, since we are not
         * permitted to rebase the PIC.
         */
        if ( DBG_LOG && ( intr < IRQ_INT ( 0 ) ) ) {
                interrupt_dump ( intr, frame32, frame64 );
                DBG ( "CPU exception: dropping to emergency shell\n" );
                shell();
        }

        /* Reissue interrupt in real mode */
        profile_start ( profiler );
        __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"
                                           "\n1:\n\t"
                                           "int $0x00\n\t" )
                               : "=a" ( discard_eax ) : "0" ( intr ) );
        profile_stop ( profiler );
        profile_exclude ( profiler );
}
static void* ioremap_pages ( unsigned long  bus_addr,
size_t  len 
) [static]

Map pages for I/O.

Parameters:
bus_addrBus address
lenLength of region
Return values:
io_addrI/O address

Definition at line 278 of file librm_mgmt.c.

References assert, count, DBGC, first, fls, IO_BASE, IO_PAGE_SIZE, io_pages, NULL, offset, page_table::page, PAGE_LAST, PAGE_P, PAGE_PCD, PAGE_PS, PAGE_PWT, PAGE_RW, PAGE_US, and start.

                                                                   {
        unsigned long start;
        unsigned int count;
        unsigned int stride;
        unsigned int first;
        unsigned int i;
        size_t offset;
        void *io_addr;

        DBGC ( &io_pages, "IO mapping %08lx+%zx\n", bus_addr, len );

        /* Sanity check */
        if ( ! len )
                return NULL;

        /* Round down start address to a page boundary */
        start = ( bus_addr & ~( IO_PAGE_SIZE - 1 ) );
        offset = ( bus_addr - start );
        assert ( offset < IO_PAGE_SIZE );

        /* Calculate number of pages required */
        count = ( ( offset + len + IO_PAGE_SIZE - 1 ) / IO_PAGE_SIZE );
        assert ( count != 0 );
        assert ( count < ( sizeof ( io_pages.page ) /
                           sizeof ( io_pages.page[0] ) ) );

        /* Round up number of pages to a power of two */
        stride = ( 1 << ( fls ( count ) - 1 ) );
        assert ( count <= stride );

        /* Allocate pages */
        for ( first = 0 ; first < ( sizeof ( io_pages.page ) /
                                    sizeof ( io_pages.page[0] ) ) ;
              first += stride ) {

                /* Calculate I/O address */
                io_addr = ( IO_BASE + ( first * IO_PAGE_SIZE ) + offset );

                /* Check that page table entries are available */
                for ( i = first ; i < ( first + count ) ; i++ ) {
                        if ( io_pages.page[i] & PAGE_P ) {
                                io_addr = NULL;
                                break;
                        }
                }
                if ( ! io_addr )
                        continue;

                /* Create page table entries */
                for ( i = first ; i < ( first + count ) ; i++ ) {
                        io_pages.page[i] = ( start | PAGE_P | PAGE_RW |
                                             PAGE_US | PAGE_PWT | PAGE_PCD |
                                             PAGE_PS );
                        start += IO_PAGE_SIZE;
                }

                /* Mark last page as being the last in this allocation */
                io_pages.page[ i - 1 ] |= PAGE_LAST;

                /* Return I/O address */
                DBGC ( &io_pages, "IO mapped %08lx+%zx to %p using PTEs "
                       "[%d-%d]\n", bus_addr, len, io_addr, first,
                       ( first + count - 1 ) );
                return io_addr;
        }

        DBGC ( &io_pages, "IO could not map %08lx+%zx\n", bus_addr, len );
        return NULL;
}
static void iounmap_pages ( volatile const void *  io_addr) [static]

Unmap pages for I/O.

Parameters:
io_addrI/O address

Definition at line 353 of file librm_mgmt.c.

References __asm__(), assert, DBGC, first, IO_BASE, IO_PAGE_SIZE, io_pages, page_table::page, PAGE_LAST, and PAGE_P.

                                                           {
        volatile const void *invalidate = io_addr;
        unsigned int first;
        unsigned int i;
        int is_last;

        DBGC ( &io_pages, "IO unmapping %p\n", io_addr );

        /* Calculate first page table entry */
        first = ( ( io_addr - IO_BASE ) / IO_PAGE_SIZE );

        /* Clear page table entries */
        for ( i = first ; ; i++ ) {

                /* Sanity check */
                assert ( io_pages.page[i] & PAGE_P );

                /* Check if this is the last page in this allocation */
                is_last = ( io_pages.page[i] & PAGE_LAST );

                /* Clear page table entry */
                io_pages.page[i] = 0;

                /* Invalidate TLB for this page */
                __asm__ __volatile__ ( "invlpg (%0)" : : "r" ( invalidate ) );
                invalidate += IO_PAGE_SIZE;

                /* Terminate if this was the last page */
                if ( is_last )
                        break;
        }

        DBGC ( &io_pages, "IO unmapped %p using PTEs [%d-%d]\n",
               io_addr, first, i );
}
PROVIDE_UACCESS_INLINE ( librm  ,
userptr_add   
)
PROVIDE_UACCESS_INLINE ( librm  ,
memcpy_user   
)
PROVIDE_UACCESS_INLINE ( librm  ,
memset_user   
)
PROVIDE_UACCESS_INLINE ( librm  ,
strlen_user   
)
PROVIDE_UACCESS_INLINE ( librm  ,
memchr_user   
)
PROVIDE_IOMAP_INLINE ( pages  ,
io_to_bus   
)
PROVIDE_IOMAP ( pages  ,
ioremap  ,
ioremap_pages   
)
PROVIDE_IOMAP ( pages  ,
iounmap  ,
iounmap_pages   
)

Variable Documentation

The interrupt wrapper.

Referenced by init_idt().

struct interrupt_vector intr_vec[NUM_INT] [static]

The interrupt vectors.

Definition at line 27 of file librm_mgmt.c.

Referenced by init_idt().

The 32-bit interrupt descriptor table.

Definition at line 30 of file librm_mgmt.c.

Referenced by init_idt(), and set_interrupt_vector().

struct idtr32 idtr32
Initial value:
 {
        .limit = ( sizeof ( idt32 ) - 1 ),
}

The 32-bit interrupt descriptor table register.

Definition at line 34 of file librm_mgmt.c.

The 64-bit interrupt descriptor table.

Definition at line 39 of file librm_mgmt.c.

Referenced by init_idt(), and set_interrupt_vector().

struct idtr64 idtr64
Initial value:
 {
        .limit = ( sizeof ( idt64 ) - 1 ),
}

The interrupt descriptor table register.

Definition at line 43 of file librm_mgmt.c.

struct profiler other_irq_profiler __profiler = { .name = "irq.timer" } [static]

Timer interrupt profiler.

Other interrupt profiler.

Definition at line 51 of file librm_mgmt.c.