Send network packets on PXE
This commit is contained in:
parent
cc4bfec8fa
commit
8e60fc8f85
3 changed files with 305 additions and 483 deletions
304
grub-core/net/drivers/i386/pc/pxe.c
Normal file
304
grub-core/net/drivers/i386/pc/pxe.c
Normal file
|
@ -0,0 +1,304 @@
|
|||
/* pxe.c - Driver to provide access to the pxe filesystem */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2008,2009,2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/dl.h>
|
||||
#include <grub/net.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/bufio.h>
|
||||
#include <grub/env.h>
|
||||
|
||||
#include <grub/machine/pxe.h>
|
||||
#include <grub/machine/int.h>
|
||||
#include <grub/machine/memory.h>
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
#define SEGMENT(x) ((x) >> 4)
|
||||
#define OFFSET(x) ((x) & 0xF)
|
||||
#define SEGOFS(x) ((SEGMENT(x) << 16) + OFFSET(x))
|
||||
#define LINEAR(x) (void *) ((((x) >> 16) << 4) + ((x) & 0xFFFF))
|
||||
|
||||
struct grub_pxe_undi_open
|
||||
{
|
||||
grub_uint16_t status;
|
||||
grub_uint16_t open_flag;
|
||||
grub_uint16_t pkt_filter;
|
||||
grub_uint16_t mcast_count;
|
||||
grub_uint8_t mcast[8][6];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_pxe_undi_isr
|
||||
{
|
||||
grub_uint16_t status;
|
||||
grub_uint16_t func_flag;
|
||||
grub_uint16_t buffer_len;
|
||||
grub_uint16_t frame_len;
|
||||
grub_uint16_t frame_hdr_len;
|
||||
grub_uint32_t buffer;
|
||||
grub_uint8_t prot_type;
|
||||
grub_uint8_t pkt_type;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum
|
||||
{
|
||||
GRUB_PXE_ISR_IN_START = 1,
|
||||
GRUB_PXE_ISR_IN_PROCESS,
|
||||
GRUB_PXE_ISR_IN_GET_NEXT
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
GRUB_PXE_ISR_OUT_OURS = 0,
|
||||
GRUB_PXE_ISR_OUT_NOT_OURS = 1
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
GRUB_PXE_ISR_OUT_DONE = 0,
|
||||
GRUB_PXE_ISR_OUT_TRANSMIT = 2,
|
||||
GRUB_PXE_ISR_OUT_RECEIVE = 3,
|
||||
GRUB_PXE_ISR_OUT_BUSY = 4,
|
||||
};
|
||||
|
||||
struct grub_pxe_undi_transmit
|
||||
{
|
||||
grub_uint16_t status;
|
||||
grub_uint8_t protocol;
|
||||
grub_uint8_t xmitflag;
|
||||
grub_uint32_t dest;
|
||||
grub_uint32_t tbd;
|
||||
grub_uint32_t reserved[2];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_pxe_undi_tbd
|
||||
{
|
||||
grub_uint16_t len;
|
||||
grub_uint32_t buf;
|
||||
grub_uint16_t blk_count;
|
||||
struct
|
||||
{
|
||||
grub_uint8_t ptr_type;
|
||||
grub_uint8_t reserved;
|
||||
grub_uint16_t len;
|
||||
grub_uint32_t ptr;
|
||||
} blocks[8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_pxe_bangpxe *grub_pxe_pxenv;
|
||||
static grub_uint32_t pxe_rm_entry = 0;
|
||||
|
||||
static struct grub_pxe_bangpxe *
|
||||
grub_pxe_scan (void)
|
||||
{
|
||||
struct grub_bios_int_registers regs;
|
||||
struct grub_pxenv *pxenv;
|
||||
struct grub_pxe_bangpxe *bangpxe;
|
||||
|
||||
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
|
||||
|
||||
regs.ebx = 0;
|
||||
regs.ecx = 0;
|
||||
regs.eax = 0x5650;
|
||||
regs.es = 0;
|
||||
|
||||
grub_bios_interrupt (0x1a, ®s);
|
||||
|
||||
if ((regs.eax & 0xffff) != 0x564e)
|
||||
return NULL;
|
||||
|
||||
pxenv = (struct grub_pxenv *) ((regs.es << 4) + (regs.ebx & 0xffff));
|
||||
if (grub_memcmp (pxenv->signature, GRUB_PXE_SIGNATURE,
|
||||
sizeof (pxenv->signature))
|
||||
!= 0)
|
||||
return NULL;
|
||||
|
||||
if (pxenv->version < 0x201)
|
||||
return NULL;
|
||||
|
||||
bangpxe = (void *) ((((pxenv->pxe_ptr & 0xffff0000) >> 16) << 4)
|
||||
+ (pxenv->pxe_ptr & 0xffff));
|
||||
|
||||
if (!bangpxe)
|
||||
return NULL;
|
||||
|
||||
if (grub_memcmp (bangpxe->signature, GRUB_PXE_BANGPXE_SIGNATURE,
|
||||
sizeof (bangpxe->signature)) != 0)
|
||||
return NULL;
|
||||
|
||||
pxe_rm_entry = bangpxe->rm_entry;
|
||||
|
||||
return bangpxe;
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
grub_pxe_recv (const struct grub_net_card *dev __attribute__ ((unused)),
|
||||
struct grub_net_buff *buf)
|
||||
{
|
||||
struct grub_pxe_undi_isr *isr;
|
||||
static int in_progress = 0;
|
||||
char *ptr, *end;
|
||||
int len;
|
||||
|
||||
isr = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
|
||||
|
||||
if (!in_progress)
|
||||
{
|
||||
grub_memset (isr, 0, sizeof (*isr));
|
||||
isr->func_flag = GRUB_PXE_ISR_IN_START;
|
||||
grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry);
|
||||
if (isr->status || isr->func_flag != GRUB_PXE_ISR_OUT_OURS)
|
||||
return -1;
|
||||
grub_memset (isr, 0, sizeof (*isr));
|
||||
isr->func_flag = GRUB_PXE_ISR_IN_PROCESS;
|
||||
grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_memset (isr, 0, sizeof (*isr));
|
||||
isr->func_flag = GRUB_PXE_ISR_IN_GET_NEXT;
|
||||
grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry);
|
||||
}
|
||||
|
||||
while (isr->func_flag != GRUB_PXE_ISR_OUT_RECEIVE)
|
||||
{
|
||||
if (isr->status || isr->func_flag == GRUB_PXE_ISR_OUT_DONE)
|
||||
return -1;
|
||||
grub_memset (isr, 0, sizeof (*isr));
|
||||
isr->func_flag = GRUB_PXE_ISR_IN_GET_NEXT;
|
||||
grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry);
|
||||
}
|
||||
|
||||
grub_netbuff_put (buf, isr->frame_len);
|
||||
ptr = buf->data;
|
||||
end = ptr + isr->frame_len;
|
||||
len = isr->frame_len;
|
||||
grub_memcpy (ptr, LINEAR (isr->buffer), isr->buffer_len);
|
||||
ptr += isr->buffer_len;
|
||||
while (ptr < end)
|
||||
{
|
||||
grub_memset (isr, 0, sizeof (*isr));
|
||||
isr->func_flag = GRUB_PXE_ISR_IN_GET_NEXT;
|
||||
grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry);
|
||||
if (isr->status || isr->func_flag != GRUB_PXE_ISR_OUT_RECEIVE)
|
||||
return -1;
|
||||
|
||||
grub_memcpy (ptr, LINEAR (isr->buffer), isr->buffer_len);
|
||||
ptr += isr->buffer_len;
|
||||
}
|
||||
|
||||
grub_printf ("<%d>\n", len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_pxe_send (const struct grub_net_card *dev __attribute__ ((unused)),
|
||||
struct grub_net_buff *pack)
|
||||
{
|
||||
struct grub_pxe_undi_transmit *trans;
|
||||
struct grub_pxe_undi_tbd *tbd;
|
||||
char *buf;
|
||||
|
||||
trans = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
|
||||
grub_memset (trans, 0, sizeof (*trans));
|
||||
tbd = (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + 128);
|
||||
grub_memset (tbd, 0, sizeof (*tbd));
|
||||
buf = (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + 256);
|
||||
grub_memcpy (buf, pack->data, pack->tail - pack->data);
|
||||
|
||||
trans->tbd = SEGOFS ((grub_addr_t) tbd);
|
||||
trans->protocol = 0;
|
||||
tbd->len = pack->tail - pack->data;
|
||||
tbd->buf = SEGOFS ((grub_addr_t) buf);
|
||||
|
||||
grub_pxe_call (GRUB_PXENV_UNDI_TRANSMIT, trans, pxe_rm_entry);
|
||||
if (trans->status)
|
||||
return grub_error (GRUB_ERR_IO, "PXE send failed (status 0x%x)",
|
||||
trans->status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct grub_net_card_driver grub_pxe_card_driver =
|
||||
{
|
||||
.send = grub_pxe_send,
|
||||
.recv = grub_pxe_recv
|
||||
};
|
||||
|
||||
struct grub_net_card grub_pxe_card =
|
||||
{
|
||||
.driver = &grub_pxe_card_driver,
|
||||
.name = "pxe"
|
||||
};
|
||||
|
||||
void
|
||||
grub_pxe_unload (void)
|
||||
{
|
||||
if (grub_pxe_pxenv)
|
||||
{
|
||||
grub_pxe_call (GRUB_PXENV_UNDI_CLOSE,
|
||||
(void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR,
|
||||
pxe_rm_entry);
|
||||
grub_net_card_unregister (&grub_pxe_card);
|
||||
grub_pxe_pxenv = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GRUB_MOD_INIT(pxe)
|
||||
{
|
||||
struct grub_pxe_bangpxe *pxenv;
|
||||
struct grub_pxenv_get_cached_info ci;
|
||||
struct grub_net_bootp_packet *bp;
|
||||
struct grub_pxe_undi_open *ou;
|
||||
|
||||
pxenv = grub_pxe_scan ();
|
||||
if (! pxenv)
|
||||
return;
|
||||
|
||||
ci.packet_type = GRUB_PXENV_PACKET_TYPE_DHCP_ACK;
|
||||
ci.buffer = 0;
|
||||
ci.buffer_size = 0;
|
||||
grub_pxe_call (GRUB_PXENV_GET_CACHED_INFO, &ci, pxe_rm_entry);
|
||||
if (ci.status)
|
||||
return;
|
||||
|
||||
bp = LINEAR (ci.buffer);
|
||||
|
||||
grub_memcpy (grub_pxe_card.default_address.mac, bp->mac_addr,
|
||||
bp->hw_len < sizeof (grub_pxe_card.default_address.mac)
|
||||
? bp->hw_len : sizeof (grub_pxe_card.default_address.mac));
|
||||
grub_pxe_card.default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
|
||||
|
||||
ou = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
|
||||
grub_memset (ou, 0, sizeof (ou));
|
||||
ou->pkt_filter = 4;
|
||||
grub_pxe_call (GRUB_PXENV_UNDI_OPEN, ou, pxe_rm_entry);
|
||||
|
||||
if (ou->status)
|
||||
return;
|
||||
|
||||
grub_net_card_register (&grub_pxe_card);
|
||||
grub_net_configure_by_dhcp_ack ("pxe", &grub_pxe_card, 0,
|
||||
bp, GRUB_PXE_BOOTP_SIZE);
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(pxe)
|
||||
{
|
||||
grub_pxe_unload ();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue