- Add a minimal implementation of SetupScanFileQueueW
[reactos.git] / msvc6 / ntoskrnl / ke_i386_multiboot.c
1 /*
2 * ReactOS kernel
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.
6 *
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...
15 *
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.
20 *
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.
25 *
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.
29 */
30
31 //
32 // TODO: Fix the MP parts
33 //
34
35 /* INCLUDES ******************************************************************/
36
37 #pragma hdrstop
38
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>
45 #include <pe.h>
46
47 #include <roscfg.h>
48 #include <internal/ntoskrnl.h>
49 #include <internal/i386/segment.h>
50 #include <internal/ps.h>
51 #include <internal/ldr.h>
52
53
54 // some notes:
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.
57
58
59 //////////////////////////////////////////////////////////////////
60 // Some macros we need
61
62 // some stuff straight from freeloaders multiboot.h
63 #define MULTIBOOT_HEADER_MAGIC (0x1BADB002)
64 #define MULTIBOOT_HEADER_FLAGS (0x00010003)
65
66 #define TARGET_LOAD_ADDR 0x00200000
67 #define BASE_TO_PHYS_DIST (KERNEL_BASE - TARGET_LOAD_ADDR)
68
69 #define V2P(x) (x - BASE_TO_PHYS_DIST)
70
71
72 #ifdef MP
73
74 #define AP_MAGIC (0x12481020)
75
76 #endif /* MP */
77
78
79
80 void initialize_page_directory(void);
81
82 void* relocate_pointer_log_to_phys(const void* p)
83 {
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);
87 }
88
89
90
91 #ifdef _DEBUG
92
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
97
98 void boch_out_hex_digit(unsigned char ch1)
99 {
100 if (ch1 <= 9) { ch1 += '0'; } else { ch1 += 'a' - 10; }
101 BOCHS_OUT_CHAR(ch1)
102 }
103
104 void bochs_dump_hex(DWORD p)
105 {
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);
123 BOCHS_OUT_CHAR('\n')
124 }
125
126 static void bochs_out_string(const char* s /* logical address! */)
127 {
128 s = relocate_pointer_log_to_phys(s);
129 __asm
130 {
131 pushad
132 mov dx, 0xe9
133 mov ebx, s
134 L1:
135 cmp byte ptr[ebx], 0
136 je end
137 mov al, [ebx]
138 out dx, al
139 inc ebx
140 jmp L1
141 end:
142 popad
143 }
144 }
145
146 #else
147
148 #define BOCHS_OUT_CHAR(c1)
149 #define bochs_dump_hex(VAL)
150 #define bochs_out_string(STR)
151
152 #endif // _DEBUG
153
154
155 //////////////////////////////////////////////////////////////////
156
157 typedef char kernel_page_t[4096];
158
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... :-(
162 //
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];
171
172 #ifdef MP
173 char apic_pagetable[4096];
174 #endif /* MP */
175
176 __int32 unmap_me[4096/4];
177 __int32 unmap_me2[4096/4];
178 __int32 unmap_me3[4096/4];
179
180 __int32 init_stack[3*4096/4];
181 int init_stack_top;
182
183
184 __int32 trap_stack[3*4096/4];
185 int trap_stack_top;
186
187
188
189 void _main();
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;
194
195
196
197 /*
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.
201 *
202 * EAX = Multiboot magic or application processor magic
203 *
204 * EBX = Points to a structure in lowmem with data from the
205 * loader
206 */
207 #pragma intrinsic(memset)
208
209
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)
212 {
213 char* pD = (char*)pDest;
214 const char* pS = (char*)pSrc;
215 if (pDest < pSrc)
216 {
217 while (size--)
218 {
219 *pD++ = *pS++;
220 }
221 }
222 else if (pSrc < pDest)
223 {
224 while (size--)
225 {
226 pD[size] = pS[size];
227 }
228 }
229 }
230 void dummy_placeholder(void)
231 {
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.
234 }
235
236
237 // This one is needed, since the boot loader hasn't relocated us
238 __declspec(naked)
239 void MultibootStub()
240 {
241 __asm
242 {
243 jmp _multiboot_entry
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
246 ALIGN 4
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
260
261 _multiboot_entry:
262
263 cld // just for good measure
264 }
265
266 {
267 /* Save the multiboot or application processor magic */
268 DWORD saved_eax;
269 DWORD saved_ebx;
270 __asm mov saved_eax, eax
271 __asm mov saved_ebx, ebx
272
273 // bochs_out_string("MultibootStub()\n");
274
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.
279
280 {
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;
284 int i;
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)
288 {
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;
293
294 if (dwSrc == dwDst)
295 {
296 continue;
297 }
298
299 //bochs_out_string("MultibootStub: relocating section\n");
300
301 if (Section->Characteristics & IMAGE_SCN_MEM_EXECUTE)
302 {
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)
307 {
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;
311 dwDst += diff;
312 dwSrc += diff;
313 dwSiz -= diff;
314 }
315 }
316
317 // obviously we must use memmove, since memory can overlap
318 our_memmove((void*)dwDst, (void*)dwSrc, dwSiz);
319
320 // While at it, we might as well zero any uninitialized data in the section...
321 if (Section->SizeOfRawData < Section->Misc.VirtualSize)
322 {
323 memset((char*)(Section->VirtualAddress + Section->SizeOfRawData + TARGET_LOAD_ADDR),
324 0,
325 Section->Misc.VirtualSize - Section->SizeOfRawData);
326 }
327 }
328
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.
331
332 {
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);
338 #if 1
339 __asm mov eax, saved_eax
340 __asm mov ebx, saved_ebx
341 __asm mov ecx, pfn
342 __asm jmp ecx
343 #else
344 __asm mov ebx, saved_ebx
345 (*pfn)((PPEB)saved_eax);
346 #endif
347 }
348 }
349 }
350 }
351
352
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!
361 VOID STDCALL
362 NtProcessStartup(
363 IN PPEB Peb
364 )
365 {
366 DWORD saved_ebx;
367 DWORD saved_eax;
368 __asm mov saved_ebx, ebx
369 __asm mov saved_eax, eax
370
371 bochs_out_string("NtProcessStartup: Just entered\n");
372
373 #ifdef MP
374 if (saved_eax != AP_MAGIC)
375 {
376 #endif /* MP */
377
378 bochs_out_string("NtProcessStartup: Calling initialize_page_directory()\n");
379
380 initialize_page_directory(); // Initialize the page directory
381
382 bochs_out_string("NtProcessStartup: Page directory initialized\n");
383
384 #ifdef MP
385
386 __asm
387 {
388 /*
389 * Initialize the page table that maps the APIC register address space
390 */
391
392 /*
393 * FIXME: APIC register address space can be non-standard so do the
394 * mapping later
395 */
396 mov esi, V2P(apic_pagetable)
397 mov edi, 0
398 mov eax, 0xFEC0001B
399 mov [esi+edi], eax
400 mov edi, 0x800
401 mov eax, 0xFEE0001B
402 mov [esi+edi], eax
403 }
404 }
405
406 #endif /* MP */
407
408 {
409 bochs_out_string("NtProcessStartup: Enabling paging...\n");
410
411 /*
412 * Enable paging and set write protect
413 * bit 31: PG, bit 16: WP
414 */
415 __asm mov eax, cr0
416 __asm or eax, 0x80010000
417 __asm mov cr0, eax
418
419 bochs_out_string("NtProcessStartup: Paging enabled!\n");
420 bochs_out_string("NtProcessStartup: But we're still at the \"low\" address\n");
421
422 /*
423 * Do an absolute jump because we now want to execute above KERNEL_BASE
424 */
425 __asm mov eax, offset l2_
426 __asm jmp eax
427 }
428
429 l2_:
430
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");
433
434 /*
435 * Load the GDTR and IDTR with new tables located above
436 * KERNEL_BASE
437 */
438
439 #ifdef _DEBUG
440 {
441 DWORD val = (DWORD)&KiGdtDescriptor;
442 bochs_out_string("&KiGdtDescriptor: ");
443 bochs_dump_hex(val);
444
445 val = (DWORD)&KiIdtDescriptor;
446 bochs_out_string("&KiIdtDescriptor: ");
447 bochs_dump_hex(val);
448 }
449 #endif
450
451 bochs_out_string("Loading GDT and IDT...\n");
452
453 /* FIXME: Application processors should have their own GDT/IDT */
454 __asm lgdt KiGdtDescriptor
455 __asm lidt KiIdtDescriptor
456
457 bochs_out_string("GDT and IDT loaded\n");
458
459 __asm
460 {
461 /*
462 * Reload the data segment registers
463 */
464 mov eax, KERNEL_DS
465 mov ds, ax
466 mov es, ax
467 mov gs, ax
468 mov ss, ax
469 mov eax, 0
470 mov fs, ax
471 }
472
473 bochs_out_string("NtProcessStartup: segment registers loaded\n");
474
475 #ifdef MP
476
477 if (saved_eax == AP_MAGIC)
478 {
479 __asm
480 {
481 /*
482 * This is an application processor executing
483 */
484
485 /*
486 * Initialize EFLAGS
487 */
488 push 0
489 popfd
490
491 /*
492 * Call the application processor initialization code
493 */
494 push 0
495 push offset l7_
496 push KERNEL_CS
497 push KiSystemStartup
498 retf
499
500 /*
501 * Catch illegal returns from KiSystemStartup
502 */
503 l7_:
504 pop eax
505 }
506
507 KeBugCheck(0);
508
509 for (;;)
510 ; /*forever */
511 }
512
513 #endif /* MP */
514
515 bochs_out_string("Loading fs with PCR_SELECTOR\n");
516
517 /* Load the PCR selector */
518 __asm mov eax, PCR_SELECTOR
519 __asm mov fs, ax
520
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);
524
525
526 /* Load the initial kernel stack */
527 __asm mov esp, offset init_stack_top
528
529 bochs_out_string("Loaded esp with init_stack_top\n");
530
531 /*
532 * Initialize EFLAGS
533 */
534 __asm push 0
535 __asm popfd
536
537 bochs_out_string("Loaded eflags\n");
538
539 /*
540 * Call the main kernel initialization
541 */
542 bochs_out_string("TMN: Calling _main...\n");
543
544 __asm
545 {
546 xor ebp,ebp
547 push ebx
548 push edx
549 push offset l5_
550 push KERNEL_CS
551 push offset _main
552 retf
553
554 /*
555 * Catch illegal returns from main, try bug checking the system,
556 * if that fails then loop forever.
557 */
558 l5_:
559 pop eax
560 pop eax
561
562 } // end of __asm block
563
564 bochs_out_string("TMN: Back from _main ?! Let's crash!\n");
565
566 KeBugCheck(0);
567
568 for (;;)
569 ; /*forever */
570 }
571
572
573 void initialize_page_directory(void)
574 {
575 /*
576 * Initialize the page directory
577 */
578
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.
583
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;
588
589 bochs_out_string("startup_pagedirectory before reloc: ");
590 bochs_dump_hex((DWORD)startup_pagedirectory);
591
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);
596
597 bochs_out_string("startup_pagedirectory after reloc : ");
598 bochs_dump_hex((DWORD)startup_pagedirectory);
599
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);
605
606 #ifdef _DEBUG
607 bochs_out_string("startup_pagedirectory aligned : ");
608 bochs_dump_hex((DWORD)startup_pagedirectory);
609 #endif
610
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)
614
615 startup_pagedirectory[0] = (kernel_page_t*)((char*)lowmem_pagetable + 0x7);
616
617 {
618 unsigned int i;
619 for (i=0; i<32; ++i)
620 {
621 DEST(i) = SRC(i);
622 }
623 }
624
625 DEST( 64) = (kernel_page_t*)((char*)lowmem_pagetable + 0x7);
626 DEST(192) = (kernel_page_t*)((char*)startup_pagedirectory + 0x7);
627 #ifdef MP
628 DEST(251) = (kernel_page_t*)((char*)apic_pagetable + 0x7);
629 #endif /* MP */
630 DEST(252) = (kernel_page_t*)((char*)kpcr_pagetable + 0x7);
631
632
633 {
634 unsigned int i;
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);
638 }
639
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);
643 }
644
645 /* Initialize the page table that maps the initial KPCR (at FF000000) */
646 kpcr_pagetable[0] = 0x1007;
647 }
648
649 /*
650 * Set up the PDBR
651 */
652 __asm mov eax, startup_pagedirectory
653 __asm mov cr3, eax
654 }
655