Many changes:
[Marmot.git] / loader.S
blob18be70ecc5d11dacdc72baae39b5d925260aff93
1         /*
2          * loader.S --
3          *
4          *      OS loader for marmot.  Tasks done here (in no particular
5          *      order):
6          *
7          *         set up protected mode data structures
8          *         build memory map
9          *         set up initial page tables
10          *         switch to 64 bit mode
11          *         set up timer devices
12          *         install disk drivers
13          *
14          */
16 #define ASM
17 #include <marmot.h>
19         .section .loader, "xa"
20         .code16
22         .global Loader
23 Loader:
24         call    ZeroBSS
26         call    MapE820
27         call    GetVESAInfo
28         .global HaveVESA
29 HaveVESA:
30         /* VESA error message is already in %si */
31         jc      RealModeFlamingDeath
33         /* enter protected mode */
34         call    EnableA20
35         cli
36         call    MaskNMI16
38         call    LoadGDT
40         movl    %cr0, %eax
41         orb     $1, %al         /* CR0.PE => 1 */
42         movl    %eax, %cr0
44         .code32
45         .byte   0x66            /* technically still in 16 bit mode */
46         ljmp    $CS32, $IntoProtectedMode
47         .code16
50         /****** Real mode subroutines ******/
52         /*
53          * RealModeFlamingDeath --
54          *
55          *      Print an error message and reboot.
56          *
57          */
59         .global RealModeFlamingDeath
60 RealModeFlamingDeath:
61         mov     $0x0e, %ah
62         mov     $0x0100, %bx
63 1:      lodsb
64         test    %al, %al
65         jz      1f
66         int     $0x10
67         jmp     1b
69         /* wait for keypress, then reboot */
70 1:      xorw    %ax, %ax
71         int     $0x16
72         int     $0x19
74         /* should never be reached */
75         cli
76         hlt
79         /*
80          * ZeroBSS --
81          *
82          *      Zero out all bytes in .bss.
83          */
84 ZeroBSS:
85         xorw    %ax, %ax
86         movw    %ax, %es
87         movl    bss_start, %edi
88         movl    bss_end, %ecx
89         subl    %edi, %ecx
90         cld
91         repnz stosb
92         ret
95         /*
96          * MapE820 --
97          *
98          *      Get the memory map from the BIOS using int 15,e820.
99          *
100          */
102 MapE820:
103         xorl    %ebx, %ebx
104         movl    %ebx, e820_entries
105         mov     %bx, %es
106         movw    $e820_map, %di
108 map_loop:
109         movl    $0xe820, %eax
110         movl    $20, %ecx
111         movl    $SMAP, %edx
112         int     $0x15
113         jc      1f
115         test    %ebx, %ebx
116         jz      1f
118         addw    $20, %di
119         incl    e820_entries
121         cmpl    $SMAP, %eax
122         je      map_loop
124         /* no "SMAP"  */
125         mov     $bad_e820, %si
126         pushw   $RealModeFlamingDeath
128 1:      ret
131         /*
132          * EnableA20 --
133          *
134          */
136 EnableA20:
137         /* Just do this the old fashioned way, via the kbd controller. */
138         call    kbd8042_ready
139         movb    $0xd1, %al
140         outb    %al, $0x64
141         call    kbd8042_ready
142         movb    $0xdf, %al
143         outb    %al, $0x60
144         call    kbd8042_ready
145         ret
147 kbd8042_ready:
148         inb     $0x64, %al
149         test    $2, %al
150         jnz     kbd8042_ready
151         ret
154         /*
155          * MaskNMI16 --
156          *
157          *      16 bit version of MaskNMI.
158          *
159          */
161 MaskNMI16:
162         pushw   %ax
163         inb     $0x70, %al
164         andb    $0x7f, %al
165         outb    %al, $0x70
166         inb     $0x71, %al
167         popw    %ax
168         ret
171         /*
172          * LoadGDT --
173          *
174          *      Move the GDT into its place in memory and issue lgdt.
175          */
177 LoadGDT:
178         movw    $GDTStart, %si
179         movw    $GDT, %di
180         movw    $(GDTEnd - GDTStart), %cx
181         rep movsb
182         movl    $GDT, DTLocation
183         movw    $(GDTEnd - GDTStart), DTSize
184         lgdt    DTSize
185         ret
189         /****** Protected mode code ******/
191         .code32
192         .global IntoProtectedMode
193 IntoProtectedMode:
194         
195         movl    $DS, %eax
196         movw    %ax, %ds
197         movw    %ax, %es
198         movw    %ax, %fs
199         movw    %ax, %gs        
200         movw    %ax, %ss
201         movl    $STACKTOP, %eax
202         movl    %eax, %esp
204         xorl    %eax, %eax
205         pushl   %eax
206         popf
208         /* Remap IRQs and set up IDT */
209         call    LoadIDT
210 #        .global IDTDone
211 #IDTDone:        
212         call    RemapIRQs
214         /* Get CPUID info and verify 64 bit support */
215         call    Check64_OK
216         jc      1f
218         /* no 64 bit mode? bail */
219         # XXX
220         cli
221         hlt
224         /* Look for other processors (startup IPI will be sent later) */
225 1:      call    FindMPConfig
226         .global HaveMPConfig
227 HaveMPConfig:
229         /* Set up initial page tables and enter long mode */
231         call    InitialPageSetup
232         .global PageDone
233 PageDone:
235         /* turn PAE on */
236         xorl    %eax, %eax
237         orl     $(1 << 5), %eax
238         movl    %eax, %cr4
240         /* load PML4 */
241         movl    $PML4BASE, %eax
242         movl    %eax, %cr3
244         /* set EFER.LME */
245         movl    $EFER, %ecx
246         rdmsr
247         btsl    $EFER_LME, %eax
248         btsl    $EFER_NXE, %eax
249         wrmsr
251         /* turn on paging and move into long mode */
252         movl    %cr0, %eax
253         orl     $0x80000000, %eax
254         movl    %eax, %cr0
256         /* jump to 64 bit CS */
257         pushl   $CS64
258         pushl   $IntoLongMode
259         lret
262         /*
263          * LoadIDT --
264          *
265          *      Build the IDT and issue lidt.
266          *
267          */
269 LoadIDT:
270         /* first zero out the entire 64 bit IDT area */
271         movl    $IDT, %edi
272         movl    $((GDT - IDT) >> 2), %ecx
273 1:      movl    $0, -4(%edi,%ecx,4)
274         loop    1b
275         
276         
277         /* make a trap gate for #0-19d  */
278         movl    $20, %ecx
279         movl    $(IDT + 20 * 16), %edi  /* offset into IDT */
280 TrapLoop:
281         movl    (IDTJumpTable-4)(,%ecx,4), %edx
282         /* low dword */
283         movl    $(CS64 << 16), %eax
284         movw    %dx, %ax
285         movl    %eax, -16(%edi)
286         /* high dword */
287         movw    $0x8f02, %dx
288         movl    %edx, -12(%edi)
289         /* extended low */
290         xorl    %eax, %eax
291         movl    %eax, -8(%edi)
292         /* extended high */
293         movl    %eax, -4(%edi)
295         subl    $16, %edi
296         loop    TrapLoop
297         
298         /* NMI (2), DF (8), and MC (18) use IST 3 instead of 2 */
299         orb     $1, (16 * 2 + 4)(%edi)
300         orb     $1, (16 * 8 + 4)(%edi)
301         orb     $1, (16 * 18 + 4)(%edi)
303         /* load the new IDT */
305         movl    $IDT, DTLocation
306         movw    $(GDT - IDT), DTSize
307         lidt    DTSize
309         ret
312         /*
313          * RemapIRQs --
314          *
315          *      Remap IRQs to start at 0x20, masking all 16.  Gate
316          *      installation and IRQ unmasking is done later in irq.c.
317          */
319 RemapIRQs:
320         /* ICW1 */
321         movb    $0x11, %al      /* need ICW4 */
322         outb    %al, $0x20
323         outb    %al, $0xa0
324         /* ICW2 */
325         movb    $0x20, %al
326         outb    %al, $0x21      /* map master to 0x20-0x27 */
327         orb     $8, %al
328         outb    %al, $0xa1      /* map slave to 0x28-0x2f */
329         /* ICW3 */
330         movb    $4, %al
331         outb    %al, $0x21      /* master IRQ2 cascades to slave */
332         movb    $2, %al
333         outb    %al, $0xa1      /* slave to IRQ2 */
334         /* ICW4 */
335         movb    $1, %al         /* 80x86 mode */
336         outb    %al, $0x21
337         outb    %al, $0xa1
338         /* done - mask all interrupts */
340         movb    $0xff, %al
341         outb    %al, $0x21
342         outb    %al, $0xa1
344         ret
347 InitialPageSetup:
348         movl    $PML4BASE, %esi
349 1:      movl    $0, (%esi)
350         addl    $4, %esi
351         cmpl    $PAGETOP, %esi
352         jb      1b
354         xorl    %ecx, %ecx      /* ecx is the address of the current page */
355         xorl    %edx, %edx      /* edx is the top 4 bytes of table entries */
357         /* For the address in ecx:
358          *     bits 47-39 - index into pml4 table (always 0 here)
359          *     bits 38-30 - index into pdp table (always 0 here)
360          *     bits 29-21 - index into pd table
361          *     bits 20-12 - index into page table
362          */
364         /* pml4e */
366         movl    $(PDPTBASE | 3), %eax   /* 3 == S/W/P */
367         movl    %eax, PML4BASE
368         movl    %edx, (PML4BASE + 4)
370         /* pdpe */
372         movl    $(PDBASE | 3), %eax     /* 3 == S/W/P */
373         movl    %eax, PDPTBASE
374         movl    %edx, (PDPTBASE + 4)
376 PageLoop:
377         /* pde */
379         /* ebp is the byte index into the pd table */
380         movl    %ecx, %ebp
381         shrl    $(21 - 3), %ebp
382         andl    $(0x1ff << 3), %ebp
383         addl    $PDBASE, %ebp
385         movl    $(PTBASE | 3), %eax
386         movl    %eax, (%ebp)
387         movl    %edx, 4(%ebp)
389         /* pte */
391         /* ebp is the byte index into the page table */
392         movl    %ecx, %ebp
393         shrl    $(12 - 3), %ebp
394         andl    $(0x1ff << 3), %ebp
395         addl    $PTBASE, %ebp
397         /* pages here are identity mapped */
398         movl    %ecx, %eax
399         andl    $0xfffff000, %eax
400         orl     $3, %eax
401         movl    %eax, (%ebp)
402         movl    %edx, 4(%ebp)
405         /* increment to next page */
406         addl    $4096, %ecx
407         cmpl    e820_map + 8, %ecx
408         jb      PageLoop
410         ret
413         /*
414          * FindMPConfig --
415          *
416          *      Search various locations for the MP floating pointer structure.
417          *
418          *      During later ACPI initialization, if an MADT is found, that
419          *      information overrides what is found here.
420          */
422 FindMPConfig:
423         /* According to MP spec, the places to look are:
424          *
425          *    - First kB of EBDA (address can be found at 40:0e)
426          *    - Last kB of system base memory (basemem top in CMOS)
427          *    - In BIOS ro space between e0000 and fffff.
428          */
430         /* EBDA */
431         xorl    %ecx, %ecx
432         movw    0x40e, %cx     /* segment of EBDA */
433         testl   %ecx, %ecx
434         jz      NoEBDA
435         shll    $4, %ecx
436         movl    (%ecx), %eax
437         testw   %ax, %ax        /* ax = number of kB in EBDA */
438         jz      NoEBDA
440         movl    %ecx, %edx
441         addl    $1024, %edx
443         call    FindMPFP
444         testl   %eax, %eax
445         jnz     StoreMPFPtr
447         /* Last kB of system base memory */
448 NoEBDA:
449         call    GetBaseMemCMOS
450         /*
451          * VMW BIOS differs in mem amount between CMOS and BDA - use CMOS
452          * here, even though the value in the BDA is correct and the value
453          * in CMOS RAM is not.  The reasoning is that on VMW, the MP
454          * structure isn't located there anyways, Linux uses the
455          * value from the CMOS as well, and it seems to work, and the the
456          * ACPI tables will very probably be used instead, so it's not too
457          * big an issue.
458          *
459          * XXX: Need to check how real hardware behaves and adjust
460          * accordingly (and file a PR if appropriate).
461          */
462         #xorl    %edx, %edx
463         #movw    0x413, %dx
464         #shll    $10, %edx
465         movl    %edx, %ecx
466         subl    $1024, %ecx
467         call    FindMPFP
468         testl   %eax, %eax
469         jnz     StoreMPFPtr
471         /* Linux also searches the first kB of memory - try that, too */
472         xorl    %ecx, %ecx
473         movl    $1024, %edx
474         call    FindMPFP
475         testl   %eax, %eax
476         jnz     StoreMPFPtr
478         /* BIOS ro space */
480         movl    $0xe0000, %ecx
481         movl    $0x100000, %edx
482         call    FindMPFP
483         testl   %eax, %eax
484         jnz     StoreMPFPtr
486         /* MP block not found */
487 NoBIOS: xorl    %eax, %eax
488 StoreMPFPtr:
489         movl    %eax, MPFloatingPointer
490         ret
493 GetBaseMemCMOS:
494         /* bytes 15h | 16h << 8 */
495         xorl    %eax, %eax
496         movb    $0x16, %al
497         outb    %al, $0x70
498         inb     $0x71, %al
499         shll    $8, %eax
501         movb    $0x15, %al
502         outb    %al, $0x70
503         inb     $0x71, %al
505         shll    $10, %eax
506         movl    %eax, %edx
507         ret
509         /*
510          * FindMPFP --
511          *
512          *      Look through the address range ecx-edx to find the MP
513          *      table.  If it is found, return its address in eax,
514          *      otherwise return 0 in eax.
515          *
516          */
518 FindMPFP:
519         xorl    %eax, %eax
520         movl    $MP_MAGIC, %ebx
521 1:      cmpl    %ebx, (%ecx)
522         jne     2f
524         /* have a match */
525         movl    %ecx, %eax
526         ret
528 2:      addl    $16, %ecx
529         cmpl    %edx, %ecx
530         jb      1b
532         /* no match */
533         ret
536         /*
537          * Check64_OK --
538          *
539          *      Check if the processor supports long mode.  Return with
540          *      carry clear if it does not, carry set otherwise.
541          */
542 Check64_OK:
543         /* check number of extended functions */
544         movl    $0x80000000, %eax
545         cpuid
546         cmpl    $0x80000000, %eax
547         ja      1f
549         clc
550         ret
552 1:      movl    $0x80000001, %eax
553         cpuid
554         bt      $29, %edx
555         ret
558         /****** long mode code ******/
560         .code64
561         .global IntoLongMode
562 IntoLongMode:
563         xorq    %rbp, %rbp      /* will be using frame pointer now */
565         call    MapMemory
567         /* Fill in the TSS now that stacks can be allocated. */
568         call    PopulateTSS
569         movl    $TS, %eax
570         ltr     %ax
572         call    UnmaskNMI
573         sti
575         call    AllocInit
576         call    VideoInit
578         call    CPUFeatures
579         call    MapPCI
580         //call    InitACPI
581         call    InitAPIC
583         /* Start devices */
584         call    StartPIT
585         call    StartClock
586         call    KeyboardInit
587         call    MouseInit
589         call    CursorInit      /* have to wait for mouse irq installation */
591         /* say hello */
592         movq    $0xff000000, %rdi
593         xorl    %esi, %esi
594         addl    $4, %esi
595         movl    %esi, %edx
596         movl    $HelloMessage, %ecx
597         call    PrintMessage
599 #        call    ForthInit
601         .global here
602 here:   nop
603 1:      hlt
604         jmp 1b
607         /*
608          * MaskNMI --
609          * UnmaskNMI --
610          *
611          *      Mask/unmask NMI using external hardware.
612          *
613          */
615 MaskNMI:
616         inb     $0x70, %al
617         andb    $0x7f, %al
618         outb    %al, $0x70
619         inb     $0x71, %al
620         ret
622 UnmaskNMI:
623         inb     $0x70, %al
624         orb     $0x80, %al
625         outb    %al, $0x70
626         inb     $0x71, %al
627         ret
630         /*
631          * PopulateTSS --
632          *
633          *      Populate the task state segment with entries for the three
634          *      IDT-related stacks and with the entries for the IO bitmask.
635          */
637         .global PopulateTSS
638 PopulateTSS:
639         pushq   %rbp
640         movq    %rsp, %rbp
642         /* First zero all bytes */
643         movl    $tss_start, %edi
644         movl    $(tss_end - tss_start), %esi
645         call    bzero
647         /* Set IO bitmask to all 1s (for now) */
649         movb    $-1, %al
650         movsbq  %al, %rax
651         movq    %rax, tss_iomap(%rip)
652         movq    %rax, tss_iomap+8(%rip)
653         movq    %rax, tss_iomap+16(%rip)
654         movq    %rax, tss_iomap+24(%rip)
655         movb    %al, tss_end - 1
657         /* IO map */
658         movl    $(tss_iomap - tss_start), %eax
659         movw    %ax, tss_iomap_base
661         /*
662          * Allocate stacks for the ISTs.
663          *
664          * The order here needs to be from low address to high address
665          * (IRQ => CRITICAL => FAULT).  This takes advantage of the
666          * adjacency of the stacks' virtual addresses and should save
667          * a few bytes.
668          */
670         movq    $IRQ_STACK_BOTTOM, %rbx
671         leaq    (IRQ_STACK_TOP - IRQ_STACK_BOTTOM)(%rbx), %rdi
672         call    TSSStackAlloc
673         movq    %rbx, tss_ist1          /* top of IRQ stack */
675         leaq    (CRITICAL_STACK_TOP - CRITICAL_STACK_BOTTOM)(%rbx), %rdi
676         call    TSSStackAlloc
677         movq    %rbx, tss_ist2          /* top of critical stack */
679         leaq    (FAULT_STACK_TOP - FAULT_STACK_BOTTOM)(%rbx), %rdi
680         call    TSSStackAlloc
681         movq    %rbx, tss_ist3          /* top of fault stack */
683         leave
684         ret
687         /*
688          * TSSStackAlloc --
689          *
690          *      Stack allocation subroutine.
691          *
692          *      Call with %rbx = bottom, %rdi = top.
693          *      Return with %rax = %rbx = top
694          */
696 TSSStackAlloc:
697         pushq   %rbp
698         movq    %rsp, %rbp
700         /* %rbx should be the limit and %rdi the base. */
701         xchgq   %rbx, %rdi
703 1:      movq    $0x8000000000000002, %rsi       /* NX|RW */
704         call    PageAlloc
705         leaq    PAGE_SIZE(%rax), %rdi
706         cmpq    %rbx, %rax
707         jne     1b
709         leave
710         ret
713         /*
714          * CPUFeatures --
715          *
716          *      Determine CPU features and install pointer to vendor-specific
717          *      TestCPUFeature routine.
718          *
719          * XXX: should use alloc() insteald of PageAlloc()
720          */
721 CPUFeatures:
722         pushq   %rbp
723         movq    %rsp, %rbp
725         /* alloc a kernel page for this info */
726         movq    $MM_VA_KERNEL_HEAP, %rdi
727         movq    $2, %rsi        /* RW */
728         call    PageAlloc
729         movq    %rax, %r9       /* %r9 is base of structure */
730         leaq    24(%rax), %r10  /* %r10 is index into feature array */
731         movq    %rax, CPUIDInfo
733         /* data structure is:
734          *
735          * struct {
736          *    uint64  nStandard;
737          *    uint64  nHypervisor;
738          *    uint64  nExtended;
739          *    struct {
740          *       UReg eax;
741          *       UReg ebx;
742          *       UReg ecx;
743          *       UReg edx;
744          *    } features[];
745          * };
746          */
748         xorl    %eax, %eax      /* function 0 */
749         call    DoCPUID
750         addq    $8, %r9         /* point to next (hv) count */
751         movq    $0, (%r9)       /* zero hv count */
753         /* Bit 31 of %ecx of CPUID leaf 0x1 is the hypervisor present bit. */
754         btl     $31, (16 + 16 + 8)(%r9)
755         jnc     1f              /* no hypervisor present */
757         movl    $0x40000000, %eax
758         call    DoCPUID
760 1:      addq    $8, %r9
761         movl    $0x80000000, %eax
762         call    DoCPUID
764         leave
765         ret
767         /*
768          * DoCPUID --
769          *
770          *      Loop over base, hypervisor, or extended CPUID functions.
771          *
772          *      Call with %r9 pointing to count entry, %r10 pointing to
773          *      cpuid array.
774          *
775          *      Adjusts %r10, uses %rax, %rbx, %rcx, %rdx, %r11, and %r12.
776          *
777          */
778 DoCPUID:
779         pushq   %rbp
780         movq    %rsp, %rbp
782         /* %r9 points to count, %r10 is current cpuid entry, %r11 is
783          * limit, %r12 is current function.
784          */
785         movq    %rax, %r12
787         cpuid
788         movq    %rax, %r11      /* Store max function */
789         movq    %rax, (%r9)
790         incq    (%r9)
791         movb    $0, 3(%r9)      /* zero out high byte (0, 4, 8) */
793 1:      cmpq    %r11, %r12
794         ja      1f
796         movl    %eax, (%r10)
797         movl    %ebx, 4(%r10)
798         movl    %ecx, 8(%r10)
799         movl    %edx, 12(%r10)
801         addq    $16, %r10
803         incq    %r12
804         movq    %r12, %rax
805         cpuid
806         jmp     1b
808 1:      leave
809         ret
812         /****** Data ******/
814         .section .data
815         .align  1
816 bad_e820:
817         .asciz  "E820 memory map failed!"
818 HelloMessage:
819         .asciz  "Welcome to Marmot!"
821         
822         .align  8
823         /* tval and tval2 are generic data holders for vprobe GUEST:test */
824         .global tval
825 tval:   .quad   0
826         .global tval2
827 tval2:  .quad   0
830 GDTStart:
831         .quad   0
832         .quad   0x00df9a000000ffff      /* CS32 */
833         .quad   0x00af9a000000ffff      /* CS64 */
834         .quad   0x00cf92000000ffff      /* DS */
835         .quad   0x008089007f000089      /* TS descriptor */
836         .quad   0x0000000000000000      /* TS continued */
837         .quad   0x0000000000000000      /* PRIV CS */
838         .quad   0x0000000000000000      /* PRIV DS */
839         .quad   0x0000000000000000      /* USER CS */
840         .quad   0x0000000000000000      /* USER DS */
841 GDTEnd:
842         
844 IDTJumpTable:
845         .long   DE64
846         .long   DB64
847         .long   NMI64
848         .long   BP64
849         .long   OF64
850         .long   BR64
851         .long   UD64
852         .long   NM64
853         .long   DF64
854         .long   0
855         .long   TS64
856         .long   NP64
857         .long   SS64
858         .long   GP64
859         .long   PF64
860         .long   0
861         .long   MF64
862         .long   AC64
863         .long   MC64
864         .long   XF64
867         .section .bss
869         .align 8
870         .global frameBuffer
871 frameBuffer:
872         .quad   0
873         .global frameBufferSize
874 frameBufferSize:
875         .quad   0
877         .global e820_entries
878 e820_entries:
879         .quad   0
881         .align  4
882         .word   0
883         .align  2
884 DTSize:
885         .word   0
886 DTLocation:
887         .long   0
889         .align  16
890         .global e820_map
891 e820_map:
892         .fill   (20 * MAX_E820), 1, 0
894         .align  4
895         .global MPFloatingPointer
896 MPFloatingPointer:
897         .long   0
899         .section .tss
901         .align  1
903         .global tss_start
904 tss_start:
905         .long   0
907         .global tss_rsp0
908 tss_rsp0:
909         .quad   0
910         .global tss_rsp1
911 tss_rsp1:
912         .quad   0
913         .global tss_rsp2
914 tss_rsp2:
915         .quad   0
917         .quad   0
919         .global tss_ist1
920 tss_ist1:
921         .quad   0
922         .global tss_ist2
923 tss_ist2:
924         .quad   0
925         .global tss_ist3
926 tss_ist3:
927         .quad   0
928         .global tss_ist4
929 tss_ist4:
930         .quad   0
931         .global tss_ist5
932 tss_ist5:
933         .quad   0
934         .global tss_ist6
935 tss_ist6:
936         .quad   0
937         .global tss_ist7
938 tss_ist7:
939         .quad   0
941         .quad   0
943         .word   0
944         .global tss_iomap_base
945 tss_iomap_base:
946         .word   0
948         .global tss_iomap
949 tss_iomap:
950         .quad   0       /* ports 0 - 63 */
951         .quad   0       /* ports 64 - 127 */
952         .quad   0       /* ports 128 - 191 */
953         .quad   0       /* ports 192 - 255 */
955         .byte   0       /* 0xff */
956         .global tss_end
957 tss_end: