[comp.os.minix] Multi boot package

MAB01057%UFRJ.BITNET@cornellc.cit.cornell.edu (Marcelo Amarante Ferreira Gomes) (06/16/90)

Hi.
Since I had no time (nor patience :-) to handle the problems I was
having with the transmition of this package, I decided to post the
entire thing without any encoding.

Though it may not get through ok :-( it is better than not having anything
at all, so here it is:
--------------------- cut here for readme.1st --------------------------
This package installs new code in the master boot record of your
hard disk. This new code will ask you wich partition you want to
boot from, thus allowing you to install multiple bootable OS's in
the same hard disk.

Unhappily I have little time to adapt the installation program to
run under Minix, but I guess nobody will find much difficulty on
that. It's harder to do the way I've done: in MS/DOS (with Turbo C).

The package contains four files:
    - readme.1st    (this file)
    - setboot.asm   (the new code, in assembler)
    - instboot.c    (the installator, in C)
    - makefile.mak  (makefile build and install everything)

If you find any difficulty installing this, please post your questions
to the list, and not directly to me. Your doubts may be everyone else's.

There is plenty of documentation in the sources, but I had little time
to translate them to English, so that most of it is still in Portuguese,
my native language. The important parts of it were translated, though.

Enjoy.

Marcelo A. Ferreira Gomes (Wally Gator) <MAB01057@UFRJ.bitnet>
------------------------- cut here for makefile.mak --------------------
SetBoot:        SetBoot.bin
        instBoot SetBoot.bin

SetBoot.bin:    SetBoot.exe
        exe2bin $*

SetBoot.exe:    SetBoot.obj
        tlink $*

SetBoot.obj:    SetBoot.asm
        tasm $*
------------------------- cut here for setboot.asm --------------------
;;======================================================;;
;;                                                      ;;
;;  SetBoot.asm                                         ;;
;;                                                      ;;
;;      Boot sector for multiple operating systems      ;;
;;      in a single hard disk.                          ;;
;;                                                      ;;
;;  Rewritten by  Marcelo A. Ferreira Gomes             ;;
;;  original  by  Motti (Mordehai) Bazar                ;;
;;                                                      ;;
;;======================================================;;
;
;
;-----------------------------------------------;
;       Definicoes de constantes:               ;
;-----------------------------------------------;
;
; Chamadas `a BIOS:
;
Video   Equ     10h                     ; Escrita no video
Kbd     Equ     16h                     ; Leitura de teclado
DiskIO  Equ     13h                     ; E/S em disco
;
; Caracteres especiais:
;
CR      Equ     0Dh                     ; Carriage return
LF      Equ     0Ah                     ; Line feed
BkSpc   Equ     08h                     ; Back space
CtrlU   Equ     15h                     ; Control-U (cancela linha)
;
; Enderecos e offsets:
;
BootLoc Equ     7C00h                   ; Endereco do Boot Sector na memoria
BootSig Equ     7DFEh                   ; Endereco da assinatura (AA55)
MoveTo  Equ     0600h                   ; Para onde relocar-se
PartTbl Equ     MoveTo + 01BEh          ; Offset da tabela de particoes
Passwd  Equ     BootMsg                 ; Senha: "BootX"; X = A, B, C ou D
;
; Setor de boot valido:
;
Signatr Equ     0AA55h
;
;
;-----------------------------------------------;
;       Inicio do codigo                        ;
;-----------------------------------------------;
CODE    Segment BYTE Public 'CODE'
;
HBoot   Proc    NEAR
;
        Org     0h
        Assume CS:Code, DS:Code
; Start of code: relocate itself, so new OS's boot sector can be loaded
BStart:
        Cli
        Xor     Ax, Ax                  ; Ax = 0
        Mov     Ss, Ax                  ; Ss = 0
        Mov     Sp, BootLoc             ; Ajusta a nova pilha
        Mov     Si, Sp                  ; Si = Endereco fonte (p/ copiar)
        Mov     Es, Ax
        Mov     Ds, Ax
        Sti
        Cld
        Mov     Di, MoveTo              ; Di = Endereco de destino
        Mov     Cx, 100h                ; 256 words = 512 bytes a mover
        Rep
        MovSW                           ; Reloca o codigo para "MoveTo"
        Push    Cx                      ; Agora Cx contem zero !
        Mov     Ax, MoveTo + OFFSET IniBoot
        Push    Ax
        RetF                            ; "Retorna" para a proxima instrucao,
IniBoot:                                ; ja' devidamente relocada.
        ;
        ; Marca todas as particoes como inativas:
        ;
        Mov     Bx, PartTbl             ; Faz Bx apontar para a tab. de part.
        Mov     BYTE PTR   [Bx], 0      ; Zero: particao inativa
        Mov     BYTE PTR 16[Bx], 0
        Mov     BYTE PTR 32[Bx], 0
        Mov     BYTE PTR 48[Bx], 0
;
        ;
        ; Mostra o prompt do boot: 'Boot da particao (A-D): '
        ;
WrPrmpt:
        Mov     Si, MoveTo + OFFSET BootMsg
        Call    ImpMsg
;
        ;
        ; Get user's response
        ;
TryAgain:
        Mov     Si, MoveTo + OFFSET Passwd
        Xor     Dx, Dx                  ; Conta teclas erradas
GetPass:
        Mov     Ah, 0                   ; Funcao: 0 = leitura ...
        Int     Kbd                     ; do teclado
        Cmp     Al, CtrlU               ; Ctrl-U ?
        Je      TryAgain                ; Sim: anula a entrada
        Cmp     Al, BkSpc               ; E' back-space ?
        Jne     NoBkSpc
        Cmp     Si, MoveTo + OFFSET Passwd
        Je      GetPass
        Dec     Si
        Jmp     GetPass
NoBkSpc:
        Cmp     Al, CR                  ; Fim da resposta ?
        Jz      GotPass
        Mov     Bl, Al
        Xor     Al, [Si]                ; Compara Al com [Si], ignorando
        And     Al, NOT 20h             ; distincao maiuscula/minuscula
        Jne     GetRest
        Inc     Si
        Jmp     GetPass
GetRest:
        Inc     Dx                      ; Mais uma tecla errada
GetAnother:
        Push    Dx
        Push    Bx
        Mov     Ah, 0                   ; Funcao: 0 = leitura ...
        Int     Kbd                     ; do teclado
        Pop     Bx
        Pop     Dx
        Cmp     Al, CtrlU               ; Ctrl-U ?
        Je      TryAgain                ; Sim: anula a entrada
        Cmp     Al, BkSpc               ; E' back-space ?
        Jne     CkCr
        Dec     Dx                      ; Sim: menos uma tecla errada
        Jz      GetPass                 ; Se erros=0, checa a senha de novo.
        Jmp     GetAnother              ; senao, pega outra tecla (errada)
CkCr:
        Cmp     Al, CR                  ; E' CR ?
        Jne     GetRest                 ; Nao: continua lendo o teclado
;
        ;
        ; Find out from wich partition one wants to boot
        ;
GotPass:
        Push    Dx
        Push    Si
        Push    Bx
        Mov     Si, MoveTo + OFFSET CrLf
        Call    ImpMsg
        Pop     Bx
        Pop     Si
        Pop     Dx
        Cmp     Dx, 1                   ; Se nao houve exatamente uma tecla
        Jnz     WrPrmpt                 ; ... "errada", volta ao comeco
        Cmp     BYTE PTR [Si], ' '      ; Verifica se a senha esta' ok
        Jne     WrPrmpt                 ; Nao: pergunta de novo.
        Or      Bl, 20h                 ; Ignora maiuscula/minuscula
        Cmp     Bl, 'a'
        Jb      WrPrmpt
        Cmp     Bl, 'd'
        Ja      WrPrmpt
        Mov     Si, PartTbl             ; Endereco base: tabela de particoes
        Sub     Bl, 'a'                 ; Calcula o offset na tabela:
        Mov     Cl, 4                   ; ( caracter - 'a' ) * 16
        Shl     Bx, Cl
        Xor     Bh, Bh                  ; Para isto, Bh tem de ser zero.
        Add     Si, Bx
;
        ;
        ; Ativate the selected partition (writting it back)
        ;
GotPart:
        Mov     BYTE PTR [Si], 80h
        Push    Si
        Mov     Ax, 0301h               ; Ah = escrever, Al = 1 setor
        Mov     Bx, MoveTo              ; Bx = este setor modificado
        Mov     Cx, 1                   ; Ch = trilha 0, Cl = setor 1
        Mov     Dx, 80h                 ; Dl = drive C:, Dh = cabeca 0
        Int     DiskIO
        ;
        ; Atencao: o registro "SI" ficou na pilha !
;
        ;
        ; "Suja" a sua assinatura no Boot Sector, para nao haver confusao
        ;
        Mov     Si, BootSig
        Mov     WORD PTR [Si], NOT Signatr
        ;
        ; Restaura o "SI" da pilha
        ;
        Pop     Si
;
        ;
        ; Tryes to read selected partition's boot sector
        ;
        Mov     Dx, [Si]                ; Dl = drive,  Dh = cabeca
        Mov     Cx, [Si+2]              ; Ch = trilha, Cl = setor
        Mov     Bp, Si                  ; Salva o ponteiro p/ particao
        Mov     Di, 5                   ; Contador de "retry's"
RdBoot:
        Mov     Bx, BootLoc             ; Lugar do boot em memoria
        Mov     Ax, 0201h               ; Ah = ler, Al = 1 setor
        Push    Di
        Int     DiskIO
        Pop     Di
        Jnc     GoodRd                  ; Sai fora se leu direito
;
        Xor     Ax, Ax                  ; Senao: recalibrar
        Int     DiskIO
        Dec     Di
        Jnz     RdBoot                  ; ... e tentar de novo
;
        ;
        ; Read error on that partition:
        ; Display error message and start over
        ;
        Mov     Si, MoveTo + OFFSET ErrLd
ImpErr:
        Call    ImpMsg
        Jmp     IniBoot
;
        ;
        ; Boot Sector read: checks signature
        ;
GoodRd:
        Mov     Si, MoveTo + OFFSET NoOSys
        Mov     Bx, BootSig
        Cmp     WORD PTR [Bx], Signatr
        Jne     ImpErr
;
        ;
        ; Finally, calls the boot sector just read
        ; Before that:
        ;    Si = Boot partition's entry to partition table
        ;    Bx = Number of starting cylinder of this partition
        ;
        Mov     Si, Bp                  ; Restaura o ponteiro p/ a entrada
        Xor     Ax, Ax                  ; Monta o endereco de "retorno"
        Push    Ax
        Mov     Ax, BootLoc
        Push    Ax
;
        Mov     Ax, WORD PTR 2[Si]      ; Ah = cilindro, Al = setor
        Xor     Bh, Bh
        Mov     Bl, Al
        Shl     Bx, 1
        Shl     Bx, 1                   ; Bh = 2 bits mais  significativos
        Mov     Bl, Ah                  ; Bl = 8 bits menos significativos
;
        RetF                            ; Chama o setor de boot
;
;-----------------------------------------------;
; Routine to print a message on the screen.     ;
; On entry, [Di] = string                       ;
;-----------------------------------------------;
ImpMsg:
        LodSB
        Or      Al, Al
        Jz      ImpFim
        Mov     Bx, 7
        Mov     Ah, 0Eh
        Int     Video
        Jmp     ImpMsg
ImpFim:
        Ret
;
;-----------------------------------------------;
; Rotine to print a hex number                  ;
; On entry:   Ax = number.                      ;
; On exit:    Si is preserved.                  ;
;-----------------------------                  ;
; This routine was used to debug the program,   ;
; and is no longer used. It may be omitted, if  ;
; necessary to save space.                      ;
;-----------------------------------------------;
PrHex:
        Push    Si
        Mov     Cl, 12
PrLoop:
        Push    Ax
        Push    Cx
        Shr     Ax, Cl
        And     Ax, 0Fh
        Or      Al, 30h
        Cmp     Al, 3Ah
        Jb      OkHex
        Add     Al, 7
OkHex:
        Mov     Bx, 7
        Mov     Ah, 0Eh
        Int     Video
        Pop     Cx
        Pop     Ax
        Sub     Cl, 4
        Jnc     PrLoop
        Push    Ax
        Mov     Si, MoveTo + OFFSET CrLf
        Call    ImpMsg
        Pop     Ax
        Pop     Si
        Ret
;-----------------------------
;
BootMsg Db      'Boot from Partition (A-D): ', 0
ErrLd   Db      'Error loading operating system.', CR, LF, 0
NoOSys  Db      'No operating sistem in partition.'
CrLf    Db      CR, LF, 0
;
HBoot   EndP
;
CODE    EndS
;
        End
--------------------------- cut here for instboot.c --------------------
/*
 *  Instboot.c:
 *
 *      THIS PROGRAM COPIES THE MASTER BOOT SECTOR OF A HARD DISK TO
 *  A DISK FILE OR VICE-VERSA. IT SHOULD BE USED TO ALTER THE CODE IN
 *  THE MASTER BOOT RECORD WITHOUT MODIFYING THE PARTITION TABLE.
 */

/*
 *  Includes necessarios ( do Turbo C )
 */
#include <bios.h>       /* Funcoes do BIOS (como "biosdisk") */
#include <mem.h>        /* Funcoes de manipulacao de memoria */
#include <stdio.h>      /* Biblioteca padrao de E/S */



/*
 *  Definicoes de constantes
 */
#define LEITURA 0x02    /* Funcao de leitura da BIOS */
#define ESCRITA 0x03    /* Funcao de escrita da BIOS */

#define WINCHST 0x80    /* Codigo do primeiro disco rigido */

#define N_BYTES 0x01BE  /* Tamanho maximo do codigo de boot */



/*
 *  Prototipos de funcoes:
 *
 *      Se seu compilador nao suporta prototipos, simplesmente remova
 *  as tres linhas abaixo.
 */

int instala     ( char *arquivo );
int guarda      ( char *arquivo );
int main        ( int argc, char *argv | );



/*
 *  Instala:
 *
 *      Esta funcao copia o conteudo de um arquivo para o setor de boot
 *  de um disco rigido, mantendo a tabela de particoes que havia la'.
 */

instala( arquivo )
char    *arquivo;
{
    FILE    *fp;
    char    newBoot  BUFSIZ |;
    char    oldBoot  BUFSIZ |;

    if ( (fp = fopen( arquivo, "rb" )) == NULL )
    {
        fprintf( stderr, "Erro na abertura de \"%s\".\n", arquivo );
        return -1;
    }

    if ( fread( newBoot, 1, sizeof( newBoot ), fp ) > N_BYTES )
        fprintf( stderr, "Aviso: \"%s\" sera truncado.\n", arquivo );

    if ( biosdisk( LEITURA, WINCHST,
                   0,   /* Lado   0 */
                   0,   /* trilha 0 */
                   1,   /* setor  1 */
                   1,   /* 1 unico setor */
                   oldBoot ) )
    {
        fprintf( stderr, "Erro na leitura da tabela de particoes.\n" );
        return -1;
    }

    /*
     *  Mantem a tabela antiga de particoes !
     */
    memcpy( & newBoot  N_BYTES |, & oldBoot
 N_BYTES |, 0x0200 - N_BYTES );

    if ( biosdisk( ESCRITA, WINCHST, 0, 0, 1, 1, newBoot ) )
    {
        fprintf( stderr, "Erro na escrita do novo boot.\n" );
        return -1;
    }

    fclose( fp );
    return 0;
}



/*
 *  Guarda:
 *
 *      Esta funcao copia o conteudo do setor de boot de um disco rigido
 *  para um arquivo, excetuando-se a tabela de particoes.
 */

guarda( arquivo )
char    *arquivo;
{
    FILE    *fp;
    char    oldBoot  BUFSIZ |;

    if ( biosdisk( LEITURA, WINCHST, 0, 0, 1, 1, oldBoot ) )
    {
        fprintf( stderr, "Erro na leitura do setor de boot.\n" );
        return -1;
    }

    if ( (fp = fopen( arquivo, "wb" )) == NULL )
    {
        fprintf( stderr, "Erro na abertura de \"%s\".\n", arquivo );
        return -1;
    }

    /*
     *  Nao guarda a tabela de particoes, so' o codigo de boot !
     */
    if ( fwrite( oldBoot, 1, N_BYTES, fp ) != N_BYTES )
    {
        fprintf( stderr, "Erro na escrita de \"%s\".\n", arquivo );
        return -1;
    }

    fclose( fp );
    return 0;
}

main ( argc, argv )
int     argc;
char    *argv |;
{
    char    resp  80 |;

    if ( argc > 2 )
    {
        fprintf( stderr, "Use: INSTBOOT\nou   INSTBOOT arqBoot\n" );
        return 1;
    }

    if ( argc == 2 )
    {
        return instala( argv  1 | );
    }
    else
        do
        {
            printf( "\n1 - Copiar o setor de boot para um arquivo\n" );
            printf( "2 - Copiar um arquivo para o setor de boot\n" );
            printf( "\nQual operacao desejada (1/2) ? " );

            gets( resp );

            switch( *resp )
            {
                case '1':
                    printf( "Para qual arquivo ? " );
                    gets( resp );
                    return guarda( resp );

                case '2':
                    printf( "Qual o arquivo a instalar ? " );
                    gets( resp );
                    return instala( resp );

                default:
                    fprintf( stderr, "Selecione a operacao 1 ou 2.\n" );
            }
        } while( 1 );
}