iPXE
librm_mgmt.c
Go to the documentation of this file.
00001 /*
00002  * librm: a library for interfacing to real-mode code
00003  *
00004  * Michael Brown <mbrown@fensystems.co.uk>
00005  *
00006  */
00007 
00008 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00009 
00010 #include <stdint.h>
00011 #include <strings.h>
00012 #include <assert.h>
00013 #include <ipxe/profile.h>
00014 #include <realmode.h>
00015 #include <pic8259.h>
00016 #include <ipxe/shell.h>
00017 
00018 /*
00019  * This file provides functions for managing librm.
00020  *
00021  */
00022 
00023 /** The interrupt wrapper */
00024 extern char interrupt_wrapper[];
00025 
00026 /** The interrupt vectors */
00027 static struct interrupt_vector intr_vec[NUM_INT];
00028 
00029 /** The 32-bit interrupt descriptor table */
00030 static struct interrupt32_descriptor
00031 idt32[NUM_INT] __attribute__ (( aligned ( 16 ) ));
00032 
00033 /** The 32-bit interrupt descriptor table register */
00034 struct idtr32 idtr32 = {
00035         .limit = ( sizeof ( idt32 ) - 1 ),
00036 };
00037 
00038 /** The 64-bit interrupt descriptor table */
00039 static struct interrupt64_descriptor
00040 idt64[NUM_INT] __attribute__ (( aligned ( 16 ) ));
00041 
00042 /** The interrupt descriptor table register */
00043 struct idtr64 idtr64 = {
00044         .limit = ( sizeof ( idt64 ) - 1 ),
00045 };
00046 
00047 /** Length of stack dump */
00048 #define STACK_DUMP_LEN 128
00049 
00050 /** Timer interrupt profiler */
00051 static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" };
00052 
00053 /** Other interrupt profiler */
00054 static struct profiler other_irq_profiler __profiler = { .name = "irq.other" };
00055 
00056 /**
00057  * Allocate space on the real-mode stack and copy data there from a
00058  * user buffer
00059  *
00060  * @v data              User buffer
00061  * @v size              Size of stack data
00062  * @ret sp              New value of real-mode stack pointer
00063  */
00064 uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
00065         userptr_t rm_stack;
00066         rm_sp -= size;
00067         rm_stack = real_to_user ( rm_ss, rm_sp );
00068         memcpy_user ( rm_stack, 0, data, 0, size );
00069         return rm_sp;
00070 };
00071 
00072 /**
00073  * Deallocate space on the real-mode stack, optionally copying back
00074  * data to a user buffer.
00075  *
00076  * @v data              User buffer
00077  * @v size              Size of stack data
00078  */
00079 void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
00080         if ( data ) {
00081                 userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
00082                 memcpy_user ( rm_stack, 0, data, 0, size );
00083         }
00084         rm_sp += size;
00085 };
00086 
00087 /**
00088  * Set interrupt vector
00089  *
00090  * @v intr              Interrupt number
00091  * @v vector            Interrupt vector, or NULL to disable
00092  */
00093 void set_interrupt_vector ( unsigned int intr, void *vector ) {
00094         struct interrupt32_descriptor *idte32;
00095         struct interrupt64_descriptor *idte64;
00096         intptr_t addr = ( ( intptr_t ) vector );
00097 
00098         /* Populate 32-bit interrupt descriptor */
00099         idte32 = &idt32[intr];
00100         idte32->segment = VIRTUAL_CS;
00101         idte32->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 );
00102         idte32->low = ( addr >> 0 );
00103         idte32->high = ( addr >> 16 );
00104 
00105         /* Populate 64-bit interrupt descriptor, if applicable */
00106         if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) {
00107                 idte64 = &idt64[intr];
00108                 idte64->segment = LONG_CS;
00109                 idte64->attr = ( vector ?
00110                                  ( IDTE_PRESENT | IDTE_TYPE_IRQ64 ) : 0 );
00111                 idte64->low = ( addr >> 0 );
00112                 idte64->mid = ( addr >> 16 );
00113                 idte64->high = ( ( ( uint64_t ) addr ) >> 32 );
00114         }
00115 }
00116 
00117 /**
00118  * Initialise interrupt descriptor table
00119  *
00120  */
00121 void init_idt ( void ) {
00122         struct interrupt_vector *vec;
00123         unsigned int intr;
00124 
00125         /* Initialise the interrupt descriptor table and interrupt vectors */
00126         for ( intr = 0 ; intr < NUM_INT ; intr++ ) {
00127                 vec = &intr_vec[intr];
00128                 vec->push = PUSH_INSN;
00129                 vec->movb = MOVB_INSN;
00130                 vec->intr = intr;
00131                 vec->jmp = JMP_INSN;
00132                 vec->offset = ( ( intptr_t ) interrupt_wrapper -
00133                                 ( intptr_t ) vec->next );
00134                 set_interrupt_vector ( intr, vec );
00135         }
00136         DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n",
00137                intr_vec, sizeof ( intr_vec[0] ),
00138                virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) );
00139 
00140         /* Initialise the 32-bit interrupt descriptor table register */
00141         idtr32.base = virt_to_phys ( idt32 );
00142 
00143         /* Initialise the 64-bit interrupt descriptor table register,
00144          * if applicable.
00145          */
00146         if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) )
00147                 idtr64.base = virt_to_phys ( idt64 );
00148 }
00149 
00150 /**
00151  * Determine interrupt profiler (for debugging)
00152  *
00153  * @v intr              Interrupt number
00154  * @ret profiler        Profiler
00155  */
00156 static struct profiler * interrupt_profiler ( int intr ) {
00157 
00158         switch ( intr ) {
00159         case IRQ_INT ( 0 ) :
00160                 return &timer_irq_profiler;
00161         default:
00162                 return &other_irq_profiler;
00163         }
00164 }
00165 
00166 /**
00167  * Display interrupt stack dump (for debugging)
00168  *
00169  * @v intr              Interrupt number
00170  * @v frame32           32-bit interrupt wrapper stack frame (or NULL)
00171  * @v frame64           64-bit interrupt wrapper stack frame (or NULL)
00172  */
00173 static __attribute__ (( unused )) void
00174 interrupt_dump ( int intr, struct interrupt_frame32 *frame32,
00175                  struct interrupt_frame64 *frame64 ) {
00176         unsigned long sp;
00177         void *stack;
00178 
00179         /* Do nothing unless debugging is enabled */
00180         if ( ! DBG_LOG )
00181                 return;
00182 
00183         /* Print register dump */
00184         if ( ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) || frame32 ) {
00185                 sp = ( frame32->esp + sizeof ( *frame32 ) -
00186                        offsetof ( typeof ( *frame32 ), esp ) );
00187                 DBGC ( &intr, "INT%d at %04x:%08x (stack %04x:%08lx):\n",
00188                        intr, frame32->cs, frame32->eip, frame32->ss, sp );
00189                 DBGC ( &intr, "cs = %04x  ds = %04x  es = %04x  fs = %04x  "
00190                        "gs = %04x  ss = %04x\n", frame32->cs, frame32->ds,
00191                        frame32->es, frame32->fs, frame32->gs, frame32->ss );
00192                 DBGC ( &intr, "eax = %08x  ebx = %08x  ecx = %08x  "
00193                        "edx = %08x  flg = %08x\n", frame32->eax, frame32->ebx,
00194                        frame32->ecx, frame32->edx, frame32->eflags );
00195                 DBGC ( &intr, "esi = %08x  edi = %08x  ebp = %08x  "
00196                        "esp = %08lx  eip = %08x\n", frame32->esi, frame32->edi,
00197                        frame32->ebp, sp, frame32->eip );
00198                 stack = ( ( ( void * ) frame32 ) + sizeof ( *frame32 ) );
00199         } else {
00200                 DBGC ( &intr, "INT%d at %04llx:%016llx (stack "
00201                        "%04llx:%016llx):\n", intr,
00202                        ( ( unsigned long long ) frame64->cs ),
00203                        ( ( unsigned long long ) frame64->rip ),
00204                        ( ( unsigned long long ) frame64->ss ),
00205                        ( ( unsigned long long ) frame64->rsp ) );
00206                 DBGC ( &intr, "rax = %016llx  rbx = %016llx  rcx = %016llx\n",
00207                        ( ( unsigned long long ) frame64->rax ),
00208                        ( ( unsigned long long ) frame64->rbx ),
00209                        ( ( unsigned long long ) frame64->rcx ) );
00210                 DBGC ( &intr, "rdx = %016llx  rsi = %016llx  rdi = %016llx\n",
00211                        ( ( unsigned long long ) frame64->rdx ),
00212                        ( ( unsigned long long ) frame64->rsi ),
00213                        ( ( unsigned long long ) frame64->rdi ) );
00214                 DBGC ( &intr, "rbp = %016llx  rsp = %016llx  flg = %016llx\n",
00215                        ( ( unsigned long long ) frame64->rbp ),
00216                        ( ( unsigned long long ) frame64->rsp ),
00217                        ( ( unsigned long long ) frame64->rflags ) );
00218                 DBGC ( &intr, "r8  = %016llx  r9  = %016llx  r10 = %016llx\n",
00219                        ( ( unsigned long long ) frame64->r8 ),
00220                        ( ( unsigned long long ) frame64->r9 ),
00221                        ( ( unsigned long long ) frame64->r10 ) );
00222                 DBGC ( &intr, "r11 = %016llx  r12 = %016llx  r13 = %016llx\n",
00223                        ( ( unsigned long long ) frame64->r11 ),
00224                        ( ( unsigned long long ) frame64->r12 ),
00225                        ( ( unsigned long long ) frame64->r13 ) );
00226                 DBGC ( &intr, "r14 = %016llx  r15 = %016llx\n",
00227                        ( ( unsigned long long ) frame64->r14 ),
00228                        ( ( unsigned long long ) frame64->r15 ) );
00229                 sp = frame64->rsp;
00230                 stack = phys_to_virt ( sp );
00231         }
00232 
00233         /* Print stack dump */
00234         DBGC_HDA ( &intr, sp, stack, STACK_DUMP_LEN );
00235 }
00236 
00237 /**
00238  * Interrupt handler
00239  *
00240  * @v intr              Interrupt number
00241  * @v frame32           32-bit interrupt wrapper stack frame (or NULL)
00242  * @v frame64           64-bit interrupt wrapper stack frame (or NULL)
00243  * @v frame             Interrupt wrapper stack frame
00244  */
00245 void __attribute__ (( regparm ( 3 ) ))
00246 interrupt ( int intr, struct interrupt_frame32 *frame32,
00247             struct interrupt_frame64 *frame64 ) {
00248         struct profiler *profiler = interrupt_profiler ( intr );
00249         uint32_t discard_eax;
00250 
00251         /* Trap CPU exceptions if debugging is enabled.  Note that we
00252          * cannot treat INT8+ as exceptions, since we are not
00253          * permitted to rebase the PIC.
00254          */
00255         if ( DBG_LOG && ( intr < IRQ_INT ( 0 ) ) ) {
00256                 interrupt_dump ( intr, frame32, frame64 );
00257                 DBG ( "CPU exception: dropping to emergency shell\n" );
00258                 shell();
00259         }
00260 
00261         /* Reissue interrupt in real mode */
00262         profile_start ( profiler );
00263         __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"
00264                                            "\n1:\n\t"
00265                                            "int $0x00\n\t" )
00266                                : "=a" ( discard_eax ) : "0" ( intr ) );
00267         profile_stop ( profiler );
00268         profile_exclude ( profiler );
00269 }
00270 
00271 /**
00272  * Map pages for I/O
00273  *
00274  * @v bus_addr          Bus address
00275  * @v len               Length of region
00276  * @ret io_addr         I/O address
00277  */
00278 static void * ioremap_pages ( unsigned long bus_addr, size_t len ) {
00279         unsigned long start;
00280         unsigned int count;
00281         unsigned int stride;
00282         unsigned int first;
00283         unsigned int i;
00284         size_t offset;
00285         void *io_addr;
00286 
00287         DBGC ( &io_pages, "IO mapping %08lx+%zx\n", bus_addr, len );
00288 
00289         /* Sanity check */
00290         if ( ! len )
00291                 return NULL;
00292 
00293         /* Round down start address to a page boundary */
00294         start = ( bus_addr & ~( IO_PAGE_SIZE - 1 ) );
00295         offset = ( bus_addr - start );
00296         assert ( offset < IO_PAGE_SIZE );
00297 
00298         /* Calculate number of pages required */
00299         count = ( ( offset + len + IO_PAGE_SIZE - 1 ) / IO_PAGE_SIZE );
00300         assert ( count != 0 );
00301         assert ( count < ( sizeof ( io_pages.page ) /
00302                            sizeof ( io_pages.page[0] ) ) );
00303 
00304         /* Round up number of pages to a power of two */
00305         stride = ( 1 << ( fls ( count ) - 1 ) );
00306         assert ( count <= stride );
00307 
00308         /* Allocate pages */
00309         for ( first = 0 ; first < ( sizeof ( io_pages.page ) /
00310                                     sizeof ( io_pages.page[0] ) ) ;
00311               first += stride ) {
00312 
00313                 /* Calculate I/O address */
00314                 io_addr = ( IO_BASE + ( first * IO_PAGE_SIZE ) + offset );
00315 
00316                 /* Check that page table entries are available */
00317                 for ( i = first ; i < ( first + count ) ; i++ ) {
00318                         if ( io_pages.page[i] & PAGE_P ) {
00319                                 io_addr = NULL;
00320                                 break;
00321                         }
00322                 }
00323                 if ( ! io_addr )
00324                         continue;
00325 
00326                 /* Create page table entries */
00327                 for ( i = first ; i < ( first + count ) ; i++ ) {
00328                         io_pages.page[i] = ( start | PAGE_P | PAGE_RW |
00329                                              PAGE_US | PAGE_PWT | PAGE_PCD |
00330                                              PAGE_PS );
00331                         start += IO_PAGE_SIZE;
00332                 }
00333 
00334                 /* Mark last page as being the last in this allocation */
00335                 io_pages.page[ i - 1 ] |= PAGE_LAST;
00336 
00337                 /* Return I/O address */
00338                 DBGC ( &io_pages, "IO mapped %08lx+%zx to %p using PTEs "
00339                        "[%d-%d]\n", bus_addr, len, io_addr, first,
00340                        ( first + count - 1 ) );
00341                 return io_addr;
00342         }
00343 
00344         DBGC ( &io_pages, "IO could not map %08lx+%zx\n", bus_addr, len );
00345         return NULL;
00346 }
00347 
00348 /**
00349  * Unmap pages for I/O
00350  *
00351  * @v io_addr           I/O address
00352  */
00353 static void iounmap_pages ( volatile const void *io_addr ) {
00354         volatile const void *invalidate = io_addr;
00355         unsigned int first;
00356         unsigned int i;
00357         int is_last;
00358 
00359         DBGC ( &io_pages, "IO unmapping %p\n", io_addr );
00360 
00361         /* Calculate first page table entry */
00362         first = ( ( io_addr - IO_BASE ) / IO_PAGE_SIZE );
00363 
00364         /* Clear page table entries */
00365         for ( i = first ; ; i++ ) {
00366 
00367                 /* Sanity check */
00368                 assert ( io_pages.page[i] & PAGE_P );
00369 
00370                 /* Check if this is the last page in this allocation */
00371                 is_last = ( io_pages.page[i] & PAGE_LAST );
00372 
00373                 /* Clear page table entry */
00374                 io_pages.page[i] = 0;
00375 
00376                 /* Invalidate TLB for this page */
00377                 __asm__ __volatile__ ( "invlpg (%0)" : : "r" ( invalidate ) );
00378                 invalidate += IO_PAGE_SIZE;
00379 
00380                 /* Terminate if this was the last page */
00381                 if ( is_last )
00382                         break;
00383         }
00384 
00385         DBGC ( &io_pages, "IO unmapped %p using PTEs [%d-%d]\n",
00386                io_addr, first, i );
00387 }
00388 
00389 PROVIDE_UACCESS_INLINE ( librm, phys_to_user );
00390 PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
00391 PROVIDE_UACCESS_INLINE ( librm, virt_to_user );
00392 PROVIDE_UACCESS_INLINE ( librm, user_to_virt );
00393 PROVIDE_UACCESS_INLINE ( librm, userptr_add );
00394 PROVIDE_UACCESS_INLINE ( librm, memcpy_user );
00395 PROVIDE_UACCESS_INLINE ( librm, memmove_user );
00396 PROVIDE_UACCESS_INLINE ( librm, memset_user );
00397 PROVIDE_UACCESS_INLINE ( librm, strlen_user );
00398 PROVIDE_UACCESS_INLINE ( librm, memchr_user );
00399 PROVIDE_IOMAP_INLINE ( pages, io_to_bus );
00400 PROVIDE_IOMAP ( pages, ioremap, ioremap_pages );
00401 PROVIDE_IOMAP ( pages, iounmap, iounmap_pages );