Version 0.4 from Mike Line
[keystone-rtos/ibl.git] / src / ethboot / ethboot.c
1 /*********************************************************************************
2  * FILE PURPOSE: The Ethernet boot wrapper
3  *********************************************************************************
4  * FILE NAME: ethboot.c
5  *
6  * DESCRIPTION: This file provides the ethernet boot wrapper used by IBL modules
7  *
8  * @file ethboot.c
9  *
10  * @brief
11  *   The ethernet boot wrapper
12  *
13  ***********************************************************************************/
14 #include "types.h"
15 #include "ibl.h"
16 #include "iblloc.h"
17 #include "ethboot.h"
18 #include "net.h"
19 #include "cpmacdrv.h"
20 #include "device.h"
21 #include "mdioapi.h"
22 #include <string.h>
25 /* Convert an IP address from unsigned char to IPN (uint32) */
26 #define FORM_IPN(x)     (  (x[0] << 24) | \
27                            (x[1] << 16) | \
28                            (x[2] <<  8) | \
29                            (x[3] <<  0) )
31 #define UNFORM_IPN(x,y)  (x)[0] = (y) >> 24;  \
32                          (x)[1] = (y) >> 16;  \
33                          (x)[2] = (y) >>  8;  \
34                          (x)[3] = (y) >>  0
36 #define MIN(a,b)         ((a) < (b)) ? (a) : (b)
38 static bool have_params;
40 /* Receive a call back when the boot file name is known */
41 void ibl_rec_params (void *params)
42 {
43     NET_DRV_DEVICE *netdev = (NET_DRV_DEVICE *)params;
45     have_params = TRUE;
47     /* Copy the information to the status fields */
48     UNFORM_IPN(iblStatus.ethParams.ipAddr, netdev->ip_address);
49     UNFORM_IPN(iblStatus.ethParams.serverIp, netdev->server_ip);
51     memcpy (iblStatus.ethParams.hwAddress, netdev->mac_address, sizeof(iblStatus.ethParams.hwAddress));
52     strncpy (iblStatus.ethParams.fileName, netdev->file_name, sizeof(iblStatus.ethParams.fileName));
54 }
57 void iblEthBoot (Int32 eIdx)
58 {
59     NET_DRV_DEVICE nDevice;
60     Uint32  nl;
61     Uint32  entry;
62     Int32   n;
63     Int32   dataSize;
64     Int32   format;
65     void    (*exit)();
66     uint8   buf[16];
67     char    *ext;
71     /* Power up the device. No action is taken if the device is already powered up */
72     if (devicePowerPeriph (TARGET_PWR_ETH(ibl.ethConfig[eIdx].port)) < 0)
73         return;
75     /* Do any mdio configuration */
76     if (ibl.mdioConfig.nMdioOps > 0)
77         hwMdio (ibl.mdioConfig.nMdioOps, ibl.mdioConfig.mdio, 
78                 ibl.mdioConfig.mdioClkDiv, ibl.mdioConfig.interDelay);
81     nDevice.port_num = ibl.ethConfig[eIdx].port;
83     /* Simple transation to initialize the driver */
84     memcpy (nDevice.mac_address, ibl.ethConfig[eIdx].ethInfo.hwAddress, sizeof(nDevice.mac_address));
86     nl = FORM_IPN(ibl.ethConfig[eIdx].ethInfo.ipAddr);
87     if (ibl.ethConfig[eIdx].doBootp == TRUE)
88         nDevice.ip_address = 0;
89     else
90         nDevice.ip_address = htonl(nl);
92     nl = FORM_IPN(ibl.ethConfig[eIdx].ethInfo.netmask);
93     nDevice.net_mask  = htonl(nl);
95     nl = FORM_IPN(ibl.ethConfig[eIdx].ethInfo.serverIp);
96     nDevice.server_ip           = htonl(nl);
97     nDevice.use_bootp_server_ip = ibl.ethConfig[eIdx].useBootpServerIp;
99     /* Note - the file name structure in nDevice is only 64 bytes, but 128 in ethInfo */
100     memcpy (nDevice.file_name, ibl.ethConfig[eIdx].ethInfo.fileName, sizeof(nDevice.file_name));
101     nDevice.use_bootp_file_name = ibl.ethConfig[eIdx].useBootpFileName;
104     nDevice.start    = cpmac_drv_start;
105     nDevice.stop     = cpmac_drv_stop;
106     nDevice.send     = cpmac_drv_send;
107     nDevice.receive  = cpmac_drv_receive;
110     /* have_params will be set to true in the tftp call back. It must be
111      * set to false before opening the module, since the call back will
112      * be from the open call if bootp is not used */
113     have_params = FALSE;
115     /* Open the network device */
116     if ((*net_boot_module.open) ((void *)&nDevice, ibl_rec_params) != 0)
117         return;
119     /* Wait for the callback with the requested filename */
120     while (have_params == FALSE)  {
122        if ((*net_boot_module.peek) ((uint8 *)&nl, sizeof(nl)) < 0)  {
124             (*net_boot_module.close)();
125             return;
126         }
127     }
129     format = ibl.ethConfig[eIdx].bootFormat;
131     /* If the data format was based on the name extension, determine
132      * the boot data format */
133     if (format == ibl_BOOT_FORMAT_NAME)  {
135         ext = strrchr (iblStatus.ethParams.fileName, '.');
137         if (ext != NULL)  {
139             if (!strcmp (ext, ".bis"))
140                 format = ibl_BOOT_FORMAT_BIS;
142             else if (!strcmp (ext, ".ais"))
143                 format = ibl_BOOT_FORMAT_BIS;
145             else if (!strcmp (ext, ".out"))
146                 format = ibl_BOOT_FORMAT_COFF;
148             else if (!strcmp (ext, ".coff"))
149                 format = ibl_BOOT_FORMAT_COFF;
151             else if (!strcmp (ext, ".btbl"))
152                 format = ibl_BOOT_FORMAT_BTBL;
153             
154             else if (!strcmp (ext, ".bin"))
155                 format = ibl_BOOT_FORMAT_BBLOB;
157             else if (!strcmp (ext, ".blob"))
158                 format = ibl_BOOT_FORMAT_BBLOB;
160         }
162         /* Name match failed it didn't change */
163         if (format == ibl_BOOT_FORMAT_NAME)  {
165             iblStatus.nameDetectFailCnt += 1;
167             /* Close up the peripheral */
168             (*net_boot_module.close)();
170             return;
171         }
173     }
175     entry = iblBoot (&net_boot_module, format, &ibl.ethConfig[eIdx].blob);
178     /* Before closing the module read any remaining data. In the coff boot mode the boot may
179      * detect an exit before the entire file has been read. Read the rest of the file
180      * to make the server happy */
182     do  {
184         dataSize = (*net_boot_module.query)();  /* Will return -1 when the data is done */
186         if (dataSize > 0)  {
188             while (dataSize > 0)  {
189             
190                 n = MIN(dataSize, sizeof(buf));
191                 (*net_boot_module.read)(buf, n);
192                 dataSize = dataSize - n;
193             }
195         /* Do not peek if the data size returned in the query was > 0 */
196         }  else if (dataSize == 0) {
198             (*net_boot_module.peek)(buf, 1);
199         }
201     } while (dataSize >= 0);
205     /* Close up the peripheral */
206     (*net_boot_module.close)();
208     if (entry != 0)  {
210         iblStatus.exitAddress = entry;
211         exit = (void (*)())entry;
212         (*exit)();
214     }