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