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.


1758b8f470459f73016e2d27dfd087a7d61d140e
[palacios.git] / palacios / 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.6 $
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 IMPORT SerialPrintHex
130 IMPORT SerialPutChar
131
132 ; Sizes of interrupt handler entry points for interrupts with
133 ; and without error codes.  The code in idt.c uses this
134 ; information to infer the layout of the table of interrupt
135 ; handler entry points, without needing a separate linker
136 ; symbol for each one (which is quite tedious to type :-)
137 EXPORT g_handlerSizeNoErr
138 EXPORT g_handlerSizeErr
139
140 ; Simple functions to load the IDTR, GDTR, and LDTR.
141 EXPORT Load_IDTR
142 EXPORT Load_GDTR
143 EXPORT Load_LDTR
144
145 ; Beginning and end of the table of interrupt entry points.
146 EXPORT g_entryPointTableStart
147 EXPORT g_entryPointTableEnd
148
149 ; Thread context switch function.
150 EXPORT Switch_To_Thread
151
152 ; Return current value of eflags register.
153 EXPORT Get_Current_EFLAGS
154
155 ; Return the return address
156 EXPORT Get_EIP
157 ; ESP
158 EXPORT Get_ESP
159 ; EBP
160 EXPORT Get_EBP
161
162 ; Virtual memory support.
163 EXPORT Enable_Paging
164 EXPORT Set_PDBR
165 EXPORT Get_PDBR
166 EXPORT Flush_TLB
167
168 ; CPUID functions
169 EXPORT cpuid_ecx
170 EXPORT cpuid_eax
171 EXPORT cpuid_edx
172
173 ; Utility Functions
174 EXPORT Set_MSR
175 EXPORT Get_MSR
176
177
178 EXPORT Get_CR2
179 EXPORT Get_CR3
180
181
182 EXPORT Proc_test
183
184 ; ----------------------------------------------------------------------
185 ; Code
186 ; ----------------------------------------------------------------------
187
188 [SECTION .text]
189
190 ; Load IDTR with 6-byte pointer whose address is passed as
191 ; the parameter.
192 align 8
193 Load_IDTR:
194         mov     eax, [esp+4]
195         lidt    [eax]
196         ret
197
198 ;  Load the GDTR with 6-byte pointer whose address is
199 ; passed as the parameter.  Assumes that interrupts
200 ; are disabled.
201 align 8
202 Load_GDTR:
203         mov     eax, [esp+4]
204         lgdt    [eax]
205         ; Reload segment registers
206         mov     ax, KERNEL_DS
207         mov     ds, ax
208         mov     es, ax
209         mov     fs, ax
210         mov     gs, ax
211         mov     ss, ax
212         jmp     KERNEL_CS:.here
213 .here:
214         ret
215
216 ; Load the LDT whose selector is passed as a parameter.
217 align 8
218 Load_LDTR:
219         mov     eax, [esp+4]
220         lldt    ax
221         ret
222
223 ;
224 ; Start paging
225 ;       load crt3 with the passed page directory pointer
226 ;       enable paging bit in cr2
227 align 8
228 Enable_Paging:
229         push    ebp
230         mov     ebp, esp
231         push    eax
232         push    ebx
233         mov     eax, [ebp+8]
234         mov     cr3, eax
235         mov     eax, cr3
236         mov     cr3, eax
237         mov     ebx, cr0
238         or      ebx, 0x80000000
239         mov     cr0, ebx
240         pop     ebx
241         pop     eax
242         pop     ebp
243         ret
244
245
246
247
248         
249 ;
250 ; Change PDBR 
251 ;       load cr3 with the passed page directory pointer
252 align 8
253 Set_PDBR:
254         mov     eax, [esp+4]
255         mov     cr3, eax
256         ret
257
258 ;
259 ; Get the current PDBR.
260 ; This is useful for lazily switching address spaces;
261 ; only switch if the new thread has a different address space.
262 ;
263 align 8
264 Get_PDBR:
265         mov     eax, cr3
266         ret
267
268 ;
269 ; Flush TLB - just need to re-load cr3 to force this to happen
270 ;
271 align 8
272 Flush_TLB:
273         mov     eax, cr3
274         mov     cr3, eax
275         ret
276
277
278
279 ;
280 ; cpuid_edx - return the edx register from cpuid
281 ;
282 align 8
283 cpuid_edx:
284         push    ebp
285         mov     ebp, esp
286         push    edx
287         push    ecx
288         push    ebx
289
290         mov     eax, [ebp + 8]
291         cpuid
292         mov     eax, edx
293
294         pop     ebx
295         pop     ecx
296         pop     edx
297         pop     ebp
298         ret
299
300
301 ;
302 ; cpuid_ecx - return the ecx register from cpuid
303 ;
304 align 8
305 cpuid_ecx:
306         push    ebp
307         mov     ebp, esp
308         push    edx
309         push    ecx
310         push    ebx
311
312         mov     eax, [ebp + 8]
313         cpuid
314         mov     eax, ecx
315
316         pop     ebx
317         pop     ecx
318         pop     edx
319         pop     ebp
320         ret
321
322 ;
323 ; cpuid_eax - return the eax register from cpuid
324 ;
325 align 8
326 cpuid_eax:
327         push    ebp
328         mov     ebp, esp
329         push    edx
330         push    ecx
331         push    ebx
332
333         mov     eax, [esp+4]
334         cpuid
335
336         pop     ebx
337         pop     ecx
338         pop     edx
339         pop     ebp
340         ret
341
342 ;
343 ; Set_MSR  - Set the value of a given MSR
344 ;
345 align 8
346 Set_MSR:
347         push    ebp
348         mov     ebp, esp
349         pusha
350         mov     eax, [ebp+16]
351         mov     edx, [ebp+12]
352         mov     ecx, [ebp+8]
353         wrmsr
354         popa
355         pop     ebp
356         ret
357
358
359
360 ;
361 ; Get_MSR  -  Get the value of a given MSR
362 ; void Get_MSR(int MSR, void * high_byte, void * low_byte);
363 ;
364 align 8
365 Get_MSR:
366         push    ebp
367         mov     ebp, esp
368         pusha
369         mov     ecx, [ebp+8]
370         rdmsr
371         mov     ebx, [ebp+12]
372         mov     [ebx], edx
373         mov     ebx, [ebp+16]
374         mov     [ebx], eax
375         popa
376         pop     ebp
377         ret
378
379
380
381
382 align 8
383 Get_CR2:
384         mov     eax, cr2
385         ret
386
387
388 align 8
389 Get_CR3:
390         mov     eax, cr3
391         ret
392
393 align 8
394 Proc_test:
395         push    ebp
396         mov     ebp, esp
397         push    ebx
398         mov     ebx, [ebp + 8]
399         mov     eax, [ebp + 12]
400
401         add     eax, ebx
402         pop     ebx
403         pop     ebp
404         ret
405
406
407 ; Common interrupt handling code.
408 ; Save registers, call C handler function,
409 ; possibly choose a new thread to run, restore
410 ; registers, return from the interrupt.
411 align 8
412 Handle_Interrupt:
413         ; Save registers (general purpose and segment)
414         Save_Registers
415
416         ; Ensure that we're using the kernel data segment
417         mov     ax, KERNEL_DS
418         mov     ds, ax
419         mov     es, ax
420
421         ; Get the address of the C handler function from the
422         ; table of handler functions.
423         mov     eax, g_interruptTable   ; get address of handler table
424         mov     esi, [esp+REG_SKIP]     ; get interrupt number
425         mov     ebx, [eax+esi*4]        ; get address of handler function
426
427         ; Call the handler.
428         ; The argument passed is a pointer to an Interrupt_State struct,
429         ; which describes the stack layout for all interrupts.
430         push    esp
431         call    ebx
432         add     esp, 4                  ; clear 1 argument
433
434         ; If preemption is disabled, then the current thread
435         ; keeps running.
436         cmp     [g_preemptionDisabled], dword 0
437         jne     .restore
438
439         ; See if we need to choose a new thread to run.
440         cmp     [g_needReschedule], dword 0
441         je      .restore
442
443         ; Put current thread back on the run queue
444         push    dword [g_currentThread]
445         call    Make_Runnable
446         add     esp, 4                  ; clear 1 argument
447
448         ; Save stack pointer in current thread context, and
449         ; clear numTicks field.
450         mov     eax, [g_currentThread]
451         mov     [eax+0], esp            ; esp field
452         mov     [eax+4], dword 0        ; numTicks field
453
454         ; Pick a new thread to run, and switch to its stack
455         call    Get_Next_Runnable
456         mov     [g_currentThread], eax
457         mov     esp, [eax+0]            ; esp field
458
459         ; Clear "need reschedule" flag
460         mov     [g_needReschedule], dword 0
461
462 .restore:
463
464 ;       push    ebp
465 ;       mov     ebp, esp
466 ;       pusha   
467 ;
468 ;       mov     eax, 0xa
469 ;       push    eax
470 ;       call    SerialPutChar
471 ;       pop     eax
472 ;       mov     eax, 0xa
473 ;       push    eax
474 ;       call    SerialPutChar
475 ;       pop     eax
476 ;
477 ;        mov     ecx, 4
478 ;        mov     edx, 24 
479 ;
480 ;.loop:
481 ;       mov     eax, [ebp + ecx]
482 ;             
483 ;        push    eax
484 ;       call    SerialPrintHex
485 ;       pop     eax
486 ;
487 ;       mov     eax, 0xa
488 ;       push    eax
489 ;       call    SerialPutChar
490 ;       pop     eax
491 ;        
492 ;        add     ecx, 4
493 ;       dec     edx
494 ;       jnz     .loop
495 ;
496 ;
497 ;       popa
498 ;       pop     ebp
499         
500         ; Activate the user context, if necessary.
501         Activate_User_Context
502
503         ; Restore registers
504         Restore_Registers
505
506         ; Return from the interrupt.
507         iret
508
509 ; ----------------------------------------------------------------------
510 ; Switch_To_Thread()
511 ;   Save context of currently executing thread, and activate
512 ;   the thread whose context object is passed as a parameter.
513
514 ; Parameter: 
515 ;   - ptr to Kernel_Thread whose state should be restored and made active
516 ;
517 ; Notes:
518 ; Called with interrupts disabled.
519 ; This must be kept up to date with definition of Kernel_Thread
520 ; struct, in kthread.h.
521 ; ----------------------------------------------------------------------
522 align 16
523 Switch_To_Thread:
524         ; Modify the stack to allow a later return via an iret instruction.
525         ; We start with a stack that looks like this:
526         ;
527         ;            thread_ptr
528         ;    esp --> return addr
529         ;
530         ; We change it to look like this:
531         ;
532         ;            thread_ptr
533         ;            eflags
534         ;            cs
535         ;    esp --> return addr
536
537         push    eax             ; save eax
538         mov     eax, [esp+4]    ; get return address
539         mov     [esp-4], eax    ; move return addr down 8 bytes from orig loc
540         add     esp, 8          ; move stack ptr up
541         pushfd                  ; put eflags where return address was
542         mov     eax, [esp-4]    ; restore saved value of eax
543         push    dword KERNEL_CS ; push cs selector
544         sub     esp, 4          ; point stack ptr at return address
545
546         ; Push fake error code and interrupt number
547         push    dword 0
548         push    dword 0
549
550         ; Save general purpose registers.
551         Save_Registers
552
553         ; Save stack pointer in the thread context struct (at offset 0).
554         mov     eax, [g_currentThread]
555         mov     [eax+0], esp
556
557         ; Clear numTicks field in thread context, since this
558         ; thread is being suspended.
559         mov     [eax+4], dword 0
560
561         ; Load the pointer to the new thread context into eax.
562         ; We skip over the Interrupt_State struct on the stack to
563         ; get the parameter.
564         mov     eax, [esp+INTERRUPT_STATE_SIZE]
565
566         ; Make the new thread current, and switch to its stack.
567         mov     [g_currentThread], eax
568         mov     esp, [eax+0]
569
570         ; Activate the user context, if necessary.
571         Activate_User_Context
572
573         ; Restore general purpose and segment registers, and clear interrupt
574         ; number and error code.
575         Restore_Registers
576
577         ; We'll return to the place where the thread was
578         ; executing last.
579         iret
580
581 ; Return current contents of eflags register.
582 align 16
583 Get_Current_EFLAGS:
584         pushfd                  ; push eflags
585         pop     eax             ; pop contents into eax
586         ret
587
588
589
590 ; Return the eip of the next instruction after the caller
591 align 16
592 Get_EIP:
593         mov     eax, [esp]
594         ret
595
596 ; Return the current esp in the procedure
597 align 16
598 Get_ESP:
599         mov     eax, esp
600         ret
601
602 ; Return the current ebp in the procedure
603 align 16
604 Get_EBP:
605         mov     eax, ebp
606         ret
607
608
609
610 ; ----------------------------------------------------------------------
611 ; Generate interrupt-specific entry points for all interrupts.
612 ; We also define symbols to indicate the extend of the table
613 ; of entry points, and the size of individual entry points.
614 ; ----------------------------------------------------------------------
615 align 8
616 g_entryPointTableStart:
617
618 ; Handlers for processor-generated exceptions, as defined by
619 ; Intel 486 manual.
620 Int_No_Err 0
621 align 8
622 Before_No_Err:
623 Int_No_Err 1
624 align 8
625 After_No_Err:
626 Int_No_Err 2    ; FIXME: not described in 486 manual
627 Int_No_Err 3
628 Int_No_Err 4
629 Int_No_Err 5
630 Int_No_Err 6
631 Int_No_Err 7
632 align 8
633 Before_Err:
634 Int_With_Err 8
635 align 8
636 After_Err:
637 Int_No_Err 9    ; FIXME: not described in 486 manual
638 Int_With_Err 10
639 Int_With_Err 11
640 Int_With_Err 12
641 Int_With_Err 13
642 Int_With_Err 14
643 Int_No_Err 15   ; FIXME: not described in 486 manual
644 Int_No_Err 16
645 Int_With_Err 17
646
647 ; The remaining interrupts (18 - 255) do not have error codes.
648 ; We can generate them all in one go with nasm's %rep construct.
649 %assign intNum 18
650 %rep (256 - 18)
651 Int_No_Err intNum
652 %assign intNum intNum+1
653 %endrep
654
655 align 8
656 g_entryPointTableEnd:
657
658 [SECTION .data]
659
660 ; Exported symbols defining the size of handler entry points
661 ; (both with and without error codes).
662 align 4
663 g_handlerSizeNoErr: dd (After_No_Err - Before_No_Err)
664 align 4
665 g_handlerSizeErr: dd (After_Err - Before_Err)