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.


initial SVM guest running
[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.3 $
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         push    ecx
283         push    ebx
284
285         mov     eax, [ebp + 8]
286         cpuid
287         mov     eax, edx
288
289         pop     ebx
290         pop     ecx
291         pop     edx
292         pop     ebp
293         ret
294
295
296 ;
297 ; cpuid_ecx - return the ecx register from cpuid
298 ;
299 align 8
300 cpuid_ecx:
301         push    ebp
302         mov     ebp, esp
303         push    edx
304         push    ecx
305         push    ebx
306
307         mov     eax, [ebp + 8]
308         cpuid
309         mov     eax, ecx
310
311         pop     ebx
312         pop     ecx
313         pop     edx
314         pop     ebp
315         ret
316
317 ;
318 ; cpuid_eax - return the eax register from cpuid
319 ;
320 align 8
321 cpuid_eax:
322         push    ebp
323         mov     ebp, esp
324         push    edx
325         push    ecx
326         push    ebx
327
328         mov     eax, [esp+4]
329         cpuid
330
331         pop     ebx
332         pop     ecx
333         pop     edx
334         pop     ebp
335         ret
336
337 ;
338 ; Set_MSR  - Set the value of a given MSR
339 ;
340 align 8
341 Set_MSR:
342         push    ebp
343         mov     ebp, esp
344         pusha
345         mov     eax, [ebp+16]
346         mov     edx, [ebp+12]
347         mov     ecx, [ebp+8]
348         wrmsr
349         popa
350         pop     ebp
351         ret
352
353
354
355 ;
356 ; Get_MSR  -  Get the value of a given MSR
357 ; void Get_MSR(int MSR, void * high_byte, void * low_byte);
358 ;
359 align 8
360 Get_MSR:
361         push    ebp
362         mov     ebp, esp
363         pusha
364         mov     ecx, [ebp+8]
365         rdmsr
366         mov     ebx, [ebp+12]
367         mov     [ebx], edx
368         mov     ebx, [ebp+16]
369         mov     [ebx], eax
370         popa
371         pop     ebp
372         ret
373
374
375
376 align 8
377 Get_CR2:
378         mov     eax, cr2
379         ret
380
381
382 align 8
383 Proc_test:
384         push    ebp
385         mov     ebp, esp
386         push    ebx
387         mov     ebx, [ebp + 8]
388         mov     eax, [ebp + 12]
389
390         add     eax, ebx
391         pop     ebx
392         pop     ebp
393         ret
394
395
396 ; Common interrupt handling code.
397 ; Save registers, call C handler function,
398 ; possibly choose a new thread to run, restore
399 ; registers, return from the interrupt.
400 align 8
401 Handle_Interrupt:
402         ; Save registers (general purpose and segment)
403         Save_Registers
404
405         ; Ensure that we're using the kernel data segment
406         mov     ax, KERNEL_DS
407         mov     ds, ax
408         mov     es, ax
409
410         ; Get the address of the C handler function from the
411         ; table of handler functions.
412         mov     eax, g_interruptTable   ; get address of handler table
413         mov     esi, [esp+REG_SKIP]     ; get interrupt number
414         mov     ebx, [eax+esi*4]        ; get address of handler function
415
416         ; Call the handler.
417         ; The argument passed is a pointer to an Interrupt_State struct,
418         ; which describes the stack layout for all interrupts.
419         push    esp
420         call    ebx
421         add     esp, 4                  ; clear 1 argument
422
423         ; If preemption is disabled, then the current thread
424         ; keeps running.
425         cmp     [g_preemptionDisabled], dword 0
426         jne     .restore
427
428         ; See if we need to choose a new thread to run.
429         cmp     [g_needReschedule], dword 0
430         je      .restore
431
432         ; Put current thread back on the run queue
433         push    dword [g_currentThread]
434         call    Make_Runnable
435         add     esp, 4                  ; clear 1 argument
436
437         ; Save stack pointer in current thread context, and
438         ; clear numTicks field.
439         mov     eax, [g_currentThread]
440         mov     [eax+0], esp            ; esp field
441         mov     [eax+4], dword 0        ; numTicks field
442
443         ; Pick a new thread to run, and switch to its stack
444         call    Get_Next_Runnable
445         mov     [g_currentThread], eax
446         mov     esp, [eax+0]            ; esp field
447
448         ; Clear "need reschedule" flag
449         mov     [g_needReschedule], dword 0
450
451 .restore:
452         ; Activate the user context, if necessary.
453         Activate_User_Context
454
455         ; Restore registers
456         Restore_Registers
457
458         ; Return from the interrupt.
459         iret
460
461 ; ----------------------------------------------------------------------
462 ; Switch_To_Thread()
463 ;   Save context of currently executing thread, and activate
464 ;   the thread whose context object is passed as a parameter.
465
466 ; Parameter: 
467 ;   - ptr to Kernel_Thread whose state should be restored and made active
468 ;
469 ; Notes:
470 ; Called with interrupts disabled.
471 ; This must be kept up to date with definition of Kernel_Thread
472 ; struct, in kthread.h.
473 ; ----------------------------------------------------------------------
474 align 16
475 Switch_To_Thread:
476         ; Modify the stack to allow a later return via an iret instruction.
477         ; We start with a stack that looks like this:
478         ;
479         ;            thread_ptr
480         ;    esp --> return addr
481         ;
482         ; We change it to look like this:
483         ;
484         ;            thread_ptr
485         ;            eflags
486         ;            cs
487         ;    esp --> return addr
488
489         push    eax             ; save eax
490         mov     eax, [esp+4]    ; get return address
491         mov     [esp-4], eax    ; move return addr down 8 bytes from orig loc
492         add     esp, 8          ; move stack ptr up
493         pushfd                  ; put eflags where return address was
494         mov     eax, [esp-4]    ; restore saved value of eax
495         push    dword KERNEL_CS ; push cs selector
496         sub     esp, 4          ; point stack ptr at return address
497
498         ; Push fake error code and interrupt number
499         push    dword 0
500         push    dword 0
501
502         ; Save general purpose registers.
503         Save_Registers
504
505         ; Save stack pointer in the thread context struct (at offset 0).
506         mov     eax, [g_currentThread]
507         mov     [eax+0], esp
508
509         ; Clear numTicks field in thread context, since this
510         ; thread is being suspended.
511         mov     [eax+4], dword 0
512
513         ; Load the pointer to the new thread context into eax.
514         ; We skip over the Interrupt_State struct on the stack to
515         ; get the parameter.
516         mov     eax, [esp+INTERRUPT_STATE_SIZE]
517
518         ; Make the new thread current, and switch to its stack.
519         mov     [g_currentThread], eax
520         mov     esp, [eax+0]
521
522         ; Activate the user context, if necessary.
523         Activate_User_Context
524
525         ; Restore general purpose and segment registers, and clear interrupt
526         ; number and error code.
527         Restore_Registers
528
529         ; We'll return to the place where the thread was
530         ; executing last.
531         iret
532
533 ; Return current contents of eflags register.
534 align 16
535 Get_Current_EFLAGS:
536         pushfd                  ; push eflags
537         pop     eax             ; pop contents into eax
538         ret
539
540
541
542 ; Return the eip of the next instruction after the caller
543 align 16
544 Get_EIP:
545         mov     eax, [esp]
546         ret
547
548 ; Return the current esp in the procedure
549 align 16
550 Get_ESP:
551         mov     eax, esp
552         ret
553
554 ; Return the current ebp in the procedure
555 align 16
556 Get_EBP:
557         mov     eax, ebp
558         ret
559
560
561
562 ; ----------------------------------------------------------------------
563 ; Generate interrupt-specific entry points for all interrupts.
564 ; We also define symbols to indicate the extend of the table
565 ; of entry points, and the size of individual entry points.
566 ; ----------------------------------------------------------------------
567 align 8
568 g_entryPointTableStart:
569
570 ; Handlers for processor-generated exceptions, as defined by
571 ; Intel 486 manual.
572 Int_No_Err 0
573 align 8
574 Before_No_Err:
575 Int_No_Err 1
576 align 8
577 After_No_Err:
578 Int_No_Err 2    ; FIXME: not described in 486 manual
579 Int_No_Err 3
580 Int_No_Err 4
581 Int_No_Err 5
582 Int_No_Err 6
583 Int_No_Err 7
584 align 8
585 Before_Err:
586 Int_With_Err 8
587 align 8
588 After_Err:
589 Int_No_Err 9    ; FIXME: not described in 486 manual
590 Int_With_Err 10
591 Int_With_Err 11
592 Int_With_Err 12
593 Int_With_Err 13
594 Int_With_Err 14
595 Int_No_Err 15   ; FIXME: not described in 486 manual
596 Int_No_Err 16
597 Int_With_Err 17
598
599 ; The remaining interrupts (18 - 255) do not have error codes.
600 ; We can generate them all in one go with nasm's %rep construct.
601 %assign intNum 18
602 %rep (256 - 18)
603 Int_No_Err intNum
604 %assign intNum intNum+1
605 %endrep
606
607 align 8
608 g_entryPointTableEnd:
609
610 [SECTION .data]
611
612 ; Exported symbols defining the size of handler entry points
613 ; (both with and without error codes).
614 align 4
615 g_handlerSizeNoErr: dd (After_No_Err - Before_No_Err)
616 align 4
617 g_handlerSizeErr: dd (After_Err - Before_Err)