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.


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