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.


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