3 * Copyright (C) 2003 Mike Nordell
4 * Based on multiboot.S (no copyright note present), but so heavily
5 * modified that it bears close to no resemblance to the original work.
7 * MSVC compatible combination of plain C and inline assembler to:
8 * 1 Relocated all the sections in the kernel - something I feel the
9 * bootloader should have done, but multiboot being just a "raw image"
10 * loader, it unfortunately had to be done here - in-place.
11 * 2 Set up page directories and stuff.
12 * 3 Load IDT, GDT and turn on paging, making us execute at the intended
13 * target address (as if the image was PE-loaded and parsed into that addr.)
14 * 4 Call _main, and let the rest of the startup run...
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32 // TODO: Fix the MP parts
35 /* INCLUDES ******************************************************************/
39 #include <ddk/ntddk.h>
40 #include <ddk/status.h>
41 #include <internal/i386/segment.h>
42 #include <internal/i386/fpu.h>
43 #include <internal/ps.h>
44 #include <ddk/defines.h>
48 #include <internal/ntoskrnl.h>
49 #include <internal/i386/segment.h>
50 #include <internal/ps.h>
51 #include <internal/ldr.h>
55 // The MSVC linker (by defult) emits no special .bss section, but uses the data
56 // section with a rawsize smaller than virtualsize. The "slack" is BSS.
59 //////////////////////////////////////////////////////////////////
60 // Some macros we need
62 // some stuff straight from freeloaders multiboot.h
63 #define MULTIBOOT_HEADER_MAGIC (0x1BADB002)
64 #define MULTIBOOT_HEADER_FLAGS (0x00010003)
66 #define TARGET_LOAD_ADDR 0x00200000
67 #define BASE_TO_PHYS_DIST (KERNEL_BASE - TARGET_LOAD_ADDR)
69 #define V2P(x) (x - BASE_TO_PHYS_DIST)
74 #define AP_MAGIC (0x12481020)
80 void initialize_page_directory(void);
82 void* relocate_pointer_log_to_phys(const void* p
)
84 // DON'T CALL this function until relocation of .data and/or .rdata,
85 // is completed - but still be sure that we have not yet enabled paging!
86 return (void*)((DWORD
)p
- BASE_TO_PHYS_DIST
);
93 // Macro to emit one character to Bochs debug-port (0x9e).
94 // We need to do it this way, since at this point of the startup, obviously
95 // we have neither HAL nor DbgPrint support.
96 #define BOCHS_OUT_CHAR(c1) __asm push eax __asm push edx __asm mov dx, 0xe9 __asm mov al, c1 __asm out dx, al __asm pop edx __asm pop eax
98 void boch_out_hex_digit(unsigned char ch1
)
100 if (ch1
<= 9) { ch1
+= '0'; } else { ch1
+= 'a' - 10; }
104 void bochs_dump_hex(DWORD p
)
106 unsigned char ch3
= (unsigned char)((p
>> 28) & 0x0f);
107 unsigned char cl3
= (unsigned char)((p
>> 24) & 0x0f);
108 unsigned char ch2
= (unsigned char)((p
>> 20) & 0x0f);
109 unsigned char cl2
= (unsigned char)((p
>> 16) & 0x0f);
110 unsigned char ch1
= (unsigned char)((p
>> 12) & 0x0f);
111 unsigned char cl1
= (unsigned char)((p
>> 8) & 0x0f);
112 unsigned char ch0
= (unsigned char)((p
>> 4) & 0x0f);
113 unsigned char cl0
= (unsigned char)((p
>> 0) & 0x0f);
114 BOCHS_OUT_CHAR('0') BOCHS_OUT_CHAR('x')
115 boch_out_hex_digit(ch3
);
116 boch_out_hex_digit(cl3
);
117 boch_out_hex_digit(ch2
);
118 boch_out_hex_digit(cl2
);
119 boch_out_hex_digit(ch1
);
120 boch_out_hex_digit(cl1
);
121 boch_out_hex_digit(ch0
);
122 boch_out_hex_digit(cl0
);
126 static void bochs_out_string(const char* s
/* logical address! */)
128 s
= relocate_pointer_log_to_phys(s
);
148 #define BOCHS_OUT_CHAR(c1)
149 #define bochs_dump_hex(VAL)
150 #define bochs_out_string(STR)
155 //////////////////////////////////////////////////////////////////
157 typedef char kernel_page_t
[4096];
159 // Use 4096 (pagesize) more bytes that actually needed for each *_holder,
160 // to be able to make sure that the other stuff is page aligned.
161 // No other way to do this portably... :-(
163 // TODO: Consider allocating just one large block of BSS memory here, align
164 // just the first pointer, and then get the other ones just as offsets from
165 // this (now-aligned) pointer. That way we could get away with wasting just
166 // one page of memory, instead of 4 (like 16KB would matter... but still)
167 static kernel_page_t
* startup_pagedirectory_holder
[1024 * 2];
168 static kernel_page_t
* lowmem_pagetable_holder
[1024 * 2];
169 static kernel_page_t
* kernel_pagetable_holder
[32*1024 + 1];
170 static __int32 kpcr_pagetable_holder
[4096/4 * 2];
173 char apic_pagetable
[4096];
176 __int32 unmap_me
[4096/4];
177 __int32 unmap_me2
[4096/4];
178 __int32 unmap_me3
[4096/4];
180 __int32 init_stack
[3*4096/4];
184 __int32 trap_stack
[3*4096/4];
190 // lie a bit about types - since C is basically typeless anyway, it
191 // doesn't really matter what type we say it is here...
192 extern int KiGdtDescriptor
;
193 extern int KiIdtDescriptor
;
198 * This is called by the realmode loader, with protected mode
199 * enabled, paging disabled and the segment registers pointing
200 * a 4Gb, 32-bit segment starting at zero.
202 * EAX = Multiboot magic or application processor magic
204 * EBX = Points to a structure in lowmem with data from the
207 #pragma intrinsic(memset)
210 // We need to implement this ourself, to be able to get to it by short call's
211 void our_memmove(void* pDest
, const void* pSrc
, DWORD size
)
213 char* pD
= (char*)pDest
;
214 const char* pS
= (char*)pSrc
;
222 else if (pSrc
< pDest
)
230 void dummy_placeholder(void)
232 // NOTE: This function MUST be placed JUST AFTER MultibootStub in memory.
233 // Yes, it's BEFORE it in this file, but linkorder.txt fixes this for us.
237 // This one is needed, since the boot loader hasn't relocated us
244 // This sucks, I know...
245 #define EMIT_DWORD(x) __asm __emit ((x) >> 0) & 0xff __asm _emit ((x) >> 8) & 0xff __asm _emit ((x) >> 16) & 0xff __asm _emit ((x) >> 24) & 0xff
247 EMIT_DWORD(MULTIBOOT_HEADER_MAGIC
)
248 EMIT_DWORD(MULTIBOOT_HEADER_FLAGS
)
249 EMIT_DWORD(-(MULTIBOOT_HEADER_MAGIC
+ MULTIBOOT_HEADER_FLAGS
))
250 EMIT_DWORD(TARGET_LOAD_ADDR
+ 0x0400 + 0x04)
251 // Now just make something up, since there is no way we can know, beforehand,
252 // where any BSS data is...
253 EMIT_DWORD((TARGET_LOAD_ADDR
))
254 EMIT_DWORD((TARGET_LOAD_ADDR
+ 1*1024*1024)) /* assume ntoskrnel.exe is < 1MB! */
255 EMIT_DWORD((TARGET_LOAD_ADDR
+ 2*1024*1024)) /* just to have something, let's say BSS is 1MB too */
256 /* This is *REALLY* ugly! If MultibootStub is *EVER* at */
257 /* any other offset, this will crash like crazy! */
258 /* 0x0400 is the file alignment of the binary (ntoskrnl.exe) */
259 EMIT_DWORD((TARGET_LOAD_ADDR
+ 0x0400)) // entry_addr
263 cld
// just for good measure
267 /* Save the multiboot or application processor magic */
270 __asm mov saved_eax
, eax
271 __asm mov saved_ebx
, ebx
273 // bochs_out_string("MultibootStub()\n");
275 // OK, time to relocate the brute-loaded image in-place...
276 // If we don't watch it, we will overwrite ourselves here - imagine
277 // the fireworks! :-) That's why the function dummy_placeholder()
278 // MUST be placed JUST JUST AFTER this function.
281 PIMAGE_NT_HEADERS NtHeader
= RtlImageNtHeader((PVOID
)TARGET_LOAD_ADDR
);
282 PIMAGE_SECTION_HEADER Section
= IMAGE_FIRST_SECTION(NtHeader
);
283 const int count
= NtHeader
->FileHeader
.NumberOfSections
;
285 Section
+= count
- 1; // make it point to the last section
286 // NOTE: We MUST walk the sections "backwards".
287 for (i
= count
-1; i
>= 0; --i
, --Section
)
289 DWORD dwSrc
= TARGET_LOAD_ADDR
+ Section
->PointerToRawData
;
290 DWORD dwDst
= TARGET_LOAD_ADDR
+ Section
->VirtualAddress
;
291 DWORD dwSiz
= Section
->SizeOfRawData
;
292 const char* pEndThisFunc
;
299 //bochs_out_string("MultibootStub: relocating section\n");
301 if (Section
->Characteristics
& IMAGE_SCN_MEM_EXECUTE
)
303 // can't get a pointer to a label from plain C :-(
304 __asm mov pEndThisFunc
, offset dummy_placeholder
305 pEndThisFunc
-= BASE_TO_PHYS_DIST
;
306 if (dwDst
< (DWORD
)pEndThisFunc
)
308 // We must not move the code from under our feet!
309 // This can only happen in the code segment - the first segment
310 DWORD diff
= (DWORD
)pEndThisFunc
- dwDst
;
317 // obviously we must use memmove, since memory can overlap
318 our_memmove((void*)dwDst
, (void*)dwSrc
, dwSiz
);
320 // While at it, we might as well zero any uninitialized data in the section...
321 if (Section
->SizeOfRawData
< Section
->Misc
.VirtualSize
)
323 memset((char*)(Section
->VirtualAddress
+ Section
->SizeOfRawData
+ TARGET_LOAD_ADDR
),
325 Section
->Misc
.VirtualSize
- Section
->SizeOfRawData
);
329 // Now all sections are relocated to their intended in-memory layout,
330 // but we are still running int the low TARGET_LOAD_ADDR memory.
333 // Time to jump to the real startup, the entry-point function.
334 // We must do this using assembler, since both eax and ebx are assumed
335 // to hold some magic values.
336 typedef VOID (STDCALL
* pfn_t
)(PPEB
);
337 pfn_t pfn
= (pfn_t
)(NtHeader
->OptionalHeader
.AddressOfEntryPoint
+ TARGET_LOAD_ADDR
);
339 __asm mov eax
, saved_eax
340 __asm mov ebx
, saved_ebx
344 __asm mov ebx
, saved_ebx
345 (*pfn
)((PPEB
)saved_eax
);
353 // TMN: TODO: Convert this to the extent possible to plain C code
354 // Due to the "magic" above, we enter this function with all kernel sections
355 // properly relocated wrt. offsets from start-of-mem. But, we are still running
356 // without paging, meaning that the address that is to be KERNEL_BASE+xyz is
357 // currently still TARGET_LOAD_ADDR+xyz.
358 // We get aways with a few of the functions call here since they are near calls
359 // (PC-relative), but don't even _think_ about calling any other functions
360 // until we have turned on paging!
368 __asm mov saved_ebx
, ebx
369 __asm mov saved_eax
, eax
371 bochs_out_string("NtProcessStartup: Just entered\n");
374 if (saved_eax
!= AP_MAGIC
)
378 bochs_out_string("NtProcessStartup: Calling initialize_page_directory()\n");
380 initialize_page_directory(); // Initialize the page directory
382 bochs_out_string("NtProcessStartup: Page directory initialized\n");
389 * Initialize the page table that maps the APIC register address space
393 * FIXME: APIC register address space can be non-standard so do the
396 mov esi
, V2P(apic_pagetable
)
409 bochs_out_string("NtProcessStartup: Enabling paging...\n");
412 * Enable paging and set write protect
413 * bit 31: PG, bit 16: WP
416 __asm
or eax
, 0x80010000
419 bochs_out_string("NtProcessStartup: Paging enabled!\n");
420 bochs_out_string("NtProcessStartup: But we're still at the \"low\" address\n");
423 * Do an absolute jump because we now want to execute above KERNEL_BASE
425 __asm mov eax
, offset l2_
431 bochs_out_string("We have now left \"low\" memory, and is flying at an altitude of...\n");
432 bochs_out_string("OK, we're not flying, we're just executing above KERNEL_BASE\n");
435 * Load the GDTR and IDTR with new tables located above
441 DWORD val
= (DWORD
)&KiGdtDescriptor
;
442 bochs_out_string("&KiGdtDescriptor: ");
445 val
= (DWORD
)&KiIdtDescriptor
;
446 bochs_out_string("&KiIdtDescriptor: ");
451 bochs_out_string("Loading GDT and IDT...\n");
453 /* FIXME: Application processors should have their own GDT/IDT */
454 __asm lgdt KiGdtDescriptor
455 __asm lidt KiIdtDescriptor
457 bochs_out_string("GDT and IDT loaded\n");
462 * Reload the data segment registers
473 bochs_out_string("NtProcessStartup: segment registers loaded\n");
477 if (saved_eax
== AP_MAGIC
)
482 * This is an application processor executing
492 * Call the application processor initialization code
501 * Catch illegal returns from KiSystemStartup
515 bochs_out_string("Loading fs with PCR_SELECTOR\n");
517 /* Load the PCR selector */
518 __asm mov eax
, PCR_SELECTOR
521 bochs_out_string("Loading esp with init_stack_top : "); bochs_dump_hex((DWORD
)&init_stack_top
);
522 bochs_out_string("Just for interest, init_stack is at: "); bochs_dump_hex((DWORD
)init_stack
);
523 bochs_out_string("Meaing the init_stack in bytes is : "); bochs_dump_hex((DWORD
)&init_stack_top
- (DWORD
)init_stack
);
526 /* Load the initial kernel stack */
527 __asm mov esp
, offset init_stack_top
529 bochs_out_string("Loaded esp with init_stack_top\n");
537 bochs_out_string("Loaded eflags\n");
540 * Call the main kernel initialization
542 bochs_out_string("TMN: Calling _main...\n");
555 * Catch illegal returns from main, try bug checking the system,
556 * if that fails then loop forever.
562 } // end of __asm block
564 bochs_out_string("TMN: Back from _main ?! Let's crash!\n");
573 void initialize_page_directory(void)
576 * Initialize the page directory
579 // First convert the pointers from the virtual address the compiler generated
580 // code thinks we are at, to the currently active physical address we actually
581 // got loaded into by the loader. At this point we have been relocated, so
582 // that there is a 1:1 mapping between KERNEL_BASE+n and TARGET_LOAD_ADDR+n.
584 kernel_page_t
** startup_pagedirectory
= startup_pagedirectory_holder
;
585 kernel_page_t
** lowmem_pagetable
= lowmem_pagetable_holder
;
586 kernel_page_t
** kernel_pagetable
= kernel_pagetable_holder
;
587 __int32
* kpcr_pagetable
= kpcr_pagetable_holder
;
589 bochs_out_string("startup_pagedirectory before reloc: ");
590 bochs_dump_hex((DWORD
)startup_pagedirectory
);
592 startup_pagedirectory
= (kernel_page_t
**)relocate_pointer_log_to_phys(startup_pagedirectory
);
593 lowmem_pagetable
= (kernel_page_t
**)relocate_pointer_log_to_phys(lowmem_pagetable
);
594 kernel_pagetable
= (kernel_page_t
**)relocate_pointer_log_to_phys(kernel_pagetable
);
595 kpcr_pagetable
= (__int32
*) relocate_pointer_log_to_phys(kpcr_pagetable
);
597 bochs_out_string("startup_pagedirectory after reloc : ");
598 bochs_dump_hex((DWORD
)startup_pagedirectory
);
600 // Now align the pointers to PAGE_SIZE...
601 startup_pagedirectory
= (kernel_page_t
**)(((ULONG_PTR
)startup_pagedirectory
+ 4095) & ~4095);
602 lowmem_pagetable
= (kernel_page_t
**)(((ULONG_PTR
)lowmem_pagetable
+ 4095) & ~4095);
603 kernel_pagetable
= (kernel_page_t
**)(((ULONG_PTR
)kernel_pagetable
+ 4095) & ~4095);
604 kpcr_pagetable
= (__int32
* ) (((ULONG_PTR
)kpcr_pagetable
+ 4095) & ~4095);
607 bochs_out_string("startup_pagedirectory aligned : ");
608 bochs_dump_hex((DWORD
)startup_pagedirectory
);
611 // Ugly macros, I know...
612 #define DEST(PAGE) startup_pagedirectory[(PAGE) + 0xc00 / 4]
613 #define SRC(PAGE) (kernel_page_t*)((char*)kernel_pagetable + (PAGE)*4096 + 0x7)
615 startup_pagedirectory
[0] = (kernel_page_t
*)((char*)lowmem_pagetable
+ 0x7);
625 DEST( 64) = (kernel_page_t
*)((char*)lowmem_pagetable
+ 0x7);
626 DEST(192) = (kernel_page_t
*)((char*)startup_pagedirectory
+ 0x7);
628 DEST(251) = (kernel_page_t
*)((char*)apic_pagetable
+ 0x7);
630 DEST(252) = (kernel_page_t
*)((char*)kpcr_pagetable
+ 0x7);
635 /* Initialize the page table that maps low memory */
636 for (i
=0; i
<1024; ++i
) {
637 lowmem_pagetable
[i
] = (kernel_page_t
*)(i
*4096 + 7);
640 /* Initialize the page table that maps kernel memory */
641 for (i
=0; i
<6144/4 /* 1536 pages = 6MB */; ++i
) {
642 kernel_pagetable
[i
] = (kernel_page_t
*)(i
*4096 + TARGET_LOAD_ADDR
+ 0x7);
645 /* Initialize the page table that maps the initial KPCR (at FF000000) */
646 kpcr_pagetable
[0] = 0x1007;
652 __asm mov eax
, startup_pagedirectory