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.


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