summary |
shortlog |
changelog |
graph |
tags |
branches |
files |
changeset |
file |
revisions |
annotate |
diff |
raw
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.
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(®s); |
| rink@424 | 46 |
x86_realmode_push16(®s, (uint16_t)((uint32_t)&rm_buffer >> 16)); |
| rink@424 | 47 |
x86_realmode_push16(®s, (uint16_t)((uint32_t)&rm_buffer & 0xffff)); |
| rink@424 | 48 |
x86_realmode_push16(®s, func); |
| rink@424 | 49 |
regs.cs = bangpxe->EntryPointSP >> 16; |
| rink@424 | 50 |
regs.ip = bangpxe->EntryPointSP & 0xffff; |
| rink@424 | 51 |
x86_realmode_call(®s); |
| 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(®s); |
| rink@414 | 119 |
regs.eax = 0x5650; /* PXE: installation check */ |
| rink@414 | 120 |
regs.interrupt = 0x1a; |
| rink@414 | 121 |
x86_realmode_call(®s); |
| 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: */ |