source: mainline/kernel/arch/ia32/src/asm.S@ b808660

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b808660 was b808660, checked in by Jakub Jermar <jakub@…>, 15 years ago

Do not align ia32 int handlers on any pre-defined power-of-two boundary but
rather define each one as a separate procedure with its own global label. The
label is used to populate the IDT and it also helps to maintain consistency
between C and assembly.

The old way was very error-prone because one did not get a warning when a
handler exceeded the size limit given by the alignment, which usually resulted
in very weird crashes.

In principle, the old way was also rather wasteful as the handler had to be
aligned on a power-of-two address. With the int handler size around 160 bytes,
the bytes 160 - 255 in each handler were simply wasted. In practice, however,
the image.iso size did not change (I'd expect it to drop by around 8K).

The old way did not detect a mistmatch between the C code idea of how many IDT
entries there are and the assembly language code idea of the same thing. It was
possible to initialize an IDT entry to point to some garbage and nobody would
notice until the int occurred.

The new method was a bit tiresome to write as there was a lot of copy'n'paste.
But since it was a one-time effort, I lumped it. If you know of a way to write a
for-loop in C preprocessor or use GAS assmebler macros in a sensible way, I will
gladly use your improvement.

  • Property mode set to 100644
File size: 13.8 KB
Line 
1/*
2 * Copyright (c) 2001 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** Very low and hardware-level functions
30 *
31 */
32
33/**
34 * Mask for interrupts 0 - 31 (bits 0 - 31) where 0 means that int
35 * has no error word and 1 means interrupt with error word
36 *
37 */
38#define ERROR_WORD_INTERRUPT_LIST 0x00027d00
39
40#include <arch/pm.h>
41#include <arch/mm/page.h>
42
43.text
44.global paging_on
45.global enable_l_apic_in_msr
46.global memsetb
47.global memsetw
48.global memcpy
49.global memcpy_from_uspace
50.global memcpy_from_uspace_failover_address
51.global memcpy_to_uspace
52.global memcpy_to_uspace_failover_address
53.global early_putchar
54
55/* Wrapper for generic memsetb */
56memsetb:
57 jmp _memsetb
58
59/* Wrapper for generic memsetw */
60memsetw:
61 jmp _memsetw
62
63#define MEMCPY_DST 4
64#define MEMCPY_SRC 8
65#define MEMCPY_SIZE 12
66
67/** Copy memory to/from userspace.
68 *
69 * This is almost conventional memcpy().
70 * The difference is that there is a failover part
71 * to where control is returned from a page fault
72 * if the page fault occurs during copy_from_uspace()
73 * or copy_to_uspace().
74 *
75 * @param MEMCPY_DST(%esp) Destination address.
76 * @param MEMCPY_SRC(%esp) Source address.
77 * @param MEMCPY_SIZE(%esp) Size.
78 *
79 * @return MEMCPY_DST(%esp) on success and 0 on failure.
80 *
81 */
82memcpy:
83memcpy_from_uspace:
84memcpy_to_uspace:
85 movl %edi, %edx /* save %edi */
86 movl %esi, %eax /* save %esi */
87
88 movl MEMCPY_SIZE(%esp), %ecx
89 shrl $2, %ecx /* size / 4 */
90
91 movl MEMCPY_DST(%esp), %edi
92 movl MEMCPY_SRC(%esp), %esi
93
94 /* Copy whole words */
95 rep movsl
96
97 movl MEMCPY_SIZE(%esp), %ecx
98 andl $3, %ecx /* size % 4 */
99 jz 0f
100
101 /* Copy the rest byte by byte */
102 rep movsb
103
104 0:
105
106 movl %edx, %edi
107 movl %eax, %esi
108
109 /* MEMCPY_DST(%esp), success */
110 movl MEMCPY_DST(%esp), %eax
111 ret
112
113/*
114 * We got here from as_page_fault() after the memory operations
115 * above had caused a page fault.
116 */
117memcpy_from_uspace_failover_address:
118memcpy_to_uspace_failover_address:
119 movl %edx, %edi
120 movl %eax, %esi
121
122 /* Return 0, failure */
123 xorl %eax, %eax
124 ret
125
126/** Turn paging on
127 *
128 * Enable paging and write-back caching in CR0.
129 *
130 */
131paging_on:
132 movl %cr0, %edx
133 orl $(1 << 31), %edx /* paging on */
134
135 /* Clear Cache Disable and not Write Though */
136 andl $~((1 << 30) | (1 << 29)), %edx
137 movl %edx, %cr0
138 jmp 0f
139
140 0:
141 ret
142
143/** Enable local APIC
144 *
145 * Enable local APIC in MSR.
146 *
147 */
148enable_l_apic_in_msr:
149 movl $0x1b, %ecx
150 rdmsr
151 orl $(1 << 11), %eax
152 orl $(0xfee00000), %eax
153 wrmsr
154 ret
155
156/** Clear nested flag
157 *
158 */
159.macro CLEAR_NT_FLAG
160 pushfl
161 andl $0xffffbfff, (%esp)
162 popfl
163.endm
164
165#define ISTATE_OFFSET_EDX 0
166#define ISTATE_OFFSET_ECX 4
167#define ISTATE_OFFSET_EBX 8
168#define ISTATE_OFFSET_ESI 12
169#define ISTATE_OFFSET_EDI 16
170#define ISTATE_OFFSET_EBP 20
171#define ISTATE_OFFSET_EAX 24
172#define ISTATE_OFFSET_EBP_FRAME 28
173#define ISTATE_OFFSET_EIP_FRAME 32
174#define ISTATE_OFFSET_GS 36
175#define ISTATE_OFFSET_FS 40
176#define ISTATE_OFFSET_ES 44
177#define ISTATE_OFFSET_DS 48
178#define ISTATE_OFFSET_ERROR_WORD 52
179#define ISTATE_OFFSET_EIP 56
180#define ISTATE_OFFSET_CS 60
181#define ISTATE_OFFSET_EFLAGS 64
182#define ISTATE_OFFSET_ESP 68
183#define ISTATE_OFFSET_SS 72
184
185/*
186 * Size of the istate structure without the hardware-saved part
187 * and without the error word.
188 */
189#define ISTATE_SOFT_SIZE 52
190
191/*
192 * Size of the entire istate structure including the error word and the
193 * hardware-saved part.
194 */
195#define ISTATE_REAL_SIZE (ISTATE_SOFT_SIZE + 24)
196
197/*
198 * The SYSENTER syscall mechanism can be used for syscalls with
199 * four or fewer arguments. To pass these four arguments, we
200 * use four registers: EDX, ECX, EBX, ESI. The syscall number
201 * is passed in EAX. We use EDI to remember the return address
202 * and EBP to remember the stack. The INT-based syscall mechanism
203 * can actually handle six arguments plus the syscall number
204 * entirely in registers.
205 */
206.global sysenter_handler
207sysenter_handler:
208 sti
209 subl $(ISTATE_REAL_SIZE), %esp
210
211 /*
212 * Save the return address and the userspace stack in the istate
213 * structure on locations that would normally be taken by them.
214 */
215 movl %ebp, ISTATE_OFFSET_ESP(%esp)
216 movl %edi, ISTATE_OFFSET_EIP(%esp)
217
218 /*
219 * Push syscall arguments onto the stack
220 */
221 movl %eax, ISTATE_OFFSET_EAX(%esp)
222 movl %ebx, ISTATE_OFFSET_EBX(%esp)
223 movl %ecx, ISTATE_OFFSET_ECX(%esp)
224 movl %edx, ISTATE_OFFSET_EDX(%esp)
225 movl %esi, ISTATE_OFFSET_ESI(%esp)
226 movl %edi, ISTATE_OFFSET_EDI(%esp) /* observability; not needed */
227 movl %ebp, ISTATE_OFFSET_EBP(%esp) /* observability; not needed */
228
229 /*
230 * Fake up the stack trace linkage.
231 */
232 movl %edi, ISTATE_OFFSET_EIP_FRAME(%esp)
233 movl $0, ISTATE_OFFSET_EBP_FRAME(%esp)
234 leal ISTATE_OFFSET_EBP_FRAME(%esp), %ebp
235
236 /*
237 * Save TLS.
238 */
239 movl %gs, %edx
240 movl %edx, ISTATE_OFFSET_GS(%esp)
241
242 /*
243 * Switch to kernel selectors.
244 */
245 movw $16, %ax
246 movw %ax, %ds
247 movw %ax, %es
248
249 cld
250 call syscall_handler
251
252 /*
253 * Restore TLS.
254 */
255 movl ISTATE_OFFSET_GS(%esp), %edx
256 movl %edx, %gs
257
258 /*
259 * Prepare return address and userspace stack for SYSEXIT.
260 */
261 movl ISTATE_OFFSET_EIP(%esp), %edx
262 movl ISTATE_OFFSET_ESP(%esp), %ecx
263
264 addl $(ISTATE_REAL_SIZE), %esp
265
266 sysexit /* return to userspace */
267
268/** Declare interrupt handlers
269 *
270 * Declare interrupt handlers for n interrupt
271 * vectors starting at vector i.
272 *
273 */
274
275.macro handler i
276.global int_\i
277int_\i:
278 .ifeq \i - 0x30
279 /* Syscall handler */
280 subl $(ISTATE_SOFT_SIZE + 4), %esp
281
282 /*
283 * Push syscall arguments onto the stack
284 *
285 * NOTE: The idea behind the order of arguments passed
286 * in registers is to use all scratch registers
287 * first and preserved registers next. An optimized
288 * libc syscall wrapper can make use of this setup.
289 * The istate structure is arranged in the way to support
290 * this idea.
291 *
292 */
293 movl %eax, ISTATE_OFFSET_EAX(%esp)
294 movl %ebx, ISTATE_OFFSET_EBX(%esp)
295 movl %ecx, ISTATE_OFFSET_ECX(%esp)
296 movl %edx, ISTATE_OFFSET_EDX(%esp)
297 movl %edi, ISTATE_OFFSET_EDI(%esp)
298 movl %esi, ISTATE_OFFSET_ESI(%esp)
299 movl %ebp, ISTATE_OFFSET_EBP(%esp)
300
301 /*
302 * Save the selector registers.
303 */
304 movl %gs, %ecx
305 movl %fs, %edx
306
307 movl %ecx, ISTATE_OFFSET_GS(%esp)
308 movl %edx, ISTATE_OFFSET_FS(%esp)
309
310 movl %es, %ecx
311 movl %ds, %edx
312
313 movl %ecx, ISTATE_OFFSET_ES(%esp)
314 movl %edx, ISTATE_OFFSET_DS(%esp)
315
316 /*
317 * Switch to kernel selectors.
318 */
319 movl $16, %eax
320 movl %eax, %ds
321 movl %eax, %es
322
323 movl $0, ISTATE_OFFSET_EBP_FRAME(%esp)
324 movl ISTATE_OFFSET_EIP(%esp), %eax
325 movl %eax, ISTATE_OFFSET_EIP_FRAME(%esp)
326 leal ISTATE_OFFSET_EBP_FRAME(%esp), %ebp
327
328 cld
329 sti
330
331 /* Call syscall_handler(edx, ecx, ebx, esi, edi, ebp, eax) */
332 call syscall_handler
333
334 CLEAR_NT_FLAG
335
336 /*
337 * Restore the selector registers.
338 */
339 movl ISTATE_OFFSET_GS(%esp), %ecx
340 movl ISTATE_OFFSET_FS(%esp), %edx
341
342 movl %ecx, %gs
343 movl %edx, %fs
344
345 movl ISTATE_OFFSET_ES(%esp), %ecx
346 movl ISTATE_OFFSET_DS(%esp), %edx
347
348 movl %ecx, %es
349 movl %edx, %ds
350
351 /*
352 * Restore the preserved registers the handler cloberred itself
353 * (i.e. EBP).
354 */
355 movl ISTATE_OFFSET_EBP(%esp), %ebp
356
357 addl $(ISTATE_SOFT_SIZE + 4), %esp
358 iret
359
360 .else
361 /*
362 * This macro distinguishes between two versions of ia32
363 * exceptions. One version has error word and the other
364 * does not have it. The latter version fakes the error
365 * word on the stack so that the handlers and istate_t
366 * can be the same for both types.
367 */
368 .iflt \i - 32
369 .if (1 << \i) & ERROR_WORD_INTERRUPT_LIST
370 /*
371 * Exception with error word: do nothing
372 */
373 .else
374 /*
375 * Exception without error word: fake up one
376 */
377 pushl $0
378 .endif
379 .else
380 /*
381 * Interrupt: fake up one
382 */
383 pushl $0
384 .endif
385
386 subl $ISTATE_SOFT_SIZE, %esp
387
388 /*
389 * Save the general purpose registers.
390 */
391 movl %eax, ISTATE_OFFSET_EAX(%esp)
392 movl %ebx, ISTATE_OFFSET_EBX(%esp)
393 movl %ecx, ISTATE_OFFSET_ECX(%esp)
394 movl %edx, ISTATE_OFFSET_EDX(%esp)
395 movl %edi, ISTATE_OFFSET_EDI(%esp)
396 movl %esi, ISTATE_OFFSET_ESI(%esp)
397 movl %ebp, ISTATE_OFFSET_EBP(%esp)
398
399 /*
400 * Save the selector registers.
401 */
402 movl %gs, %eax
403 movl %fs, %ebx
404 movl %es, %ecx
405 movl %ds, %edx
406
407 movl %eax, ISTATE_OFFSET_GS(%esp)
408 movl %ebx, ISTATE_OFFSET_FS(%esp)
409 movl %ecx, ISTATE_OFFSET_ES(%esp)
410 movl %edx, ISTATE_OFFSET_DS(%esp)
411
412 /*
413 * Switch to kernel selectors.
414 */
415 movl $16, %eax
416 movl %eax, %ds
417 movl %eax, %es
418
419 /*
420 * Imitate a regular stack frame linkage.
421 * Stop stack traces here if we came from userspace.
422 */
423 cmpl $8, ISTATE_OFFSET_CS(%esp)
424 jz 0f
425 xorl %ebp, %ebp
426
427 0:
428
429 movl %ebp, ISTATE_OFFSET_EBP_FRAME(%esp)
430 movl ISTATE_OFFSET_EIP(%esp), %eax
431 movl %eax, ISTATE_OFFSET_EIP_FRAME(%esp)
432 leal ISTATE_OFFSET_EBP_FRAME(%esp), %ebp
433
434 cld
435
436 pushl %esp /* pass istate address */
437 pushl $(\i) /* pass intnum */
438
439 /* Call exc_dispatch(intnum, istate) */
440 call exc_dispatch
441
442 addl $8, %esp /* clear arguments from the stack */
443
444 CLEAR_NT_FLAG
445
446 /*
447 * Restore the selector registers.
448 */
449 movl ISTATE_OFFSET_GS(%esp), %eax
450 movl ISTATE_OFFSET_FS(%esp), %ebx
451 movl ISTATE_OFFSET_ES(%esp), %ecx
452 movl ISTATE_OFFSET_DS(%esp), %edx
453
454 movl %eax, %gs
455 movl %ebx, %fs
456 movl %ecx, %es
457 movl %edx, %ds
458
459 /*
460 * Restore the scratch registers and the preserved
461 * registers the handler cloberred itself
462 * (i.e. EBX and EBP).
463 */
464 movl ISTATE_OFFSET_EAX(%esp), %eax
465 movl ISTATE_OFFSET_EBX(%esp), %ebx
466 movl ISTATE_OFFSET_ECX(%esp), %ecx
467 movl ISTATE_OFFSET_EDX(%esp), %edx
468 movl ISTATE_OFFSET_EBP(%esp), %ebp
469
470 addl $(ISTATE_SOFT_SIZE + 4), %esp
471 iret
472
473 .endif
474.endm
475
476#define LIST_0_63 \
477 0, 1, 2, 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,\
478 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,\
479 53,54,55,56,57,58,59,60,61,62,63
480
481interrupt_handlers:
482.irp cnt, LIST_0_63
483 handler \cnt
484.endr
485
486/** Print Unicode character to EGA display.
487 *
488 * If CONFIG_EGA is undefined or CONFIG_FB is defined
489 * then this function does nothing.
490 *
491 * Since the EGA can only display Extended ASCII (usually
492 * ISO Latin 1) characters, some of the Unicode characters
493 * can be displayed in a wrong way. Only newline and backspace
494 * are interpreted, all other characters (even unprintable) are
495 * printed verbatim.
496 *
497 * @param %ebp+0x08 Unicode character to be printed.
498 *
499 */
500early_putchar:
501
502#if ((defined(CONFIG_EGA)) && (!defined(CONFIG_FB)))
503
504 /* Prologue, save preserved registers */
505 pushl %ebp
506 movl %esp, %ebp
507 pushl %ebx
508 pushl %esi
509 pushl %edi
510
511 movl $(PA2KA(0xb8000)), %edi /* base of EGA text mode memory */
512 xorl %eax, %eax
513
514 /* Read bits 8 - 15 of the cursor address */
515 movw $0x3d4, %dx
516 movb $0xe, %al
517 outb %al, %dx
518
519 movw $0x3d5, %dx
520 inb %dx, %al
521 shl $8, %ax
522
523 /* Read bits 0 - 7 of the cursor address */
524 movw $0x3d4, %dx
525 movb $0xf, %al
526 outb %al, %dx
527
528 movw $0x3d5, %dx
529 inb %dx, %al
530
531 /* Sanity check for the cursor on screen */
532 cmp $2000, %ax
533 jb early_putchar_cursor_ok
534
535 movw $1998, %ax
536
537 early_putchar_cursor_ok:
538
539 movw %ax, %bx
540 shl $1, %eax
541 addl %eax, %edi
542
543 movl 0x08(%ebp), %eax
544
545 cmp $0x0a, %al
546 jne early_putchar_backspace
547
548 /* Interpret newline */
549
550 movw %bx, %ax /* %bx -> %dx:%ax */
551 xorw %dx, %dx
552
553 movw $80, %cx
554 idivw %cx, %ax /* %dx = %bx % 80 */
555
556 /* %bx <- %bx + 80 - (%bx % 80) */
557 addw %cx, %bx
558 subw %dx, %bx
559
560 jmp early_putchar_skip
561
562 early_putchar_backspace:
563
564 cmp $0x08, %al
565 jne early_putchar_print
566
567 /* Interpret backspace */
568
569 cmp $0x0000, %bx
570 je early_putchar_skip
571
572 dec %bx
573 jmp early_putchar_skip
574
575 early_putchar_print:
576
577 /* Print character */
578
579 movb $0x0e, %ah /* black background, yellow foreground */
580 stosw
581 inc %bx
582
583 early_putchar_skip:
584
585 /* Sanity check for the cursor on the last line */
586 cmp $2000, %bx
587 jb early_putchar_no_scroll
588
589 /* Scroll the screen (24 rows) */
590 movl $(PA2KA(0xb80a0)), %esi
591 movl $(PA2KA(0xb8000)), %edi
592 movl $960, %ecx
593 rep movsl
594
595 /* Clear the 24th row */
596 xorl %eax, %eax
597 movl $40, %ecx
598 rep stosl
599
600 /* Go to row 24 */
601 movw $1920, %bx
602
603 early_putchar_no_scroll:
604
605 /* Write bits 8 - 15 of the cursor address */
606 movw $0x3d4, %dx
607 movb $0xe, %al
608 outb %al, %dx
609
610 movw $0x3d5, %dx
611 movb %bh, %al
612 outb %al, %dx
613
614 /* Write bits 0 - 7 of the cursor address */
615 movw $0x3d4, %dx
616 movb $0xf, %al
617 outb %al, %dx
618
619 movw $0x3d5, %dx
620 movb %bl, %al
621 outb %al, %dx
622
623 /* Epilogue, restore preserved registers */
624 popl %edi
625 popl %esi
626 popl %ebx
627 leave
628
629#endif
630
631 ret
632
Note: See TracBrowser for help on using the repository browser.