MODULE EFIA2Loader;
IMPORT
SYSTEM, EFI, Machine := EFIMachine, EFILib, EFIFileProtocol, EFIGraphicsOutput, Trace;
CONST
traceDebug = TRUE;
kernelAddress = 1000H;
kernelRelocAddress = -1;
bootTableAddress = -1;
VAR
bootTableBegin : SYSTEM.ADDRESS;
bootTableEnd : SYSTEM.ADDRESS;
bootTableCfgStrEntry : SYSTEM.ADDRESS;
PROCEDURE SearchGraphicsMode(prot : EFIGraphicsOutput.Protocol; prefWidth, prefHeight, prefDepth, prefFormat : LONGINT): LONGINT;
VAR
mode : EFIGraphicsOutput.GraphicsMode;
info : EFIGraphicsOutput.GraphicsModeInfo;
sizeofInfo : EFI.Int;
maxMode : EFI.Int32;
i : LONGINT;
status : EFI.Status;
BEGIN
mode := prot.Mode;
maxMode := mode.MaxMode;
FOR i := 0 TO maxMode-1 DO
status := prot.QueryMode(prot, i, sizeofInfo, info);
IF (status # EFI.Success) THEN
RETURN -1;
END;
IF (prefWidth = info.HorizontalResolution) & (prefHeight = info.VerticalResolution)
& (prefDepth = 32) THEN
RETURN i;
END;
END;
RETURN -1;
END SearchGraphicsMode;
PROCEDURE GetFrameBuffer(prefWidth, prefHeight, prefDepth, prefFormat : LONGINT; VAR framebufAddr : SYSTEM.ADDRESS; VAR framebufSize : SYSTEM.SIZE): EFI.Status;
VAR
handle : EFI.Handle;
handleBuf : ARRAY 128 OF EFI.Handle;
handleBufSize, i : EFI.Int;
prot : EFI.Protocol; goProt : EFIGraphicsOutput.Protocol;
modeNumber : LONGINT;
framebufPhysAddr : EFI.PhysicalAddress;
status : EFI.Status;
BEGIN
handleBufSize := LEN(handleBuf)*SYSTEM.SIZEOF(EFI.Handle);
status := EFI.table.BS.LocateHandle(EFI.ByProtocol, EFIGraphicsOutput.GUID, 0, handleBufSize, handleBuf);
IF status = EFI.Success THEN
i := handleBufSize DIV SYSTEM.SIZEOF(EFI.Handle);
WHILE i > 0 DO
DEC(i);
handle := handleBuf[i];
status := EFI.table.BS.HandleProtocol(handle, EFIGraphicsOutput.GUID, prot);
goProt := SYSTEM.VAL(EFIGraphicsOutput.Protocol, prot);
IF status = EFI.Success THEN
modeNumber := SearchGraphicsMode(goProt, prefWidth, prefHeight, prefDepth, prefFormat);
IF modeNumber >= 0 THEN
status := goProt.SetMode(goProt, modeNumber);
IF (status = EFI.Success) & (goProt.Mode.Mode = modeNumber) THEN
framebufPhysAddr := goProt.Mode.FrameBufferBase;
framebufAddr := SYSTEM.VAL(SYSTEM.ADDRESS, framebufPhysAddr);
framebufSize := goProt.Mode.FrameBufferSize;
RETURN EFI.Success;
END;
END;
END;
END;
END;
RETURN EFI.Error;
END GetFrameBuffer;
PROCEDURE PrintGraphicsModes;
VAR
handle : EFI.Handle;
handleBuf : ARRAY 512 OF EFI.Handle;
handleBufSize, i: EFI.Int; j : LONGINT;
prot : EFI.Protocol; goProt : EFIGraphicsOutput.Protocol;
mode : EFIGraphicsOutput.GraphicsMode;
info : EFIGraphicsOutput.GraphicsModeInfo;
sizeofInfo : EFI.Int;
maxMode : EFI.Int32;
status : EFI.Status;
BEGIN
handleBufSize := LEN(handleBuf)*SYSTEM.SIZEOF(EFI.Handle);
status := EFI.table.BS.LocateHandle(EFI.ByProtocol, EFIGraphicsOutput.GUID, 0, handleBufSize, handleBuf);
IF status = EFI.Success THEN
i := handleBufSize DIV SYSTEM.SIZEOF(EFI.Handle);
IF (i = 0) THEN
Trace.String(" - none - "); Trace.Ln;
END;
WHILE i > 0 DO
DEC(i);
handle := handleBuf[i];
status := EFI.table.BS.HandleProtocol(handle, EFIGraphicsOutput.GUID, prot);
goProt := SYSTEM.VAL(EFIGraphicsOutput.Protocol, prot);
IF status = EFI.Success THEN
mode := goProt.Mode;
maxMode := mode.MaxMode;
FOR j := 0 TO maxMode-1 DO
status := goProt.QueryMode(goProt, j, sizeofInfo, info);
IF (status # EFI.Success) THEN
RETURN;
END;
Trace.Int(info.HorizontalResolution, 0); Trace.String("x");
Trace.Int(info.VerticalResolution, 0); Trace.String("x");
Trace.Int(32, 0); Trace.String(" - ");
CASE info.PixelFormat OF
EFIGraphicsOutput.PFRGBX8Bit : Trace.String("RGB");
| EFIGraphicsOutput.PFBGRX8Bit : Trace.String("BGR");
| EFIGraphicsOutput.PFBitMask : Trace.String("R: "); Trace.Hex(info.PixelBitmask.RedMask,8);
Trace.String("G: "); Trace.Hex(info.PixelBitmask.GreenMask,8);
Trace.String("B: "); Trace.Hex(info.PixelBitmask.BlueMask,8);
| EFIGraphicsOutput.PFBltOnly : Trace.String("Blocktransfer only - no physical framebuffer");
END;
Trace.Ln;
END;
END;
END;
ELSE
Trace.String("Error while accessing GraphicsOutputProtocol.");Trace.Ln;
EFILib.ReportError(status);
END;
END PrintGraphicsModes;
PROCEDURE LoadBootTable(CONST configFileName : ARRAY OF EFI.Char16; VAR btAddr : SYSTEM.ADDRESS) : EFI.Status;
VAR
configFile : EFIFileProtocol.Protocol;
cfAddr, cfPos, cfEnd : SYSTEM.ADDRESS;
btAddrPhys : EFI.PhysicalAddress;
btPos : SYSTEM.ADDRESS;
status : EFI.Status;
type : LONGINT;
fileSize : LONGINT;
cfgname, cfgval : SYSTEM.ADDRESS;
cfgnamelen, cfgvallen : LONGINT;
parseError : BOOLEAN;
ramSize : EFI.Int64;
PROCEDURE ReportError(at : SYSTEM.ADDRESS);
VAR status : EFI.Status;
BEGIN
parseError := TRUE;
Trace.String("Sytnax error in file ");
status := EFI.table.ConOut.OutputString(EFI.table.ConOut, configFileName);
Trace.String(" at position ");
Trace.Address(at);
Trace.Ln;
END ReportError;
PROCEDURE IsWhitespace(ch : CHAR) : BOOLEAN;
BEGIN
RETURN (ch = ' ') OR (ch = 09X) OR (ch = 0AX);
END IsWhitespace;
PROCEDURE ReadConfig (VAR name : SYSTEM.ADDRESS; VAR namelen : LONGINT; VAR val : SYSTEM.ADDRESS; VAR vallen : LONGINT) : BOOLEAN;
VAR ch : CHAR;
BEGIN
IF (cfPos # cfEnd) THEN SYSTEM.GET(cfPos, ch); INC(cfPos); END;
WHILE (cfPos # cfEnd) & IsWhitespace(ch) DO
SYSTEM.GET(cfPos, ch); INC(cfPos);
END;
IF (ch = '~') THEN RETURN FALSE; END;
name := cfPos-1;
namelen := 0;
WHILE (cfPos # cfEnd) & (ch # '=') & ~IsWhitespace(ch) DO
INC(namelen);
SYSTEM.GET(cfPos, ch); INC(cfPos);
END;
WHILE (cfPos # cfEnd) & IsWhitespace(ch) DO
SYSTEM.GET(cfPos, ch); INC(cfPos);
END;
IF (ch # '=') THEN ReportError(cfPos - cfAddr); RETURN FALSE; END;
IF (cfPos # cfEnd) THEN SYSTEM.GET(cfPos, ch); INC(cfPos); END;
WHILE (cfPos # cfEnd) & IsWhitespace(ch) DO
SYSTEM.GET(cfPos, ch); INC(cfPos);
END;
IF (ch # '"') THEN ReportError(cfPos - cfAddr); RETURN FALSE; END;
IF (cfPos # cfEnd) THEN SYSTEM.GET(cfPos, ch); INC(cfPos); END;
val := cfPos-1;
vallen := 0;
WHILE (cfPos # cfEnd) & (ch # '"') DO
INC(vallen);
SYSTEM.GET(cfPos, ch); INC(cfPos);
END;
IF (ch # '"') THEN ReportError(cfPos - cfAddr); RETURN FALSE; END;
IF (name = cfEnd) OR (val = cfEnd) THEN
RETURN FALSE;
END;
RETURN TRUE;
END ReadConfig;
BEGIN
configFile := EFILib.OpenFile(configFileName);
IF (configFile = NIL) THEN
Trace.String("Error: Could not find file ");
status := EFI.table.ConOut.OutputString(EFI.table.ConOut, configFileName);
Trace.Ln;
RETURN EFI.ErrNotFound;
END;
cfAddr := -1;
status := EFILib.LoadFile(configFile, cfAddr);
IF (status # EFI.Success) THEN
RETURN status;
END;
fileSize := SHORT(EFILib.GetFileSize(configFile));
cfPos := cfAddr;
cfEnd := cfAddr + fileSize;
btAddrPhys := bootTableAddress;
status := EFILib.AllocateMemory(btAddrPhys, SHORT(fileSize DIV EFI.PageSize) + 3);
IF status # EFI.Success THEN
RETURN status;
END;
bootTableBegin := SYSTEM.VAL(SYSTEM.ADDRESS,btAddrPhys);
btAddr := bootTableBegin;
btPos := bootTableBegin;
type := 3;
SYSTEM.PUT32(btPos, type); INC(btPos, 4);
SYSTEM.PUT32(btPos, 16); INC(btPos, 4);
SYSTEM.PUT32(btPos, 0); INC(btPos, 4);
SYSTEM.PUT32(btPos, 640*1024); INC(btPos, 4);
status := EFILib.GetMemorySize(ramSize);
type := 4;
SYSTEM.PUT32(btPos, type); INC(btPos, 4);
SYSTEM.PUT32(btPos, 16); INC(btPos, 4);
SYSTEM.PUT32(btPos, 100000H); INC(btPos, 4);
SYSTEM.PUT32(btPos, SHORT(ramSize) - 100000H); INC(btPos, 4);
IF traceDebug THEN
Trace.String("DEBUG: ramsize: "); Trace.Hex(SHORT(ramSize), 0); Trace.String("H B"); Trace.Ln;
END;
type := 8;
bootTableCfgStrEntry := btPos;
SYSTEM.PUT32(btPos, type); INC(btPos, 4);
SYSTEM.PUT32(btPos, 0); INC(btPos, 4);
bootTableEnd := btPos;
SYSTEM.PUT8(btPos, 0); INC(btPos);
SYSTEM.PUT32(btPos, -1); INC(btPos, 4);
parseError := FALSE;
WHILE (ReadConfig(cfgname, cfgnamelen, cfgval, cfgvallen)) DO
AddConfigA(cfgname, cfgnamelen, cfgval, cfgvallen);
END;
IF (parseError) THEN
RETURN EFI.Error;
ELSE
RETURN EFI.Success;
END;
END LoadBootTable;
PROCEDURE AddConfig(CONST name, val : ARRAY OF CHAR);
VAR strlenName, strlenVal : LONGINT;
BEGIN
strlenName:=0; WHILE (name[strlenName] # 0X) & (strlenName < LEN(name)) DO INC(strlenName); END;
strlenVal:=0; WHILE (val[strlenVal] # 0X) & (strlenVal < LEN(val)) DO INC(strlenVal); END;
AddConfigA(SYSTEM.ADR(name[0]), strlenName, SYSTEM.ADR(val[0]), strlenVal);
END AddConfig;
PROCEDURE AddConfigA(name : SYSTEM.ADDRESS; namelen : LONGINT; val : SYSTEM.ADDRESS; vallen : LONGINT);
VAR btEnd : SYSTEM.ADDRESS;
cfgStrSize : SYSTEM.SIZE;
BEGIN
btEnd := bootTableEnd;
SYSTEM.MOVE(name,btEnd,namelen);
INC(btEnd,namelen);
SYSTEM.PUT(btEnd, 0); INC(btEnd);
SYSTEM.MOVE(val,btEnd,vallen);
INC(btEnd,vallen);
SYSTEM.PUT(btEnd, 0); INC(btEnd);
INC(bootTableEnd, namelen + 1 + vallen + 1);
SYSTEM.PUT(btEnd, 0); INC(btEnd);
SYSTEM.PUT32(btEnd, -1);
cfgStrSize := bootTableEnd + 1 - bootTableCfgStrEntry;
SYSTEM.PUT32(bootTableCfgStrEntry + 4, cfgStrSize);
END AddConfigA;
PROCEDURE GetConfig(CONST name : ARRAY OF CHAR; VAR val : ARRAY OF CHAR);
VAR btIdx : SYSTEM.ADDRESS; i : LONGINT; ch : CHAR;
BEGIN
btIdx := bootTableCfgStrEntry + 8;
LOOP
SYSTEM.GET(btIdx,ch);
IF ch = 0X THEN EXIT END;
i := 0;
LOOP
SYSTEM.GET(btIdx,ch);
IF (ch # name[i]) OR (name[i] = 0X) THEN EXIT END;
INC (i); INC (btIdx)
END;
IF (ch = 0X) & (name[i] = 0X) THEN
i := 0;
REPEAT
INC (btIdx); SYSTEM.GET(btIdx,ch); val[i] := ch; INC (i);
IF i = LEN(val) THEN val[i - 1] := 0X; RETURN END
UNTIL ch = 0X;
val[i] := 0X; RETURN
ELSE
WHILE ch # 0X DO
INC (btIdx); SYSTEM.GET(btIdx,ch);
END;
INC (btIdx);
REPEAT
SYSTEM.GET(btIdx,ch); INC (btIdx)
UNTIL ch = 0X
END
END;
val[0] := 0X
END GetConfig;
PROCEDURE Allocate(allocAddr: EFI.PhysicalAddress; kernelPages: LONGINT): EFI.Status;
VAR allocAdrCopy : EFI.PhysicalAddress; chunkPages: LONGINT; status: EFI.Status;
BEGIN
chunkPages := kernelPages;
chunkPages := 1;
REPEAT
allocAdrCopy := allocAddr;
status := EFILib.AllocateMemory(allocAdrCopy, chunkPages);
IF status = EFI.Success THEN
DEC(kernelPages, chunkPages);
allocAddr := allocAddr + EFI.PageSize*chunkPages;
ELSE
TRACE(kernelPages, chunkPages, allocAddr, status);
chunkPages := chunkPages DIV 2;
IF chunkPages > kernelPages THEN chunkPages := kernelPages END;
END;
UNTIL (kernelPages = 0) OR (chunkPages = 0);
RETURN status;
END Allocate;
PROCEDURE LoadKernel(CONST kernelFileName : ARRAY OF EFI.Char16; VAR kernelAddr: SYSTEM.ADDRESS; VAR kernelSize : LONGINT) : EFI.Status;
VAR
loadAddr : SYSTEM.ADDRESS;
allocAddr : EFI.PhysicalAddress;
kernelImageFile : EFIFileProtocol.Protocol;
kernelPages : LONGINT;
i : LONGINT;
status : EFI.Status;
BEGIN
kernelImageFile := EFILib.OpenFile(kernelFileName);
IF (kernelImageFile # NIL) THEN
kernelSize := SHORT(EFILib.GetFileSize(kernelImageFile));
TRACE(kernelSize);
kernelPages := (kernelSize DIV EFI.PageSize) + 1;
IF (kernelRelocAddress # -1) THEN
TRACE(status);
END;
loadAddr := kernelRelocAddress;
allocAddr:= 0;
status := EFILib.AllocateMemory(allocAddr, 2);
IF status # EFI.Success THEN
Trace.String("could not allocate page 0 and 1 - this might be a problem for the relocation process");
END;
loadAddr := -1;
status := EFILib.LoadFile(kernelImageFile, loadAddr);
IF (status = EFI.Success ) THEN
TRACE(loadAddr);
kernelAddr := loadAddr;
TRACE(kernelAddress);
RETURN EFI.Success;
ELSE
TRACE("could not load kernel with fixed adr");
TRACE(status);
loadAddr := -1;
status := EFILib.LoadFile(kernelImageFile, loadAddr);
TRACE(status);
IF (status = EFI.Success) THEN
TRACE("loaded kernel to ", loadAddr);
kernelAddr := loadAddr;
TRACE(kernelAddress);
RETURN EFI.WarnWriteFailure;
ELSE
RETURN EFI.Error;
END;
END;
ELSE
Trace.String("Error: Could not find file ");
status := EFI.table.ConOut.OutputString(EFI.table.ConOut, kernelFileName);
Trace.Ln;
RETURN EFI.ErrNotFound;
END;
END LoadKernel;
PROCEDURE LoadA2;
VAR
kernelAddr, btAddr, fbAddr, highAddr, memmapAddr: SYSTEM.ADDRESS;
fbSize: EFI.Int;
adr: EFI.PhysicalAddress;
kernelSize: LONGINT;
kernelStat, btStat, fbStat, status, memmapStat : EFI.Status;
kernelFileName, configFileName, arg : ARRAY 128 OF EFI.Char16;
val : ARRAY 100 OF CHAR;
i, dWidth, dHeight, dDepth, dFormat : LONGINT;
size: EFI.Int64;
buf : ARRAY 100 OF EFI.Char16;
BEGIN
Trace.String("Starting"); Trace.Ln;
status := EFILib.GetMemorySize(size);
TRACE(size);
IF EFILib.GetNextArg(kernelFileName) & EFILib.GetNextArg(configFileName) THEN
kernelStat := LoadKernel(kernelFileName, kernelAddr, kernelSize);
btStat := LoadBootTable(configFileName, btAddr);
IF (btStat = EFI.Success) THEN
GetConfig("DWidth",val); i:=0; dWidth := EFILib.StringToInt(i, val);
GetConfig("DHeight",val); i:=0; dHeight := EFILib.StringToInt(i, val);
GetConfig("DDepth",val); i:=0; dDepth := EFILib.StringToInt(i, val);
dFormat := EFIGraphicsOutput.PFBGRX8Bit;
fbStat := GetFrameBuffer(dWidth,dHeight,dDepth, dFormat, fbAddr, fbSize);
TRACE(fbAddr);
IF (fbStat = EFI.Success) THEN
ELSE
Trace.String("Warning: Requested display mode ");
Trace.Int(dWidth,0); Trace.String("x"); Trace.Int(dHeight,0); Trace.String("x"); Trace.Int(dDepth,0);
Trace.String(" not available in BGR mode. Available modes are :"); Trace.Ln;
PrintGraphicsModes; Trace.Ln;
Trace.String("Continuing happily."); Trace.Ln;
END;
END;
IF traceDebug THEN
Trace.String("DEBUG: Kernel at ");Trace.Address(kernelAddr);Trace.Ln;
Trace.String("DEBUG: BootTable at ");Trace.Address(btAddr);Trace.Ln;
Trace.String("DEBUG: FrameBuffer at "); Trace.Address(fbAddr); Trace.Ln;
END;
IF (EFILib.GetNextArg(arg) & (CHR(arg[0]) = '-') & (CHR(arg[1]) = 'd')) THEN
Trace.Ln;
Trace.String("Dry Run Complete"); Trace.Ln;
RETURN;
END;
memmapStat := EFILib.GetMemoryMapping(adr);
memmapAddr := SYSTEM.VAL(SYSTEM.ADDRESS,adr);
TRACE(memmapAddr);
IF (memmapStat = EFI.Success ) THEN
Trace.Ln;
Trace.String("saved memory mappings");
ELSE
Trace.Ln;
Trace.String("failed to saved memory mappings");
END;
IF ((kernelStat = EFI.Success) OR (kernelStat = EFI.WarnWriteFailure)) & (btStat = EFI.Success) THEN
TRACE(kernelAddr, btAddr, kernelSize, fbAddr);
Trace.String("Shutting down Boot Services - WTF?!"); Trace.Ln;
status := EFILib.ExitBootServices();
IF status = EFI.Success THEN
Machine.JumpTo(kernelAddr, btAddr, kernelSize, fbAddr, memmapAddr);
ELSE
Trace.String("Could not exit boot services"); Trace.Ln;
END;
ELSE
Trace.String("Error! ");
IF ~((kernelStat = EFI.Success) OR (kernelStat = EFI.WarnWriteFailure)) THEN
Trace.String("Kernel could not be loaded. ");
END;
IF ~(btStat = EFI.Success) THEN
Trace.String("Boot table could not be loaded. ");
END;
Trace.String("Aborting"); Trace.Ln;
END;
ELSE
Trace.String("Arguments not correct. Usage:");Trace.Ln;Trace.String("name.efi kernel config"); Trace.Ln;
END;
EFILib.FreeMemory;
END LoadA2;
BEGIN
LoadA2;
END EFIA2Loader.
PET.Open EFI.Tool ~