]> git.proxmox.com Git - grub2.git/blame - grub-core/net/drivers/i386/pc/pxe.c
Add gcc_struct to all packed structures when compiling with mingw.
[grub2.git] / grub-core / net / drivers / i386 / pc / pxe.c
CommitLineData
8e60fc8f
VS
1/* pxe.c - Driver to provide access to the pxe filesystem */
2/*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008,2009,2011 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <grub/dl.h>
21#include <grub/net.h>
22#include <grub/mm.h>
23#include <grub/file.h>
24#include <grub/misc.h>
8e60fc8f 25#include <grub/env.h>
9c4b5c13 26#include <grub/i18n.h>
3c491b47 27#include <grub/loader.h>
8e60fc8f
VS
28
29#include <grub/machine/pxe.h>
30#include <grub/machine/int.h>
31#include <grub/machine/memory.h>
6708faaf 32#include <grub/machine/kernel.h>
8e60fc8f
VS
33
34GRUB_MOD_LICENSE ("GPLv3+");
35
36#define SEGMENT(x) ((x) >> 4)
37#define OFFSET(x) ((x) & 0xF)
38#define SEGOFS(x) ((SEGMENT(x) << 16) + OFFSET(x))
39#define LINEAR(x) (void *) ((((x) >> 16) << 4) + ((x) & 0xFFFF))
40
41struct grub_pxe_undi_open
42{
43 grub_uint16_t status;
44 grub_uint16_t open_flag;
45 grub_uint16_t pkt_filter;
46 grub_uint16_t mcast_count;
47 grub_uint8_t mcast[8][6];
7e47e27b 48} GRUB_PACKED;
8e60fc8f 49
6708faaf
VS
50struct grub_pxe_undi_info
51{
52 grub_uint16_t status;
53 grub_uint16_t base_io;
54 grub_uint16_t int_number;
55 grub_uint16_t mtu;
56 grub_uint16_t hwtype;
57 grub_uint16_t hwaddrlen;
58 grub_uint8_t current_addr[16];
59 grub_uint8_t permanent_addr[16];
60 grub_uint32_t romaddr;
61 grub_uint16_t rxbufct;
62 grub_uint16_t txbufct;
7e47e27b 63} GRUB_PACKED;
6708faaf
VS
64
65
8e60fc8f
VS
66struct grub_pxe_undi_isr
67{
68 grub_uint16_t status;
69 grub_uint16_t func_flag;
70 grub_uint16_t buffer_len;
71 grub_uint16_t frame_len;
72 grub_uint16_t frame_hdr_len;
73 grub_uint32_t buffer;
74 grub_uint8_t prot_type;
75 grub_uint8_t pkt_type;
7e47e27b 76} GRUB_PACKED;
8e60fc8f
VS
77
78enum
79 {
80 GRUB_PXE_ISR_IN_START = 1,
81 GRUB_PXE_ISR_IN_PROCESS,
82 GRUB_PXE_ISR_IN_GET_NEXT
83 };
84
85enum
86 {
87 GRUB_PXE_ISR_OUT_OURS = 0,
88 GRUB_PXE_ISR_OUT_NOT_OURS = 1
89 };
90
91enum
92 {
93 GRUB_PXE_ISR_OUT_DONE = 0,
94 GRUB_PXE_ISR_OUT_TRANSMIT = 2,
95 GRUB_PXE_ISR_OUT_RECEIVE = 3,
96 GRUB_PXE_ISR_OUT_BUSY = 4,
97 };
98
99struct grub_pxe_undi_transmit
100{
101 grub_uint16_t status;
102 grub_uint8_t protocol;
103 grub_uint8_t xmitflag;
104 grub_uint32_t dest;
105 grub_uint32_t tbd;
106 grub_uint32_t reserved[2];
7e47e27b 107} GRUB_PACKED;
8e60fc8f
VS
108
109struct grub_pxe_undi_tbd
110{
111 grub_uint16_t len;
112 grub_uint32_t buf;
113 grub_uint16_t blk_count;
114 struct
115 {
116 grub_uint8_t ptr_type;
117 grub_uint8_t reserved;
118 grub_uint16_t len;
119 grub_uint32_t ptr;
120 } blocks[8];
7e47e27b 121} GRUB_PACKED;
8e60fc8f
VS
122
123struct grub_pxe_bangpxe *grub_pxe_pxenv;
124static grub_uint32_t pxe_rm_entry = 0;
125
126static struct grub_pxe_bangpxe *
127grub_pxe_scan (void)
128{
129 struct grub_bios_int_registers regs;
130 struct grub_pxenv *pxenv;
131 struct grub_pxe_bangpxe *bangpxe;
132
133 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
134
135 regs.ebx = 0;
136 regs.ecx = 0;
137 regs.eax = 0x5650;
138 regs.es = 0;
139
140 grub_bios_interrupt (0x1a, &regs);
141
142 if ((regs.eax & 0xffff) != 0x564e)
143 return NULL;
144
145 pxenv = (struct grub_pxenv *) ((regs.es << 4) + (regs.ebx & 0xffff));
146 if (grub_memcmp (pxenv->signature, GRUB_PXE_SIGNATURE,
147 sizeof (pxenv->signature))
148 != 0)
149 return NULL;
150
151 if (pxenv->version < 0x201)
152 return NULL;
153
154 bangpxe = (void *) ((((pxenv->pxe_ptr & 0xffff0000) >> 16) << 4)
155 + (pxenv->pxe_ptr & 0xffff));
156
157 if (!bangpxe)
158 return NULL;
159
160 if (grub_memcmp (bangpxe->signature, GRUB_PXE_BANGPXE_SIGNATURE,
161 sizeof (bangpxe->signature)) != 0)
162 return NULL;
163
164 pxe_rm_entry = bangpxe->rm_entry;
165
166 return bangpxe;
167}
168
e2955971 169static struct grub_net_buff *
3e747239 170grub_pxe_recv (struct grub_net_card *dev __attribute__ ((unused)))
8e60fc8f
VS
171{
172 struct grub_pxe_undi_isr *isr;
173 static int in_progress = 0;
5c62099a 174 grub_uint8_t *ptr, *end;
e2955971 175 struct grub_net_buff *buf;
8e60fc8f
VS
176
177 isr = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
178
179 if (!in_progress)
180 {
181 grub_memset (isr, 0, sizeof (*isr));
182 isr->func_flag = GRUB_PXE_ISR_IN_START;
183 grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry);
e555f379
SG
184 /* Normally according to the specification we should also check
185 that isr->func_flag != GRUB_PXE_ISR_OUT_OURS but unfortunately it
186 breaks on intel cards.
187 */
188 if (isr->status)
59b455fc
VS
189 {
190 in_progress = 0;
e2955971 191 return NULL;
59b455fc 192 }
8e60fc8f
VS
193 grub_memset (isr, 0, sizeof (*isr));
194 isr->func_flag = GRUB_PXE_ISR_IN_PROCESS;
195 grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry);
196 }
197 else
198 {
199 grub_memset (isr, 0, sizeof (*isr));
200 isr->func_flag = GRUB_PXE_ISR_IN_GET_NEXT;
201 grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry);
202 }
203
204 while (isr->func_flag != GRUB_PXE_ISR_OUT_RECEIVE)
205 {
206 if (isr->status || isr->func_flag == GRUB_PXE_ISR_OUT_DONE)
59b455fc
VS
207 {
208 in_progress = 0;
e2955971 209 return NULL;
59b455fc 210 }
8e60fc8f
VS
211 grub_memset (isr, 0, sizeof (*isr));
212 isr->func_flag = GRUB_PXE_ISR_IN_GET_NEXT;
213 grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry);
214 }
215
531e2241 216 buf = grub_netbuff_alloc (isr->frame_len + 2);
e2955971
VS
217 if (!buf)
218 return NULL;
219 /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible
220 by 4. So that IP header is aligned on 4 bytes. */
221 grub_netbuff_reserve (buf, 2);
222 if (!buf)
223 {
224 grub_netbuff_free (buf);
225 return NULL;
226 }
8e60fc8f
VS
227 ptr = buf->data;
228 end = ptr + isr->frame_len;
e2955971 229 grub_netbuff_put (buf, isr->frame_len);
8e60fc8f
VS
230 grub_memcpy (ptr, LINEAR (isr->buffer), isr->buffer_len);
231 ptr += isr->buffer_len;
232 while (ptr < end)
233 {
234 grub_memset (isr, 0, sizeof (*isr));
235 isr->func_flag = GRUB_PXE_ISR_IN_GET_NEXT;
236 grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry);
237 if (isr->status || isr->func_flag != GRUB_PXE_ISR_OUT_RECEIVE)
59b455fc
VS
238 {
239 in_progress = 1;
e2955971
VS
240 grub_netbuff_free (buf);
241 return NULL;
59b455fc 242 }
8e60fc8f
VS
243
244 grub_memcpy (ptr, LINEAR (isr->buffer), isr->buffer_len);
245 ptr += isr->buffer_len;
246 }
59b455fc 247 in_progress = 1;
8e60fc8f 248
e2955971 249 return buf;
8e60fc8f
VS
250}
251
252static grub_err_t
3e747239 253grub_pxe_send (struct grub_net_card *dev __attribute__ ((unused)),
8e60fc8f
VS
254 struct grub_net_buff *pack)
255{
256 struct grub_pxe_undi_transmit *trans;
257 struct grub_pxe_undi_tbd *tbd;
258 char *buf;
259
260 trans = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
261 grub_memset (trans, 0, sizeof (*trans));
262 tbd = (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + 128);
263 grub_memset (tbd, 0, sizeof (*tbd));
264 buf = (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + 256);
265 grub_memcpy (buf, pack->data, pack->tail - pack->data);
266
267 trans->tbd = SEGOFS ((grub_addr_t) tbd);
268 trans->protocol = 0;
269 tbd->len = pack->tail - pack->data;
270 tbd->buf = SEGOFS ((grub_addr_t) buf);
271
272 grub_pxe_call (GRUB_PXENV_UNDI_TRANSMIT, trans, pxe_rm_entry);
273 if (trans->status)
9c4b5c13 274 return grub_error (GRUB_ERR_IO, N_("couldn't send network packet"));
8e60fc8f
VS
275 return 0;
276}
277
72b9ad93 278static void
3e747239 279grub_pxe_close (struct grub_net_card *dev __attribute__ ((unused)))
8e60fc8f 280{
6708faaf 281 if (pxe_rm_entry)
671a78ac
VS
282 grub_pxe_call (GRUB_PXENV_UNDI_CLOSE,
283 (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR,
284 pxe_rm_entry);
671a78ac
VS
285}
286
287static grub_err_t
3e747239 288grub_pxe_open (struct grub_net_card *dev __attribute__ ((unused)))
671a78ac
VS
289{
290 struct grub_pxe_undi_open *ou;
291 ou = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
292 grub_memset (ou, 0, sizeof (*ou));
293 ou->pkt_filter = 4;
294 grub_pxe_call (GRUB_PXENV_UNDI_OPEN, ou, pxe_rm_entry);
295
296 if (ou->status)
0bc2cd0f 297 return grub_error (GRUB_ERR_IO, "can't open UNDI");
671a78ac
VS
298 return GRUB_ERR_NONE;
299}
300
0bc2cd0f
VS
301struct grub_net_card_driver grub_pxe_card_driver =
302{
303 .open = grub_pxe_open,
304 .close = grub_pxe_close,
305 .send = grub_pxe_send,
306 .recv = grub_pxe_recv
307};
308
309struct grub_net_card grub_pxe_card =
310{
311 .driver = &grub_pxe_card_driver,
312 .name = "pxe"
313};
8e60fc8f 314
3c491b47
VS
315static grub_err_t
316grub_pxe_shutdown (int flags)
317{
318 if (flags & GRUB_LOADER_FLAG_PXE_NOT_UNLOAD)
319 return GRUB_ERR_NONE;
320 if (!pxe_rm_entry)
321 return GRUB_ERR_NONE;
322
323 grub_pxe_call (GRUB_PXENV_UNDI_CLOSE,
324 (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR,
325 pxe_rm_entry);
326 grub_pxe_call (GRUB_PXENV_UNDI_SHUTDOWN,
327 (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR,
328 pxe_rm_entry);
329 grub_pxe_call (GRUB_PXENV_UNLOAD_STACK,
330 (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR,
331 pxe_rm_entry);
332 pxe_rm_entry = 0;
333 grub_net_card_unregister (&grub_pxe_card);
334
335 return GRUB_ERR_NONE;
336}
337
338/* Nothing we can do. */
339static grub_err_t
340grub_pxe_restore (void)
341{
342 return GRUB_ERR_NONE;
343}
344
0a96117d
VS
345void *
346grub_pxe_get_cached (grub_uint16_t type)
8e60fc8f 347{
6708faaf 348 struct grub_pxenv_get_cached_info ci;
0a96117d 349 ci.packet_type = type;
8e60fc8f
VS
350 ci.buffer = 0;
351 ci.buffer_size = 0;
352 grub_pxe_call (GRUB_PXENV_GET_CACHED_INFO, &ci, pxe_rm_entry);
353 if (ci.status)
0a96117d
VS
354 return 0;
355
356 return LINEAR (ci.buffer);
357}
8e60fc8f 358
0a96117d
VS
359static void
360grub_pc_net_config_real (char **device, char **path)
361{
362 struct grub_net_bootp_packet *bp;
363
364 bp = grub_pxe_get_cached (GRUB_PXENV_PACKET_TYPE_DHCP_ACK);
8e60fc8f 365
0a96117d
VS
366 if (!bp)
367 return;
6708faaf
VS
368 grub_net_configure_by_dhcp_ack ("pxe", &grub_pxe_card, 0,
369 bp, GRUB_PXE_BOOTP_SIZE,
370 1, device, path);
671a78ac 371
6708faaf
VS
372}
373
3c491b47
VS
374static struct grub_preboot *fini_hnd;
375
6708faaf
VS
376GRUB_MOD_INIT(pxe)
377{
378 struct grub_pxe_bangpxe *pxenv;
6708faaf
VS
379 struct grub_pxe_undi_info *ui;
380 unsigned i;
381
382 pxenv = grub_pxe_scan ();
383 if (! pxenv)
384 return;
385
386 ui = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
387 grub_memset (ui, 0, sizeof (*ui));
388 grub_pxe_call (GRUB_PXENV_UNDI_GET_INFORMATION, ui, pxe_rm_entry);
389
390 grub_memcpy (grub_pxe_card.default_address.mac, ui->current_addr,
391 sizeof (grub_pxe_card.default_address.mac));
392 for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++)
393 if (grub_pxe_card.default_address.mac[i] != 0)
394 break;
395 if (i != sizeof (grub_pxe_card.default_address.mac))
396 {
397 for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++)
398 if (grub_pxe_card.default_address.mac[i] != 0xff)
399 break;
400 }
401 if (i == sizeof (grub_pxe_card.default_address.mac))
402 grub_memcpy (grub_pxe_card.default_address.mac, ui->permanent_addr,
403 sizeof (grub_pxe_card.default_address.mac));
6a1af81a 404 grub_pxe_card.mtu = ui->mtu;
6708faaf 405
8e60fc8f
VS
406 grub_pxe_card.default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
407
8e60fc8f 408 grub_net_card_register (&grub_pxe_card);
6708faaf 409 grub_pc_net_config = grub_pc_net_config_real;
3c491b47
VS
410 fini_hnd = grub_loader_register_preboot_hook (grub_pxe_shutdown,
411 grub_pxe_restore,
412 GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
8e60fc8f
VS
413}
414
415GRUB_MOD_FINI(pxe)
416{
6708faaf 417 grub_pc_net_config = 0;
3c491b47
VS
418 grub_pxe_shutdown (0);
419 grub_loader_unregister_preboot_hook (fini_hnd);
8e60fc8f 420}