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