Initial bug fixes for Nyquist IBL
[keystone-rtos/ibl.git] / src / main / iblinit.c
1 /**
2  *  @file iblinit.c
3  *
4  *  @brief
5  *              This file contains code which runs prior to loading the full IBL
6  *
7  *  @details
8  *              The IBL loads itself in a two stage process. The ROM boot loader
9  *              loads this first stage IBL first. This entire program must be
10  *              endian independent in execution.
11  *
12  *      This first loader reads the IBL parameters, and will endian
13  *      switch them if required. The PLL is configured if indicated
14  *      by the parameters.
15  *
16  *      The I2C block which contains the I2C EEPROM address for both
17  *      the big and little endian images is then read. Based on the
18  *      endianness of the device the rest of the IBL is read from
19  *      the I2C EEPROM, and execution is transferred to the full
20  *      IBL.
21  *
22  *      The subsequent reads are allowed to cross 16 bit i2c EEPROM
23  *      addresses. When the boundary is crossed the i2c address
24  *      field is incremented.
25  *
26  */
28 #include "ibl.h"
29 #include "iblloc.h"
30 #include "iblcfg.h"
31 #include "device.h"
32 #include "iblbtbl.h"
33 #include "i2c.h"
34 #include <string.h>
37 /**
38  *  @brief
39  *      Data structures shared between the 1st and 2nd stage IBL load
40  *      are declared in a single header file, included in both stages
41  */
42 #include "iblStage.h"
44 /**
45  *  @brief
46  *      byte swapping of i2c data must be done when in little endian mode
47  */
48 bool littleEndian;
50 /**
51  *  @brief
52  *      The boot table processing status is declared in the boot table wrapper,
53  *      and used here in the main status fields.
54  */
55 extern Int32 btblWrapEcode;
57 /**
58  *  @brief
59  *      The malloc function used for both boot stages of the ibl
60  */
61 void *iblMalloc (Uint32 size)
62 {
63     return (malloc (size));
64 }
66 /**
67  *  @brief
68  *      The free function used for both stages of the ibl
69  */
70 void iblFree (void *mem)
71 {
72     free (mem);
73 }
75 /**
76  *  @brief
77  *      The memset function used for both stages of the ibl
78  */
79 void *iblMemset (void *mem, Int32 ch, Uint32 n)
80 {
81     return (memset (mem, ch, n));
82 }
84 /**
85  *  @brief
86  *      The memcpy function used for both stages of the ibl
87  */
88 void *iblMemcpy (void *s1, const void *s2, Uint32 n)
89 {
90     return (memcpy (s1, s2, n));
92 }
94 /**
95  *  @brief
96  *      Ones complement addition
97  */
98 inline uint16 onesComplementAdd (uint16 value1, uint16 value2)
99 {
100   uint32 result;
102   result = (uint32)value1 + (uint32)value2;
104   result = (result >> 16) + (result & 0xFFFF); /* add in carry   */
105   result += (result >> 16);                    /* maybe one more */
106   return ((uint16)result);
110 /**
111  *  @brief
112  *      Ones complement checksum computation 
113  */
114 uint16 onesComplementChksum (uint16 * restrict p_data, uint16 len)
116   uint16 chksum = 0;
118   while (len > 0)
119   {
120     chksum = onesComplementAdd(chksum, *p_data);
121     p_data++;
122     len--;
123   }
124   return (chksum);
125
129 /**
130  *  @brief
131  *      Do a 4 byte endian swap
132  */
133 uint32 swap32val (uint32 v)
135     v =  (((v >> 24) & 0xff) <<  0)  |
136          (((v >> 16) & 0xff) <<  8)  |
137          (((v >>  8) & 0xff) << 16)  |
138          (((v >>  0) & 0xff) << 24);
140     return (v);
144 /**
145  *  @brief
146  *      Do a 2 byte endian swap
147  */
148 uint16 swap16val (uint16 v)
150     v = (((v >> 8) & 0xff) << 0)  |
151         (((v >> 0) & 0xff) << 8);
153     return (v);
157 /**
158  *  @brief
159  *  Do an endian swap on the ibl structure
160  */
161 void iblSwap (void)
163     int i;
165     ibl.iblMagic = swap32val (ibl.iblMagic);
167     for (i = 0; i < ibl_N_PLL_CFGS; i++)  {
168         ibl.pllConfig[i].doEnable      = swap16val (ibl.pllConfig[i].doEnable);
169         ibl.pllConfig[i].prediv        = swap32val (ibl.pllConfig[i].prediv);
170         ibl.pllConfig[i].mult          = swap32val (ibl.pllConfig[i].mult);
171         ibl.pllConfig[i].postdiv       = swap32val (ibl.pllConfig[i].postdiv);
172         ibl.pllConfig[i].pllOutFreqMhz = swap32val (ibl.pllConfig[i].pllOutFreqMhz);
173     }
175     ibl.ddrConfig.configDdr = swap16val (ibl.ddrConfig.configDdr);
177 #define targetEmifType()   ibl_EMIF_TYPE_40
179     if (targetEmifType() == ibl_EMIF_TYPE_31)  { 
180         ibl.ddrConfig.uEmif.emif3p1.sdcfg  = swap32val(ibl.ddrConfig.uEmif.emif3p1.sdcfg);
181         ibl.ddrConfig.uEmif.emif3p1.sdrfc  = swap32val(ibl.ddrConfig.uEmif.emif3p1.sdrfc);
182         ibl.ddrConfig.uEmif.emif3p1.sdtim1 = swap32val(ibl.ddrConfig.uEmif.emif3p1.sdtim1);
183         ibl.ddrConfig.uEmif.emif3p1.sdtim2 = swap32val(ibl.ddrConfig.uEmif.emif3p1.sdtim2);
184         ibl.ddrConfig.uEmif.emif3p1.dmcctl = swap32val(ibl.ddrConfig.uEmif.emif3p1.dmcctl);
185        
186     } else if (targetEmifType() == ibl_EMIF_TYPE_40)  {
187         ibl.ddrConfig.uEmif.emif4p0.registerMask          = swap32val(ibl.ddrConfig.uEmif.emif4p0.registerMask);
188         ibl.ddrConfig.uEmif.emif4p0.sdRamConfig           = swap32val(ibl.ddrConfig.uEmif.emif4p0.sdRamConfig);
189         ibl.ddrConfig.uEmif.emif4p0.sdRamConfig2          = swap32val(ibl.ddrConfig.uEmif.emif4p0.sdRamConfig2);
190         ibl.ddrConfig.uEmif.emif4p0.sdRamRefreshCtl       = swap32val(ibl.ddrConfig.uEmif.emif4p0.sdRamRefreshCtl);
191         ibl.ddrConfig.uEmif.emif4p0.sdRamTiming1          = swap32val(ibl.ddrConfig.uEmif.emif4p0.sdRamTiming1);
192         ibl.ddrConfig.uEmif.emif4p0.sdRamTiming2          = swap32val(ibl.ddrConfig.uEmif.emif4p0.sdRamTiming2);
193         ibl.ddrConfig.uEmif.emif4p0.sdRamTiming3          = swap32val(ibl.ddrConfig.uEmif.emif4p0.sdRamTiming3);
194         ibl.ddrConfig.uEmif.emif4p0.lpDdrNvmTiming        = swap32val(ibl.ddrConfig.uEmif.emif4p0.lpDdrNvmTiming);
195         ibl.ddrConfig.uEmif.emif4p0.powerManageCtl        = swap32val(ibl.ddrConfig.uEmif.emif4p0.powerManageCtl);
196         ibl.ddrConfig.uEmif.emif4p0.iODFTTestLogic        = swap32val(ibl.ddrConfig.uEmif.emif4p0.iODFTTestLogic);
197         ibl.ddrConfig.uEmif.emif4p0.performCountCfg       = swap32val(ibl.ddrConfig.uEmif.emif4p0.performCountCfg);
198         ibl.ddrConfig.uEmif.emif4p0.performCountMstRegSel = swap32val(ibl.ddrConfig.uEmif.emif4p0.performCountMstRegSel);
199         ibl.ddrConfig.uEmif.emif4p0.readIdleCtl           = swap32val(ibl.ddrConfig.uEmif.emif4p0.readIdleCtl);
200         ibl.ddrConfig.uEmif.emif4p0.sysVbusmIntEnSet      = swap32val(ibl.ddrConfig.uEmif.emif4p0.sysVbusmIntEnSet);
201         ibl.ddrConfig.uEmif.emif4p0.sdRamOutImpdedCalCfg  = swap32val(ibl.ddrConfig.uEmif.emif4p0.sdRamOutImpdedCalCfg);
202         ibl.ddrConfig.uEmif.emif4p0.tempAlterCfg          = swap32val(ibl.ddrConfig.uEmif.emif4p0.tempAlterCfg);
203         ibl.ddrConfig.uEmif.emif4p0.ddrPhyCtl1            = swap32val(ibl.ddrConfig.uEmif.emif4p0.ddrPhyCtl1);
204         ibl.ddrConfig.uEmif.emif4p0.ddrPhyCtl2            = swap32val(ibl.ddrConfig.uEmif.emif4p0.ddrPhyCtl2);
205         ibl.ddrConfig.uEmif.emif4p0.priClassSvceMap       = swap32val(ibl.ddrConfig.uEmif.emif4p0.priClassSvceMap);
206         ibl.ddrConfig.uEmif.emif4p0.mstId2ClsSvce1Map     = swap32val(ibl.ddrConfig.uEmif.emif4p0.mstId2ClsSvce1Map);
207         ibl.ddrConfig.uEmif.emif4p0.mstId2ClsSvce2Map     = swap32val(ibl.ddrConfig.uEmif.emif4p0.mstId2ClsSvce2Map);
208         ibl.ddrConfig.uEmif.emif4p0.eccCtl                = swap32val(ibl.ddrConfig.uEmif.emif4p0.eccCtl);
209         ibl.ddrConfig.uEmif.emif4p0.eccRange1             = swap32val(ibl.ddrConfig.uEmif.emif4p0.eccRange1);
210         ibl.ddrConfig.uEmif.emif4p0.eccRange2             = swap32val(ibl.ddrConfig.uEmif.emif4p0.eccRange2);
211         ibl.ddrConfig.uEmif.emif4p0.rdWrtExcThresh        = swap32val(ibl.ddrConfig.uEmif.emif4p0.rdWrtExcThresh);
212     }
213     
215     for (i = 0; i < ibl_N_ETH_PORTS; i++)  {
216         ibl.ethConfig[i].ethPriority        = swap32val (ibl.ethConfig[i].ethPriority);
217         ibl.ethConfig[i].port               = swap32val (ibl.ethConfig[i].port);
218         ibl.ethConfig[i].doBootp            = swap16val (ibl.ethConfig[i].doBootp);
219         ibl.ethConfig[i].useBootpServerIp   = swap16val (ibl.ethConfig[i].useBootpServerIp);
220         ibl.ethConfig[i].useBootpFileName   = swap16val (ibl.ethConfig[i].useBootpFileName);
221         ibl.ethConfig[i].bootFormat         = swap32val (ibl.ethConfig[i].bootFormat);
222         ibl.ethConfig[i].blob.startAddress  = swap32val (ibl.ethConfig[i].blob.startAddress);
223         ibl.ethConfig[i].blob.sizeBytes     = swap32val (ibl.ethConfig[i].blob.sizeBytes);
224         ibl.ethConfig[i].blob.branchAddress = swap32val (ibl.ethConfig[i].blob.branchAddress);
226         ibl.sgmiiConfig[i].adviseAbility = swap32val (ibl.sgmiiConfig[i].adviseAbility);
227         ibl.sgmiiConfig[i].control       = swap32val (ibl.sgmiiConfig[i].control);
228         ibl.sgmiiConfig[i].txConfig      = swap32val (ibl.sgmiiConfig[i].txConfig);
229         ibl.sgmiiConfig[i].rxConfig      = swap32val (ibl.sgmiiConfig[i].rxConfig);
230         ibl.sgmiiConfig[i].auxConfig     = swap32val (ibl.sgmiiConfig[i].auxConfig);
231     }
233     ibl.mdioConfig.nMdioOps   = swap16val (ibl.mdioConfig.nMdioOps);
234     ibl.mdioConfig.mdioClkDiv = swap16val (ibl.mdioConfig.mdioClkDiv);
235     ibl.mdioConfig.interDelay = swap32val (ibl.mdioConfig.interDelay);
237     for (i = 0; i < ibl_N_MDIO_CFGS; i++)
238         ibl.mdioConfig.mdio[i] = swap32val (ibl.mdioConfig.mdio[i]);
240     ibl.nandConfig.nandPriority       = swap32val (ibl.nandConfig.nandPriority);
241     ibl.nandConfig.bootFormat         = swap32val (ibl.nandConfig.bootFormat);
242     ibl.nandConfig.blob.startAddress  = swap32val (ibl.nandConfig.blob.startAddress);
243     ibl.nandConfig.blob.sizeBytes     = swap32val (ibl.nandConfig.blob.sizeBytes);
244     ibl.nandConfig.blob.branchAddress = swap32val (ibl.nandConfig.blob.branchAddress);
246     ibl.nandConfig.nandInfo.busWidthBits  = swap32val (ibl.nandConfig.nandInfo.busWidthBits);
247     ibl.nandConfig.nandInfo.pageSizeBytes = swap32val (ibl.nandConfig.nandInfo.pageSizeBytes);
248     ibl.nandConfig.nandInfo.pageEccBytes  = swap32val (ibl.nandConfig.nandInfo.pageEccBytes);
249     ibl.nandConfig.nandInfo.pagesPerBlock = swap32val (ibl.nandConfig.nandInfo.pagesPerBlock);
250     ibl.nandConfig.nandInfo.totalBlocks   = swap32val (ibl.nandConfig.nandInfo.totalBlocks);
251     ibl.nandConfig.nandInfo.addressBytes  = swap32val (ibl.nandConfig.nandInfo.addressBytes);
252     ibl.nandConfig.nandInfo.lsbFirst      = swap16val (ibl.nandConfig.nandInfo.lsbFirst);
253     ibl.nandConfig.nandInfo.blockOffset   = swap32val (ibl.nandConfig.nandInfo.blockOffset);
254     ibl.nandConfig.nandInfo.pageOffset    = swap32val (ibl.nandConfig.nandInfo.pageOffset);
255     ibl.nandConfig.nandInfo.columnOffset  = swap32val (ibl.nandConfig.nandInfo.columnOffset);
256     ibl.nandConfig.nandInfo.postCommand   = swap16val (ibl.nandConfig.nandInfo.postCommand);
258     ibl.chkSum = swap16val (ibl.chkSum);
261         
263 /**
264  *  @brief
265  *      The i2c load context consists of the address of the next block 
266  *      to read, and a simple fifo holding any existing data.
267  */
268 #define I2C_MAX_BLOCK_SIZE      0x80
269 uint32 i2cReadAddress;
271 uint32 i2cFifoIn  = 0;
272 uint32 i2cFifoOut = 0;
273 uint8  i2cData[I2C_MAX_BLOCK_SIZE];
274 uint16 i2cSum[I2C_MAX_BLOCK_SIZE >> 1];
277 /**
278  *  @brief
279  *      Return the number of elements in the fifo
280  */
281 Uint32 i2cFifoCount (void)
283     Int32 count;
285     if (i2cFifoIn >= i2cFifoOut)
286         count = i2cFifoIn - i2cFifoOut;
287     else
288         count = i2cFifoIn + I2C_MAX_BLOCK_SIZE - i2cFifoOut;
290     return (count);
294     
295 /**
296  *  @brief
297  *      Read a byte from the fifo
298  */
299 Uint8 i2cFifoRead(void)
301     Uint8 v;
303     v = i2cData[i2cFifoOut];
305     i2cFifoOut += 1;
307     if (i2cFifoOut == i2cFifoIn)
308         i2cFifoOut = i2cFifoIn = 0;
310     if (i2cFifoOut >= I2C_MAX_BLOCK_SIZE)
311         i2cFifoOut = 0;
313     return (v);
317 /**
318  *  @brief
319  *      Read a block of data from the I2C eeprom and put it in the fifo
320  */
321 void i2cReadBlock (void)
323     uint16 len;
324     int32  i, j;
325     uint32 v;
327     for (;;) {
328         while (hwI2cMasterRead (i2cReadAddress & 0xffff,    /* The address on the eeprom of the table */
329                                 4,                          /* The number of bytes to read */
330                                 i2cData,                    /* Where to store the bytes */
331                                 i2cReadAddress >> 16,       /* The bus address of the eeprom */
332                                 IBL_CFG_I2C_ADDR_DELAY)     /* The delay between sending the address and reading data */
333     
334              != I2C_RET_OK)  {
336             iblStatus.i2cDataRetries += 1;
337         }
339         /* Form the length. The received bytes are always in big endian format */
340         len    = (i2cData[0] << 8) | i2cData[1];
343         if (len > I2C_MAX_BLOCK_SIZE)
344             continue;
347         while (hwI2cMasterRead (i2cReadAddress & 0xffff,    /* The address on the eeprom of the table */
348                                 len,                        /* The number of bytes to read */
349                                 i2cData,                    /* Where to store the bytes */
350                                 i2cReadAddress >> 16,       /* The bus address of the eeprom */
351                                 IBL_CFG_I2C_ADDR_DELAY)     /* The delay between sending the address and reading data */
352     
353              != I2C_RET_OK)  {
355             iblStatus.i2cDataRetries += 1;
356         }
359         /* Must do endian conversion to verify the checksum */
360         for (i = j = 0; i < len; i += 2, j += 1) 
361             i2cSum[j] = (i2cData[i+0] << 8) | i2cData[i+1];
363         v = onesComplementChksum (i2cSum, j);
364         if ((v == 0) || (v == 0xffff))
365             break;
367         
368         iblStatus.i2cDataRetries += 1;
370     }
373     i2cReadAddress += len;
374     
375     i2cFifoIn  = len;
376     i2cFifoOut = 4;    /* The i2c header is effectively removed */
380              
383 /**
384  *  @brief
385  *      Read data from the I2C to pass to the interpreter
386  */
387 Int32 iblI2cRead (Uint8 *buf, Uint32 num_bytes)
389     int i;
391     for (i = 0; i < num_bytes; i++)  {
393         if (i2cFifoCount() == 0)
394             i2cReadBlock ();
396         buf[i] = i2cFifoRead();
397     }
399     return (0);
403 #define iblBITMASK(x,y)      (   (   (  ((UINT32)1 << (((UINT32)x)-((UINT32)y)+(UINT32)1) ) - (UINT32)1 )   )   <<  ((UINT32)y)   )
404 #define iblREAD_BITFIELD(z,x,y)   (((UINT32)z) & iblBITMASK(x,y)) >> (y)
405 /**
406  *  @brief
407  *      Return the lower 16 bits of a 32 bit value. A function is used (with cross-function optomization off)
408  *      which results in an endian independent version
409  */
410 uint16 readLower16 (uint32 v)
412     return (iblREAD_BITFIELD(v,15,0));
416 /**
417  * @brief
418  *      Return the upper 16 bits of a 32 bit value. A function is used to force an endian independent version
419  */
420 uint16 readUpper16 (uint32 v)
422   return (iblREAD_BITFIELD(v,31,16));
426 /**
427  *  @brief
428  *      The module function table used for boot from i2c
429  */
430 BOOT_MODULE_FXN_TABLE i2cinit_boot_module = 
432     NULL,           /* Open  API */
433     NULL,           /* Close API */
434     iblI2cRead,     /* Read  API */
435     NULL,           /* Write API */
436     NULL,           /* Peek  API */
437     NULL,           /* Seek  API */
438     NULL            /* Query API */
439 };
443 /**
444  *  @brief
445  *      The main function
446  *
447  *  @details
448  *      The ibl configuration parameters are read from the i2c, 
449  *      followed by the i2c mapping information. The second stage
450  *      of the IBL is then loaded, and execution transferred 
451  *      to the second stage.
452  */
453 void main (void)
456     uint16       v;
457     uint16       configAddrLsw;
458     uint16       configAddrMsw;
459     uint32       entry;
460     void         (*exit)();
461     iblI2cMap_t  map;
463     memset (&iblStatus, 0, sizeof(iblStatus_t));
464     iblStatus.iblMagic     = ibl_MAGIC_VALUE;
465     iblStatus.iblVersion   = ibl_VERSION;
466     iblStatus.activePeriph = ibl_ACTIVE_PERIPH_I2C;
468     /* Read the endianness setting of the device */
469     littleEndian = deviceIsLittleEndian();
470     
471     /* Load the default configuration table from the i2c. The actual speed of the device
472      * isn't really known here, since it is part of the table, so a compile time
473      * value is used (the pll may have been configured during the initial load) */
474     hwI2Cinit (IBL_CFG_I2C_DEV_FREQ_MHZ,        /* The CPU frequency during I2C data load */
475                DEVICE_I2C_MODULE_DIVISOR,       /* The divide down of CPU that drives the i2c */
476                IBL_CFG_I2C_CLK_FREQ_KHZ,        /* The I2C data rate used during table load */
477                IBL_CFG_I2C_OWN_ADDR);           /* The address used by this device on the i2c bus */
480     /* Read the I2C mapping information from the eeprom */
481     for (;;)  {
482         if (hwI2cMasterRead (IBL_CFG_I2C_MAP_TABLE_DATA_ADDR,     /* The address on the eeprom of the data mapping */
483                              sizeof(iblI2cMap_t),                 /* The number of bytes to read */
484                              (UINT8 *)&map,                       /* Where to store the bytes */
485                              IBL_CFG_I2C_MAP_TABLE_DATA_BUS_ADDR, /* The bus address of the eeprom */
486                              IBL_CFG_I2C_ADDR_DELAY)              /* The delay between sending the address and reading data */
488              == I2C_RET_OK)  {
490                 /* On the I2C EEPROM the table is always formatted with the most significant
491                  * byte first. So if the device is running little endain the endian must be
492                  * swapped */
493                 if (littleEndian == TRUE)  {
494                     map.length   = swap16val (map.length);
495                     map.chkSum   = swap16val (map.chkSum);
496                     map.addrLe   = swap32val (map.addrLe);
497                     map.configLe = swap32val (map.configLe);
498                     map.addrBe   = swap32val (map.addrBe);
499                     map.configBe = swap32val (map.configBe);
501                     configAddrLsw = readLower16 (map.configLe);
502                     configAddrMsw = readUpper16 (map.configLe);
504                 }  else  {
505                     configAddrLsw = readLower16 (map.configBe);
506                     configAddrMsw = readUpper16 (map.configLe);
508                 }
511                 if (map.length != sizeof(iblI2cMap_t))  {
512                     iblStatus.mapSizeFail += 1;
513                     continue;
514                 }
516                 if (map.chkSum != 0)  {
517                     
518                     v = onesComplementChksum ((UINT16 *)&map, sizeof(iblI2cMap_t));
519                     if ((v != 0) && (v != 0xffff))  {
520                         iblStatus.mapRetries += 1;
521                         continue;
522                     }
523                 }
525                 break;
526         }
528         iblStatus.mapRetries += 1;
530     }
533     /* Read the i2c configuration tables until the checksum passes and the magic number
534      * matches. The checksum must be verified before the endian re-ordering is done */
535     for (;;)  {
537         if (hwI2cMasterRead (configAddrLsw,                  /* The address on the eeprom of the table */
538                              sizeof(ibl_t),                  /* The number of bytes to read */
539                              (UINT8 *)&ibl,                  /* Where to store the bytes */
540                              configAddrMsw,                  /* The bus address of the eeprom */
541                              IBL_CFG_I2C_ADDR_DELAY)         /* The delay between sending the address and reading data */
543              == I2C_RET_OK)  {
545                  if (ibl.chkSum != 0)  {
547                     v = onesComplementChksum ((UINT16 *)&ibl, sizeof(ibl_t) / sizeof(UINT16));
548                     if ((v != 0) && (v != 0xffff))  {
549                         iblStatus.i2cRetries += 1;
550                         continue;
551                     }
553                  }  
556                 if (ibl.iblMagic == ibl_MAGIC_VALUE)
557                     break;
559                 if (swap32val (ibl.iblMagic) == ibl_MAGIC_VALUE)  {
560                     iblSwap ();
561                     break;
562                 }
564                 iblStatus.magicRetries += 1;
566             }
568             iblStatus.i2cRetries += 1;
569     }
571     /* Pll configuration is device specific */
572     devicePllConfig ();
575     /* The rest of the IBL is in boot table format. Read and process the data */
576     if (littleEndian == TRUE) 
577         i2cReadAddress = map.addrLe;
578     else
579         i2cReadAddress = map.addrBe;
581     if (i2cReadAddress == 0xffffffff)  {
582         iblStatus.iblFail = ibl_FAIL_CODE_INVALID_I2C_ADDRESS;
583         for (;;);
584     }
587     /* Pass control to the boot table processor */
588     iblBootBtbl (&i2cinit_boot_module, &entry);
590     if (btblWrapEcode != 0)  {
591         iblStatus.iblFail = ibl_FAIL_CODE_BTBL_FAIL;
592         for (;;);
593     }
595     /* jump to the exit point, which will be the entry point for the full IBL */
596     exit = (void (*)())entry;
597     (*exit)();