You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

librm_mgmt.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /*
  2. * librm: a library for interfacing to real-mode code
  3. *
  4. * Michael Brown <mbrown@fensystems.co.uk>
  5. *
  6. */
  7. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  8. #include <stdint.h>
  9. #include <strings.h>
  10. #include <assert.h>
  11. #include <ipxe/profile.h>
  12. #include <realmode.h>
  13. #include <pic8259.h>
  14. #include <ipxe/shell.h>
  15. /*
  16. * This file provides functions for managing librm.
  17. *
  18. */
  19. /** The interrupt wrapper */
  20. extern char interrupt_wrapper[];
  21. /** The interrupt vectors */
  22. static struct interrupt_vector intr_vec[NUM_INT];
  23. /** The 32-bit interrupt descriptor table */
  24. static struct interrupt32_descriptor
  25. idt32[NUM_INT] __attribute__ (( aligned ( 16 ) ));
  26. /** The 32-bit interrupt descriptor table register */
  27. struct idtr32 idtr32 = {
  28. .limit = ( sizeof ( idt32 ) - 1 ),
  29. };
  30. /** The 64-bit interrupt descriptor table */
  31. static struct interrupt64_descriptor
  32. idt64[NUM_INT] __attribute__ (( aligned ( 16 ) ));
  33. /** The interrupt descriptor table register */
  34. struct idtr64 idtr64 = {
  35. .limit = ( sizeof ( idt64 ) - 1 ),
  36. };
  37. /** Length of stack dump */
  38. #define STACK_DUMP_LEN 128
  39. /** Timer interrupt profiler */
  40. static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" };
  41. /** Other interrupt profiler */
  42. static struct profiler other_irq_profiler __profiler = { .name = "irq.other" };
  43. /**
  44. * Allocate space on the real-mode stack and copy data there from a
  45. * user buffer
  46. *
  47. * @v data User buffer
  48. * @v size Size of stack data
  49. * @ret sp New value of real-mode stack pointer
  50. */
  51. uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
  52. userptr_t rm_stack;
  53. rm_sp -= size;
  54. rm_stack = real_to_user ( rm_ss, rm_sp );
  55. memcpy_user ( rm_stack, 0, data, 0, size );
  56. return rm_sp;
  57. };
  58. /**
  59. * Deallocate space on the real-mode stack, optionally copying back
  60. * data to a user buffer.
  61. *
  62. * @v data User buffer
  63. * @v size Size of stack data
  64. */
  65. void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
  66. if ( data ) {
  67. userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
  68. memcpy_user ( rm_stack, 0, data, 0, size );
  69. }
  70. rm_sp += size;
  71. };
  72. /**
  73. * Set interrupt vector
  74. *
  75. * @v intr Interrupt number
  76. * @v vector Interrupt vector, or NULL to disable
  77. */
  78. void set_interrupt_vector ( unsigned int intr, void *vector ) {
  79. struct interrupt32_descriptor *idte32;
  80. struct interrupt64_descriptor *idte64;
  81. intptr_t addr = ( ( intptr_t ) vector );
  82. /* Populate 32-bit interrupt descriptor */
  83. idte32 = &idt32[intr];
  84. idte32->segment = VIRTUAL_CS;
  85. idte32->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 );
  86. idte32->low = ( addr >> 0 );
  87. idte32->high = ( addr >> 16 );
  88. /* Populate 64-bit interrupt descriptor, if applicable */
  89. if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) {
  90. idte64 = &idt64[intr];
  91. idte64->segment = LONG_CS;
  92. idte64->attr = ( vector ?
  93. ( IDTE_PRESENT | IDTE_TYPE_IRQ64 ) : 0 );
  94. idte64->low = ( addr >> 0 );
  95. idte64->mid = ( addr >> 16 );
  96. idte64->high = ( ( ( uint64_t ) addr ) >> 32 );
  97. }
  98. }
  99. /**
  100. * Initialise interrupt descriptor table
  101. *
  102. */
  103. void init_idt ( void ) {
  104. struct interrupt_vector *vec;
  105. unsigned int intr;
  106. /* Initialise the interrupt descriptor table and interrupt vectors */
  107. for ( intr = 0 ; intr < NUM_INT ; intr++ ) {
  108. vec = &intr_vec[intr];
  109. vec->push = PUSH_INSN;
  110. vec->movb = MOVB_INSN;
  111. vec->intr = intr;
  112. vec->jmp = JMP_INSN;
  113. vec->offset = ( ( intptr_t ) interrupt_wrapper -
  114. ( intptr_t ) vec->next );
  115. set_interrupt_vector ( intr, vec );
  116. }
  117. DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n",
  118. intr_vec, sizeof ( intr_vec[0] ),
  119. virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) );
  120. /* Initialise the 32-bit interrupt descriptor table register */
  121. idtr32.base = virt_to_phys ( idt32 );
  122. /* Initialise the 64-bit interrupt descriptor table register,
  123. * if applicable.
  124. */
  125. if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) )
  126. idtr64.base = virt_to_phys ( idt64 );
  127. }
  128. /**
  129. * Determine interrupt profiler (for debugging)
  130. *
  131. * @v intr Interrupt number
  132. * @ret profiler Profiler
  133. */
  134. static struct profiler * interrupt_profiler ( int intr ) {
  135. switch ( intr ) {
  136. case IRQ_INT ( 0 ) :
  137. return &timer_irq_profiler;
  138. default:
  139. return &other_irq_profiler;
  140. }
  141. }
  142. /**
  143. * Display interrupt stack dump (for debugging)
  144. *
  145. * @v intr Interrupt number
  146. * @v frame32 32-bit interrupt wrapper stack frame (or NULL)
  147. * @v frame64 64-bit interrupt wrapper stack frame (or NULL)
  148. */
  149. static __attribute__ (( unused )) void
  150. interrupt_dump ( int intr, struct interrupt_frame32 *frame32,
  151. struct interrupt_frame64 *frame64 ) {
  152. unsigned long sp;
  153. void *stack;
  154. /* Do nothing unless debugging is enabled */
  155. if ( ! DBG_LOG )
  156. return;
  157. /* Print register dump */
  158. if ( ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) || frame32 ) {
  159. sp = ( frame32->esp + sizeof ( *frame32 ) -
  160. offsetof ( typeof ( *frame32 ), esp ) );
  161. DBGC ( &intr, "INT%d at %04x:%08x (stack %04x:%08lx):\n",
  162. intr, frame32->cs, frame32->eip, frame32->ss, sp );
  163. DBGC ( &intr, "cs = %04x ds = %04x es = %04x fs = %04x "
  164. "gs = %04x ss = %04x\n", frame32->cs, frame32->ds,
  165. frame32->es, frame32->fs, frame32->gs, frame32->ss );
  166. DBGC ( &intr, "eax = %08x ebx = %08x ecx = %08x "
  167. "edx = %08x flg = %08x\n", frame32->eax, frame32->ebx,
  168. frame32->ecx, frame32->edx, frame32->eflags );
  169. DBGC ( &intr, "esi = %08x edi = %08x ebp = %08x "
  170. "esp = %08lx eip = %08x\n", frame32->esi, frame32->edi,
  171. frame32->ebp, sp, frame32->eip );
  172. stack = ( ( ( void * ) frame32 ) + sizeof ( *frame32 ) );
  173. } else {
  174. DBGC ( &intr, "INT%d at %04llx:%016llx (stack "
  175. "%04llx:%016llx):\n", intr,
  176. ( ( unsigned long long ) frame64->cs ),
  177. ( ( unsigned long long ) frame64->rip ),
  178. ( ( unsigned long long ) frame64->ss ),
  179. ( ( unsigned long long ) frame64->rsp ) );
  180. DBGC ( &intr, "rax = %016llx rbx = %016llx rcx = %016llx\n",
  181. ( ( unsigned long long ) frame64->rax ),
  182. ( ( unsigned long long ) frame64->rbx ),
  183. ( ( unsigned long long ) frame64->rcx ) );
  184. DBGC ( &intr, "rdx = %016llx rsi = %016llx rdi = %016llx\n",
  185. ( ( unsigned long long ) frame64->rdx ),
  186. ( ( unsigned long long ) frame64->rsi ),
  187. ( ( unsigned long long ) frame64->rdi ) );
  188. DBGC ( &intr, "rbp = %016llx rsp = %016llx flg = %016llx\n",
  189. ( ( unsigned long long ) frame64->rbp ),
  190. ( ( unsigned long long ) frame64->rsp ),
  191. ( ( unsigned long long ) frame64->rflags ) );
  192. DBGC ( &intr, "r8 = %016llx r9 = %016llx r10 = %016llx\n",
  193. ( ( unsigned long long ) frame64->r8 ),
  194. ( ( unsigned long long ) frame64->r9 ),
  195. ( ( unsigned long long ) frame64->r10 ) );
  196. DBGC ( &intr, "r11 = %016llx r12 = %016llx r13 = %016llx\n",
  197. ( ( unsigned long long ) frame64->r11 ),
  198. ( ( unsigned long long ) frame64->r12 ),
  199. ( ( unsigned long long ) frame64->r13 ) );
  200. DBGC ( &intr, "r14 = %016llx r15 = %016llx\n",
  201. ( ( unsigned long long ) frame64->r14 ),
  202. ( ( unsigned long long ) frame64->r15 ) );
  203. sp = frame64->rsp;
  204. stack = phys_to_virt ( sp );
  205. }
  206. /* Print stack dump */
  207. DBGC_HDA ( &intr, sp, stack, STACK_DUMP_LEN );
  208. }
  209. /**
  210. * Interrupt handler
  211. *
  212. * @v intr Interrupt number
  213. * @v frame32 32-bit interrupt wrapper stack frame (or NULL)
  214. * @v frame64 64-bit interrupt wrapper stack frame (or NULL)
  215. * @v frame Interrupt wrapper stack frame
  216. */
  217. void __attribute__ (( regparm ( 3 ) ))
  218. interrupt ( int intr, struct interrupt_frame32 *frame32,
  219. struct interrupt_frame64 *frame64 ) {
  220. struct profiler *profiler = interrupt_profiler ( intr );
  221. uint32_t discard_eax;
  222. /* Trap CPU exceptions if debugging is enabled. Note that we
  223. * cannot treat INT8+ as exceptions, since we are not
  224. * permitted to rebase the PIC.
  225. */
  226. if ( DBG_LOG && ( intr < IRQ_INT ( 0 ) ) ) {
  227. interrupt_dump ( intr, frame32, frame64 );
  228. DBG ( "CPU exception: dropping to emergency shell\n" );
  229. shell();
  230. }
  231. /* Reissue interrupt in real mode */
  232. profile_start ( profiler );
  233. __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"
  234. "\n1:\n\t"
  235. "int $0x00\n\t" )
  236. : "=a" ( discard_eax ) : "0" ( intr ) );
  237. profile_stop ( profiler );
  238. profile_exclude ( profiler );
  239. }
  240. /**
  241. * Map pages for I/O
  242. *
  243. * @v bus_addr Bus address
  244. * @v len Length of region
  245. * @ret io_addr I/O address
  246. */
  247. static void * ioremap_pages ( unsigned long bus_addr, size_t len ) {
  248. unsigned long start;
  249. unsigned int count;
  250. unsigned int stride;
  251. unsigned int first;
  252. unsigned int i;
  253. size_t offset;
  254. void *io_addr;
  255. DBGC ( &io_pages, "IO mapping %08lx+%zx\n", bus_addr, len );
  256. /* Sanity check */
  257. if ( ! len )
  258. return NULL;
  259. /* Round down start address to a page boundary */
  260. start = ( bus_addr & ~( IO_PAGE_SIZE - 1 ) );
  261. offset = ( bus_addr - start );
  262. assert ( offset < IO_PAGE_SIZE );
  263. /* Calculate number of pages required */
  264. count = ( ( offset + len + IO_PAGE_SIZE - 1 ) / IO_PAGE_SIZE );
  265. assert ( count != 0 );
  266. assert ( count < ( sizeof ( io_pages.page ) /
  267. sizeof ( io_pages.page[0] ) ) );
  268. /* Round up number of pages to a power of two */
  269. stride = ( 1 << ( fls ( count ) - 1 ) );
  270. assert ( count <= stride );
  271. /* Allocate pages */
  272. for ( first = 0 ; first < ( sizeof ( io_pages.page ) /
  273. sizeof ( io_pages.page[0] ) ) ;
  274. first += stride ) {
  275. /* Calculate I/O address */
  276. io_addr = ( IO_BASE + ( first * IO_PAGE_SIZE ) + offset );
  277. /* Check that page table entries are available */
  278. for ( i = first ; i < ( first + count ) ; i++ ) {
  279. if ( io_pages.page[i] & PAGE_P ) {
  280. io_addr = NULL;
  281. break;
  282. }
  283. }
  284. if ( ! io_addr )
  285. continue;
  286. /* Create page table entries */
  287. for ( i = first ; i < ( first + count ) ; i++ ) {
  288. io_pages.page[i] = ( start | PAGE_P | PAGE_RW |
  289. PAGE_US | PAGE_PWT | PAGE_PCD |
  290. PAGE_PS );
  291. start += IO_PAGE_SIZE;
  292. }
  293. /* Mark last page as being the last in this allocation */
  294. io_pages.page[ i - 1 ] |= PAGE_LAST;
  295. /* Return I/O address */
  296. DBGC ( &io_pages, "IO mapped %08lx+%zx to %p using PTEs "
  297. "[%d-%d]\n", bus_addr, len, io_addr, first,
  298. ( first + count - 1 ) );
  299. return io_addr;
  300. }
  301. DBGC ( &io_pages, "IO could not map %08lx+%zx\n", bus_addr, len );
  302. return NULL;
  303. }
  304. /**
  305. * Unmap pages for I/O
  306. *
  307. * @v io_addr I/O address
  308. */
  309. static void iounmap_pages ( volatile const void *io_addr ) {
  310. volatile const void *invalidate = io_addr;
  311. unsigned int first;
  312. unsigned int i;
  313. int is_last;
  314. DBGC ( &io_pages, "IO unmapping %p\n", io_addr );
  315. /* Calculate first page table entry */
  316. first = ( ( io_addr - IO_BASE ) / IO_PAGE_SIZE );
  317. /* Clear page table entries */
  318. for ( i = first ; ; i++ ) {
  319. /* Sanity check */
  320. assert ( io_pages.page[i] & PAGE_P );
  321. /* Check if this is the last page in this allocation */
  322. is_last = ( io_pages.page[i] & PAGE_LAST );
  323. /* Clear page table entry */
  324. io_pages.page[i] = 0;
  325. /* Invalidate TLB for this page */
  326. __asm__ __volatile__ ( "invlpg (%0)" : : "r" ( invalidate ) );
  327. invalidate += IO_PAGE_SIZE;
  328. /* Terminate if this was the last page */
  329. if ( is_last )
  330. break;
  331. }
  332. DBGC ( &io_pages, "IO unmapped %p using PTEs [%d-%d]\n",
  333. io_addr, first, i );
  334. }
  335. PROVIDE_UACCESS_INLINE ( librm, phys_to_user );
  336. PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
  337. PROVIDE_UACCESS_INLINE ( librm, virt_to_user );
  338. PROVIDE_UACCESS_INLINE ( librm, user_to_virt );
  339. PROVIDE_UACCESS_INLINE ( librm, userptr_add );
  340. PROVIDE_UACCESS_INLINE ( librm, memcpy_user );
  341. PROVIDE_UACCESS_INLINE ( librm, memmove_user );
  342. PROVIDE_UACCESS_INLINE ( librm, memset_user );
  343. PROVIDE_UACCESS_INLINE ( librm, strlen_user );
  344. PROVIDE_UACCESS_INLINE ( librm, memchr_user );
  345. PROVIDE_IOMAP_INLINE ( pages, io_to_bus );
  346. PROVIDE_IOMAP ( pages, ioremap, ioremap_pages );
  347. PROVIDE_IOMAP ( pages, iounmap, iounmap_pages );