rink.nu / projects / ananas / loader/x86/pxe.c@500fdeba0928 (annotated)
loader/x86/pxe.c
author Rink Springer <rink@rink.nu>
Sat Sep 18 21:18:19 2010 +0200 (2010-09-18 ago)
changeset 547 500fdeba0928
parent 424 812b4bde3b13
child 759 e7661e46f1bb
permissions -rw-r--r--
Loader: Migrate the loader to the new include structure.

While here, get rid of the ugly <sys/lib.h> and <std*.h> includes to get memcpy/printf/etc and introduce loader/lib.h; this makes it clearer what the seperation between the loader and the kernel is.
rink@547
     1
#include <loader/lib.h>
rink@414
     2
#include <loader/x86.h>
rink@414
     3
#include <loader/pxe.h>
rink@424
     4
#include <loader/platform.h>
rink@424
     5
#include <loader/vfs.h>
rink@414
     6
#include "param.h"
rink@414
     7
rink@424
     8
#ifdef PXE
rink@424
     9
rink@424
    10
#define TFTP_PORT 69
rink@424
    11
#define TFTP_PACKET_SIZE 512
rink@424
    12
rink@424
    13
static struct PXE_BANGPXE* bangpxe = NULL;
rink@424
    14
#define PXE_SCRATCHPAD_LENGTH 1024
rink@424
    15
static void* pxe_scratchpad;
rink@424
    16
static uint32_t pxe_server_ip;
rink@424
    17
static uint32_t pxe_gateway_ip;
rink@424
    18
static uint32_t pxe_my_ip;
rink@424
    19
rink@424
    20
/*
rink@424
    21
 * PXE only allows to read PACKET_SIZE chunks at a time, so we
rink@424
    22
 * use this value to determine how much we have left in our
rink@424
    23
 * input buffer.
rink@424
    24
 */
rink@424
    25
static uint32_t pxe_readbuf_offset;
rink@424
    26
static uint32_t pxe_readbuf_left;
rink@424
    27
rink@424
    28
static uint32_t htonl(uint32_t n)
rink@424
    29
{
rink@424
    30
	return (n >> 24) | ((n >> 16) & 0xff) << 8 | ((n >> 8) & 0xff) << 16 | (n & 0xff) << 24;
rink@424
    31
}
rink@424
    32
rink@424
    33
static uint32_t htons(uint32_t n)
rink@424
    34
{
rink@424
    35
	return ((n >> 8) & 0xff) | (n & 0xff) << 8;
rink@424
    36
}
rink@424
    37
rink@424
    38
static int
rink@424
    39
pxe_call(uint16_t func)
rink@424
    40
{
rink@424
    41
	if (bangpxe == NULL)
rink@424
    42
		return PXENV_EXIT_FAILURE;
rink@424
    43
rink@424
    44
	struct REALMODE_REGS regs;
rink@424
    45
	x86_realmode_init(&regs);
rink@424
    46
	x86_realmode_push16(&regs, (uint16_t)((uint32_t)&rm_buffer >> 16));
rink@424
    47
	x86_realmode_push16(&regs, (uint16_t)((uint32_t)&rm_buffer & 0xffff));
rink@424
    48
	x86_realmode_push16(&regs, func);
rink@424
    49
	regs.cs = bangpxe->EntryPointSP >> 16;
rink@424
    50
	regs.ip = bangpxe->EntryPointSP & 0xffff;
rink@424
    51
	x86_realmode_call(&regs);
rink@424
    52
	return regs.eax & 0xffff;
rink@424
    53
}
rink@424
    54
rink@424
    55
static int
rink@424
    56
pxe_tftp_open(const char* name)
rink@424
    57
{
rink@424
    58
	t_PXENV_TFTP_OPEN* to = (t_PXENV_TFTP_OPEN*)&rm_buffer;
rink@424
    59
	to->ServerIPAddress = pxe_server_ip;
rink@424
    60
	to->GatewayIPAddress = pxe_gateway_ip;
rink@424
    61
	strcpy(to->FileName, name);
rink@424
    62
	to->TFTPPort = htons(TFTP_PORT);
rink@424
    63
	to->PacketSize = TFTP_PACKET_SIZE;
rink@424
    64
	if (pxe_call(PXENV_TFTP_OPEN) != PXENV_EXIT_SUCCESS || (to->Status == PXENV_STATUS_SUCCESS))
rink@424
    65
		return PXENV_STATUS_FAILURE;
rink@424
    66
	pxe_readbuf_offset = 0;
rink@424
    67
	pxe_readbuf_left = 0;
rink@424
    68
	vfs_curfile_offset = 0;
rink@424
    69
	return 1;
rink@424
    70
}
rink@424
    71
rink@424
    72
static void
rink@424
    73
