{ 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.