{
Stub Loader Copyright Rossi Gianluca [ranger@racine.ra.it]
log history :
V0.0 tiny assembler version
V0.1 Turbo C version
V0.2 TC version bug fix
V0.3 TC / MSC
V0.4 TC / MSC, added command line for kernel
V0.5 Pascal & Nasm, reduced size [about from 20K to 10K]
V0.6 P&N addedd paging for virtual address != 0
V0.61 alloc dos memory & bug correction
[NOTE] la dimensione del file caricato deve essere < 1M :)
}
{$M $400,0,0}
{$I-,W-,G+,V-,S-,R-,Q-}
{***$DEFINE NDEBUG}
function XMS_check: boolean; external;
function XMS_init: boolean; external;
procedure XMS_clean; external;
procedure XMS_move; external;
procedure XMS_switch; external;
procedure kernel_buf_setup; external;
{$L nxms.obj}
type string9 = string[9];
const cmdl_debug: integer = 0;
const cmdl_text: integer = 0;
const cmdl_fname: string[64] = 'a.out';
const cmdl_pages: word = 0;
const cmdl_kernel: string[64] = '';
const _dos_mem: pointer = nil;
var
{ codici di errore del driver specifico (XMS, raw, vcpi) }
driver_code: word;
{ procedura per spostare memoria }
server_move_mem: function (vaddr: longint; var from; size: word): boolean;
{ procedura di switch }
server_switch: function(eip: longint): integer;
{ buffer utilizzato per la comunicazione }
kernel_buffer: array [0.. 255] of byte;
{ base in cui deve essere caricato il kernel }
virtual_base: longint;
var lastmode: word;
procedure set_rows(row: integer);
begin
if (row = 49) then
asm
mov ax,1112h
xor bx,bx
int 10h
end
else
asm
mov ax,3
int 10h
end;
asm
mov ax,600h
mov bx,0700h
mov cx,0
mov dh,row.byte
mov dl,79
int 10h
xor dx,dx
mov ah,2
xor bh,bh
int 10h
end;
end;
function get_rows: word; assembler;
asm
push es
mov ax,40h
mov es,ax
mov al,es:[84h]
mov ah,0
pop es
end;
procedure help;
begin
writeln('stub loader V0.61 copyright Rossi Gianluca [ranger@racine.ra.it]');
writeln;
writeln('use: stub [-d -v -p# -l"ARG"] -fNAME"');
writeln(' -d set debug mode [debug version only]');
writeln(' -v 80x50 text mode');
writeln(' -pNNN reserve NNN memory pages [default all memory]');
writeln(' -l"ARG" set loaded program command line');
writeln(' -fNAME set file name [default a.out]');
writeln;
halt(1);
end;
procedure abort;
begin
set_rows(lastmode);
writeln('Abort!');
halt(3);
end;
procedure errors(const s: string);
begin
set_rows(lastmode);
writeln(s);
halt(2);
end;
procedure ioerror;
var b: byte;
begin
b := IOResult;
if (b <> 0) then
begin
set_rows(lastmode);
writeln('I/O error #',b);
halt(2);
end;
end;
procedure memerror;
begin
errors('not enought memory');
end;
procedure drivererror;
begin
set_rows(lastmode);
writeln('driver error #', driver_code);
halt(2);
end;
function hexstr(l: longint): string9;
const hs: string[16] = '0123456789ABCDEF';
var
r: string9;
i: integer;
begin
for i := 1 to 8 do
begin
r[9 - i] := hs[((l shr ((i-1)*4)) and $F) + 1];
end;
r[0] := #9;
r[9] := 'h';
hexstr := r;
end;
{
parser per la linea di comando, gli switch validi sono :
-d per debug
-v per 80x50
-pNNN per indicare il numero di pagine da allocare.
NNN puo` essere un numero decimalo o esadecimale
in formato pascal,C o assembler
-larg1
oppure
-l"arg1 arg2..." indica quali argomenti passare al programma
-fNOME indica quale file caricare
}
procedure parse_cmdl;
{
riconosce almeno una stringa esadecimale nel formato :
$nnn (pascal)
0xnnn (c)
nnnh (assembler)
come effetto collaterale funzionano anche :
$nnnh
0xnnnh
}
function hstr2int(const s: string): longint;
var v,i:longint;
begin
i := 1;
v := 0;
if (s[1] = '$') then inc(i)
else if (s[1] = '0') and (s[2] = 'x') then inc (i,2);
while ((upcase(s[i]) in ['0' .. '9', 'A' .. 'F']) and (i < length(s))) do
begin
if (s[i] in ['0' .. '9']) then v := v*16+(byte(s[i]) - byte('0'))
else v := v*16+(byte(s[i]) - byte('A')+10);
inc(i);
end;
if (i = length(s)) or ((upcase(s[i]) = 'H') and (length(s) = i+1)) then hstr2int := v
else
begin
errors('invalid number');
end;
end;
function str2int(const s: string): longint;
var i: longint;
c: integer;
begin
val(s,i,c);
if (c <> 0) then i := hstr2int(s);
str2int := i;
end;
var s: string[64];
i: integer;
l: longint;
begin
for i := 1 to paramcount do
begin
s := paramstr(i);
if (s[1] = '/') or (s[1] = '-') then
begin
case s[2] of
'h','?': help;
{$IFNDEF NDEBUG}
'd' : cmdl_debug := 1;
{$ENDIF}
'v' : cmdl_text := 1;
'f' : begin
cmdl_fname := s;
delete(cmdl_fname, 1, 2);
end;
'p' : begin
delete(s, 1, 2);
l := str2int(s);
if (l > $3ff0) then
begin
errors('too many pages (max 3ff0h)');
end;
cmdl_pages := l;
end;
'l' : begin
if (s[3] <> '"') then
begin
delete(s, 1, 2);
if (length(s) = 0) and (paramcount > i) then
begin
inc(i);
s := paramstr(i);
end;
cmdl_kernel := s;
if length(cmdl_kernel) = 0 then
begin
errors('no args after -l');
end;
end
else
begin
delete(s, 1, 3);
cmdl_kernel := s;
inc(i);
s := paramstr(i);
while (pos('"', s) <> length(s)) do
begin
cmdl_kernel := concat(cmdl_kernel, ' ',s);
inc(i);
s := paramstr(i);
end;
cmdl_kernel := concat(cmdl_kernel, ' ',s);
if (cmdl_kernel[length(cmdl_kernel)] = '"') then dec(cmdl_kernel[0]);
end;
end
end;
end
else
begin
errors('invalid switch : '+s);
end
end
end;
function fileopen(var s: string) : word;
begin
s[length(s) +1] := #0;
asm
push ds
xor cx,cx
mov ah,3dh
mov al,filemode
lds dx,s
inc dx
int 21h
pop ds
jnc @@1
mov InOutres,ax
xor ax,ax
@@1:
mov @result,ax
end;
end;
function fileread(h: word; var buf; count: word): word;
begin
asm
push ds
mov ah,3fh
mov bx,h
mov cx,count
lds dx,buf
int 21h
pop ds
jnc @@1
mov inOutres,ax
xor ax,ax
@@1:
mov @result,ax
end
end;
procedure fileclose(h: word);
begin
asm
mov ah,3eh
mov bx,h
int 21h
end
end;
const F_START = 0;
F_CUR = 1;
F_END = 2;
function fileseek(h: word; fofs: longint; from: byte): longint;
begin
asm
mov bx,h
mov al,from
mov ah,42h
mov dx,fofs.word
mov cx,fofs.word[2]
mov bx,h
int 21h
jnc @@1
mov inOutres,ax
xor ax,ax
xor dx,dx
dec ax
dec dx
@@1:
mov @result.word,ax
mov @result.word[2],dx
end;
end;
function xmalloc(size: longint): pointer;
begin
size := (size + 15) shr 4;
asm
mov ah,48h
mov bx,size.word[0]
int 21h
jnc @@1
call memerror
@@1:
mov @result.word[2],ax
mov @result.word,0
end;
end;
procedure xfree(p: pointer);
begin
asm
mov ah,49h
mov es,p.word[2]
int 21h
end;
end;
procedure sread(h:word; ofs: longint; vaddr: longint; size: longint);
const MEM_SZ = 1024;
var p: pointer;
r: word;
begin
fileseek(h, ofs, F_START);
ioerror;
p := xmalloc(MEM_SZ);
while (size > 0) do
begin
r := fileread(h, p^, MEM_SZ);
ioerror;
if r = 0 then abort;
if (not server_move_mem(vaddr, p^, r)) then
begin
drivererror;
end;
dec(size, r);
inc(vaddr, r);
end;
xfree(p);
end;
function load_coff: longint;
function strcmp(var _a; const b:string): boolean;
var r: boolean;
i: integer;
a: array [1..8] of char absolute _a;
begin
r := true;
for i := 1 to length(b) do
begin
r := r and (a[i] = b[i]);
end;
strcmp := r;
end;
type hdrt = record
magic: word;
nsec: word;
timed: longint;
fsymptr: longint;
nsym: longint;
hdrsize: word;
flags: word;
magic2: word;
ver: word;
tsz: longint;
dsz: longint;
bsz: longint;
eip: longint;
tst: longint;
dst: longint;
text: array[1..8] of char;
tpa: longint;
tva: longint;
tsize: longint;
tfptr: longint;
tfrel: longint;
tflnum: longint;
trelocs: word;
tline: word;
tflags: longint;
data: array[1..8] of char;
dpa: longint;
dva: longint;
dsize: longint;
dfptr: longint;
dfrel: longint;
dflnum: longint;
drelocs: word;
dline: word;
dflags: longint;
bss: array[1..8] of char;
bpa: longint;
bva: longint;
bsize: longint;
bfptr: longint;
bfrel: longint;
bflnum: longint;
brelocs: word;
bline: word;
bflags: longint;
end;
var h: word;
hdr: hdrt;
begin
h := fileopen(cmdl_fname);
if (IOResult <> 0) then errors('file not found');
fileread(h, hdr, sizeof(hdr));
ioerror;
if ((hdr.magic <> $14C) or
(hdr.magic2 <> $10B) or
(hdr.nsec <> 3) or
(hdr.hdrsize <> 28) or
(strcmp(hdr.text, '.text') = false) or
(strcmp(hdr.data, '.data') = false) or
(strcmp(hdr.bss, '.bss') = false)) then errors('invalid COFF');
if (hdr.trelocs <> 0) or (hdr.trelocs <> 0) then errors('relocation not supported');
virtual_base := hdr.tva;
{$IFNDEF NDEBUG}
if cmdl_debug = 1 then
begin
writeln('COFF file "', cmdl_fname,'" info :');
writeln('.text vaddr ', hexstr(hdr.tva));
writeln(' size ', hexstr(hdr.tsize));
writeln('.data vaddr ', hexstr(hdr.dva));
writeln(' size ', hexstr(hdr.dsize));
writeln('.bss vaddr ', hexstr(hdr.bva));
writeln(' size ', hexstr(hdr.bsize));
writeln('eip ', hexstr(hdr.eip));
end;
{$ENDIF}
if (hdr.bva - hdr.tva + hdr.bsize + $FFF) < cmdl_pages*longint(4096) then
begin
sread(h, hdr.tfptr, hdr.tva and $FFFFF, hdr.tsize);
sread(h, hdr.dfptr, hdr.dva and $FFFFF, hdr.dsize);
fileclose(h);
load_coff := hdr.eip;
end
else memerror;
end;
function VCPI_check: boolean;
begin
VCPI_check := false;
end;
function RAW_check: boolean;
begin
RAW_check := false;
end;
procedure check_sys;
begin
if (VCPI_check) then
begin
end
else if (XMS_check) then
begin
exitproc := @XMS_clean;
@server_move_mem := @XMS_move;
@server_switch := @XMS_switch;
if (XMS_init = false) then
begin
drivererror;
end;
end
else if (RAW_check) then
begin
end
else errors('cpu already in V86!');
end;
{
mappa l'indirizzo virtual_base addr all'indirizzo phaddr,
ed il primo Mb 1:1.
NOTE: questa procedura viene eseguita quando si e` gia` in pmode !!
}
function map_memory_and_getcr3(phaddr: longint): integer;
var
pde: array [0..1023] of longint absolute $ba00:0; { directory di pagina }
pte1: array [0..1023] of longint absolute $bb00:0; { mappa primi 4Mb }
pte2: array [0..1023] of longint absolute $bc00:0; { mappa altri 4Mb }
pte3: array [0..1023] of longint absolute $bd00:0; { mappa altri 4Mb }
pte4: array [0..1023] of longint absolute $be00:0; { mappa altri 4Mb }
pte5: array [0..1023] of longint absolute $bf00:0; { mappa altri 4Mb }
pdi, pti, i: integer;
l : longint;
begin
pdi := virtual_base shr 22;
pti := (virtual_base shr 12) and $3FF;
{$IFNDEF NDEBUG}
if (cmdl_debug = 1) then
begin
writeln('memory mapping stats : ');
writeln('virtual address ',hexstr(virtual_base),' phisical address ', hexstr(phaddr));
writeln('cr3 = ',hexstr(seg(pde) * longint(16)));
end;
{$ENDIF}
if (pti <> 0) then errors('virtual address not 4Mb aligned!!');
if (phaddr > $C00000) then
begin
errors('base address must be < 12Mb');
end;
{ **** mappa il primo Mb (256 pagine) ****}
fillchar(pde, 1024, 0);
fillchar(pte1, 1024, 0);
fillchar(pte2, 1024, 0);
{ questo sarebbe : ((seg(pte1) * 16) shr 12) shl 12; }
pde[0] := 3 + (seg(pte1) shl longint(4));
pde[1] := 3 + (seg(pte2) shl longint(4));
pde[2] := 3 + (seg(pte3) shl longint(4));
pde[3] := 3 + (seg(pte4) shl longint(4));
{
l := 0;
for i := 0 to 1023 do
begin
pte1[i] := 3 + (l shl 12);
inc (l);
end;
for i := 0 to 1023 do
begin
pte2[i] := 3 + (l shl 12);
inc (l);
end;
for i := 0 to 1023 do
begin
pte3[i] := 3 + (l shl 12);
inc (l);
end;
for i := 0 to 1023 do
begin
pte4[i] := 3 + (l shl 12);
inc (l);
end;
}
l := 0;
for i := 0 to 1023 do
begin
pte1[i] := 3 + l;
pte2[i] := $400003 + l;
pte3[i] := $800003 + l;
pte4[i] := $C00003 + l;
inc(l, $1000);
end;
pde[pdi] := 3 + (seg(pte5) shl longint(4));
l := phaddr;
for i := 0 to 1023 do
begin
pte5[i] := 3 + l;
inc(l, $1000);
end;
map_memory_and_getcr3 := seg(pde);
end;
var ec: integer;
eip: longint;
begin
lastmode := get_rows;
if (Test8086 < 2) then errors('386 required');
_dos_mem := xmalloc($ffff);
parse_cmdl;
check_sys;
if (cmdl_text = 1) and (lastmode <> 49) then set_rows(49);
if (cmdl_text = 0) and (lastmode <> 24) then set_rows(24);
eip := load_coff;
kernel_buf_setup;
ec := server_switch(eip);
{$IFNDEF NDEBUG}
if (cmdl_debug = 1) then
begin
asm
xor ax,ax
int 16h
end;
end;
{$ENDIF}
set_rows(lastmode);
{$IFNDEF NDEBUG}
if (cmdl_debug = 1) then writeln('exit code #',ec);
{$ENDIF}
halt(ec);
end.