blob: 093729efc7453c24bf47e9e66ca004999e2606e0 [file] [log] [blame] [raw]
/*
* PXE file system for GRUB
*
* Copyright (C) 2007 Bean (bean123@126.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef FSYS_PXE
#include "shared.h"
#include "filesys.h"
#include "pxe.h"
#include "../netboot/osdep.h"
#ifdef GRUB_UTIL
int pxe_mount(void) { return 0; }
unsigned long pxe_read (char *buf, unsigned long len) { return -1; }
int pxe_dir (char *dirname) { return 0; }
void pxe_close (void) {}
#else
#define TFTP_PORT htons(69)
#define PXE_BUF FSYS_BUF
#define PXE_BUFLEN FSYS_BUFLEN
IP4 pxe_yip,pxe_sip,pxe_gip;
UINT8 pxe_mac_len,pxe_mac_type,pxe_tftp_opened;
MAC_ADDR pxe_mac;
unsigned long pxe_saved_pos,pxe_cur_ofs,pxe_read_ofs;
PXENV_TFTP_OPEN_t pxe_tftp_open;
char *pxe_tftp_name;
char* pxe_outhex(char* pc,unsigned char c)
{
int i;
pc+=2;
for (i=1;i<=2;i++)
{
unsigned char t;
t=c & 0xF;
if (t>=10)
t+='A'-10;
else
t+='0';
*(pc-i)=t;
c=c>>4;
}
return pc;
}
void pxe_detect(void)
{
PXENV_GET_CACHED_INFO_t get_cached_info;
BOOTPLAYER *bp;
unsigned long tmp;
char *pc;
int i,ret;
if (! pxe_scan())
return;
get_cached_info.PacketType=PXENV_PACKET_TYPE_DHCP_ACK;
get_cached_info.Buffer=get_cached_info.BufferSize=0;
pxe_call(PXENV_GET_CACHED_INFO,&get_cached_info);
if (get_cached_info.Status)
return;
bp=LINEAR(get_cached_info.Buffer);
pxe_yip=bp->yip;
pxe_sip=bp->sip;
pxe_gip=bp->gip;
pxe_mac_type=bp->Hardware;
pxe_mac_len=bp->Hardlen;
grub_memmove(&pxe_mac,&bp->CAddr,pxe_mac_len);
get_cached_info.PacketType=PXENV_PACKET_TYPE_CACHED_REPLY;
get_cached_info.Buffer=get_cached_info.BufferSize=0;
pxe_call(PXENV_GET_CACHED_INFO,&get_cached_info);
if (get_cached_info.Status)
return;
bp=LINEAR(get_cached_info.Buffer);
if (bp->bootfile[0])
{
int n;
n=grub_strlen((char*)bp->bootfile)-1;
grub_strcpy((char*)&pxe_tftp_open.FileName,(char*)bp->bootfile);
while ((n>=0) && (pxe_tftp_open.FileName[n]!='/'))
n--;
if (n<0)
n=0;
pxe_tftp_name=(char*)&pxe_tftp_open.FileName[n];
}
else
pxe_tftp_name=(char*)&pxe_tftp_open.FileName[0];
pxe_tftp_opened=0;
ret=0;
grub_strcpy(pxe_tftp_name,"/menu.lst/");
pc=pxe_tftp_name+10;
pc=pxe_outhex(pc,pxe_mac_type);
for (i=0;i<pxe_mac_len;i++)
{
*(pc++)='-';
pc=pxe_outhex(pc,pxe_mac[i]);
}
*pc=0;
grub_printf("\n%s\n",pxe_tftp_open.FileName);
if (pxe_dir(pxe_tftp_name))
{
ret=1;
goto done;
}
pc=pxe_tftp_name+10;
tmp=pxe_yip;
for (i=0;i<4;i++)
{
pc=pxe_outhex(pc,tmp & 0xFF);
tmp >>=8;
}
*pc=0;
do
{
grub_printf("%s\n",pxe_tftp_open.FileName);
if (pxe_dir(pxe_tftp_name))
{
ret=1;
goto done;
}
*(--pc)=0;
} while (pc>pxe_tftp_name+10);
grub_strcpy(pc,"default");
grub_printf("%s\n",pxe_tftp_open.FileName);
ret=pxe_dir(pxe_tftp_name);
done:
if (ret)
{
unsigned long nr;
nr=4096-1;
if (nr>filemax)
nr=filemax;
nr=pxe_read((char*)0x800,nr);
if (nr!=PXE_ERR_LEN)
{
*(char*)(0x800+nr)=0;
if (preset_menu!=(char*)0x800)
preset_menu=(char*)0x800;
}
pxe_close();
}
//getkey();
}
/* Mount the network drive. If the drive is ready, return 1, otherwise
return 0. */
int pxe_mount(void)
{
if (current_drive != PXE_DRIVE)
return 0;
return 1;
}
/* Read num packets , BUF must be segment aligned*/
unsigned long pxe_read_blk(unsigned long buf,int num)
{
PXENV_TFTP_READ_t tftp_read;
tftp_read.Buffer=SEGOFS(buf);
while (num>0)
{
pxe_call(PXENV_TFTP_READ, &tftp_read);
if (tftp_read.Status)
return PXE_ERR_LEN;
tftp_read.Buffer+=tftp_read.BufferSize;
if (tftp_read.BufferSize<pxe_blk_len)
break;
num--;
}
return (tftp_read.Buffer & 0xFFFF);
}
unsigned long pxe_read_len (char* buf, unsigned long len)
{
unsigned long old_ofs,sz;
if (len==0)
return 0;
sz=0;
old_ofs=pxe_cur_ofs;
pxe_cur_ofs+=len;
if (pxe_cur_ofs>pxe_read_ofs)
{
unsigned long nb,nr;
sz=(pxe_read_ofs-old_ofs);
if ((buf) && (sz))
{
grub_memmove(buf,(char*)(PXE_BUF+old_ofs),sz);
buf+=sz;
}
pxe_cur_ofs-=pxe_read_ofs;
nb=pxe_cur_ofs / pxe_blk_len;
pxe_cur_ofs-=pxe_blk_len*nb;
while (nb>0)
{
unsigned long nn;
nn=PXE_BUFLEN / pxe_blk_len;
if (nn>nb)
nn=nb;
nr=pxe_read_blk(PXE_BUF,nn);
if (nr==PXE_ERR_LEN)
return nr;
sz+=nr;
if (buf)
{
grub_memmove(buf,(char*)PXE_BUF,nr);
buf+=nr;
}
if (nr<nn*pxe_blk_len)
{
pxe_cur_ofs=pxe_read_ofs=nr;
return sz;
}
nb-=nn;
}
if (pxe_cur_ofs)
{
nr=pxe_read_blk(PXE_BUF,1);
if (nr==PXE_ERR_LEN)
return nr;
sz+=pxe_cur_ofs;
if (buf)
grub_memmove(buf,(char*)PXE_BUF,pxe_cur_ofs);
pxe_read_ofs=nr;
}
else
pxe_read_ofs=0;
}
else
{
sz+=len;
if (buf)
grub_memmove(buf,(char*)PXE_BUF+old_ofs,len);
}
return sz;
}
/* Read up to SIZE bytes, returned in ADDR. */
unsigned long pxe_read (char *buf, unsigned long len)
{
unsigned long nr;
if (! pxe_tftp_opened)
return PXE_ERR_LEN;
if (pxe_saved_pos!=filepos)
{
if (pxe_saved_pos>filepos)
{
pxe_close();
pxe_call(PXENV_TFTP_OPEN, &pxe_tftp_open);
if (pxe_tftp_open.Status)
return -1;
pxe_tftp_opened=1;
pxe_saved_pos=pxe_cur_ofs=pxe_read_ofs=0;
}
nr=pxe_read_len(NULL, filepos-pxe_saved_pos);
if ((nr==PXE_ERR_LEN) || (pxe_saved_pos+nr!=filepos))
return PXE_ERR_LEN;
pxe_saved_pos=filepos;
}
nr=pxe_read_len(buf,len);
if (nr!=PXE_ERR_LEN)
{
filepos+=nr;
pxe_saved_pos=filepos;
}
return nr;
}
/* Check if the file DIRNAME really exists. Get the size and save it in
FILEMAX. return 1 if succeed, 0 if fail. */
int pxe_dir (char *dirname)
{
PXENV_TFTP_GET_FSIZE_t *tftp_get_fsize;
int ret,ch;
if (print_possibilities)
return 1;
pxe_close();
ret=1;
ch=nul_terminate(dirname);
tftp_get_fsize=(void*)&pxe_tftp_open;
tftp_get_fsize->ServerIPAddress=pxe_sip;
tftp_get_fsize->GatewayIPAddress=pxe_gip;
if (dirname!=pxe_tftp_name)
grub_strcpy(pxe_tftp_name,dirname);
pxe_call(PXENV_TFTP_GET_FSIZE,tftp_get_fsize);
if (tftp_get_fsize->Status)
{
errnum=ERR_FILE_NOT_FOUND;
ret=0;
goto quit;
}
filepos=pxe_saved_pos=pxe_cur_ofs=pxe_read_ofs=0;
filemax=tftp_get_fsize->FileSize;
pxe_tftp_open.TFTPPort=TFTP_PORT;
pxe_tftp_open.PacketSize=pxe_blk_len;
pxe_call(PXENV_TFTP_OPEN,&pxe_tftp_open);
if (pxe_tftp_open.Status)
{
errnum=ERR_READ;
ret=0;
goto quit;
}
pxe_blk_len=pxe_tftp_open.PacketSize;
pxe_tftp_opened=1;
quit:
dirname[grub_strlen(dirname)]=ch;
return ret;
}
/* Close the file. */
void pxe_close (void)
{
if (pxe_tftp_opened)
{
PXENV_TFTP_CLOSE_t tftp_close;
pxe_call(PXENV_TFTP_CLOSE,&tftp_close);
pxe_tftp_opened=0;
}
}
#endif
#endif