efi_selftest_snp.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * efi_selftest_snp
  4. *
  5. * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
  6. *
  7. * This unit test covers the Simple Network Protocol as well as
  8. * the CopyMem and SetMem boottime services.
  9. *
  10. * A DHCP discover message is sent. The test is successful if a
  11. * DHCP reply is received.
  12. *
  13. * TODO: Once ConnectController and DisconnectController are implemented
  14. * we should connect our code as controller.
  15. */
  16. #include <efi_selftest.h>
  17. /*
  18. * MAC address for broadcasts
  19. */
  20. static const u8 BROADCAST_MAC[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
  21. struct dhcp_hdr {
  22. u8 op;
  23. #define BOOTREQUEST 1
  24. #define BOOTREPLY 2
  25. u8 htype;
  26. # define HWT_ETHER 1
  27. u8 hlen;
  28. # define HWL_ETHER 6
  29. u8 hops;
  30. u32 xid;
  31. u16 secs;
  32. u16 flags;
  33. #define DHCP_FLAGS_UNICAST 0x0000
  34. #define DHCP_FLAGS_BROADCAST 0x0080
  35. u32 ciaddr;
  36. u32 yiaddr;
  37. u32 siaddr;
  38. u32 giaddr;
  39. u8 chaddr[16];
  40. u8 sname[64];
  41. u8 file[128];
  42. };
  43. /*
  44. * Message type option.
  45. */
  46. #define DHCP_MESSAGE_TYPE 0x35
  47. #define DHCPDISCOVER 1
  48. #define DHCPOFFER 2
  49. #define DHCPREQUEST 3
  50. #define DHCPDECLINE 4
  51. #define DHCPACK 5
  52. #define DHCPNAK 6
  53. #define DHCPRELEASE 7
  54. struct dhcp {
  55. struct ethernet_hdr eth_hdr;
  56. struct ip_udp_hdr ip_udp;
  57. struct dhcp_hdr dhcp_hdr;
  58. u8 opt[128];
  59. } __packed;
  60. static struct efi_boot_services *boottime;
  61. static struct efi_simple_network *net;
  62. static struct efi_event *timer;
  63. static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID;
  64. /* IP packet ID */
  65. static unsigned int net_ip_id;
  66. /*
  67. * Compute the checksum of the IP header. We cover even values of length only.
  68. * We cannot use net/checksum.c due to different CFLAGS values.
  69. *
  70. * @buf: IP header
  71. * @len: length of header in bytes
  72. * @return: checksum
  73. */
  74. static unsigned int efi_ip_checksum(const void *buf, size_t len)
  75. {
  76. size_t i;
  77. u32 sum = 0;
  78. const u16 *pos = buf;
  79. for (i = 0; i < len; i += 2)
  80. sum += *pos++;
  81. sum = (sum >> 16) + (sum & 0xffff);
  82. sum += sum >> 16;
  83. sum = ~sum & 0xffff;
  84. return sum;
  85. }
  86. /*
  87. * Transmit a DHCPDISCOVER message.
  88. */
  89. static efi_status_t send_dhcp_discover(void)
  90. {
  91. efi_status_t ret;
  92. struct dhcp p = {};
  93. /*
  94. * Fill ethernet header
  95. */
  96. boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN);
  97. boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address,
  98. ARP_HLEN);
  99. p.eth_hdr.et_protlen = htons(PROT_IP);
  100. /*
  101. * Fill IP header
  102. */
  103. p.ip_udp.ip_hl_v = 0x45;
  104. p.ip_udp.ip_len = htons(sizeof(struct dhcp) -
  105. sizeof(struct ethernet_hdr));
  106. p.ip_udp.ip_id = htons(++net_ip_id);
  107. p.ip_udp.ip_off = htons(IP_FLAGS_DFRAG);
  108. p.ip_udp.ip_ttl = 0xff; /* time to live */
  109. p.ip_udp.ip_p = IPPROTO_UDP;
  110. boottime->set_mem(&p.ip_udp.ip_dst, 4, 0xff);
  111. p.ip_udp.ip_sum = efi_ip_checksum(&p.ip_udp, IP_HDR_SIZE);
  112. /*
  113. * Fill UDP header
  114. */
  115. p.ip_udp.udp_src = htons(68);
  116. p.ip_udp.udp_dst = htons(67);
  117. p.ip_udp.udp_len = htons(sizeof(struct dhcp) -
  118. sizeof(struct ethernet_hdr) -
  119. sizeof(struct ip_hdr));
  120. /*
  121. * Fill DHCP header
  122. */
  123. p.dhcp_hdr.op = BOOTREQUEST;
  124. p.dhcp_hdr.htype = HWT_ETHER;
  125. p.dhcp_hdr.hlen = HWL_ETHER;
  126. p.dhcp_hdr.flags = htons(DHCP_FLAGS_UNICAST);
  127. boottime->copy_mem(&p.dhcp_hdr.chaddr,
  128. &net->mode->current_address, ARP_HLEN);
  129. /*
  130. * Fill options
  131. */
  132. p.opt[0] = 0x63; /* DHCP magic cookie */
  133. p.opt[1] = 0x82;
  134. p.opt[2] = 0x53;
  135. p.opt[3] = 0x63;
  136. p.opt[4] = DHCP_MESSAGE_TYPE;
  137. p.opt[5] = 0x01; /* length */
  138. p.opt[6] = DHCPDISCOVER;
  139. p.opt[7] = 0x39; /* maximum message size */
  140. p.opt[8] = 0x02; /* length */
  141. p.opt[9] = 0x02; /* 576 bytes */
  142. p.opt[10] = 0x40;
  143. p.opt[11] = 0xff; /* end of options */
  144. /*
  145. * Transmit DHCPDISCOVER message.
  146. */
  147. ret = net->transmit(net, 0, sizeof(struct dhcp), &p, NULL, NULL, 0);
  148. if (ret != EFI_SUCCESS)
  149. efi_st_error("Sending a DHCP request failed\n");
  150. else
  151. efi_st_printf("DHCP Discover\n");
  152. return ret;
  153. }
  154. /*
  155. * Setup unit test.
  156. *
  157. * Create a 1 s periodic timer.
  158. * Start the network driver.
  159. *
  160. * @handle: handle of the loaded image
  161. * @systable: system table
  162. * @return: EFI_ST_SUCCESS for success
  163. */
  164. static int setup(const efi_handle_t handle,
  165. const struct efi_system_table *systable)
  166. {
  167. efi_status_t ret;
  168. boottime = systable->boottime;
  169. /*
  170. * Create a timer event.
  171. */
  172. ret = boottime->create_event(EVT_TIMER, TPL_CALLBACK, NULL, NULL,
  173. &timer);
  174. if (ret != EFI_SUCCESS) {
  175. efi_st_error("Failed to create event\n");
  176. return EFI_ST_FAILURE;
  177. }
  178. /*
  179. * Set timer period to 1s.
  180. */
  181. ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000);
  182. if (ret != EFI_SUCCESS) {
  183. efi_st_error("Failed to set timer\n");
  184. return EFI_ST_FAILURE;
  185. }
  186. /*
  187. * Find an interface implementing the SNP protocol.
  188. */
  189. ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net);
  190. if (ret != EFI_SUCCESS) {
  191. net = NULL;
  192. efi_st_error("Failed to locate simple network protocol\n");
  193. return EFI_ST_FAILURE;
  194. }
  195. /*
  196. * Check hardware address size.
  197. */
  198. if (!net->mode) {
  199. efi_st_error("Mode not provided\n");
  200. return EFI_ST_FAILURE;
  201. }
  202. if (net->mode->hwaddr_size != ARP_HLEN) {
  203. efi_st_error("HwAddressSize = %u, expected %u\n",
  204. net->mode->hwaddr_size, ARP_HLEN);
  205. return EFI_ST_FAILURE;
  206. }
  207. /*
  208. * Check that WaitForPacket event exists.
  209. */
  210. if (!net->wait_for_packet) {
  211. efi_st_error("WaitForPacket event missing\n");
  212. return EFI_ST_FAILURE;
  213. }
  214. /*
  215. * Initialize network adapter.
  216. */
  217. ret = net->initialize(net, 0, 0);
  218. if (ret != EFI_SUCCESS) {
  219. efi_st_error("Failed to initialize network adapter\n");
  220. return EFI_ST_FAILURE;
  221. }
  222. /*
  223. * Start network adapter.
  224. */
  225. ret = net->start(net);
  226. if (ret != EFI_SUCCESS) {
  227. efi_st_error("Failed to start network adapter\n");
  228. return EFI_ST_FAILURE;
  229. }
  230. return EFI_ST_SUCCESS;
  231. }
  232. /*
  233. * Execute unit test.
  234. *
  235. * A DHCP discover message is sent. The test is successful if a
  236. * DHCP reply is received within 10 seconds.
  237. *
  238. * @return: EFI_ST_SUCCESS for success
  239. */
  240. static int execute(void)
  241. {
  242. efi_status_t ret;
  243. struct efi_event *events[2];
  244. efi_uintn_t index;
  245. union {
  246. struct dhcp p;
  247. u8 b[PKTSIZE];
  248. } buffer;
  249. struct efi_mac_address srcaddr;
  250. struct efi_mac_address destaddr;
  251. size_t buffer_size;
  252. u8 *addr;
  253. /*
  254. * The timeout is to occur after 10 s.
  255. */
  256. unsigned int timeout = 10;
  257. /* Setup may have failed */
  258. if (!net || !timer) {
  259. efi_st_error("Cannot execute test after setup failure\n");
  260. return EFI_ST_FAILURE;
  261. }
  262. /*
  263. * Send DHCP discover message
  264. */
  265. ret = send_dhcp_discover();
  266. if (ret != EFI_SUCCESS)
  267. return EFI_ST_FAILURE;
  268. /*
  269. * If we would call WaitForEvent only with the WaitForPacket event,
  270. * our code would block until a packet is received which might never
  271. * occur. By calling WaitFor event with both a timer event and the
  272. * WaitForPacket event we can escape this blocking situation.
  273. *
  274. * If the timer event occurs before we have received a DHCP reply
  275. * a further DHCP discover message is sent.
  276. */
  277. events[0] = timer;
  278. events[1] = net->wait_for_packet;
  279. for (;;) {
  280. /*
  281. * Wait for packet to be received or timer event.
  282. */
  283. boottime->wait_for_event(2, events, &index);
  284. if (index == 0) {
  285. /*
  286. * The timer event occurred. Check for timeout.
  287. */
  288. --timeout;
  289. if (!timeout) {
  290. efi_st_error("Timeout occurred\n");
  291. return EFI_ST_FAILURE;
  292. }
  293. /*
  294. * Send further DHCP discover message
  295. */
  296. ret = send_dhcp_discover();
  297. if (ret != EFI_SUCCESS)
  298. return EFI_ST_FAILURE;
  299. continue;
  300. }
  301. /*
  302. * Receive packet
  303. */
  304. buffer_size = sizeof(buffer);
  305. net->receive(net, NULL, &buffer_size, &buffer,
  306. &srcaddr, &destaddr, NULL);
  307. if (ret != EFI_SUCCESS) {
  308. efi_st_error("Failed to receive packet");
  309. return EFI_ST_FAILURE;
  310. }
  311. /*
  312. * Check the packet is meant for this system.
  313. * Unfortunately QEMU ignores the broadcast flag.
  314. * So we have to check for broadcasts too.
  315. */
  316. if (efi_st_memcmp(&destaddr, &net->mode->current_address,
  317. ARP_HLEN) &&
  318. efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
  319. continue;
  320. /*
  321. * Check this is a DHCP reply
  322. */
  323. if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) ||
  324. buffer.p.ip_udp.ip_hl_v != 0x45 ||
  325. buffer.p.ip_udp.ip_p != IPPROTO_UDP ||
  326. buffer.p.ip_udp.udp_src != ntohs(67) ||
  327. buffer.p.ip_udp.udp_dst != ntohs(68) ||
  328. buffer.p.dhcp_hdr.op != BOOTREPLY)
  329. continue;
  330. /*
  331. * We successfully received a DHCP reply.
  332. */
  333. break;
  334. }
  335. /*
  336. * Write a log message.
  337. */
  338. addr = (u8 *)&buffer.p.ip_udp.ip_src;
  339. efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ",
  340. addr[0], addr[1], addr[2], addr[3], &srcaddr);
  341. if (!efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
  342. efi_st_printf("as broadcast message.\n");
  343. else
  344. efi_st_printf("as unicast message.\n");
  345. return EFI_ST_SUCCESS;
  346. }
  347. /*
  348. * Tear down unit test.
  349. *
  350. * Close the timer event created in setup.
  351. * Shut down the network adapter.
  352. *
  353. * @return: EFI_ST_SUCCESS for success
  354. */
  355. static int teardown(void)
  356. {
  357. efi_status_t ret;
  358. int exit_status = EFI_ST_SUCCESS;
  359. if (timer) {
  360. /*
  361. * Stop timer.
  362. */
  363. ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0);
  364. if (ret != EFI_SUCCESS) {
  365. efi_st_error("Failed to stop timer");
  366. exit_status = EFI_ST_FAILURE;
  367. }
  368. /*
  369. * Close timer event.
  370. */
  371. ret = boottime->close_event(timer);
  372. if (ret != EFI_SUCCESS) {
  373. efi_st_error("Failed to close event");
  374. exit_status = EFI_ST_FAILURE;
  375. }
  376. }
  377. if (net) {
  378. /*
  379. * Stop network adapter.
  380. */
  381. ret = net->stop(net);
  382. if (ret != EFI_SUCCESS) {
  383. efi_st_error("Failed to stop network adapter\n");
  384. exit_status = EFI_ST_FAILURE;
  385. }
  386. /*
  387. * Shut down network adapter.
  388. */
  389. ret = net->shutdown(net);
  390. if (ret != EFI_SUCCESS) {
  391. efi_st_error("Failed to shut down network adapter\n");
  392. exit_status = EFI_ST_FAILURE;
  393. }
  394. }
  395. return exit_status;
  396. }
  397. EFI_UNIT_TEST(snp) = {
  398. .name = "simple network protocol",
  399. .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
  400. .setup = setup,
  401. .execute = execute,
  402. .teardown = teardown,
  403. };