pxe_tftp_close()
rink@424
    74
{
rink@424
    75
	t_PXENV_TFTP_CLOSE* tc = (t_PXENV_TFTP_CLOSE*)&rm_buffer;
rink@424
    76
	pxe_call(PXENV_TFTP_CLOSE);
rink@424
    77
}
rink@424
    78
rink@424
    79
static size_t
rink@424
    80
pxe_tftp_read(void* ptr, size_t length)
rink@424
    81
{
rink@424
    82
	size_t numread = 0;
rink@424
    83
	while (length > 0) {
rink@424
    84
		/* If we have some left in our read buffer, use it */
rink@424
    85
		if (pxe_readbuf_left > 0) {
rink@424
    86
			int chunklen = (pxe_readbuf_left < length) ? pxe_readbuf_left : length;
rink@424
    87
			if (ptr != NULL)
rink@424
    88
				memcpy((ptr + numread), (pxe_scratchpad + pxe_readbuf_offset), chunklen);
rink@424
    89
			numread += chunklen; length -= chunklen;
rink@424
    90
			pxe_readbuf_offset += chunklen;
rink@424
    91
			pxe_readbuf_left -= chunklen;
rink@424
    92
			continue;
rink@424
    93
		}
rink@424
    94
rink@424
    95
		/* Nothing left; prepare for reading more */
rink@424
    96
		pxe_readbuf_offset = 0;
rink@424
    97
rink@424
    98
		/* Have to read a new chunk */
rink@424
    99
		t_PXENV_TFTP_READ* tr = (t_PXENV_TFTP_READ*)&rm_buffer;
rink@424
   100
		tr->BufferOffset = ((uint32_t)pxe_scratchpad & 0xffff);
rink@424
   101
		tr->BufferSegment = ((uint32_t)pxe_scratchpad >> 16);
rink@424
   102
		if ((pxe_call(PXENV_TFTP_READ) != PXENV_EXIT_SUCCESS) || tr->Status != PXENV_STATUS_SUCCESS)
rink@424
   103
			break;
rink@424
   104
rink@424
   105
		pxe_readbuf_left = tr->BufferSize;
rink@424
   106
		vfs_curfile_offset += tr->BufferSize;
rink@424
   107
		if (pxe_readbuf_left == 0)
rink@424
   108
			break;
rink@424
   109
	}
rink@424
   110
	return numread;
rink@424
   111
}
rink@424
   112
rink@414
   113
int
rink@414
   114
platform_init_netboot()
rink@414
   115
{
rink@414
   116
	struct REALMODE_REGS regs;
rink@414
   117
rink@414
   118
	x86_realmode_init(&regs);
rink@414
   119
	regs.eax = 0x5650; /* PXE: installation check */
rink@414
   120
	regs.interrupt = 0x1a;
rink@414
   121
	x86_realmode_call(&regs);
rink@414
   122
	if ((regs.eax & 0xffff) != 0x564e)
rink@414
   123
		return 0;
rink@414
   124
	if ((regs.eflags & EFLAGS_CF) != 0)
rink@414
   125
		return 0;
rink@414
   126
rink@414
   127
	struct PXE_ENVPLUS* pxeplus = (struct PXE_ENVPLUS*)((uint32_t)(regs.es << 4) + regs.ebx);
rink@414
   128
rink@414
   129
	/* We only verify the checksum; this should be enough */
rink@414
   130
	uint8_t sum = 0;
rink@414
   131
	for (unsigned int i = 0; i < pxeplus->Length; i++) {
rink@414
   132
		sum += *(uint8_t*)((void*)pxeplus + i);
rink@414
   133
	}
rink@414
   134
	if (sum != 0)
rink@414
   135
		return 0;
rink@414
   136
rink@424
   137
	printf(">> Netboot: PXE %u.%u - ",
rink@414
   138
	 pxeplus->Version >> 8, pxeplus->Version & 0xff);
rink@414
   139
	if (pxeplus->Version < 0x201) {
rink@414
   140
		printf("unsupported version - ignoring\n");
rink@414
   141
		return 0;
rink@414
   142
	}
rink@424
   143
	bangpxe = (struct PXE_BANGPXE*)((pxeplus->UNDICodeSeg << 4) + pxeplus->PXEPtr);
rink@414
   144
rink@414
   145
	/* Verify the checksum once more; should be enough once again :-) */
rink@414
   146
	sum = 0;
rink@414
   147
	for (unsigned int i = 0; i < bangpxe->StructLength; i++) {
rink@414
   148
		sum += *(uint8_t*)((void*)bangpxe + i);
rink@414
   149
	}
rink@414
   150
	if (sum != 0) {
rink@414
   151
		printf("!PXE unavailable - ignoring\n");
rink@424
   152
		bangpxe = NULL;
rink@414
   153
		return 0;
rink@414
   154
	}
rink@414
   155
rink@424
   156
	/* If we got this far, initialize some scratchpad memory */
rink@424
   157
	pxe_scratchpad = platform_get_memory(PXE_SCRATCHPAD_LENGTH);
rink@414
   158
rink@424
   159
	/*
rink@424
   160
	 * Note: it appears that supplying your own buffer does not work with all PXE's,
rink@424
   161
 	 * so just obtain their pointer instead.
rink@424
   162
	 */
rink@424
   163
	t_PXENV_GET_CACHED_INFO* gci = (t_PXENV_GET_CACHED_INFO*)&rm_buffer;
rink@424
   164
	memset(gci, 0, sizeof(t_PXENV_GET_CACHED_INFO));
rink@424
   165
	gci->PacketType = PXENV_PACKET_TYPE_CACHED_REPLY;
rink@424
   166
	if (pxe_call(PXENV_GET_CACHED_INFO) != PXENV_EXIT_SUCCESS) {
rink@424
   167
		printf("cannot obtain IP information - ignoring\n");
rink@424
   168
		bangpxe = NULL;
rink@424
   169
		return 0;
rink@424
   170
	}
rink@424
   171
	struct BOOTP* bootp = (struct BOOTP*)((uint32_t)(gci->BufferSegment << 4) + (uint32_t)gci->BufferOffset);
rink@424
   172
	pxe_server_ip = bootp->sip;
rink@424
   173
	pxe_gateway_ip = bootp->gip;
rink@424
   174
	pxe_my_ip = bootp->yip;
rink@424
   175
	printf("IP: %$, server IP: %$\n", pxe_my_ip, pxe_server_ip);
rink@414
   176
	return 1;
rink@414
   177
}
rink@414
   178
