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.


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