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 "sgmii.h"
21 #include "device.h"
22 #include "mdioapi.h"
23 #include <string.h>
25 /**
26 * @brief Remove the possible re-definition of iblEthBoot. iblcfg.h defines this to be a void
27 * statement if there is no ethernet boot support. It must be re-enabled for the compile
28 */
29 #ifdef iblEthBoot
30 #undef iblEthBoot
31 #endif
33 /* Convert an IP address from unsigned char to IPN (uint32) */
34 #define FORM_IPN(x) ( (x[0] << 24) | \
35 (x[1] << 16) | \
36 (x[2] << 8) | \
37 (x[3] << 0) )
39 #define UNFORM_IPN(x,y) (x)[0] = (y) >> 24; \
40 (x)[1] = (y) >> 16; \
41 (x)[2] = (y) >> 8; \
42 (x)[3] = (y) >> 0
44 #define MIN(a,b) ((a) < (b)) ? (a) : (b)
46 static bool have_params;
48 /* Receive a call back when the boot file name is known */
49 void ibl_rec_params (void *params)
50 {
51 NET_DRV_DEVICE *netdev = (NET_DRV_DEVICE *)params;
53 have_params = TRUE;
55 /* Copy the information to the status fields */
56 UNFORM_IPN(iblStatus.ethParams.ipAddr, netdev->ip_address);
57 UNFORM_IPN(iblStatus.ethParams.serverIp, netdev->server_ip);
59 memcpy (iblStatus.ethParams.hwAddress, netdev->mac_address, sizeof(iblStatus.ethParams.hwAddress));
60 strncpy (iblStatus.ethParams.fileName, netdev->file_name, sizeof(iblStatus.ethParams.fileName));
62 }
65 void iblEthBoot (Int32 eIdx)
66 {
67 NET_DRV_DEVICE nDevice;
68 Uint32 nl;
69 Uint32 entry;
70 Int32 n;
71 Int32 dataSize;
72 Int32 format;
73 void (*exit)();
74 uint8 buf[16];
75 char *ext;
79 /* Power up the device. No action is taken if the device is already powered up */
80 if (devicePowerPeriph (TARGET_PWR_ETH(ibl.ethConfig[eIdx].port)) < 0)
81 return;
83 /* Do any mdio configuration */
84 if (ibl.mdioConfig.nMdioOps > 0)
85 hwMdio (ibl.mdioConfig.nMdioOps, ibl.mdioConfig.mdio,
86 ibl.mdioConfig.mdioClkDiv, ibl.mdioConfig.interDelay);
89 /* SGMII configuration. If sgmii is not present this statement is defined
90 * to void in target.h */
91 hwSgmiiConfig (ibl.ethConfig[eIdx].port, &ibl.sgmiiConfig[eIdx]);
94 nDevice.port_num = ibl.ethConfig[eIdx].port;
96 /* Simple transation to initialize the driver */
97 memcpy (nDevice.mac_address, ibl.ethConfig[eIdx].ethInfo.hwAddress, sizeof(nDevice.mac_address));
99 nl = FORM_IPN(ibl.ethConfig[eIdx].ethInfo.ipAddr);
100 if (ibl.ethConfig[eIdx].doBootp == TRUE)
101 nDevice.ip_address = 0;
102 else
103 nDevice.ip_address = htonl(nl);
105 nl = FORM_IPN(ibl.ethConfig[eIdx].ethInfo.netmask);
106 nDevice.net_mask = htonl(nl);
108 nl = FORM_IPN(ibl.ethConfig[eIdx].ethInfo.serverIp);
109 nDevice.server_ip = htonl(nl);
110 nDevice.use_bootp_server_ip = ibl.ethConfig[eIdx].useBootpServerIp;
112 /* Note - the file name structure in nDevice is only 64 bytes, but 128 in ethInfo */
113 memcpy (nDevice.file_name, ibl.ethConfig[eIdx].ethInfo.fileName, sizeof(nDevice.file_name));
114 nDevice.use_bootp_file_name = ibl.ethConfig[eIdx].useBootpFileName;
117 nDevice.start = cpmac_drv_start;
118 nDevice.stop = cpmac_drv_stop;
119 nDevice.send = cpmac_drv_send;
120 nDevice.receive = cpmac_drv_receive;
123 /* have_params will be set to true in the tftp call back. It must be
124 * set to false before opening the module, since the call back will
125 * be from the open call if bootp is not used */
126 have_params = FALSE;
128 /* Open the network device */
129 if ((*net_boot_module.open) ((void *)&nDevice, ibl_rec_params) != 0)
130 return;
132 /* Wait for the callback with the requested filename */
133 while (have_params == FALSE) {
135 if ((*net_boot_module.peek) ((uint8 *)&nl, sizeof(nl)) < 0) {
137 (*net_boot_module.close)();
138 return;
139 }
140 }
142 format = ibl.ethConfig[eIdx].bootFormat;
144 /* If the data format was based on the name extension, determine
145 * the boot data format */
146 if (format == ibl_BOOT_FORMAT_NAME) {
148 ext = strrchr (iblStatus.ethParams.fileName, '.');
150 if (ext != NULL) {
152 if (!strcmp (ext, ".bis"))
153 format = ibl_BOOT_FORMAT_BIS;
155 else if (!strcmp (ext, ".ais"))
156 format = ibl_BOOT_FORMAT_BIS;
158 else if (!strcmp (ext, ".out"))
159 format = ibl_BOOT_FORMAT_COFF;
161 else if (!strcmp (ext, ".coff"))
162 format = ibl_BOOT_FORMAT_COFF;
164 else if (!strcmp (ext, ".btbl"))
165 format = ibl_BOOT_FORMAT_BTBL;
167 else if (!strcmp (ext, ".bin"))
168 format = ibl_BOOT_FORMAT_BBLOB;
170 else if (!strcmp (ext, ".blob"))
171 format = ibl_BOOT_FORMAT_BBLOB;
173 }
175 /* Name match failed it didn't change */
176 if (format == ibl_BOOT_FORMAT_NAME) {
178 iblStatus.nameDetectFailCnt += 1;
180 /* Close up the peripheral */
181 (*net_boot_module.close)();
183 return;
184 }
186 }
188 entry = iblBoot (&net_boot_module, format, &ibl.ethConfig[eIdx].blob);
191 /* Before closing the module read any remaining data. In the coff boot mode the boot may
192 * detect an exit before the entire file has been read. Read the rest of the file
193 * to make the server happy */
195 do {
197 dataSize = (*net_boot_module.query)(); /* Will return -1 when the data is done */
199 if (dataSize > 0) {
201 while (dataSize > 0) {
203 n = MIN(dataSize, sizeof(buf));
204 (*net_boot_module.read)(buf, n);
205 dataSize = dataSize - n;
206 }
208 /* Do not peek if the data size returned in the query was > 0 */
209 } else if (dataSize == 0) {
211 (*net_boot_module.peek)(buf, 1);
212 }
214 } while (dataSize >= 0);
218 /* Close up the peripheral */
219 (*net_boot_module.close)();
221 if (entry != 0) {
223 iblStatus.exitAddress = entry;
224 exit = (void (*)())entry;
225 (*exit)();
227 }
230 }