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