Files
stonedDiscord ca9ad653fc Squashed commit of the following:
commit 39e237e99907449bac31254ceca13a2171cbe8ad
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 03:25:37 2025 +0000

    delete ci

commit 3bd4ecbbe904d8f39efbeea9b6fc49bc1ff4a98c
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 03:18:53 2025 +0000

    bracket

commit ecb3a8d077dc12c1df204366bb2637aaedb8c2ad
Merge: dfb1a56 0fd256c
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 12:16:02 2025 +0900

    Merge branch 'Wack0:main' into main

commit dfb1a567d3418550d102e1704a506d5bf70fe9af
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 03:13:02 2025 +0000

    component instead of type casting

commit 9a9233bbcb4fa5b60639284a7790e4fb278c9938
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:56:48 2025 +0100

    shutup wget

commit e2269523d81aa885b384a4fdb2c1b703790f156c
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:55:10 2025 +0100

    add caching

commit 7b7ec9d4441c53ef42e1a9d907734eab23dca54d
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:49:45 2025 +0100

    minor spelling mistake

commit 6023e330c073d580af73c605cec8579a6362fc57
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:48:28 2025 +0100

    upload fw

commit 15e43e774bc35f0984e8b9766c05857caeaab973
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:45:20 2025 +0100

    timer

commit de6347e81be38414109c658787c70a576b930995
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:41:41 2025 +0100

    udellay

commit 8f204e6b9ee4a32589b8524e733ad29a5e1e4de2
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:39:08 2025 +0100

    ulong

commit f88453f816d1eb2fdbe839bec7cc7e904bde40c7
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:36:18 2025 +0100

    diskio and alloc

commit 62a6bc38b626d0a136f217db698534301847ba8e
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:30:55 2025 +0100

    usb keyboard header

commit 97b51a1a6550d99fc38ec92740074df0a54e4c99
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:26:32 2025 +0100

    strcmp type

commit 849b110b09e42b0178c30ad61d98152a527de95f
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:11:48 2025 +0100

    void no bool

commit f0fb86f99589c50df2975534e16d62a0d986fd79
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:08:36 2025 +0100

    framebuffer type

commit 72012cac4b9fcde0489dccf848bec886a78f37bf
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:05:08 2025 +0100

    more components

commit 28e1766fa155d7c7906ff17c96a657eb6937b077
Author: stonedDiscord <Tukz@gmx.de>
Date:   Mon Mar 3 02:00:46 2025 +0100

    config component

commit 6d19c6a782adf5c1cb87035dfdc5fb060594c017
Author: Rairii <2650838+Wack0@users.noreply.github.com>
Date:   Sun Mar 2 14:50:22 2025 +0000

    arcfw(usb): when shutting down usb, ensure the device change callback is unlocked on the IOS side

    this prevents deadlocks when loading iosusb.sys in NT

commit 04b1726461cb94d96bcaa12e5c051a58c74a4fa6
Author: Rairii <2650838+Wack0@users.noreply.github.com>
Date:   Sun Mar 2 14:26:37 2025 +0000

    arc(usb): if no devices are present after first poll, poll again with timeout

    fixes scenarios with usb hubs etc

commit 14533184d7613756da674e038fb961cd9230333b
Author: stonedDiscord <Tukz@gmx.de>
Date:   Sun Mar 2 12:51:09 2025 +0900

    speling

commit 43861984bcde644d394b4fa90ac49c00db5e33e6
Author: stonedDiscord <Tukz@gmx.de>
Date:   Sun Mar 2 12:41:46 2025 +0900

    rearrange

commit 99064493f4d93900850bb74d18c14a17416b7d7a
Author: stonedDiscord <Tukz@gmx.de>
Date:   Sun Mar 2 12:38:58 2025 +0900

    loader artifact

commit da1a9c9628a47d77d8b04416cb1c7ad18f27b781
Author: stonedDiscord <Tukz@gmx.de>
Date:   Sun Mar 2 12:17:00 2025 +0900

    build folder

commit e6367d7f314238b4c3fe99585b2e360d36f60be2
Author: stonedDiscord <Tukz@gmx.de>
Date:   Sun Mar 2 12:12:39 2025 +0900

    zstd

commit 0202f7d95b2eaf1ec0c2e1bba34a997a4af9db38
Author: stonedDiscord <Tukz@gmx.de>
Date:   Sun Mar 2 12:10:31 2025 +0900

    libc

commit aac5fdb880108416b63736a1865f93c23f7b9e10
Author: Rairii <2650838+Wack0@users.noreply.github.com>
Date:   Sat Mar 1 23:40:23 2025 +0000

    fpexiblk: not using eximap for now, so don't try to finalise it on sad paths

    this is probably what's causing bugchecks when no exi device is present

