1 /* pxe.c - Driver to provide access to the pxe filesystem */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008,2009,2011 Free Software Foundation, Inc.
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.
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.
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/>.
23 #include <grub/file.h>
24 #include <grub/misc.h>
26 #include <grub/i18n.h>
27 #include <grub/loader.h>
29 #include <grub/machine/pxe.h>
30 #include <grub/machine/int.h>
31 #include <grub/machine/memory.h>
32 #include <grub/machine/kernel.h>
34 GRUB_MOD_LICENSE ("GPLv3+");
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))
41 struct grub_pxe_undi_open
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];
48 } __attribute__ ((packed
));
50 struct grub_pxe_undi_info
53 grub_uint16_t base_io
;
54 grub_uint16_t int_number
;
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
;
63 } __attribute__ ((packed
));
66 struct grub_pxe_undi_isr
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
;
74 grub_uint8_t prot_type
;
75 grub_uint8_t pkt_type
;
76 } __attribute__ ((packed
));
80 GRUB_PXE_ISR_IN_START
= 1,
81 GRUB_PXE_ISR_IN_PROCESS
,
82 GRUB_PXE_ISR_IN_GET_NEXT
87 GRUB_PXE_ISR_OUT_OURS
= 0,
88 GRUB_PXE_ISR_OUT_NOT_OURS
= 1
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,
99 struct grub_pxe_undi_transmit
101 grub_uint16_t status
;
102 grub_uint8_t protocol
;
103 grub_uint8_t xmitflag
;
106 grub_uint32_t reserved
[2];
107 } __attribute__ ((packed
));
109 struct grub_pxe_undi_tbd
113 grub_uint16_t blk_count
;
116 grub_uint8_t ptr_type
;
117 grub_uint8_t reserved
;
121 } __attribute__ ((packed
));
123 struct grub_pxe_bangpxe
*grub_pxe_pxenv
;
124 static grub_uint32_t pxe_rm_entry
= 0;
126 static struct grub_pxe_bangpxe
*
129 struct grub_bios_int_registers regs
;
130 struct grub_pxenv
*pxenv
;
131 struct grub_pxe_bangpxe
*bangpxe
;
133 regs
.flags
= GRUB_CPU_INT_FLAGS_DEFAULT
;
140 grub_bios_interrupt (0x1a, ®s
);
142 if ((regs
.eax
& 0xffff) != 0x564e)
145 pxenv
= (struct grub_pxenv
*) ((regs
.es
<< 4) + (regs
.ebx
& 0xffff));
146 if (grub_memcmp (pxenv
->signature
, GRUB_PXE_SIGNATURE
,
147 sizeof (pxenv
->signature
))
151 if (pxenv
->version
< 0x201)
154 bangpxe
= (void *) ((((pxenv
->pxe_ptr
& 0xffff0000) >> 16) << 4)
155 + (pxenv
->pxe_ptr
& 0xffff));
160 if (grub_memcmp (bangpxe
->signature
, GRUB_PXE_BANGPXE_SIGNATURE
,
161 sizeof (bangpxe
->signature
)) != 0)
164 pxe_rm_entry
= bangpxe
->rm_entry
;
169 static struct grub_net_buff
*
170 grub_pxe_recv (struct grub_net_card
*dev
__attribute__ ((unused
)))
172 struct grub_pxe_undi_isr
*isr
;
173 static int in_progress
= 0;
174 grub_uint8_t
*ptr
, *end
;
175 struct grub_net_buff
*buf
;
177 isr
= (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
;
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
);
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.
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
);
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
);
204 while (isr
->func_flag
!= GRUB_PXE_ISR_OUT_RECEIVE
)
206 if (isr
->status
|| isr
->func_flag
== GRUB_PXE_ISR_OUT_DONE
)
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
);
216 buf
= grub_netbuff_alloc (isr
->frame_len
+ 2);
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);
224 grub_netbuff_free (buf
);
228 end
= ptr
+ isr
->frame_len
;
229 grub_netbuff_put (buf
, isr
->frame_len
);
230 grub_memcpy (ptr
, LINEAR (isr
->buffer
), isr
->buffer_len
);
231 ptr
+= isr
->buffer_len
;
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
)
240 grub_netbuff_free (buf
);
244 grub_memcpy (ptr
, LINEAR (isr
->buffer
), isr
->buffer_len
);
245 ptr
+= isr
->buffer_len
;
253 grub_pxe_send (struct grub_net_card
*dev
__attribute__ ((unused
)),
254 struct grub_net_buff
*pack
)
256 struct grub_pxe_undi_transmit
*trans
;
257 struct grub_pxe_undi_tbd
*tbd
;
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
);
267 trans
->tbd
= SEGOFS ((grub_addr_t
) tbd
);
269 tbd
->len
= pack
->tail
- pack
->data
;
270 tbd
->buf
= SEGOFS ((grub_addr_t
) buf
);
272 grub_pxe_call (GRUB_PXENV_UNDI_TRANSMIT
, trans
, pxe_rm_entry
);
274 return grub_error (GRUB_ERR_IO
, N_("couldn't send network packet"));
279 grub_pxe_close (struct grub_net_card
*dev
__attribute__ ((unused
)))
282 grub_pxe_call (GRUB_PXENV_UNDI_CLOSE
,
283 (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
,
288 grub_pxe_open (struct grub_net_card
*dev
__attribute__ ((unused
)))
290 struct grub_pxe_undi_open
*ou
;
291 ou
= (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
;
292 grub_memset (ou
, 0, sizeof (*ou
));
294 grub_pxe_call (GRUB_PXENV_UNDI_OPEN
, ou
, pxe_rm_entry
);
297 return grub_error (GRUB_ERR_IO
, "can't open UNDI");
298 return GRUB_ERR_NONE
;
301 struct grub_net_card_driver grub_pxe_card_driver
=
303 .open
= grub_pxe_open
,
304 .close
= grub_pxe_close
,
305 .send
= grub_pxe_send
,
306 .recv
= grub_pxe_recv
309 struct grub_net_card grub_pxe_card
=
311 .driver
= &grub_pxe_card_driver
,
316 grub_pxe_shutdown (int flags
)
318 if (flags
& GRUB_LOADER_FLAG_PXE_NOT_UNLOAD
)
319 return GRUB_ERR_NONE
;
321 return GRUB_ERR_NONE
;
323 grub_pxe_call (GRUB_PXENV_UNDI_CLOSE
,
324 (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
,
326 grub_pxe_call (GRUB_PXENV_UNDI_SHUTDOWN
,
327 (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
,
329 grub_pxe_call (GRUB_PXENV_UNLOAD_STACK
,
330 (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
,
333 grub_net_card_unregister (&grub_pxe_card
);
335 return GRUB_ERR_NONE
;
338 /* Nothing we can do. */
340 grub_pxe_restore (void)
342 return GRUB_ERR_NONE
;
346 grub_pxe_get_cached (grub_uint16_t type
)
348 struct grub_pxenv_get_cached_info ci
;
349 ci
.packet_type
= type
;
352 grub_pxe_call (GRUB_PXENV_GET_CACHED_INFO
, &ci
, pxe_rm_entry
);
356 return LINEAR (ci
.buffer
);
360 grub_pc_net_config_real (char **device
, char **path
)
362 struct grub_net_bootp_packet
*bp
;
364 bp
= grub_pxe_get_cached (GRUB_PXENV_PACKET_TYPE_DHCP_ACK
);
368 grub_net_configure_by_dhcp_ack ("pxe", &grub_pxe_card
, 0,
369 bp
, GRUB_PXE_BOOTP_SIZE
,
374 static struct grub_preboot
*fini_hnd
;
378 struct grub_pxe_bangpxe
*pxenv
;
379 struct grub_pxe_undi_info
*ui
;
382 pxenv
= grub_pxe_scan ();
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
);
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)
395 if (i
!= sizeof (grub_pxe_card
.default_address
.mac
))
397 for (i
= 0; i
< sizeof (grub_pxe_card
.default_address
.mac
); i
++)
398 if (grub_pxe_card
.default_address
.mac
[i
] != 0xff)
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
));
404 grub_pxe_card
.mtu
= ui
->mtu
;
406 grub_pxe_card
.default_address
.type
= GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET
;
408 grub_net_card_register (&grub_pxe_card
);
409 grub_pc_net_config
= grub_pc_net_config_real
;
410 fini_hnd
= grub_loader_register_preboot_hook (grub_pxe_shutdown
,
412 GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK
);
417 grub_pc_net_config
= 0;
418 grub_pxe_shutdown (0);
419 grub_loader_unregister_preboot_hook (fini_hnd
);