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.


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