commit 80f88ecfa87aa70934a85ff960fc94d1038a7520
Author: Rairii <2650838+Wack0@users.noreply.github.com>
Date:   Sat Mar 1 22:25:06 2025 +0000

    fpgx35: work around GDI bug

    GDI ExtFloodFill tries to convert a device specific bitmap to a normal bitmap for speed.

    Prior to NT 4 this function has a bug, and so it will delete the device surface, which causes crashes and thus bugcheck (as it brings down csrss).

    Do some pattern matching in DrvCopyBits and make it fail if it's being called by this conversion function. This causes a fallback to using the device specific bitmap, which is what is wanted.

    Fixes bugcheck under NT 3.51 when running (at least) VC4 IDE.

commit b99ccd4406e5eb06cc00112d8de51af613a8eef7
Author: Rairii <2650838+Wack0@users.noreply.github.com>
Date:   Sat Mar 1 21:36:47 2025 +0000

    halartx: don't need this debug print anymore

commit e5e086d1f052f642ed06e3eee4230ec3e435bcab
Author: Rairii <2650838+Wack0@users.noreply.github.com>
Date:   Sat Mar 1 01:10:33 2025 +0000

    arcfw: disable return to loader, seems this only works under NT for some reason

commit e78be8a73eaba223f3318c2fea1ac5d27f0a0c42
Author: Rairii <2650838+Wack0@users.noreply.github.com>
Date:   Sat Mar 1 01:00:03 2025 +0000

    make: split up compile and asm-preproc

    compiler errors are ignored by make when one is piped to the other

commit 4d501b30f20ecb67aed4c1bfa740ed517fb86e79
Author: Rairii <2650838+Wack0@users.noreply.github.com>
Date:   Sat Mar 1 00:38:28 2025 +0000

    iossdmc: enforce lock when fatfs calls GetStatus

commit a045ca7879040f3752d55d25bbbce4280068f525
Author: Rairii <2650838+Wack0@users.noreply.github.com>
Date:   Sat Mar 1 00:00:38 2025 +0000

    headers(ntddk): KeAcquireSpinLockRaiseToDpc was introduced in NT4, use KeAcquireSpinLock function instead

commit 3d0850ad707cbe02886ee7c36d454243cc73eafb
Author: Rairii <2650838+Wack0@users.noreply.github.com>
Date:   Fri Feb 28 12:07:27 2025 +0000

    gdi: only skip single pixel or line on bounds checks fail, add slight optimisation for copying two consecutive pixels

    fixes #5

commit 9050ba4fd3334d815d9bb0e76f5bc19f90722d7e
Author: stonedDiscord <Tukz@gmx.de>
Date:   Fri Feb 28 05:19:18 2025 +0100

    sleep header

commit 27dff2d63a4640fbdeddeb914148641af24d5ab2
Author: stonedDiscord <Tukz@gmx.de>
Date:   Fri Feb 28 05:17:30 2025 +0100

    sleep and card

commit b1915a38e4723fcb0f1cc9d400e54ad5ee2b9557
Author: stonedDiscord <Tukz@gmx.de>
Date:   Fri Feb 28 05:13:33 2025 +0100

    missing header for gecko

commit 78a6c4a0fd493e42c1162e42667d03314ee06e26
Author: stonedDiscord <Tukz@gmx.de>
Date:   Fri Feb 28 05:02:19 2025 +0100

    usleep header

commit 24fcd1b0b280a408eff08949442b8dbf1d572988
Author: stonedDiscord <Tukz@gmx.de>
Date:   Fri Feb 28 04:26:08 2025 +0100

    update makefile name in readme

commit 78047b1d3f9970a5dfe70b7fb2fd217802719561
Author: stonedDiscord <Tukz@gmx.de>
Date:   Fri Feb 28 04:25:04 2025 +0100

    makefile name

commit fc8ab08b117dddec9c07607f870df56de882782a
Author: stonedDiscord <Tukz@gmx.de>
Date:   Fri Feb 28 04:14:07 2025 +0100

    arc firmware ci
2025-03-03 03:30:54 +00:00

838 lines
31 KiB
C

#include <stddef.h>
#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#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;
}
/// <summary>
/// Loads a COFF executable.
/// </summary>
/// <param name="ImagePath">Path of the executable to load.</param>
/// <param name="TopAddress">Address to load the COFF to</param>
/// <param name="EntryAddress">Entry point is written here</param>
/// <param name="LowAddress">End address of the loaded executable is written here</param>
/// <returns>ARC status code</returns>
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;
}
/// <summary>
/// Calls the entry point of a previously loaded program.
/// </summary>
/// <param name="EntryAddress">Entry point to call.</param>
/// <param name="StackAddress">Stack pointer to set.</param>
/// <param name="Argc">Number of arguments.</param>
/// <param name="Argv">Array of arguments.</param>
/// <param name="Envp">Environment variables.</param>
/// <returns>ARC status code.</returns>
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;
}
/// <summary>
/// Loads and executes a COFF executable.
/// </summary>
/// <param name="ImagePath">Path of the executable to load.</param>
/// <param name="Argc">Number of arguments.</param>
/// <param name="Argv">Array of arguments.</param>
/// <param name="Envp">Environment variables.</param>
/// <returns>ARC status code.</returns>
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);
}