rink@424
   179
void
rink@424
   180
platform_cleanup_netboot()
rink@424
   181
{
rink@424
   182
	/*
rink@424
   183
	 * XXX The order here does not seem to confirm with the PXE spec, but
rink@424
   184
	 * at least on gPXE you cannot shutdown UNDI after unloading the stack;
rink@424
   185
	 * we just stop everything and get rid of the stack, that should
rink@424
   186
	 * be enough.
rink@424
   187
	 */
rink@424
   188
	pxe_call(PXENV_STOP_UNDI);
rink@424
   189
	pxe_call(PXENV_STOP_BASE);
rink@424
   190
	pxe_call(PXENV_UNLOAD_STACK);
rink@424
   191
}
rink@424
   192
rink@424
   193
static int
rink@424
   194
pxe_tftp_mount(int iodevice)
rink@424
   195
{
rink@424
   196
	/* Always succeeds if asked to mount the network disk */
rink@424
   197
	return (iodevice == -1);
rink@424
   198
}
rink@424
   199
rink@424
   200
static int
rink@424
   201
pxe_tftp_seek(uint32_t offset)
rink@424
   202
{
rink@424
   203
	/*
rink@424
   204
	 * TFTP is streaming, so we can only seek forwards. Note that we can only
rink@424
   205
	 * read in chunks of TFTP_PACKET_SIZE, and vfs_curfile_offset thus is
rink@424
   206
	 * always a multiple of this.
rink@424
   207
	 */
rink@424
   208
	unsigned int real_offset = vfs_curfile_offset - pxe_readbuf_left;
rink@424
   209
	if (real_offset >= offset)
rink@424
   210
		return real_offset == offset;
rink@424
   211
rink@424
   212
	/*
rink@424
   213
	 * Now, we just have to throw away as much as needed.
rink@424
   214
	 */
rink@424
   215
	return pxe_tftp_read(NULL, offset - real_offset) == (offset - real_offset);
rink@424
   216
}
rink@424
   217
rink@424
   218
struct LOADER_FS_DRIVER loaderfs_pxe_tftp = {
rink@424
   219
	.mount = pxe_tftp_mount,
rink@424
   220
	.open  = pxe_tftp_open,
rink@424
   221
	.close = pxe_tftp_close,
rink@424
   222
	.read  = pxe_tftp_read,
rink@424
   223
	.seek  = pxe_tftp_seek
rink@424
   224
};
rink@424
   225
rink@424
   226
#else /* !PXE */
rink@424
   227
int
rink@424
   228
platform_init_netboot()
rink@424
   229
{
rink@424
   230
	return 0;
rink@424
   231
}
rink@424
   232
rink@424
   233
void
rink@424
   234
platform_cleanup_netboot()
rink@424
   235
{
rink@424
   236
}
rink@424
   237
#endif
rink@424
   238
rink@414
   239
/* vim:set ts=2 sw=2: */
Powered by FreeBSD, PostgreSQL and Perl
© 2001 - 2014 Rink Springer