#include #include #include #include #include #include #include "arc.h" #include "arcio.h" #include "arcmem.h" #include "coff.h" #include "ppcinst.h" #include "oslhooks.h" #include "runtime.h" enum { STYP_REG = 0x00000000, STYP_TEXT = 0x00000020, STYP_INIT = 0x80000000, STYP_RDATA = 0x00000100, STYP_DATA = 0x00000040, STYP_LIT8 = 0x08000000, STYP_LIT4 = 0x10000000, STYP_SDATA = 0x00000200, STYP_SBSS = 0x00000080, STYP_BSS = 0x00000400, STYP_LIB = 0x40000000, STYP_UCODE = 0x00000800, S_NRELOC_OVFL = 0x20000000, SECTION_REQUIRES_LOAD = STYP_TEXT | STYP_INIT | STYP_RDATA | STYP_DATA | STYP_SDATA, SECTION_REQUIRES_ZERO = STYP_BSS | STYP_SBSS, SECTION_REQUIRES_LOAD_PE = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA, SECTION_REQUIRES_ZERO_PE = IMAGE_SCN_CNT_UNINITIALIZED_DATA }; enum { R_SN_TEXT = 1, R_SN_RDATA, R_SN_DATA, R_SN_SDATA, R_SN_SBSS, R_SN_BSS, R_SN_INIT, R_SN_LIT8, R_SN_LIT4, R_SN_MAX }; enum { SECTOR_SIZE = 0x200 }; typedef struct _SECTION_RELOCATION_ENTRY { ULONG FixupValue; ULONG PointerToRelocations; USHORT NumberOfRelocations; } SECTION_RELOCATION_ENTRY, * PSECTION_RELOCATION_ENTRY; #define MAX_ARGUMENT (512 - sizeof(ULONG) - 16*sizeof(PUCHAR)) typedef struct _SAVED_ARGUMENTS { ULONG Argc; U32LE Argv[16]; U32LE Envp[16]; UCHAR Arguments[MAX_ARGUMENT]; } SAVED_ARGUMENTS, * PSAVED_ARGUMENTS; static SAVED_ARGUMENTS SavedArgs; static PVOID ScratchAddress = NULL; static bool s_OldCoffLoaded = false; static bool s_MemoryMapFixedForOldCoff = false; static bool s_MemoryMapFixedForAnyCoff = false; static inline ARC_FORCEINLINE ULONG ScratchEnd() { return (ULONG)ScratchAddress + ARCFW_MEM2_SIZE; } PVOID ArcLoadGetScratchAddress(void) { return ScratchAddress; } #define mfpvr() ({u32 _rval; \ __asm__ __volatile__ ("mfpvr %0" : "=r"(_rval)); _rval;}) static void InstructionPerformPatch( PU32LE SectionBaseLittle, ULONG Offset ) { ULONG instruction = SectionBaseLittle[Offset].v; static ULONG PvrVersion = 0; if (PvrVersion == 0) PvrVersion = mfpvr() >> 16; // NT 4 osloader checks pvr, if it's not in hardcoded list then panic // so patch this check :) if (PvrVersion > 9 && PvrVersion != 20) { PPC_INSTRUCTION Insn; Insn.Long = instruction; if (Insn.Primary_Op == X31_OP && Insn.XFXform_XO == MFSPR_OP && Insn.XFXform_spr == 1000) { // Make NT believe this is an Arthur derivative // addis rx, 0, 8 PPC_INSTRUCTION Patch; Patch.Long = 0; Patch.Primary_Op = ADDIS_OP; Patch.Dform_RT = Insn.XFXform_RT; Patch.Dform_RA = 0; Patch.Dform_D = 8; instruction = Patch.Long; SectionBaseLittle[Offset].v = instruction; } } } static ARC_STATUS RelocatePEBlock(ULONG VirtualAddress, ULONG Length, PU16LE Block, LONG Diff, bool IsLittleEndian) { for (ULONG Index = 0; Index < Length; Index++) { USHORT Offset = Block[Index].v; USHORT Type = Offset >> 12; Offset &= (ARC_BIT(12) - 1); ULONG FixupVA = VirtualAddress + Offset; PU16BE DataBig = (PU16BE)FixupVA; PU16LE DataLittle = (PU16LE)FixupVA; switch (Type) { case IMAGE_REL_BASED_HIGHLOW: // 32-bit relocation { LONG Base; memcpy(&Base, (PVOID)FixupVA, sizeof(Base)); if (!IsLittleEndian) { Base = SwapEndianness32(Base); } Base += Diff; if (!IsLittleEndian) { Base = SwapEndianness32(Base); } memcpy((PVOID)FixupVA, &Base, sizeof(Base)); } break; case IMAGE_REL_BASED_HIGH: // high 16-bit relocation { ULONG Temp = (IsLittleEndian ? DataLittle->v : DataBig->v) << 16; Temp += Diff; if (IsLittleEndian) DataLittle->v = (Temp >> 16); else DataBig->v = (Temp >> 16); } break; case IMAGE_REL_BASED_HIGHADJ: // high 16-bit relocation with adjustment { if (Index == Length) { // whoops, better not overflow printf("HighAdj relocation overflows table\n"); return _EBADF; } ULONG Temp = (IsLittleEndian ? DataLittle->v : DataBig->v) << 16; Index++; PU16BE BlockBig = (PU16BE)(ULONG)Block; // tfw MS seriously did this Temp += (IsLittleEndian ? Block[Index].v : BlockBig[Index].v); Temp += Diff; Temp += INT16_MAX + 1; if (IsLittleEndian) DataLittle->v = (Temp >> 16); else DataBig->v = (Temp >> 16); } break; case IMAGE_REL_BASED_LOW: // low 16-bit relocation { ULONG Temp = (IsLittleEndian ? DataLittle->v : DataBig->v); Temp += Diff; if (IsLittleEndian) DataLittle->v = Temp; else DataBig->v = Temp; } break; //case IMAGE_REL_BASED_MIPS_JMPADDR: // not valid for PowerPC, osloader does it for all architectures for some reason case IMAGE_REL_BASED_ABSOLUTE: // no fixup required break; default: // invalid for powerpc printf("Invalid relocation %x\n", Type); return _EBADF; } } // all good return _ESUCCESS; } static ARC_STATUS RelocatePE(ULONG ImageBase, PIMAGE_FILE_HEADER FileHeader, PIMAGE_OPTIONAL_HEADER OptionalHeader, PIMAGE_SECTION_HEADER Sections) { bool IsLittleEndian = FileHeader->Machine == IMAGE_FILE_MACHINE_POWERPC; PIMAGE_DATA_DIRECTORY RelocDir = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; bool HasRelocations = RelocDir->VirtualAddress != 0 && RelocDir->Size != 0; PIMAGE_DATA_DIRECTORY ExceptionDir = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]; bool HasExceptions = ExceptionDir->VirtualAddress != 0 && ExceptionDir->Size != 0; if (ImageBase == OptionalHeader->ImageBase) { // no need to relocate HasRelocations = false; } else if (HasRelocations) { // no relocations yet base moved printf("Needs relocation but image has none\n"); return _EBADF; } if (!HasRelocations && !IsLittleEndian) { // nothing needs to be done here return _ESUCCESS; } ARC_STATUS Status; // apply all PE relocations first. remember endianness. if (HasRelocations) { ULONG OldBase = OptionalHeader->ImageBase; PIMAGE_BASE_RELOCATION Block = (PIMAGE_BASE_RELOCATION)(ImageBase + RelocDir->VirtualAddress); ULONG BlockStart = (ULONG)Block; ULONG RelocLength = RelocDir->Size; for (ULONG Offset = 0; Offset < RelocLength;) { Offset += Block->SizeOfBlock; ULONG SizeOfBlock = Block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION); PU16LE Entries = (PU16LE)(ULONG)&Block[1]; Status = RelocatePEBlock( ImageBase + Block->VirtualAddress, SizeOfBlock / sizeof(USHORT), Entries, ImageBase - OldBase, IsLittleEndian ); if (ARC_FAIL(Status)) return Status; Block = (PIMAGE_BASE_RELOCATION)(BlockStart + Offset); } } // Instructions need patching here. { // Exception data is never present, just check every 32 bit value of every section containing code. for (int i = 0; i < FileHeader->NumberOfSections - 1; i++) { if ((Sections[i].Characteristics & IMAGE_SCN_CNT_CODE) == 0) continue; ULONG SectionBase = ImageBase + Sections[i].VirtualAddress; PU32BE SectionBaseBig = (PU32BE)SectionBase; PU32LE SectionBaseLittle = (PU32LE)SectionBase; for (ULONG off = 0; off < Sections[i].SizeOfRawData / sizeof(ULONG); off++) { InstructionPerformPatch(SectionBaseLittle, off); } } } return _ESUCCESS; } // Reads the COFF symbol table and string table to scratch buffer static ARC_STATUS ReadCoffSymbolTable(ULONG FileId, ULONG PointerToSymbolTable, ULONG NumberOfSymbols) { PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS(); // If no scratch address was allocated, error if (ScratchAddress == NULL) { return _ENOMEM; } PULONG SymbolCount = (PULONG)ScratchAddress; *SymbolCount = NumberOfSymbols; PVOID ScratchAddress = (SymbolCount + 1); // Get file length FILE_INFORMATION Info; ARC_STATUS Status = Api->GetFileInformationRoutine(FileId, &Info); if (ARC_FAIL(Status)) return Status; // Seek to symbol table LARGE_INTEGER Offset = Int64ToLargeInteger(PointerToSymbolTable); Status = Api->SeekRoutine(FileId, &Offset, SeekAbsolute); if (ARC_FAIL(Status)) return Status; int64_t ExpectedLength64 = Info.EndingAddress.QuadPart - PointerToSymbolTable; // Needs to fit in ULONG if (ExpectedLength64 > UINT32_MAX) return _E2BIG; ULONG ExpectedLength = (ULONG)ExpectedLength64; // If the symbol table is too long for the scratch memory, error if ((ULONG)ScratchAddress + ExpectedLength > ScratchEnd()) { return _ENOMEM; } // Read to scratch memory U32LE Count; Status = Api->ReadRoutine(FileId, ScratchAddress, ExpectedLength, &Count); if (ARC_FAIL(Status)) return Status; if (Count.v != ExpectedLength) return _EBADF; return _ESUCCESS; } // Gets the address of a symbol using the loaded COFF symbol table in scratch static PVOID GetSymbolEntryCoff(ULONG ImageBase, const char* SymbolName) { PIMAGE_SYMBOL SymbolTable = (PIMAGE_SYMBOL)((ULONG)ScratchAddress + sizeof(ULONG)); ULONG SymbolCount = *(PULONG)ScratchAddress; PCHAR StringTable = (PCHAR)&SymbolTable[SymbolCount]; // for each symbol for (ULONG i = 0; i < SymbolCount; i += (1 + SymbolTable[i].NumberOfAuxSymbols)) { // storage class must be external for a function if (SymbolTable[i].StorageClass != IMAGE_SYM_CLASS_EXTERNAL) continue; // symbol cannot be absolute if (SymbolTable[i].SectionNumber == 0) continue; if (SymbolTable[i].N.Name.Short == 0) { // offset into string-table if (!strcmp(SymbolName, &StringTable[SymbolTable[i].N.Name.Long])) { // found it return (PVOID)(ImageBase + SymbolTable[i].Value); } continue; } else { // short symbol if ( !memcmp(SymbolName, SymbolTable[i].N.ShortName, sizeof(SymbolTable[i].N.ShortName)) || // 8 character names aren't null terminated !strcmp(SymbolName, SymbolTable[i].N.ShortName) ) { // found it return (PVOID)(ImageBase + SymbolTable[i].Value); } continue; } } return NULL; } static ARC_STATUS ArcLoadImpl( IN PCHAR ImagePath, IN ULONG TopAddress, OUT PULONG EntryAddress, OUT PULONG LowAddress, OUT PULONG ImageBasePage, OUT PULONG ImageSizePage ) { if (EntryAddress == NULL || LowAddress == NULL) { printf("Required pointers are NULL\n"); return _EFAULT; } PSECTION_RELOCATION_ENTRY RelocationTable = NULL; BYTE LocalBuffer[2 * SECTOR_SIZE + 0x40]; // Align the COFF header to a dcache line. PBYTE LocalPointer = (PVOID)(((ULONG)(&LocalBuffer[DCACHE_LINE_SIZE - 1])) & ~(DCACHE_LINE_SIZE - 1)); // Initialise the entry address to NULL. *EntryAddress = 0; PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS(); // Open the file to load readonly. U32LE _FileId; ARC_STATUS Status = Api->OpenRoutine(ImagePath, ArcOpenReadOnly, &_FileId); if (ARC_FAIL(Status)) { return Status; } ULONG FileId = _FileId.v; do { // Read two sectors to get the COFF header. // Two sectors can fit COFF file header + full PE optional header + 19 section headers. U32LE Count; Status = Api->ReadRoutine(FileId, LocalPointer, SECTOR_SIZE * 2, &Count); if (ARC_FAIL(Status)) break; if (Count.v != SECTOR_SIZE * 2) { printf("Tried to read %x bytes and read %x bytes", SECTOR_SIZE * 2, Count.v); Status = _EFAULT; break; } PIMAGE_FILE_HEADER FileHeader = (PIMAGE_FILE_HEADER)LocalPointer; PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)LocalPointer; bool IsFullPE = (DosHeader->e_magic == IMAGE_DOS_SIGNATURE); if (IsFullPE) { // this is full PE Status = _EBADF; break; #if 0 if ((DosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)) > Count.v) { printf("Bad PE\n"); Status = _EBADF; break; } NtHeaders = (PIMAGE_NT_HEADERS)(LocalPointer + DosHeader->e_lfanew); FileHeader = &NtHeaders->FileHeader; #endif } if ( // Don't accept any COFF that isn't for PPC(LE). (FileHeader->Machine != IMAGE_FILE_MACHINE_POWERPC) || // && FileHeader->Machine != IMAGE_FILE_MACHINE_POWERPCBE) || // Don't accept a COFF that isn't executable. (FileHeader->Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0 || // Don't accept a COFF with an optional header that's too small. FileHeader->SizeOfOptionalHeader < sizeof(IMAGE_OPTIONAL_HEADER) // sizeof(IMAGE_OPTIONAL_HEADER_COFF) ) { printf("Bad COFF\n"); Status = _EBADF; break; } // NT kernels before (somewhere between 1234 and 1314) hardcode setting BATs to the first 8MB of RAM. // So we need to set our memchunks up to handle this if we load an older NT bootloader. // 1314 bootloaders were compiled mid-April 1996. // NT 3.51 SP5 provides a newer veneer, but not a newer osloader. // Thus, we can rely on the TimeDateStamp for this. bool IsOldCoff = FileHeader->TimeDateStamp < 828316800; // 1996-04-01 bool IsLittleEndian = FileHeader->Machine == IMAGE_FILE_MACHINE_POWERPC; //bool HasPEOptionalHeader = FileHeader->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER); bool HasRelocations = (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) == 0; bool HasExceptions = false; USHORT NumberOfSections = FileHeader->NumberOfSections; PIMAGE_OPTIONAL_HEADER OptionalHeader = (PIMAGE_OPTIONAL_HEADER)&FileHeader[1]; PIMAGE_SECTION_HEADER Sections = (PIMAGE_SECTION_HEADER)((size_t)OptionalHeader + FileHeader->SizeOfOptionalHeader); //if (HasPEOptionalHeader) { if ( // Optional header magic must be PE32 OptionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC || // With the full PE optional header, the header size is known. // If this is greater than what was read, then don't accept this file. OptionalHeader->SizeOfHeaders > Count.v ) { printf("Bad opthdr\n"); Status = _EBADF; break; } // If base relocation directory addr or size is 0, then this executable has no relocations PIMAGE_DATA_DIRECTORY RelocDir = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; HasRelocations = RelocDir->VirtualAddress != 0 && RelocDir->Size != 0; PIMAGE_DATA_DIRECTORY ExceptionDir = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]; HasExceptions = ExceptionDir->VirtualAddress != 0 && ExceptionDir->Size != 0; } #if 0 else if (HasRelocations) { // Walk through all sections, if one section has relocations then reloc is allowed. HasRelocations = false; for (int i = 0; i < NumberOfSections; i++) { if (Sections[i].NumberOfRelocations != 0) { HasRelocations = true; break; } } } #endif ULONG SizeOfImage; ULONG ImageBase; //if (HasPEOptionalHeader) { // Ignore the caller. // This is a COFF with a full PE optional header, and has a known base. ImageBase = OptionalHeader->ImageBase & ~0xC0000000; SizeOfImage = OptionalHeader->SizeOfImage; } #if 0 else if (HasRelocations) { SizeOfImage = OptionalHeader->SizeOfCode + OptionalHeader->SizeOfInitializedData + OptionalHeader->SizeOfUninitializedData; ImageBase = (TopAddress - SizeOfImage) - ~(PAGE_SIZE - 1); } else { // a COFF with no relocations ImageBase = OptionalHeader->BaseOfCode; SizeOfImage = OptionalHeader->BaseOfData + OptionalHeader->SizeOfInitializedData - ImageBase; } #endif //ULONG ImageBasePage = (ImageBase & 0x1FFFFFFF) >> PAGE_SHIFT; // If the last section is named ".debug", don't load it if (!strcmp((const char*)Sections[NumberOfSections - 1].Name, ".debug")) { NumberOfSections--; SizeOfImage -= Sections[NumberOfSections].SizeOfRawData; OptionalHeader->SizeOfImage = SizeOfImage; FileHeader->NumberOfSections = NumberOfSections; } if (NumberOfSections == 0) { // nothing to load? printf("No sections to load\n"); Status = _EBADF; break; } ULONG ImageBaseK0 = (ULONG)MEM_PHYSICAL_TO_K0(ImageBase); *LowAddress = ImageBaseK0; *EntryAddress = ImageBaseK0 + OptionalHeader->AddressOfEntryPoint - OptionalHeader->BaseOfCode; if (ImageBasePage) *ImageBasePage = ImageBase >> PAGE_SHIFT; if (ImageSizePage) *ImageSizePage = (SizeOfImage + PAGE_SIZE - 1) >> PAGE_SHIFT; // Allocate and zero the relocation table if needed. #if 0 if (HasRelocations && !HasPEOptionalHeader) { size_t LenRelocs = sizeof(RelocationTable[0]) * NumberOfSections; RelocationTable = malloc(LenRelocs); if (RelocationTable == NULL) { Status = _ENOMEM; break; } memset(RelocationTable, 0, LenRelocs); } #endif ULONG SectionOffset = 0; // Load sections into memory. USHORT NumberOfSectionsToLoad = NumberOfSections; //if (IsLittleEndian) NumberOfSectionsToLoad--; for (int i = 0; i < NumberOfSectionsToLoad; i++) { ULONG Flags = Sections[i].Characteristics; ULONG SectionBase = ImageBaseK0 + SectionOffset; #if 0 if (HasRelocations && !HasPEOptionalHeader) { PSECTION_RELOCATION_ENTRY RelocEntry = &RelocationTable[i]; RelocEntry->FixupValue = SectionBase - Sections[i].VirtualAddress; RelocEntry->NumberOfRelocations = Sections[i].NumberOfRelocations; RelocEntry->PointerToRelocations = Sections[i].PointerToRelocations; } else #endif { SectionBase = Sections[i].VirtualAddress; //if (HasPEOptionalHeader) { SectionBase += ImageBaseK0; } } // If needed, read this section into memory bool IncreaseOffset = false; if ((Flags & SECTION_REQUIRES_LOAD) != 0) { LARGE_INTEGER SeekPosition = Int64ToLargeInteger(Sections[i].PointerToRawData); Status = Api->SeekRoutine(FileId, &SeekPosition, SeekAbsolute); if (ARC_FAIL(Status)) break; Status = Api->ReadRoutine(FileId, (PVOID)SectionBase, Sections[i].SizeOfRawData, &Count); if (ARC_FAIL(Status)) break; if (Count.v != Sections[i].SizeOfRawData) { printf("Tried to read %x bytes and read %x bytes\n", Sections[i].SizeOfRawData, Count.v); Status = _EFAULT; break; } IncreaseOffset = true; } else if ((Flags & SECTION_REQUIRES_ZERO) != 0) { memset((PVOID)SectionBase, 0, Sections[i].SizeOfRawData); IncreaseOffset = true; } if (IncreaseOffset) { SectionOffset += Sections[i].SizeOfRawData; } } if (ARC_FAIL(Status)) break; // Relocate the COFF. #if 0 // Required code patching is done here (to fix overly strict processor checks). Enforce it. if (HasRelocations || IsLittleEndian) #endif { Status = RelocatePE(ImageBaseK0, FileHeader, OptionalHeader, Sections); #if 0 if (HasPEOptionalHeader) { Status = RelocatePE(ImageBaseK0, FileHeader, OptionalHeader, Sections); } else { Status = RelocateCoff(FileId, RelocationTable, NumberOfSections, FileHeader->PointerToSymbolTable); } #endif } if (ARC_FAIL(Status)) break; s_OldCoffLoaded = IsOldCoff; #if 1 // no more hooking any more, no more hooking any more // For a COFF binary with a PE optional header; we need to hook things. if (s_RuntimePointers[RUNTIME_SYSTEM_TYPE].v >= ARTX_SYSTEM_VEGAS) { // Read entire symbol table to scratch memory in DDR Status = ReadCoffSymbolTable(FileId, FileHeader->PointerToSymbolTable, FileHeader->NumberOfSymbols); if (ARC_FAIL(Status)) break; //printf("Image Base: %08x\r\n", ImageBaseK0); // Get address of BlOpen, BlFileTable, BlSetupForNt using COFF symbol table PVOID BlOpen = GetSymbolEntryCoff(ImageBaseK0, "..BlOpen"); PVOID BlFileTable = GetSymbolEntryCoff(ImageBaseK0, "BlFileTable"); PVOID BlSetupForNt = GetSymbolEntryCoff(ImageBaseK0, "..BlSetupForNt"); PVOID BlReadSignature = GetSymbolEntryCoff(ImageBaseK0, "..BlReadSignature"); if (BlOpen != NULL && BlFileTable != NULL && BlSetupForNt != NULL && BlReadSignature != NULL) { // This must be an osloader binary (linked with ARC bootlib) OslHookInit(BlOpen, BlFileTable, BlSetupForNt, BlReadSignature); } } #endif // Flush all caches. Api->FlushAllCachesRoutine(); } while (false); if (RelocationTable != NULL) free(RelocationTable); Api->CloseRoutine(FileId); return Status; } /// /// Loads a COFF executable. /// /// Path of the executable to load. /// Address to load the COFF to /// Entry point is written here /// End address of the loaded executable is written here /// ARC status code static ARC_STATUS ArcLoad( IN PCHAR ImagePath, IN ULONG TopAddress, OUT PU32LE EntryAddress, OUT PU32LE LowAddress ) { ULONG LocalEntry, LocalLow; ARC_STATUS Status = ArcLoadImpl(ImagePath, TopAddress, &LocalEntry, &LocalLow, NULL, NULL); if (ARC_FAIL(Status)) return Status; if (EntryAddress != NULL) EntryAddress->v = LocalEntry; if (LowAddress != NULL) LowAddress->v = LocalLow; return Status; } /// /// Calls the entry point of a previously loaded program. /// /// Entry point to call. /// Stack pointer to set. /// Number of arguments. /// Array of arguments. /// Environment variables. /// ARC status code. static ARC_STATUS ArcInvoke( IN ULONG EntryAddress, IN ULONG StackAddress, IN ULONG Argc, IN PCHAR Argv[], IN PCHAR Envp[] ) { // Both entry point and stack pointer must be aligned to 32 bits. if ((EntryAddress & 3) != 0 || (StackAddress & 3) != 0) { printf("Unaligned entry/stack addrs\n"); return _EFAULT; } // If we have loaded an older COFF executable, fix the memory chunks to set anything > 8MB and < 256MB as LoadedProgram. if (s_OldCoffLoaded && !s_MemoryMapFixedForOldCoff) { PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS(); for ( PMEMORY_DESCRIPTOR MemChunk = Api->MemoryRoutine(NULL); MemChunk != NULL; MemChunk = Api->MemoryRoutine(MemChunk) ) { // Looking for a free memchunk if (MemChunk->MemoryType != MemoryFree) continue; // Starting from 8MB if (MemChunk->BasePage < (0x800000 / PAGE_SIZE)) continue; // Ending at or before 256MB ULONG ChunkEnd = MemChunk->BasePage + MemChunk->PageCount; if (ChunkEnd > (256 * (0x100000 / PAGE_SIZE))) continue; // Mark it as LoadedProgram. // (Motorola and IBM ARC firmware implementations do this) // Older implementations use FirmwareTemporary, but if we have a false positive then newer NT bootloaders will detect LoadedProgram here and treat it as free. MemChunk->MemoryType = MemoryLoadedProgram; } s_MemoryMapFixedForOldCoff = true; } if (!s_MemoryMapFixedForAnyCoff) { PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS(); for ( PMEMORY_DESCRIPTOR MemChunk = Api->MemoryRoutine(NULL); MemChunk != NULL; MemChunk = Api->MemoryRoutine(MemChunk) ) { // Looking for a free memchunk if (MemChunk->MemoryType != MemoryFree) continue; // Starting from 256MB if (MemChunk->BasePage < (0x10000000 / PAGE_SIZE)) continue; // Mark it as FirmwareTemporary MemChunk->MemoryType = MemoryFirmwareTemporary; } s_MemoryMapFixedForAnyCoff = true; } // Stack pointer is otherwise unused, do not change it. PU32LE CallingConv = (PU32LE)EntryAddress; //printf("Entry point: %08x - toc: %08x\r\n", CallingConv[0].v, CallingConv[1].v); // Read from the entry point to make sure it's mapped, yay for having pagetables instead of BATs! *(volatile ULONG*)(CallingConv[0].v); extern void __ArcInvokeImpl(ULONG EntryAddress, ULONG Toc, ULONG Argc, PCHAR Argv[], PCHAR Envp[]); __ArcInvokeImpl(CallingConv[0].v, CallingConv[1].v, Argc, Argv, Envp); return _ESUCCESS; } // Copy arguments to a buffer handled by the ARC firmware implementation static ARC_STATUS CopyArguments(ULONG Argc, PCHAR Argv[], PCHAR Envp[]) { SavedArgs.Argc = Argc; memset(SavedArgs.Arguments, 0, sizeof(SavedArgs.Arguments)); ULONG Length = sizeof(SavedArgs.Arguments); ULONG Offset = 0; for (ULONG Arg = 0; Arg < Argc; Arg++) { ULONG RemainingLength = Length - Offset; int err = snprintf(&SavedArgs.Arguments[Offset], RemainingLength, "%s", Argv[Arg]); if (err < 0) { printf("Could not copy argument %d\n", Arg); return _EFAULT; } if (err >= RemainingLength) return _E2BIG; SavedArgs.Argv[Arg].v = (ULONG)&SavedArgs.Arguments[Offset]; Offset += err; SavedArgs.Arguments[Offset] = 0; Offset++; } memset(SavedArgs.Envp, 0, sizeof(SavedArgs.Envp)); for (ULONG Arg = 0; Envp[Arg] != NULL; Arg++) { SavedArgs.Envp[Arg].v = (ULONG)Envp[Arg]; } return _ESUCCESS; } /// /// Loads and executes a COFF executable. /// /// Path of the executable to load. /// Number of arguments. /// Array of arguments. /// Environment variables. /// ARC status code. static ARC_STATUS ArcExecute( IN PCHAR ImagePath, IN ULONG Argc, IN PCHAR Argv[], IN PCHAR Envp[] ) { PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS(); char CopyPath[260]; // Copy the arguments and path so the provided buffers can be overwritten if needed ARC_STATUS Status = CopyArguments(Argc, Argv, Envp); if (ARC_FAIL(Status)) return Status; int err = snprintf(CopyPath, sizeof(CopyPath), "%s", ImagePath); if (err < 0) return _EFAULT; if (err >= sizeof(CopyPath)) return _E2BIG; // Find an area of memory. for ( PMEMORY_DESCRIPTOR MemChunk = Api->MemoryRoutine(NULL); MemChunk != NULL; MemChunk = Api->MemoryRoutine(MemChunk) ) { // Looking for a free memchunk if (MemChunk->MemoryType != MemoryFree) continue; // At least 4MB if (MemChunk->PageCount < (ARC_MB(4) / PAGE_SIZE)) continue; // Try to load here ULONG EntryPoint, BaseAddress, ImageBasePage, ImageSizePage; Status = ArcLoadImpl(CopyPath, MemChunk->BasePage + MemChunk->PageCount, &EntryPoint, &BaseAddress, &ImageBasePage, &ImageSizePage); if (ARC_FAIL(Status)) { if (Status != _ENOMEM) return Status; continue; } // Find the memory descriptor used. PMEMORY_DESCRIPTOR UsedChunk = ArcMemFindChunk(ImageBasePage, ImageSizePage); if (UsedChunk == NULL) { // what? printf("Could not find used mem chunk\n"); return _EFAULT; } // Initialise a memchunk for this executable Status = ArcMemAllocateFromFreeChunk(UsedChunk, ImageBasePage, ImageSizePage, MemoryLoadedProgram); if (ARC_FAIL(Status)) return Status; // Dump memchunks for debug. #if 0 printf("Prg: %08x-%08x\r\n", ImageBasePage * PAGE_SIZE, (ImageBasePage + ImageSizePage) * PAGE_SIZE); for (PMEMORY_DESCRIPTOR MemChunk = Api->MemoryRoutine(NULL); MemChunk != NULL; MemChunk = Api->MemoryRoutine(MemChunk)) { printf("%x: %08x-%08x\r\n", MemChunk->MemoryType, MemChunk->BasePage * PAGE_SIZE, (MemChunk->BasePage + MemChunk->PageCount) * PAGE_SIZE); } IOSKBD_ReadChar(); #endif Status = Api->InvokeRoutine(EntryPoint, BaseAddress, SavedArgs.Argc, (PCHAR*) SavedArgs.Argv, (PCHAR*) SavedArgs.Envp); if (ARC_SUCCESS(Status)) UsedChunk->MemoryType = MemoryLoadedProgram; return Status; } return _ENOMEM; } void ArcLoadInit(void) { PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS(); Api->LoadRoutine = ArcLoad; Api->InvokeRoutine = ArcInvoke; Api->ExecuteRoutine = ArcExecute; // Initialise the scratch address. // Try to allocate some memory for this, hopefully in an area that cannot be used by boot. ScratchAddress = ArcMemAllocTemp(ARCFW_MEM2_SIZE, true); if (ScratchAddress == NULL) ScratchAddress = ArcMemAllocTemp(ARCFW_MEM2_SIZE, false); }