Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


2c64e8a5fae32fc7a6b7e3ac168fcd5fa87eb00e
[palacios.git] / geekos / src / geekos / lowlevel.asm
1 ; -*- fundamental -*-
2 ; Low level interrupt/thread handling code for GeekOS.
3 ; Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
4 ; Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
5 ; $Revision: 1.8 $
6
7 ; This is free software.  You are permitted to use,
8 ; redistribute, and modify it as specified in the file "COPYING".
9
10 ; This is 32 bit code to be linked into the kernel.
11 ; It defines low level interrupt handler entry points that we'll use
12 ; to populate the IDT.  It also contains the interrupt handling
13 ; and thread context switch code.
14
15 %include "defs.asm"
16 %include "symbol.asm"
17 %include "util.asm"
18
19
20 [BITS 32]
21
22 ; ----------------------------------------------------------------------
23 ; Definitions
24 ; ----------------------------------------------------------------------
25
26 ; This is the size of the Interrupt_State struct in int.h
27 INTERRUPT_STATE_SIZE equ 64
28
29 ; Save registers prior to calling a handler function.
30 ; This must be kept up to date with:
31 ;   - Interrupt_State struct in int.h
32 ;   - Setup_Initial_Thread_Context() in kthread.c
33 %macro Save_Registers 0
34         push    eax
35         push    ebx
36         push    ecx
37         push    edx
38         push    esi
39         push    edi
40         push    ebp
41         push    ds
42         push    es
43         push    fs
44         push    gs
45 %endmacro
46
47 ; Restore registers and clean up the stack after calling a handler function
48 ; (i.e., just before we return from the interrupt via an iret instruction).
49 %macro Restore_Registers 0
50         pop     gs
51         pop     fs
52         pop     es
53         pop     ds
54         pop     ebp
55         pop     edi
56         pop     esi
57         pop     edx
58         pop     ecx
59         pop     ebx
60         pop     eax
61         add     esp, 8  ; skip int num and error code
62 %endmacro
63
64 ; Code to activate a new user context (if necessary), before returning
65 ; to executing a thread.  Should be called just before restoring
66 ; registers (because the interrupt context is used).
67 %macro Activate_User_Context 0  
68         ; If the new thread has a user context which is not the current
69         ; one, activate it.
70         push    esp                     ; Interrupt_State pointer
71         push    dword [g_currentThread] ; Kernel_Thread pointer
72 ;       call    Switch_To_User_Context 
73         add     esp, 8                  ; clear 2 arguments
74 %endmacro
75
76 ; Number of bytes between the top of the stack and
77 ; the interrupt number after the general-purpose and segment
78 ; registers have been saved.
79 REG_SKIP equ (11*4)
80
81 ; Template for entry point code for interrupts that have
82 ; an explicit processor-generated error code.
83 ; The argument is the interrupt number.
84 %macro Int_With_Err 1
85 align 8
86         push    dword %1        ; push interrupt number
87         jmp     Handle_Interrupt ; jump to common handler
88 %endmacro
89
90 ; Template for entry point code for interrupts that do not
91 ; generate an explicit error code.  We push a dummy error
92 ; code on the stack, so the stack layout is the same
93 ; for all interrupts.
94 %macro Int_No_Err 1
95 align 8
96         push    dword 0         ; fake error code
97         push    dword %1        ; push interrupt number
98         jmp     Handle_Interrupt ; jump to common handler
99 %endmacro
100
101
102 ; ----------------------------------------------------------------------
103 ; Symbol imports and exports
104 ; ----------------------------------------------------------------------
105
106 ; This symbol is defined in idt.c, and is a table of addresses
107 ; of C handler functions for interrupts.
108 IMPORT g_interruptTable
109
110 ; Global variable pointing to context struct for current thread.
111 IMPORT g_currentThread
112
113 ; Set to non-zero when we need to choose a new thread
114 ; in the interrupt return code.
115 IMPORT g_needReschedule
116
117 ; Set to non-zero when preemption is disabled.
118 IMPORT g_preemptionDisabled
119
120 ; This is the function that returns the next runnable thread.
121 IMPORT Get_Next_Runnable
122
123 ; Function to put a thread on the run queue.
124 IMPORT Make_Runnable
125
126 ; Function to activate a new user context (if needed).
127 IMPORT Switch_To_User_Context
128
129 ; Debug functions
130 IMPORT SerialPrintHex
131 IMPORT SerialPutChar
132
133 ; Sizes of interrupt handler entry points for interrupts with
134 ; and without error codes.  The code in idt.c uses this
135 ; information to infer the layout of the table of interrupt
136 ; handler entry points, without needing a separate linker
137 ; symbol for each one (which is quite tedious to type :-)
138 EXPORT g_handlerSizeNoErr
139 EXPORT g_handlerSizeErr
140
141 ; Simple functions to load the IDTR, GDTR, and LDTR.
142 EXPORT Load_IDTR
143 EXPORT Load_GDTR
144 EXPORT Load_LDTR
145
146 ; Beginning and end of the table of interrupt entry points.
147 EXPORT g_entryPointTableStart
148 EXPORT g_entryPointTableEnd
149
150 ; Thread context switch function.
151 EXPORT Switch_To_Thread
152
153 ; Return current value of eflags register.
154 EXPORT Get_Current_EFLAGS
155
156 ; Return the return address
157 EXPORT Get_EIP
158 ; ESP
159 EXPORT Get_ESP
160 ; EBP
161 EXPORT Get_EBP
162
163 ; Virtual memory support.
164 EXPORT Enable_Paging
165 EXPORT Set_PDBR
166 EXPORT Get_PDBR
167 EXPORT Flush_TLB
168
169 ; CPUID functions
170 ;EXPORT cpuid_ecx
171 ;EXPORT cpuid_eax
172 ;EXPORT cpuid_edx
173
174 ; Utility Functions
175 ;EXPORT Set_MSR
176 ;EXPORT Get_MSR
177
178
179 EXPORT Get_CR2
180 EXPORT Get_CR3
181
182
183 EXPORT Proc_test
184
185 ; ----------------------------------------------------------------------
186 ; Code
187 ; ----------------------------------------------------------------------
188
189 [SECTION .text]
190
191 ; Load IDTR with 6-byte pointer whose address is passed as
192 ; the parameter.
193 align 8
194 Load_IDTR:
195         mov     eax, [esp+4]
196         lidt    [eax]
197         ret
198
199 ;  Load the GDTR with 6-byte pointer whose address is
200 ; passed as the parameter.  Assumes that interrupts
201 ; are disabled.
202 align 8
203 Load_GDTR:
204         mov     eax, [esp+4]
205         lgdt    [eax]
206         ; Reload segment registers
207         mov     ax, KERNEL_DS
208         mov     ds, ax
209         mov     es, ax
210         mov     fs, ax
211         mov     gs, ax
212         mov     ss, ax
213         jmp     KERNEL_CS:.here
214 .here:
215         ret
216
217 ; Load the LDT whose selector is passed as a parameter.
218 align 8
219 Load_LDTR:
220         mov     eax, [esp+4]
221         lldt    ax
222         ret
223
224 ;
225 ; Start paging
226 ;       load crt3 with the passed page directory pointer
227 ;       enable paging bit in cr2
228 align 8
229 Enable_Paging:
230         push    ebp
231         mov     ebp, esp
232         push    eax
233         push    ebx
234         mov     eax, [ebp+8]
235         mov     cr3, eax
236         mov     eax, cr3
237         mov     cr3, eax
238         mov     ebx, cr0
239         or      ebx, 0x80000000
240         mov     cr0, ebx
241         pop     ebx
242         pop     eax
243         pop     ebp
244         ret
245
246
247
248
249         
250 ;
251 ; Change PDBR 
252 ;       load cr3 with the passed page directory pointer
253 align 8
254 Set_PDBR:
255         mov     eax, [esp+4]
256         mov     cr3, eax
257         ret
258
259 ;
260 ; Get the current PDBR.
261 ; This is useful for lazily switching address spaces;
262 ; only switch if the new thread has a different address space.
263 ;
264 align 8
265 Get_PDBR:
266         mov     eax, cr3
267         ret
268
269 ;
270 ; Flush TLB - just need to re-load cr3 to force this to happen
271 ;
272 align 8
273 Flush_TLB:
274         mov     eax, cr3
275         mov     cr3, eax
276         ret
277
278
279
280 ;
281 ; cpuid_edx - return the edx register from cpuid
282 ;
283 align 8
284 cpuid_edx:
285         push    ebp
286         mov     ebp, esp
287         push    edx
288         push    ecx
289         push    ebx
290
291         mov     eax, [ebp + 8]
292         cpuid
293         mov     eax, edx
294
295         pop     ebx
296         pop     ecx
297         pop     edx
298         pop     ebp
299         ret
300
301
302 ;
303 ; cpuid_ecx - return the ecx register from cpuid
304 ;
305 align 8
306 cpuid_ecx:
307         push    ebp
308         mov     ebp, esp
309         push    edx
310         push    ecx
311         push    ebx
312
313         mov     eax, [ebp + 8]
314         cpuid
315         mov     eax, ecx
316
317         pop     ebx
318         pop     ecx
319         pop     edx
320         pop     ebp
321         ret
322
323 ;
324 ; cpuid_eax - return the eax register from cpuid
325 ;
326 align 8
327 cpuid_eax:
328         push    ebp
329         mov     ebp, esp
330         push    edx
331         push    ecx
332         push    ebx
333
334         mov     eax, [esp+4]
335         cpuid
336
337         pop     ebx
338         pop     ecx
339         pop     edx
340         pop     ebp
341         ret
342
343 ;
344 ; Set_MSR  - Set the value of a given MSR
345 ;
346 align 8
347 Set_MSR:
348         push    ebp
349         mov     ebp, esp
350         pusha
351         mov     eax, [ebp+16]
352         mov     edx, [ebp+12]
353         mov     ecx, [ebp+8]
354         wrmsr
355         popa
356         pop     ebp
357         ret
358
359
360
361 ;
362 ; Get_MSR  -  Get the value of a given MSR
363 ; void Get_MSR(int MSR, void * high_byte, void * low_byte);
364 ;
365 align 8
366 Get_MSR:
367         push    ebp
368         mov     ebp, esp
369         pusha
370         mov     ecx, [ebp+8]
371         rdmsr
372         mov     ebx, [ebp+12]
373         mov     [ebx], edx
374         mov     ebx, [ebp+16]
375         mov     [ebx], eax
376         popa
377         pop     ebp
378         ret
379
380
381
382
383 align 8
384 Get_CR2:
385         mov     eax, cr2
386         ret
387
388
389 align 8
390 Get_CR3:
391         mov     eax, cr3
392         ret
393
394 align 8
395 Proc_test:
396         push    ebp
397         mov     ebp, esp
398         push    ebx
399         mov     ebx, [ebp + 8]
400         mov     eax, [ebp + 12]
401
402         add     eax, ebx
403         pop     ebx
404         pop     ebp
405         ret
406
407
408 ; Common interrupt handling code.
409 ; Save registers, call C handler function,
410 ; possibly choose a new thread to run, restore
411 ; registers, return from the interrupt.
412 align 8
413 Handle_Interrupt:
414         ; Save registers (general purpose and segment)
415         Save_Registers
416
417         ; Ensure that we're using the kernel data segment
418         mov     ax, KERNEL_DS
419         mov     ds, ax
420         mov     es, ax
421
422         ; Get the address of the C handler function from the
423         ; table of handler functions.
424         mov     eax, g_interruptTable   ; get address of handler table
425         mov     esi, [esp+REG_SKIP]     ; get interrupt number
426         mov     ebx, [eax+esi*4]        ; get address of handler function
427
428 ;       push    esi
429 ;       call    SerialPrintHex
430 ;       pop     esi
431
432 ;       push    eax
433 ;       mov     eax, 0xa
434 ;       push    eax
435 ;       call    SerialPutChar
436 ;       pop     eax
437 ;       pop     eax
438
439         ; Call the handler.
440         ; The argument passed is a pointer to an Interrupt_State struct,
441         ; which describes the stack layout for all interrupts.
442         push    esp
443         call    ebx
444         add     esp, 4                  ; clear 1 argument
445
446         ; If preemption is disabled, then the current thread
447         ; keeps running.
448         cmp     [g_preemptionDisabled], dword 0
449         jne     .restore
450
451         ; See if we need to choose a new thread to run.
452         cmp     [g_needReschedule], dword 0
453         je      .restore
454
455         ; Put current thread back on the run queue
456         push    dword [g_currentThread]
457         call    Make_Runnable
458         add     esp, 4                  ; clear 1 argument
459
460         ; Save stack pointer in current thread context, and
461         ; clear numTicks field.
462         mov     eax, [g_currentThread]
463         mov     [eax+0], esp            ; esp field
464         mov     [eax+4], dword 0        ; numTicks field
465
466         ; Pick a new thread to run, and switch to its stack
467         call    Get_Next_Runnable
468         mov     [g_currentThread], eax
469         mov     esp, [eax+0]            ; esp field
470
471         ; Clear "need reschedule" flag
472         mov     [g_needReschedule], dword 0
473
474 .restore:
475
476 ;       push    ebp
477 ;       mov     ebp, esp
478 ;       pusha   
479 ;
480 ;       mov     eax, 0xa
481 ;       push    eax
482 ;       call    SerialPutChar
483 ;       pop     eax
484 ;       mov     eax, 0xa
485 ;       push    eax
486 ;       call    SerialPutChar
487 ;       pop     eax
488 ;
489 ;        mov     ecx, 4
490 ;        mov     edx, 24 
491 ;
492 ;.loop:
493 ;       mov     eax, [ebp + ecx]
494 ;             
495 ;        push    eax
496 ;       call    SerialPrintHex
497 ;       pop     eax
498 ;
499 ;       mov     eax, 0xa
500 ;       push    eax
501 ;       call    SerialPutChar
502 ;       pop     eax
503 ;        
504 ;        add     ecx, 4
505 ;       dec     edx
506 ;       jnz     .loop
507 ;
508 ;
509 ;       popa
510 ;       pop     ebp
511         
512         ; Activate the user context, if necessary.
513         Activate_User_Context
514
515         ; Restore registers
516         Restore_Registers
517
518
519 ;       pusha   
520
521 ;       mov     eax, 0xaa
522 ;       push    eax
523 ;       call    SerialPrintHex
524 ;       pop     eax
525
526 ;       mov     eax, 0xa
527 ;       push    eax
528 ;       call    SerialPutChar
529 ;       pop     eax
530
531 ;       popa
532
533         ; Return from the interrupt.
534         iret
535
536 ; ----------------------------------------------------------------------
537 ; Switch_To_Thread()
538 ;   Save context of currently executing thread, and activate
539 ;   the thread whose context object is passed as a parameter.
540
541 ; Parameter: 
542 ;   - ptr to Kernel_Thread whose state should be restored and made active
543 ;
544 ; Notes:
545 ; Called with interrupts disabled.
546 ; This must be kept up to date with definition of Kernel_Thread
547 ; struct, in kthread.h.
548 ; ----------------------------------------------------------------------
549 align 16
550 Switch_To_Thread:
551         ; Modify the stack to allow a later return via an iret instruction.
552         ; We start with a stack that looks like this:
553         ;
554         ;            thread_ptr
555         ;    esp --> return addr
556         ;
557         ; We change it to look like this:
558         ;
559         ;            thread_ptr
560         ;            eflags
561         ;            cs
562         ;    esp --> return addr
563
564         push    eax             ; save eax
565         mov     eax, [esp+4]    ; get return address
566         mov     [esp-4], eax    ; move return addr down 8 bytes from orig loc
567         add     esp, 8          ; move stack ptr up
568         pushfd                  ; put eflags where return address was
569         mov     eax, [esp-4]    ; restore saved value of eax
570         push    dword KERNEL_CS ; push cs selector
571         sub     esp, 4          ; point stack ptr at return address
572
573         ; Push fake error code and interrupt number
574         push    dword 0
575         push    dword 0
576
577         ; Save general purpose registers.
578         Save_Registers
579
580         ; Save stack pointer in the thread context struct (at offset 0).
581         mov     eax, [g_currentThread]
582         mov     [eax+0], esp
583
584         ; Clear numTicks field in thread context, since this
585         ; thread is being suspended.
586         mov     [eax+4], dword 0
587
588         ; Load the pointer to the new thread context into eax.
589         ; We skip over the Interrupt_State struct on the stack to
590         ; get the parameter.
591         mov     eax, [esp+INTERRUPT_STATE_SIZE]
592
593         ; Make the new thread current, and switch to its stack.
594         mov     [g_currentThread], eax
595         mov     esp, [eax+0]
596
597         ; Activate the user context, if necessary.
598         Activate_User_Context
599
600         ; Restore general purpose and segment registers, and clear interrupt
601         ; number and error code.
602         Restore_Registers
603
604         ; We'll return to the place where the thread was
605         ; executing last.
606         iret
607
608 ; Return current contents of eflags register.
609 align 16
610 Get_Current_EFLAGS:
611         pushfd                  ; push eflags
612         pop     eax             ; pop contents into eax
613         ret
614
615
616
617 ; Return the eip of the next instruction after the caller
618 align 16
619 Get_EIP:
620         mov     eax, [esp]
621         ret
622
623 ; Return the current esp in the procedure
624 align 16
625 Get_ESP:
626         mov     eax, esp
627         ret
628
629 ; Return the current ebp in the procedure
630 align 16
631 Get_EBP:
632         mov     eax, ebp
633         ret
634
635
636
637 ; ----------------------------------------------------------------------
638 ; Generate interrupt-specific entry points for all interrupts.
639 ; We also define symbols to indicate the extend of the table
640 ; of entry points, and the size of individual entry points.
641 ; ----------------------------------------------------------------------
642 align 8
643 g_entryPointTableStart:
644
645 ; Handlers for processor-generated exceptions, as defined by
646 ; Intel 486 manual.
647 Int_No_Err 0
648 align 8
649 Before_No_Err:
650 Int_No_Err 1
651 align 8
652 After_No_Err:
653 Int_No_Err 2    ; FIXME: not described in 486 manual
654 Int_No_Err 3
655 Int_No_Err 4
656 Int_No_Err 5
657 Int_No_Err 6
658 Int_No_Err 7
659 align 8
660 Before_Err:
661 Int_With_Err 8
662 align 8
663 After_Err:
664 Int_No_Err 9    ; FIXME: not described in 486 manual
665 Int_With_Err 10
666 Int_With_Err 11
667 Int_With_Err 12
668 Int_With_Err 13
669 Int_With_Err 14
670 Int_No_Err 15   ; FIXME: not described in 486 manual
671 Int_No_Err 16
672 Int_With_Err 17
673
674 ; The remaining interrupts (18 - 255) do not have error codes.
675 ; We can generate them all in one go with nasm's %rep construct.
676 %assign intNum 18
677 %rep (256 - 18)
678 Int_No_Err intNum
679 %assign intNum intNum+1
680 %endrep
681
682 align 8
683 g_entryPointTableEnd:
684
685 [SECTION .data]
686
687 ; Exported symbols defining the size of handler entry points
688 ; (both with and without error codes).
689 align 4
690 g_handlerSizeNoErr: dd (After_No_Err - Before_No_Err)
691 align 4
692 g_handlerSizeErr: dd (After_Err - Before_Err)