344d34fefc8c277afd64ed43ecb3c51e456dff6b
[etherboot.git] / src / arch / i386 / core / pxe_callbacks.c
1 /* PXE callback mechanisms.  This file contains only the portions
2  * specific to i386: i.e. the low-level mechanisms for calling in from
3  * an NBP to the PXE stack and for starting an NBP from the PXE stack.
4  */
5
6 #ifdef PXE_EXPORT
7
8 #include "etherboot.h"
9 #include "callbacks.h"
10 #include "realmode.h"
11 #include "pxe.h"
12 #include "pxe_callbacks.h"
13 #include "pxe_export.h"
14 #include "hidemem.h"
15 #include <stdarg.h>
16
17 #define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) \
18                                       - &pxe_callback_interface \
19                                       + (void*)&pxe_stack->arch_data ) )
20 #define pxe_intercept_int1a     INSTALLED(_pxe_intercept_int1a)
21 #define pxe_intercepted_int1a   INSTALLED(_pxe_intercepted_int1a)
22 #define pxe_pxenv_location      INSTALLED(_pxe_pxenv_location)
23 #define INT1A_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x1a ) ) )
24
25 /* The overall size of the PXE stack is ( sizeof(pxe_stack_t) +
26  * pxe_callback_interface_size + rm_callback_interface_size ).
27  * Unfortunately, this isn't a compile-time constant, since
28  * {pxe,rm}_callback_interface_size depend on the length of the
29  * assembly code in these interfaces.
30  *
31  * We used to have a function pxe_stack_size() which returned this
32  * value.  However, it actually needs to be a link-time constant, so
33  * that it can appear in the UNDIROMID structure in romprefix.S.  We
34  * therefore export the three component sizes as absolute linker
35  * symbols, get the linker to add them together and generate a new
36  * absolute symbol _pxe_stack_size.  We then import this value into a
37  * C variable pxe_stack_size, for access from C code.
38  */
39
40 /* gcc won't let us use extended asm outside a function (compiler
41  * bug), ao we have to put these asm statements inside a dummy
42  * function.
43  */
44 static void work_around_gcc_bug ( void ) __attribute__ ((used));
45 static void work_around_gcc_bug ( void ) {
46         /* Export sizeof(pxe_stack_t) as absolute linker symbol */
47         __asm__ ( ".globl _pxe_stack_t_size" );
48         __asm__ ( ".equ _pxe_stack_t_size, %c0"
49                   : : "i" (sizeof(pxe_stack_t)) );
50 }
51 /* Import _pxe_stack_size absolute linker symbol into C variable */
52 extern int pxe_stack_size;
53 __asm__ ( "pxe_stack_size: .long _pxe_stack_size" );
54
55 /* Utility routine: byte checksum
56  */
57 uint8_t byte_checksum ( void *address, size_t size ) {
58         unsigned int i, sum = 0;
59
60         for ( i = 0; i < size; i++ ) {
61                 sum += ((uint8_t*)address)[i];
62         }
63         return (uint8_t)sum;
64 }
65
66 /* install_pxe_stack(): install PXE stack.
67  * 
68  * Use base = NULL for auto-allocation of base memory
69  *
70  * IMPORTANT: no further allocation of base memory should take place
71  * before the PXE stack is removed.  This is to work around a small
72  * but important deficiency in the PXE specification.
73  */
74 pxe_stack_t * install_pxe_stack ( void *base ) {
75         pxe_t *pxe;
76         pxenv_t *pxenv;
77         void *pxe_callback_code;
78         void (*pxe_in_call_far)(void);
79         void (*pxenv_in_call_far)(void);
80         void *rm_callback_code;
81         void *e820mangler_code;
82         void *end;
83
84         /* If already installed, just return */
85         if ( pxe_stack != NULL ) return pxe_stack;
86
87         /* Allocate base memory if requested to do so
88          */
89         if ( base == NULL ) {
90                 base = allot_base_memory ( pxe_stack_size );
91                 if ( base == NULL ) return NULL;
92         }
93
94         /* Round address up to 16-byte physical alignment */
95         pxe_stack = (pxe_stack_t *)
96                 ( phys_to_virt ( ( virt_to_phys(base) + 0xf ) & ~0xf ) );
97         /* Zero out allocated stack */
98         memset ( pxe_stack, 0, sizeof(*pxe_stack) );
99         
100         /* Calculate addresses for portions of the stack */
101         pxe = &(pxe_stack->pxe);
102         pxenv = &(pxe_stack->pxenv);
103         pxe_callback_code = &(pxe_stack->arch_data);
104         pxe_in_call_far = _pxe_in_call_far +  
105                 ( pxe_callback_code - &pxe_callback_interface );
106         pxenv_in_call_far = _pxenv_in_call_far +
107                 ( pxe_callback_code - &pxe_callback_interface );
108         rm_callback_code = pxe_callback_code + pxe_callback_interface_size;
109         
110         e820mangler_code = (void*)(((int)rm_callback_code +
111                                     rm_callback_interface_size + 0xf ) & ~0xf);
112         end = e820mangler_code + e820mangler_size;
113
114         /* Initialise !PXE data structures */
115         memcpy ( pxe->Signature, "!PXE", 4 );
116         pxe->StructLength = sizeof(*pxe);
117         pxe->StructRev = 0;
118         pxe->reserved_1 = 0;
119         /* We don't yet have an UNDI ROM ID structure */
120         pxe->UNDIROMID.segment = 0;
121         pxe->UNDIROMID.offset = 0;
122         /* or a BC ROM ID structure */
123         pxe->BaseROMID.segment = 0;
124         pxe->BaseROMID.offset = 0;
125         pxe->EntryPointSP.segment = SEGMENT(pxe_stack);
126         pxe->EntryPointSP.offset = (void*)pxe_in_call_far - (void*)pxe_stack;
127         /* No %esp-compatible entry point yet */
128         pxe->EntryPointESP.segment = 0;
129         pxe->EntryPointESP.offset = 0;
130         pxe->StatusCallout.segment = -1;
131         pxe->StatusCallout.offset = -1;
132         pxe->reserved_2 = 0;
133         pxe->SegDescCn = 7;
134         pxe->FirstSelector = 0;
135         /* PXE specification doesn't say anything about when the stack
136          * space should get freed.  We work around this by claiming it
137          * as our data segment as well.
138          */
139         pxe->Stack.Seg_Addr = pxe->UNDIData.Seg_Addr = real_mode_stack >> 4;
140         pxe->Stack.Phy_Addr = pxe->UNDIData.Phy_Addr = real_mode_stack;
141         pxe->Stack.Seg_Size = pxe->UNDIData.Seg_Size = real_mode_stack_size;
142         /* Code segment has to be the one containing the data structures... */
143         pxe->UNDICode.Seg_Addr = SEGMENT(pxe_stack);
144         pxe->UNDICode.Phy_Addr = virt_to_phys(pxe_stack);
145         pxe->UNDICode.Seg_Size = end - (void*)pxe_stack;
146         /* No base code loaded */
147         pxe->BC_Data.Seg_Addr = 0;
148         pxe->BC_Data.Phy_Addr = 0;
149         pxe->BC_Data.Seg_Size = 0;
150         pxe->BC_Code.Seg_Addr = 0;
151         pxe->BC_Code.Phy_Addr = 0;
152         pxe->BC_Code.Seg_Size = 0;
153         pxe->BC_CodeWrite.Seg_Addr = 0;
154         pxe->BC_CodeWrite.Phy_Addr = 0;
155         pxe->BC_CodeWrite.Seg_Size = 0;
156         pxe->StructCksum -= byte_checksum ( pxe, sizeof(*pxe) );
157
158         /* Initialise PXENV+ data structures */
159         memcpy ( pxenv->Signature, "PXENV+", 6 );
160         pxenv->Version = 0x201;
161         pxenv->Length = sizeof(*pxenv);
162         pxenv->RMEntry.segment = SEGMENT(pxe_stack);
163         pxenv->RMEntry.offset = (void*)pxenv_in_call_far - (void*)pxe_stack;
164         pxenv->PMOffset = 0; /* "Do not use" says the PXE spec */
165         pxenv->PMSelector = 0; /* "Do not use" says the PXE spec */
166         pxenv->StackSeg = pxenv->UNDIDataSeg = real_mode_stack >> 4;
167         pxenv->StackSize = pxenv->UNDIDataSize = real_mode_stack_size;
168         pxenv->BC_CodeSeg = 0;
169         pxenv->BC_CodeSize = 0;
170         pxenv->BC_DataSeg = 0;
171         pxenv->BC_DataSize = 0;
172         /* UNDIData{Seg,Size} set above */
173         pxenv->UNDICodeSeg = SEGMENT(pxe_stack);
174         pxenv->UNDICodeSize = end - (void*)pxe_stack;
175         pxenv->PXEPtr.segment = SEGMENT(pxe);
176         pxenv->PXEPtr.offset = OFFSET(pxe);
177         pxenv->Checksum -= byte_checksum ( pxenv, sizeof(*pxenv) );
178
179         /* Mark stack as inactive */
180         pxe_stack->state = CAN_UNLOAD;
181
182         /* Install PXE and RM callback code and E820 mangler */
183         memcpy ( pxe_callback_code, &pxe_callback_interface,
184                  pxe_callback_interface_size );
185         install_rm_callback_interface ( rm_callback_code, 0 );
186         install_e820mangler ( e820mangler_code );
187
188         return pxe_stack;
189 }
190
191 /* Use the UNDI data segment as our real-mode stack.  This is for when
192  * we have been loaded via the UNDI loader
193  */
194 void use_undi_ds_for_rm_stack ( uint16_t ds ) {
195         forget_real_mode_stack();
196         real_mode_stack = virt_to_phys ( VIRTUAL ( ds, 0 ) );
197         lock_real_mode_stack = 1;
198 }
199
200 /* Activate PXE stack (i.e. hook interrupt vectors).  The PXE stack
201  * *can* be used before it is activated, but it really shoudln't.
202  */
203 int hook_pxe_stack ( void ) {
204         if ( pxe_stack == NULL ) return 0;
205         if ( pxe_stack->state >= MIDWAY ) return 1;
206
207         /* Hook INT15 handler */
208         hide_etherboot();
209
210         /* Hook INT1A handler */
211         *pxe_intercepted_int1a = *INT1A_VECTOR;
212         pxe_pxenv_location->segment = SEGMENT(pxe_stack);
213         pxe_pxenv_location->offset = (void*)&pxe_stack->pxenv
214                 - (void*)pxe_stack;
215         INT1A_VECTOR->segment = SEGMENT(&pxe_stack->arch_data);
216         INT1A_VECTOR->offset = (void*)pxe_intercept_int1a
217                 - (void*)&pxe_stack->arch_data;
218
219         /* Mark stack as active */
220         pxe_stack->state = MIDWAY;
221         return 1;
222 }
223
224 /* Deactivate the PXE stack (i.e. unhook interrupt vectors).
225  */
226 int unhook_pxe_stack ( void ) {
227         if ( pxe_stack == NULL ) return 0;
228         if ( pxe_stack->state <= CAN_UNLOAD ) return 1;
229
230         /* Restore original INT15 and INT1A handlers */
231         *INT1A_VECTOR = *pxe_intercepted_int1a;
232         if ( !unhide_etherboot() ) {
233                 /* Cannot unhook INT15.  We're up the creek without
234                  * even a suitable log out of which to fashion a
235                  * paddle.  There are some very badly behaved NBPs
236                  * that will ignore plaintive pleas such as
237                  * PXENV_KEEP_UNDI and just zero out our code anyway.
238                  * This means they end up vapourising an active INT15
239                  * handler, which is generally not a good thing to do.
240                  */
241                 return 0;
242         }
243
244         /* Mark stack as inactive */
245         pxe_stack->state = CAN_UNLOAD;
246         return 1;
247 }
248
249 /* remove_pxe_stack(): remove PXE stack installed by install_pxe_stack()
250  */
251 void remove_pxe_stack ( void ) {
252         /* Ensure stack is deactivated, then free up the memory */
253         if ( ensure_pxe_state ( CAN_UNLOAD ) ) {
254                 forget_base_memory ( pxe_stack, pxe_stack_size );
255                 pxe_stack = NULL;
256         } else {
257                 printf ( "Cannot remove PXE stack!\n" );
258         }
259 }
260
261 /* xstartpxe(): start up a PXE image
262  */
263 int xstartpxe ( void ) {
264         int nbp_exit;
265         struct {
266                 reg16_t bx;
267                 reg16_t es;
268                 segoff_t pxe;
269         } PACKED in_stack;
270         
271         /* Set up registers and stack parameters to pass to PXE NBP */
272         in_stack.es.word = SEGMENT(&(pxe_stack->pxenv));
273         in_stack.bx.word = OFFSET(&(pxe_stack->pxenv));
274         in_stack.pxe.segment = SEGMENT(&(pxe_stack->pxe));
275         in_stack.pxe.offset = OFFSET(&(pxe_stack->pxe));
276
277         /* Real-mode trampoline fragment used to jump to PXE NBP
278          */
279         RM_FRAGMENT(jump_to_pxe_nbp, 
280                 "popw %bx\n\t"
281                 "popw %es\n\t"
282                 "lcall $" RM_STR(PXE_LOAD_SEGMENT) ", $" RM_STR(PXE_LOAD_OFFSET) "\n\t"
283         );
284
285         /* Call to PXE image */
286         gateA20_unset();
287         nbp_exit = real_call ( jump_to_pxe_nbp, &in_stack, NULL );
288         gateA20_set();
289
290         return nbp_exit;
291 }
292
293 int pxe_in_call ( in_call_data_t *in_call_data, va_list params ) {
294         /* i386 calling conventions; the only two defined by Intel's
295          * PXE spec.
296          *
297          * Assembly code must pass a long containing the PXE version
298          * code (i.e. 0x201 for !PXE, 0x200 for PXENV+) as the first
299          * parameter after the in_call opcode.  This is used to decide
300          * whether to take parameters from the stack (!PXE) or from
301          * registers (PXENV+).
302          */
303         uint32_t api_version = va_arg ( params, typeof(api_version) );
304         uint16_t opcode;
305         segoff_t segoff;
306         t_PXENV_ANY *structure;
307                 
308         if ( api_version >= 0x201 ) {
309                 /* !PXE calling convention */
310                 pxe_call_params_t pxe_params
311                         = va_arg ( params, typeof(pxe_params) );
312                 opcode = pxe_params.opcode;
313                 segoff = pxe_params.segoff;
314         } else {
315                 /* PXENV+ calling convention */
316                 opcode = in_call_data->pm->regs.bx;
317                 segoff.segment = in_call_data->rm->seg_regs.es;
318                 segoff.offset = in_call_data->pm->regs.di;
319         }
320         structure = VIRTUAL ( segoff.segment, segoff.offset );
321         return pxe_api_call ( opcode, structure );
322 }
323
324 #ifdef TEST_EXCLUDE_ALGORITHM
325 /* This code retained because it's a difficult algorithm to tweak with
326  * confidence
327  */
328 int ___test_exclude ( int start, int len, int estart, int elen, int fixbase );
329 void __test_exclude ( int start, int len, int estart, int elen, int fixbase ) {
330         int newrange = ___test_exclude ( start, len, estart, elen, fixbase );
331         int newstart = ( newrange >> 16 ) & 0xffff;
332         int newlen = ( newrange & 0xffff );
333
334         printf ( "[%x,%x): excluding [%x,%x) %s gives [%x,%x)\n",
335                  start, start + len,
336                  estart, estart + elen,
337                  ( fixbase == 0 ) ? "  " : "fb",
338                  newstart, newstart + newlen );
339 }
340 void _test_exclude ( int start, int len, int estart, int elen ) {
341         __test_exclude ( start, len, estart, elen, 0 );
342         __test_exclude ( start, len, estart, elen, 1 );
343 }
344 void test_exclude ( void ) {
345         _test_exclude ( 0x8000, 0x1000, 0x0400, 0x200 ); /* before */
346         _test_exclude ( 0x8000, 0x1000, 0x9000, 0x200 ); /* after */
347         _test_exclude ( 0x8000, 0x1000, 0x7f00, 0x200 ); /* before overlap */
348         _test_exclude ( 0x8000, 0x1000, 0x8f00, 0x200 ); /* after overlap */
349         _test_exclude ( 0x8000, 0x1000, 0x8000, 0x200 ); /* align start */
350         _test_exclude ( 0x8000, 0x1000, 0x8e00, 0x200 ); /* align end */
351         _test_exclude ( 0x8000, 0x1000, 0x8100, 0x200 ); /* early overlap */
352         _test_exclude ( 0x8000, 0x1000, 0x8d00, 0x200 ); /* late overlap */
353         _test_exclude ( 0x8000, 0x1000, 0x7000, 0x3000 ); /* total overlap */
354         _test_exclude ( 0x8000, 0x1000, 0x8000, 0x1000 ); /* exact overlap */
355 }
356 #endif /* TEST_EXCLUDE_ALGORITHM */
357
358 #else /* PXE_EXPORT */
359
360 /* Define symbols used by the linker scripts, to prevent link errors */
361 __asm__ ( ".globl _pxe_stack_t_size" );
362 __asm__ ( ".equ _pxe_stack_t_size, 0" );
363
364 #endif /* PXE_EXPORT */