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
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 /* Load Address of Next Module */
60 ULONG_PTR NextModuleBase
= 0;
62 /* Currently Opened Module */
63 PLOADER_MODULE CurrentModule
= NULL
;
65 /* Unrelocated Kernel Base in Virtual Memory */
68 /* Wether PAE is to be used or not */
69 BOOLEAN PaeModeEnabled
;
71 /* Kernel Entrypoint in Physical Memory */
72 ULONG_PTR KernelEntryPoint
;
74 /* Dummy to bring in memmove */
75 PVOID memmove_dummy
= memmove
;
79 LdrGetModuleObject(PCHAR ModuleName
);
83 LdrPEFixupImports(IN PVOID DllBase
,
86 VOID
PpcInitializeMmu(int max
);
88 /* FUNCTIONS *****************************************************************/
94 * Prepares the system for loading the Kernel.
97 * Magic - Multiboot Magic
107 typedef void (*KernelEntryFn
)( void * );
109 int MmuPageMiss(int trapCode
, ppc_trap_frame_t
*trap
)
112 printf("TRAP %x\n", trapCode
);
113 for( i
= 0; i
< 40; i
++ )
114 printf("r[%d] %x\n", i
, trap
->gpr
[i
]);
119 typedef struct _ppc_map_set_t
{
122 ppc_map_info_t
*info
;
125 extern int mmu_handle
;
126 paddr_t
MmuTranslate(paddr_t possibly_virtual
)
130 /* Openfirmware takes liberties with boot-time memory.
131 * if you're in a unitary kernel, it's not as difficult, but since
132 * we rely on loading things into virtual space from here, we need
133 * to detect the mappings so far.
136 args
[0] = possibly_virtual
;
137 args
[1] = 1; /* Marker to tell we want a physical addr */
138 return (paddr_t
)ofw_callmethod_ret("translate", mmu_handle
, 2, args
, 3);
142 /* Other booters don't remap ram */
143 return possibly_virtual
;
149 FrLdrAddPageMapping(ppc_map_set_t
*set
, int proc
, paddr_t phys
, vaddr_t virt
)
152 paddr_t page
= ROUND_DOWN(phys
, (1<<PFN_SHIFT
));
155 virt
= ROUND_DOWN(page
, (1<<PFN_SHIFT
));
157 virt
= ROUND_DOWN(virt
, (1<<PFN_SHIFT
));
159 page
= MmuTranslate(page
);
161 //printf("Mapping virt [%x] to phys [%x (from) %x]\n", virt, page, phys);
163 for( j
= 0; j
< set
->usecount
; j
++ )
165 if(set
->info
[j
].addr
== page
) return;
171 set
->info
= MmAllocateMemory(0x80 * sizeof(*set
->info
));
173 else if (set
->mapsize
<= set
->usecount
)
175 ppc_map_info_t
*newinfo
= MmAllocateMemory(set
->mapsize
* 2 * sizeof(*set
->info
));
176 memcpy(newinfo
, set
->info
, set
->mapsize
* sizeof(*set
->info
));
177 MmFreeMemory(set
->info
);
182 set
->info
[set
->usecount
].flags
= MMU_ALL_RW
;
183 set
->info
[set
->usecount
].proc
= proc
;
184 set
->info
[set
->usecount
].addr
= virt
;
185 set
->info
[set
->usecount
].phys
= page
;
189 extern int _start
[], _end
[];
193 FrLdrStartup(ULONG Magic
)
195 ULONG_PTR i
, tmp
, OldModCount
= 0;
197 CHAR ModulesTreated
[64] = { 0 };
198 ULONG NumberOfEntries
= 0, UsedEntries
= 0;
199 PPAGE_LOOKUP_TABLE_ITEM FreeLdrMap
= MmGetMemoryMap(&NumberOfEntries
);
200 ppc_map_set_t memmap
= { };
202 printf("FrLdrStartup\n");
205 __asm__("mfmsr %0" : "=r" (tmp
));
207 __asm__("mtmsr %0" : : "r" (tmp
));
209 while(OldModCount
!= LoaderBlock
.ModsCount
)
211 printf("Added %d modules last pass\n",
212 LoaderBlock
.ModsCount
- OldModCount
);
214 OldModCount
= LoaderBlock
.ModsCount
;
216 for(i
= 0; i
< LoaderBlock
.ModsCount
; i
++)
218 if (!ModulesTreated
[i
])
220 ModulesTreated
[i
] = 1;
221 ModHeader
= ((PCHAR
)reactos_modules
[i
].ModStart
);
222 if(ModHeader
[0] == 'M' && ModHeader
[1] == 'Z')
224 ((PVOID
)reactos_modules
[i
].ModStart
,
225 (PCHAR
)reactos_modules
[i
].String
);
230 printf("Starting mmu\n");
234 printf("Allocating vsid 0 (kernel)\n");
235 MmuAllocVsid(0, 0xff00);
237 /* We'll use vsid 1 for freeldr (expendable) */
238 printf("Allocating vsid 1 (freeldr)\n");
239 MmuAllocVsid(1, 0xff);
241 printf("Mapping Freeldr Code (%x-%x)\n", _start
, _end
);
243 /* Map memory zones */
245 for( i
= (int)_start
;
247 i
+= (1<<PFN_SHIFT
) ) {
248 FrLdrAddPageMapping(&memmap
, 1, i
, 0);
251 printf("KernelBase %x\n", KernelBase
);
253 /* Heap pages -- this gets the entire freeldr heap */
254 for( i
= 0; i
< NumberOfEntries
; i
++ ) {
256 if (FreeLdrMap
[i
].PageAllocated
== LoaderSystemCode
) {
258 if (tmp
>= (ULONG
)KernelMemory
&&
259 tmp
< (ULONG
)KernelMemory
+ KernelMemorySize
) {
260 FrLdrAddPageMapping(&memmap
, 0, tmp
, KernelBase
+ tmp
- (ULONG
)KernelMemory
);
262 FrLdrAddPageMapping(&memmap
, 1, tmp
, 0);
267 MmuMapPage(memmap
.info
, memmap
.usecount
);
269 printf("Finished Mapping the Freeldr Heap (used %d pages)\n", UsedEntries
);
271 printf("Setting initial segments\n");
273 MmuSetVsid(8, 16, 0);
275 printf("Segments set!\n");
277 MmuTurnOn((KernelEntryFn
)KernelEntryPoint
, &LoaderBlock
);
287 * Configures PAE on a MP System, and sets the PDBR if it's supported, or if
291 * Magic - Multiboot Magic
302 FrLdrSetupPae(ULONG Magic
)
310 * Gets the Kernel Base to use.
318 * Sets both the FreeLdr internal variable as well as the one which
319 * will be used by the Kernel.
324 FrLdrGetKernelBase(VOID
)
328 /* Default kernel base at 2GB */
329 KernelBase
= 0x80800000;
332 LoaderBlock
.KernelBase
= 0x80000000;
334 /* Read Command Line */
335 p
= (PCHAR
)LoaderBlock
.CommandLine
;
336 while ((p
= strchr(p
, '/')) != NULL
) {
339 if (!_strnicmp(p
+ 1, "3GB", 3)) {
341 /* Make sure there's nothing following it */
342 if (p
[4] == ' ' || p
[4] == 0) {
345 KernelBase
= 0xE0000000;
346 LoaderBlock
.KernelBase
= 0xC0000000;
358 * Determines whether PAE mode shoudl be enabled or not.
372 FrLdrGetPaeMode(VOID
)
377 * FrLdrSetupPageDirectory
380 * Sets up the ReactOS Startup Page Directory.
389 * We are setting PDEs, but using the equvivalent (for our purpose) PTE structure.
390 * As such, please note that PageFrameNumber == PageEntryNumber.
395 FrLdrSetupPageDirectory(VOID
)
403 * Loads the indicated elf image as PE. The target will appear to be
404 * a PE image whose ImageBase has ever been KernelAddr.
407 * Image -- File to load
408 * ImageName -- Name of image for the modules list
409 * MemLoadAddr -- Freeldr address of module
410 * KernelAddr -- Kernel address of module
412 #define ELF_SECTION(n) ((Elf32_Shdr*)(sptr + (n * shsize)))
413 #define COFF_FIRST_SECTION(h) ((PIMAGE_SECTION_HEADER) ((DWORD)h+FIELD_OFFSET(IMAGE_NT_HEADERS,OptionalHeader)+(SWAPW(((PIMAGE_NT_HEADERS)(h))->FileHeader.SizeOfOptionalHeader))))
417 FrLdrMapModule(FILE *KernelImage
, PCHAR ImageName
, PCHAR MemLoadAddr
, ULONG KernelAddr
)
419 PIMAGE_DOS_HEADER ImageHeader
= 0;
420 PIMAGE_NT_HEADERS NtHeader
= 0;
421 PIMAGE_SECTION_HEADER Section
;
425 PLOADER_MODULE ModuleData
;
426 int phsize
, phnum
, shsize
, shnum
, relsize
, SectionAddr
= 0;
432 TempName
= strrchr(ImageName
, '\\');
433 if(TempName
) TempName
++; else TempName
= (LPSTR
)ImageName
;
434 ModuleData
= LdrGetModuleObject(TempName
);
442 KernelAddr
= (ULONG
)NextModuleBase
- (ULONG
)KernelMemory
+ KernelBase
;
444 MemLoadAddr
= (PCHAR
)NextModuleBase
;
446 ModuleData
= &reactos_modules
[LoaderBlock
.ModsCount
];
447 //printf("Loading file (elf at %x)\n", KernelAddr);
449 /* Load the first 1024 bytes of the kernel image so we can read the PE header */
450 if (!FsReadFile(KernelImage
, sizeof(ehdr
), NULL
, &ehdr
)) {
452 /* Fail if we couldn't read */
453 printf("Couldn't read the elf header\n");
457 /* Start by getting elf headers */
458 phsize
= ehdr
.e_phentsize
;
459 phnum
= ehdr
.e_phnum
;
460 shsize
= ehdr
.e_shentsize
;
461 shnum
= ehdr
.e_shnum
;
462 sptr
= (PCHAR
)MmHeapAlloc(shnum
* shsize
);
464 /* Read section headers */
465 FsSetFilePointer(KernelImage
, ehdr
.e_shoff
);
466 FsReadFile(KernelImage
, shsize
* shnum
, NULL
, sptr
);
468 /* Now we'll get the PE Header */
469 for( i
= 0; i
< shnum
; i
++ )
471 shdr
= ELF_SECTION(i
);
474 /* Find the PE Header */
475 if (shdr
->sh_type
== TYPE_PEHEADER
)
477 FsSetFilePointer(KernelImage
, shdr
->sh_offset
);
478 FsReadFile(KernelImage
, shdr
->sh_size
, NULL
, MemLoadAddr
);
479 ImageHeader
= (PIMAGE_DOS_HEADER
)MemLoadAddr
;
480 NtHeader
= (PIMAGE_NT_HEADERS
)((PCHAR
)MemLoadAddr
+ SWAPD(ImageHeader
->e_lfanew
));
482 printf("NtHeader at %x\n", SWAPD(ImageHeader
->e_lfanew
));
483 printf("SectionAlignment %x\n",
484 SWAPD(NtHeader
->OptionalHeader
.SectionAlignment
));
485 SectionAddr
= ROUND_UP
486 (shdr
->sh_size
, SWAPD(NtHeader
->OptionalHeader
.SectionAlignment
));
487 printf("Header ends at %x\n", SectionAddr
);
495 printf("No peheader section encountered :-(\n");
501 printf("DOS SIG: %s\n", (PCHAR
)MemLoadAddr
);
505 /* Save the Image Base */
506 NtHeader
->OptionalHeader
.ImageBase
= SWAPD(KernelAddr
);
508 /* Load the file image */
509 Section
= COFF_FIRST_SECTION(NtHeader
);
510 SectionCount
= SWAPW(NtHeader
->FileHeader
.NumberOfSections
);
512 /* Walk each section */
513 for (i
=0; i
< SectionCount
; i
++, Section
++)
515 shdr
= ELF_SECTION((SWAPD(Section
->PointerToRawData
)+1));
517 shdr
->sh_addr
= SectionAddr
= SWAPD(Section
->VirtualAddress
);
518 shdr
->sh_addr
+= KernelAddr
;
520 Section
->PointerToRawData
= SWAPD((Section
->VirtualAddress
- KernelAddr
));
522 if (shdr
->sh_type
!= SHT_NOBITS
)
525 printf("Loading section %d at %x (real: %x:%d)\n", i
, KernelAddr
+ SectionAddr
, MemLoadAddr
+SectionAddr
, shdr
->sh_size
);
526 FsSetFilePointer(KernelImage
, shdr
->sh_offset
);
527 FsReadFile(KernelImage
, shdr
->sh_size
, NULL
, MemLoadAddr
+ SectionAddr
);
532 printf("BSS section %d at %x\n", i
, KernelAddr
+ SectionAddr
);
533 memset(MemLoadAddr
+ SectionAddr
, 0,
534 ROUND_UP(shdr
->sh_size
,
535 SWAPD(NtHeader
->OptionalHeader
.SectionAlignment
)));
539 ImageSize
= SWAPD(NtHeader
->OptionalHeader
.SizeOfImage
);
540 printf("Total image size is %x\n", ImageSize
);
542 /* Handle relocation sections */
543 for (i
= 0; i
< shnum
; i
++) {
544 Elf32_Rela reloc
= { };
547 int numreloc
, relstart
, targetSection
;
549 PCHAR RelocSection
, SymbolSection
;
551 shdr
= ELF_SECTION(i
);
552 /* Only relocs here */
553 if((shdr
->sh_type
!= SHT_REL
) &&
554 (shdr
->sh_type
!= SHT_RELA
)) continue;
556 relstart
= shdr
->sh_offset
;
557 relsize
= shdr
->sh_type
== SHT_RELA
? 12 : 8;
558 numreloc
= shdr
->sh_size
/ relsize
;
559 targetSection
= shdr
->sh_info
;
561 if (!ELF_SECTION(targetSection
)->sh_addr
) continue;
563 RelocSection
= MmHeapAlloc(shdr
->sh_size
);
564 FsSetFilePointer(KernelImage
, relstart
);
565 FsReadFile(KernelImage
, shdr
->sh_size
, NULL
, RelocSection
);
567 /* Get the symbol section */
568 shdr
= ELF_SECTION(shdr
->sh_link
);
570 SymbolSection
= MmHeapAlloc(shdr
->sh_size
);
571 FsSetFilePointer(KernelImage
, shdr
->sh_offset
);
572 FsReadFile(KernelImage
, shdr
->sh_size
, NULL
, SymbolSection
);
574 for(j
= 0; j
< numreloc
; j
++)
579 memcpy(&reloc
, RelocSection
+ (j
* relsize
), sizeof(reloc
));
582 memcpy(&symbol
, SymbolSection
+ (ELF32_R_SYM(reloc
.r_info
) * sizeof(symbol
)), sizeof(symbol
));
584 /* Compute addends */
585 S
= symbol
.st_value
+ ELF_SECTION(symbol
.st_shndx
)->sh_addr
;
587 P
= reloc
.r_offset
+ ELF_SECTION(targetSection
)->sh_addr
;
590 printf("Symbol[%d] %d -> %d(%x:%x) -> %x(+%x)@%x\n",
591 ELF32_R_TYPE(reloc
.r_info
),
592 ELF32_R_SYM(reloc
.r_info
),
594 ELF_SECTION(symbol
.st_shndx
)->sh_addr
,
601 Target32
= (ULONG
*)(((PCHAR
)MemLoadAddr
) + (P
- KernelAddr
));
602 Target16
= (USHORT
*)Target32
;
604 switch (ELF32_R_TYPE(reloc
.r_info
))
612 *Target32
= S
+ A
- P
;
614 case R_PPC_UADDR32
: /* Special: Treat as RVA */
615 *Target32
= S
+ A
- KernelAddr
;
618 *Target32
= (ADDR24_MASK
& (S
+A
)) | (*Target32
& ~ADDR24_MASK
);
621 *Target32
= (ADDR24_MASK
& (S
+A
-P
)) | (*Target32
& ~ADDR24_MASK
);
623 case R_PPC_ADDR16_LO
:
626 case R_PPC_ADDR16_HA
:
627 *Target16
= (S
+ A
+ 0x8000) >> 16;
634 printf("reloc[%d:%x]: (type %x sym %d val %d) off %x add %x (old %x new %x)\n",
636 ((ULONG
)Target32
) - ((ULONG
)MemLoadAddr
),
637 ELF32_R_TYPE(reloc
.r_info
),
638 ELF32_R_SYM(reloc
.r_info
),
640 reloc
.r_offset
, reloc
.r_addend
,
645 MmHeapFree(SymbolSection
);
646 MmHeapFree(RelocSection
);
651 ModuleData
->ModStart
= (ULONG
)MemLoadAddr
;
652 /* Increase the next Load Base */
653 NextModuleBase
= ROUND_UP((ULONG
)MemLoadAddr
+ ImageSize
, PAGE_SIZE
);
654 ModuleData
->ModEnd
= NextModuleBase
;
655 ModuleData
->String
= (ULONG
)MmAllocateMemory(strlen(ImageName
)+1);
656 strcpy((PCHAR
)ModuleData
->String
, ImageName
);
657 printf("Module %s (%x-%x) next at %x\n",
659 ModuleData
->ModStart
,
662 LoaderBlock
.ModsCount
++;
672 * Maps the Kernel into memory, does PE Section Mapping, initalizes the
673 * uninitialized data sections, and relocates the image.
676 * KernelImage - FILE Structure representing the ntoskrnl image file.
679 * TRUE if the Kernel was mapped.
687 FrLdrMapKernel(FILE *KernelImage
)
689 /* Get Kernel Base */
690 FrLdrGetKernelBase();
692 /* Allocate kernel memory */
693 KernelMemory
= MmAllocateMemory(KernelMemorySize
);
695 return FrLdrMapModule(KernelImage
, "ntoskrnl.exe", KernelMemory
, KernelBase
);
700 FrLdrLoadModule(FILE *ModuleImage
,
704 ULONG LocalModuleSize
;
705 ULONG_PTR ThisModuleBase
= NextModuleBase
;
706 PLOADER_MODULE ModuleData
;
710 /* Get current module data structure and module name string array */
711 ModuleData
= &reactos_modules
[LoaderBlock
.ModsCount
];
713 /* Get only the Module Name */
716 TempName
= strchr(ModuleName
, '\\');
719 ModuleName
= TempName
+ 1;
723 NameBuffer
= reactos_module_strings
[LoaderBlock
.ModsCount
];
726 /* Get Module Size */
727 LocalModuleSize
= FsGetFileSize(ModuleImage
);
729 /* Fill out Module Data Structure */
730 ModuleData
->ModStart
= NextModuleBase
;
731 ModuleData
->ModEnd
= NextModuleBase
+ LocalModuleSize
;
734 strcpy(NameBuffer
, ModuleName
);
735 ModuleData
->String
= (ULONG_PTR
)NameBuffer
;
737 /* Load the file image */
738 FsReadFile(ModuleImage
, LocalModuleSize
, NULL
, (PVOID
)NextModuleBase
);
740 /* Move to next memory block and increase Module Count */
741 NextModuleBase
= ROUND_UP(ModuleData
->ModEnd
, PAGE_SIZE
);
742 LoaderBlock
.ModsCount
++;
744 /* Return Module Size if required */
745 if (ModuleSize
!= NULL
) {
746 *ModuleSize
= LocalModuleSize
;
749 printf("Module %s (%x-%x) next at %x\n",
751 ModuleData
->ModStart
,
755 return ThisModuleBase
;
760 FrLdrMapImage(IN
FILE *Image
, IN PCHAR ShortName
, IN ULONG ImageType
)
764 printf("Loading image %s (type %d)\n", ShortName
, ImageType
);
768 if(FrLdrMapKernel(Image
))
769 Result
= (PVOID
)KernelMemory
;
773 PVOID ModuleBase
= (PVOID
)NextModuleBase
;
775 if(FrLdrMapModule(Image
, ShortName
, 0, 0))
783 FrLdrCreateModule(LPCSTR ModuleName
)
785 PLOADER_MODULE ModuleData
;
788 /* Get current module data structure and module name string array */
789 ModuleData
= &reactos_modules
[LoaderBlock
.ModsCount
];
790 NameBuffer
= reactos_module_strings
[LoaderBlock
.ModsCount
];
792 /* Set up the structure */
793 ModuleData
->ModStart
= NextModuleBase
;
794 ModuleData
->ModEnd
= -1;
797 strcpy(NameBuffer
, ModuleName
);
798 ModuleData
->String
= (ULONG_PTR
)NameBuffer
;
800 /* Set the current Module */
801 CurrentModule
= ModuleData
;
803 /* Return Module Base Address */
804 return(ModuleData
->ModStart
);
809 FrLdrCloseModule(ULONG_PTR ModuleBase
,
812 PLOADER_MODULE ModuleData
= CurrentModule
;
814 /* Make sure a module is opened */
817 /* Make sure this is the right module and that it hasn't been closed */
818 if ((ModuleBase
== ModuleData
->ModStart
) && (ModuleData
->ModEnd
== MAXULONG_PTR
)) {
820 /* Close the Module */
821 ModuleData
->ModEnd
= ModuleData
->ModStart
+ ModuleSize
;
823 /* Set the next Module Base and increase the number of modules */
824 NextModuleBase
= ROUND_UP(ModuleData
->ModEnd
, PAGE_SIZE
);
825 LoaderBlock
.ModsCount
++;
827 /* Close the currently opened module */
828 CurrentModule
= NULL
;