3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 * Copyright (C) 2005 Alex Ionescu <alex@relsoft.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <elf/reactos.h>
25 #include "ppcmmu/mmu.h"
31 /* We'll check this to see if we're in OFW land */
32 extern of_proxy ofproxy
;
34 PVOID KernelMemory
= 0;
36 /* Bits to shift to convert a Virtual Address into an Offset in the Page Table */
39 /* Bits to shift to convert a Virtual Address into an Offset in the Page Directory */
41 #define PDE_SHIFT_PAE 18
43 #define STARTUP_BASE 0xC0000000
44 #define HYPERSPACE_BASE 0xC0400000
45 #define HYPERSPACE_PAE_BASE 0xC0800000
46 #define APIC_BASE 0xFEC00000
47 #define KPCR_BASE 0xFF000000
49 #define LowMemPageTableIndex 0
50 #define StartupPageTableIndex (STARTUP_BASE >> 22)
51 #define HyperspacePageTableIndex (HYPERSPACE_BASE >> 22)
52 #define KpcrPageTableIndex (KPCR_BASE >> 22)
53 #define ApicPageTableIndex (APIC_BASE >> 22)
55 #define BAT_GRANULARITY (64 * 1024)
56 #define KernelMemorySize (8 * 1024 * 1024)
57 #define XROUNDUP(x,n) ((((ULONG)x) + ((n) - 1)) & (~((n) - 1)))
59 #define TAG_MBOOT 'oobM'
61 char reactos_module_strings
[64][256]; // Array to hold module names
63 /* Load Address of Next Module */
64 ULONG_PTR NextModuleBase
= 0;
66 /* Currently Opened Module */
67 PLOADER_MODULE CurrentModule
= NULL
;
69 /* Unrelocated Kernel Base in Virtual Memory */
72 /* Wether PAE is to be used or not */
73 BOOLEAN PaeModeEnabled
;
75 /* Kernel Entrypoint in Physical Memory */
76 ULONG_PTR KernelEntryPoint
;
78 /* Dummy to bring in memmove */
79 PVOID memmove_dummy
= memmove
;
83 LdrGetModuleObject(PCHAR ModuleName
);
87 LdrPEFixupImports(IN PVOID DllBase
,
90 VOID
PpcInitializeMmu(int max
);
92 /* FUNCTIONS *****************************************************************/
98 * Prepares the system for loading the Kernel.
101 * Magic - Multiboot Magic
111 typedef void (*KernelEntryFn
)( void * );
113 int MmuPageMiss(int trapCode
, ppc_trap_frame_t
*trap
)
116 printf("TRAP %x\n", trapCode
);
117 for( i
= 0; i
< 40; i
++ )
118 printf("r[%d] %x\n", i
, trap
->gpr
[i
]);
123 typedef struct _ppc_map_set_t
{
126 ppc_map_info_t
*info
;
129 extern int mmu_handle
;
130 paddr_t
MmuTranslate(paddr_t possibly_virtual
)
134 /* Openfirmware takes liberties with boot-time memory.
135 * if you're in a unitary kernel, it's not as difficult, but since
136 * we rely on loading things into virtual space from here, we need
137 * to detect the mappings so far.
140 args
[0] = possibly_virtual
;
141 args
[1] = 1; /* Marker to tell we want a physical addr */
142 return (paddr_t
)ofw_callmethod_ret("translate", mmu_handle
, 2, args
, 3);
146 /* Other booters don't remap ram */
147 return possibly_virtual
;
153 FrLdrAddPageMapping(ppc_map_set_t
*set
, int proc
, paddr_t phys
, vaddr_t virt
)
156 paddr_t page
= ROUND_DOWN(phys
, (1<<PFN_SHIFT
));
159 virt
= ROUND_DOWN(page
, (1<<PFN_SHIFT
));
161 virt
= ROUND_DOWN(virt
, (1<<PFN_SHIFT
));
163 page
= MmuTranslate(page
);
165 //printf("Mapping virt [%x] to phys [%x (from) %x]\n", virt, page, phys);
167 for( j
= 0; j
< set
->usecount
; j
++ )
169 if(set
->info
[j
].addr
== page
) return;
175 set
->info
= MmAllocateMemory(0x80 * sizeof(*set
->info
));
177 else if (set
->mapsize
<= set
->usecount
)
179 ppc_map_info_t
*newinfo
= MmAllocateMemory(set
->mapsize
* 2 * sizeof(*set
->info
));
180 memcpy(newinfo
, set
->info
, set
->mapsize
* sizeof(*set
->info
));
181 MmFreeMemory(set
->info
);
186 set
->info
[set
->usecount
].flags
= MMU_ALL_RW
;
187 set
->info
[set
->usecount
].proc
= proc
;
188 set
->info
[set
->usecount
].addr
= virt
;
189 set
->info
[set
->usecount
].phys
= page
;
193 extern int _start
[], _end
[];
197 FrLdrStartup(ULONG Magic
)
199 ULONG_PTR i
, tmp
, OldModCount
= 0;
201 CHAR ModulesTreated
[64] = { 0 };
202 ULONG NumberOfEntries
= 0, UsedEntries
= 0;
203 PPAGE_LOOKUP_TABLE_ITEM FreeLdrMap
= MmGetMemoryMap(&NumberOfEntries
);
204 ppc_map_set_t memmap
= { };
206 printf("FrLdrStartup\n");
209 __asm__("mfmsr %0" : "=r" (tmp
));
211 __asm__("mtmsr %0" : : "r" (tmp
));
213 while(OldModCount
!= LoaderBlock
.ModsCount
)
215 printf("Added %d modules last pass\n",
216 LoaderBlock
.ModsCount
- OldModCount
);
218 OldModCount
= LoaderBlock
.ModsCount
;
220 for(i
= 0; i
< LoaderBlock
.ModsCount
; i
++)
222 if (!ModulesTreated
[i
])
224 ModulesTreated
[i
] = 1;
225 ModHeader
= ((PCHAR
)reactos_modules
[i
].ModStart
);
226 if(ModHeader
[0] == 'M' && ModHeader
[1] == 'Z')
228 ((PVOID
)reactos_modules
[i
].ModStart
,
229 (PCHAR
)reactos_modules
[i
].String
);
234 printf("Starting mmu\n");
238 printf("Allocating vsid 0 (kernel)\n");
239 MmuAllocVsid(0, 0xff00);
241 /* We'll use vsid 1 for freeldr (expendable) */
242 printf("Allocating vsid 1 (freeldr)\n");
243 MmuAllocVsid(1, 0xff);
245 printf("Mapping Freeldr Code (%x-%x)\n", _start
, _end
);
247 /* Map memory zones */
249 for( i
= (int)_start
;
251 i
+= (1<<PFN_SHIFT
) ) {
252 FrLdrAddPageMapping(&memmap
, 1, i
, 0);
255 printf("KernelBase %x\n", KernelBase
);
257 /* Heap pages -- this gets the entire freeldr heap */
258 for( i
= 0; i
< NumberOfEntries
; i
++ ) {
260 if (FreeLdrMap
[i
].PageAllocated
== LoaderSystemCode
) {
262 if (tmp
>= (ULONG
)KernelMemory
&&
263 tmp
< (ULONG
)KernelMemory
+ KernelMemorySize
) {
264 FrLdrAddPageMapping(&memmap
, 0, tmp
, KernelBase
+ tmp
- (ULONG
)KernelMemory
);
266 FrLdrAddPageMapping(&memmap
, 1, tmp
, 0);
271 MmuMapPage(memmap
.info
, memmap
.usecount
);
273 printf("Finished Mapping the Freeldr Heap (used %d pages)\n", UsedEntries
);
275 printf("Setting initial segments\n");
277 MmuSetVsid(8, 16, 0);
279 printf("Segments set!\n");
281 MmuTurnOn((KernelEntryFn
)KernelEntryPoint
, &LoaderBlock
);
291 * Configures PAE on a MP System, and sets the PDBR if it's supported, or if
295 * Magic - Multiboot Magic
306 FrLdrSetupPae(ULONG Magic
)
314 * Gets the Kernel Base to use.
322 * Sets both the FreeLdr internal variable as well as the one which
323 * will be used by the Kernel.
328 FrLdrGetKernelBase(VOID
)
332 /* Default kernel base at 2GB */
333 KernelBase
= 0x80800000;
336 LoaderBlock
.KernelBase
= 0x80000000;
338 /* Read Command Line */
339 p
= (PCHAR
)LoaderBlock
.CommandLine
;
340 while ((p
= strchr(p
, '/')) != NULL
) {
343 if (!_strnicmp(p
+ 1, "3GB", 3)) {
345 /* Make sure there's nothing following it */
346 if (p
[4] == ' ' || p
[4] == 0) {
349 KernelBase
= 0xE0000000;
350 LoaderBlock
.KernelBase
= 0xC0000000;
362 * Determines whether PAE mode shoudl be enabled or not.
376 FrLdrGetPaeMode(VOID
)
381 * FrLdrSetupPageDirectory
384 * Sets up the ReactOS Startup Page Directory.
393 * We are setting PDEs, but using the equvivalent (for our purpose) PTE structure.
394 * As such, please note that PageFrameNumber == PageEntryNumber.
399 FrLdrSetupPageDirectory(VOID
)
407 * Loads the indicated elf image as PE. The target will appear to be
408 * a PE image whose ImageBase has ever been KernelAddr.
411 * Image -- File to load
412 * ImageName -- Name of image for the modules list
413 * MemLoadAddr -- Freeldr address of module
414 * KernelAddr -- Kernel address of module
416 #define ELF_SECTION(n) ((Elf32_Shdr*)(sptr + (n * shsize)))
417 #define COFF_FIRST_SECTION(h) ((PIMAGE_SECTION_HEADER) ((DWORD)h+FIELD_OFFSET(IMAGE_NT_HEADERS,OptionalHeader)+(SWAPW(((PIMAGE_NT_HEADERS)(h))->FileHeader.SizeOfOptionalHeader))))
421 FrLdrMapModule(FILE *KernelImage
, PCHAR ImageName
, PCHAR MemLoadAddr
, ULONG KernelAddr
)
423 PIMAGE_DOS_HEADER ImageHeader
= 0;
424 PIMAGE_NT_HEADERS NtHeader
= 0;
425 PIMAGE_SECTION_HEADER Section
;
429 PLOADER_MODULE ModuleData
;
431 int shsize
, shnum
, relsize
, SectionAddr
= 0;
437 TempName
= strrchr(ImageName
, '\\');
438 if(TempName
) TempName
++; else TempName
= (LPSTR
)ImageName
;
439 ModuleData
= LdrGetModuleObject(TempName
);
447 KernelAddr
= (ULONG
)NextModuleBase
- (ULONG
)KernelMemory
+ KernelBase
;
449 MemLoadAddr
= (PCHAR
)NextModuleBase
;
451 ModuleData
= &reactos_modules
[LoaderBlock
.ModsCount
];
452 //printf("Loading file (elf at %x)\n", KernelAddr);
454 /* Load the first 1024 bytes of the kernel image so we can read the PE header */
455 if (!FsReadFile(KernelImage
, sizeof(ehdr
), NULL
, &ehdr
)) {
457 /* Fail if we couldn't read */
458 printf("Couldn't read the elf header\n");
462 /* Start by getting elf headers */
463 //phsize = ehdr.e_phentsize;
464 //phnum = ehdr.e_phnum;
465 shsize
= ehdr
.e_shentsize
;
466 shnum
= ehdr
.e_shnum
;
467 sptr
= (PCHAR
)FrLdrTempAlloc(shnum
* shsize
, TAG_MBOOT
);
469 /* Read section headers */
470 FsSetFilePointer(KernelImage
, ehdr
.e_shoff
);
471 FsReadFile(KernelImage
, shsize
* shnum
, NULL
, sptr
);
473 /* Now we'll get the PE Header */
474 for( i
= 0; i
< shnum
; i
++ )
476 shdr
= ELF_SECTION(i
);
479 /* Find the PE Header */
480 if (shdr
->sh_type
== TYPE_PEHEADER
)
482 FsSetFilePointer(KernelImage
, shdr
->sh_offset
);
483 FsReadFile(KernelImage
, shdr
->sh_size
, NULL
, MemLoadAddr
);
484 ImageHeader
= (PIMAGE_DOS_HEADER
)MemLoadAddr
;
485 NtHeader
= (PIMAGE_NT_HEADERS
)((PCHAR
)MemLoadAddr
+ SWAPD(ImageHeader
->e_lfanew
));
487 printf("NtHeader at %x\n", SWAPD(ImageHeader
->e_lfanew
));
488 printf("SectionAlignment %x\n",
489 SWAPD(NtHeader
->OptionalHeader
.SectionAlignment
));
490 SectionAddr
= ROUND_UP
491 (shdr
->sh_size
, SWAPD(NtHeader
->OptionalHeader
.SectionAlignment
));
492 printf("Header ends at %x\n", SectionAddr
);
500 printf("No peheader section encountered :-(\n");
506 printf("DOS SIG: %s\n", (PCHAR
)MemLoadAddr
);
510 /* Save the Image Base */
511 NtHeader
->OptionalHeader
.ImageBase
= SWAPD(KernelAddr
);
513 /* Load the file image */
514 Section
= COFF_FIRST_SECTION(NtHeader
);
515 SectionCount
= SWAPW(NtHeader
->FileHeader
.NumberOfSections
);
517 /* Walk each section */
518 for (i
=0; i
< SectionCount
; i
++, Section
++)
520 shdr
= ELF_SECTION((SWAPD(Section
->PointerToRawData
)+1));
522 shdr
->sh_addr
= SectionAddr
= SWAPD(Section
->VirtualAddress
);
523 shdr
->sh_addr
+= KernelAddr
;
525 Section
->PointerToRawData
= SWAPD((Section
->VirtualAddress
- KernelAddr
));
527 if (shdr
->sh_type
!= SHT_NOBITS
)
530 printf("Loading section %d at %x (real: %x:%d)\n", i
, KernelAddr
+ SectionAddr
, MemLoadAddr
+SectionAddr
, shdr
->sh_size
);
531 FsSetFilePointer(KernelImage
, shdr
->sh_offset
);
532 FsReadFile(KernelImage
, shdr
->sh_size
, NULL
, MemLoadAddr
+ SectionAddr
);
537 printf("BSS section %d at %x\n", i
, KernelAddr
+ SectionAddr
);
538 memset(MemLoadAddr
+ SectionAddr
, 0,
539 ROUND_UP(shdr
->sh_size
,
540 SWAPD(NtHeader
->OptionalHeader
.SectionAlignment
)));
544 ImageSize
= SWAPD(NtHeader
->OptionalHeader
.SizeOfImage
);
545 printf("Total image size is %x\n", ImageSize
);
547 /* Handle relocation sections */
548 for (i
= 0; i
< shnum
; i
++) {
549 Elf32_Rela reloc
= { };
552 int numreloc
, relstart
, targetSection
;
554 PCHAR RelocSection
, SymbolSection
;
556 shdr
= ELF_SECTION(i
);
557 /* Only relocs here */
558 if((shdr
->sh_type
!= SHT_REL
) &&
559 (shdr
->sh_type
!= SHT_RELA
)) continue;
561 relstart
= shdr
->sh_offset
;
562 relsize
= shdr
->sh_type
== SHT_RELA
? 12 : 8;
563 numreloc
= shdr
->sh_size
/ relsize
;
564 targetSection
= shdr
->sh_info
;
566 if (!ELF_SECTION(targetSection
)->sh_addr
) continue;
568 RelocSection
= FrLdrTempAlloc(shdr
->sh_size
, TAG_MBOOT
);
569 FsSetFilePointer(KernelImage
, relstart
);
570 FsReadFile(KernelImage
, shdr
->sh_size
, NULL
, RelocSection
);
572 /* Get the symbol section */
573 shdr
= ELF_SECTION(shdr
->sh_link
);
575 SymbolSection
= FrLdrTempAlloc(shdr
->sh_size
, TAG_MBOOT
);
576 FsSetFilePointer(KernelImage
, shdr
->sh_offset
);
577 FsReadFile(KernelImage
, shdr
->sh_size
, NULL
, SymbolSection
);
579 for(j
= 0; j
< numreloc
; j
++)
584 memcpy(&reloc
, RelocSection
+ (j
* relsize
), sizeof(reloc
));
587 memcpy(&symbol
, SymbolSection
+ (ELF32_R_SYM(reloc
.r_info
) * sizeof(symbol
)), sizeof(symbol
));
589 /* Compute addends */
590 S
= symbol
.st_value
+ ELF_SECTION(symbol
.st_shndx
)->sh_addr
;
592 P
= reloc
.r_offset
+ ELF_SECTION(targetSection
)->sh_addr
;
595 printf("Symbol[%d] %d -> %d(%x:%x) -> %x(+%x)@%x\n",
596 ELF32_R_TYPE(reloc
.r_info
),
597 ELF32_R_SYM(reloc
.r_info
),
599 ELF_SECTION(symbol
.st_shndx
)->sh_addr
,
606 Target32
= (ULONG
*)(((PCHAR
)MemLoadAddr
) + (P
- KernelAddr
));
607 Target16
= (USHORT
*)Target32
;
609 switch (ELF32_R_TYPE(reloc
.r_info
))
617 *Target32
= S
+ A
- P
;
619 case R_PPC_UADDR32
: /* Special: Treat as RVA */
620 *Target32
= S
+ A
- KernelAddr
;
623 *Target32
= (ADDR24_MASK
& (S
+A
)) | (*Target32
& ~ADDR24_MASK
);
626 *Target32
= (ADDR24_MASK
& (S
+A
-P
)) | (*Target32
& ~ADDR24_MASK
);
628 case R_PPC_ADDR16_LO
:
631 case R_PPC_ADDR16_HA
:
632 *Target16
= (S
+ A
+ 0x8000) >> 16;
639 printf("reloc[%d:%x]: (type %x sym %d val %d) off %x add %x (old %x new %x)\n",
641 ((ULONG
)Target32
) - ((ULONG
)MemLoadAddr
),
642 ELF32_R_TYPE(reloc
.r_info
),
643 ELF32_R_SYM(reloc
.r_info
),
645 reloc
.r_offset
, reloc
.r_addend
,
650 FrLdrTempFree(SymbolSection
, TAG_MBOOT
);
651 FrLdrTempFree(RelocSection
, TAG_MBOOT
);
654 FrLdrTempFree(sptr
, TAG_MBOOT
);
656 ModuleData
->ModStart
= (ULONG
)MemLoadAddr
;
657 /* Increase the next Load Base */
658 NextModuleBase
= ROUND_UP((ULONG
)MemLoadAddr
+ ImageSize
, PAGE_SIZE
);
659 ModuleData
->ModEnd
= NextModuleBase
;
660 ModuleData
->String
= (ULONG
)MmAllocateMemory(strlen(ImageName
)+1);
661 strcpy((PCHAR
)ModuleData
->String
, ImageName
);
662 printf("Module %s (%x-%x) next at %x\n",
664 ModuleData
->ModStart
,
667 LoaderBlock
.ModsCount
++;
677 * Maps the Kernel into memory, does PE Section Mapping, initializes the
678 * uninitialized data sections, and relocates the image.
681 * KernelImage - FILE Structure representing the ntoskrnl image file.
684 * TRUE if the Kernel was mapped.
692 FrLdrMapKernel(FILE *KernelImage
)
694 /* Get Kernel Base */
695 FrLdrGetKernelBase();
697 /* Allocate kernel memory */
698 KernelMemory
= MmAllocateMemory(KernelMemorySize
);
700 return FrLdrMapModule(KernelImage
, "ntoskrnl.exe", KernelMemory
, KernelBase
);
705 FrLdrLoadModule(FILE *ModuleImage
,
709 ULONG LocalModuleSize
;
710 ULONG_PTR ThisModuleBase
= NextModuleBase
;
711 PLOADER_MODULE ModuleData
;
715 /* Get current module data structure and module name string array */
716 ModuleData
= &reactos_modules
[LoaderBlock
.ModsCount
];
718 /* Get only the Module Name */
721 TempName
= strchr(ModuleName
, '\\');
724 ModuleName
= TempName
+ 1;
728 NameBuffer
= reactos_module_strings
[LoaderBlock
.ModsCount
];
731 /* Get Module Size */
732 LocalModuleSize
= FsGetFileSize(ModuleImage
);
734 /* Fill out Module Data Structure */
735 ModuleData
->ModStart
= NextModuleBase
;
736 ModuleData
->ModEnd
= NextModuleBase
+ LocalModuleSize
;
739 strcpy(NameBuffer
, ModuleName
);
740 ModuleData
->String
= (ULONG_PTR
)NameBuffer
;
742 /* Load the file image */
743 FsReadFile(ModuleImage
, LocalModuleSize
, NULL
, (PVOID
)NextModuleBase
);
745 /* Move to next memory block and increase Module Count */
746 NextModuleBase
= ROUND_UP(ModuleData
->ModEnd
, PAGE_SIZE
);
747 LoaderBlock
.ModsCount
++;
749 /* Return Module Size if required */
750 if (ModuleSize
!= NULL
) {
751 *ModuleSize
= LocalModuleSize
;
754 printf("Module %s (%x-%x) next at %x\n",
756 ModuleData
->ModStart
,
760 return ThisModuleBase
;
765 FrLdrMapImage(IN
FILE *Image
, IN PCHAR ShortName
, IN ULONG ImageType
)
769 printf("Loading image %s (type %d)\n", ShortName
, ImageType
);
773 if(FrLdrMapKernel(Image
))
774 Result
= (PVOID
)KernelMemory
;
778 PVOID ModuleBase
= (PVOID
)NextModuleBase
;
780 if(FrLdrMapModule(Image
, ShortName
, 0, 0))
788 FrLdrCreateModule(LPCSTR ModuleName
)
790 PLOADER_MODULE ModuleData
;
793 /* Get current module data structure and module name string array */
794 ModuleData
= &reactos_modules
[LoaderBlock
.ModsCount
];
795 NameBuffer
= reactos_module_strings
[LoaderBlock
.ModsCount
];
797 /* Set up the structure */
798 ModuleData
->ModStart
= NextModuleBase
;
799 ModuleData
->ModEnd
= -1;
802 strcpy(NameBuffer
, ModuleName
);
803 ModuleData
->String
= (ULONG_PTR
)NameBuffer
;
805 /* Set the current Module */
806 CurrentModule
= ModuleData
;
808 /* Return Module Base Address */
809 return(ModuleData
->ModStart
);
814 FrLdrCloseModule(ULONG_PTR ModuleBase
,
817 PLOADER_MODULE ModuleData
= CurrentModule
;
819 /* Make sure a module is opened */
822 /* Make sure this is the right module and that it hasn't been closed */
823 if ((ModuleBase
== ModuleData
->ModStart
) && (ModuleData
->ModEnd
== MAXULONG_PTR
)) {
825 /* Close the Module */
826 ModuleData
->ModEnd
= ModuleData
->ModStart
+ ModuleSize
;
828 /* Set the next Module Base and increase the number of modules */
829 NextModuleBase
= ROUND_UP(ModuleData
->ModEnd
, PAGE_SIZE
);
830 LoaderBlock
.ModsCount
++;
832 /* Close the currently opened module */
833 CurrentModule
= NULL
;