1 /*
2  * Copyright 2013-2014 Intel Corporation - All Rights Reserved
3  */
4 
5 #include "efi.h"
6 #include "net.h"
7 #include "fs/pxe/pxe.h"
8 
9 extern EFI_GUID Tcp4ServiceBindingProtocol;
10 extern EFI_GUID Tcp4Protocol;
11 
12 
13 extern struct efi_binding *efi_create_binding(EFI_GUID *, EFI_GUID *);
14 extern void efi_destroy_binding(struct efi_binding *, EFI_GUID *);
core_tcp_open(struct pxe_pvt_inode * socket)15 int core_tcp_open(struct pxe_pvt_inode *socket)
16 {
17     struct efi_binding *b;
18 
19     b = efi_create_binding(&Tcp4ServiceBindingProtocol, &Tcp4Protocol);
20     if (!b)
21 	return -1;
22 
23     socket->net.efi.binding = b;
24 
25     return 0;
26 }
27 
null_cb(EFI_EVENT ev,void * context)28 static EFIAPI void null_cb(EFI_EVENT ev, void *context)
29 {
30     EFI_TCP4_COMPLETION_TOKEN *token = context;
31 
32     (void)ev;
33 
34     uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
35 }
36 
37 static int volatile cb_status = -1;
tcp_cb(EFI_EVENT ev,void * context)38 static EFIAPI void tcp_cb(EFI_EVENT ev, void *context)
39 {
40     EFI_TCP4_COMPLETION_TOKEN *token = context;
41 
42     (void)ev;
43 
44     if (token->Status == EFI_SUCCESS)
45 	cb_status = 0;
46     else
47 	cb_status = 1;
48 }
49 
core_tcp_connect(struct pxe_pvt_inode * socket,uint32_t ip,uint16_t port)50 int core_tcp_connect(struct pxe_pvt_inode *socket, uint32_t ip, uint16_t port)
51 {
52     EFI_TCP4_CONNECTION_TOKEN token;
53     EFI_TCP4_ACCESS_POINT *ap;
54     EFI_TCP4_CONFIG_DATA tdata;
55     struct efi_binding *b = socket->net.efi.binding;
56     EFI_STATUS status;
57     EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
58     int rv = -1;
59     int unmapped = 1;
60     jiffies_t start, last, cur;
61 
62     memset(&tdata, 0, sizeof(tdata));
63 
64     ap = &tdata.AccessPoint;
65     ap->UseDefaultAddress = TRUE;
66     memcpy(&ap->RemoteAddress, &ip, sizeof(ip));
67     ap->RemotePort = port;
68     ap->ActiveFlag = TRUE; /* Initiate active open */
69 
70     tdata.TimeToLive = 64;
71 
72     last = start = jiffies();
73     while (unmapped){
74 	status = uefi_call_wrapper(tcp->Configure, 2, tcp, &tdata);
75 	if (status != EFI_NO_MAPPING)
76 		unmapped = 0;
77 	else {
78 	    cur = jiffies();
79 	    if ( (cur - last) >= EFI_NOMAP_PRINT_DELAY ) {
80 		last = cur;
81 		Print(L"core_tcp_connect: stalling on configure with no mapping\n");
82 	    } else if ( (cur - start) > EFI_NOMAP_PRINT_DELAY * EFI_NOMAP_PRINT_COUNT) {
83 		Print(L"core_tcp_connect: aborting on no mapping\n");
84 		unmapped = 0;
85 	    }
86 	}
87     }
88     if (status != EFI_SUCCESS)
89 	return -1;
90 
91     status = efi_setup_event(&token.CompletionToken.Event,
92 			    (EFI_EVENT_NOTIFY)tcp_cb, &token.CompletionToken);
93     if (status != EFI_SUCCESS)
94 	return -1;
95 
96     status = uefi_call_wrapper(tcp->Connect, 2, tcp, &token);
97     if (status != EFI_SUCCESS) {
98 	Print(L"Failed to connect: %d\n", status);
99 	goto out;
100     }
101 
102     while (cb_status == -1)
103 	uefi_call_wrapper(tcp->Poll, 1, tcp);
104 
105     if (cb_status == 0)
106 	rv = 0;
107 
108     /* Reset */
109     cb_status = -1;
110 
111 out:
112     uefi_call_wrapper(BS->CloseEvent, 1, token.CompletionToken.Event);
113     return rv;
114 }
115 
core_tcp_is_connected(struct pxe_pvt_inode * socket)116 bool core_tcp_is_connected(struct pxe_pvt_inode *socket)
117 {
118     if (socket->net.efi.binding)
119 	return true;
120 
121     return false;
122 }
123 
core_tcp_write(struct pxe_pvt_inode * socket,const void * data,size_t len,bool copy)124 int core_tcp_write(struct pxe_pvt_inode *socket, const void *data,
125 		   size_t len, bool copy)
126 {
127     EFI_TCP4_TRANSMIT_DATA txdata;
128     EFI_TCP4_FRAGMENT_DATA *frag;
129     struct efi_binding *b = socket->net.efi.binding;
130     EFI_TCP4_IO_TOKEN iotoken;
131     EFI_STATUS status;
132     EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
133     int rv = -1;
134 
135     (void)copy;
136 
137     memset(&iotoken, 0, sizeof(iotoken));
138     memset(&txdata, 0, sizeof(txdata));
139 
140     txdata.DataLength = len;
141     txdata.FragmentCount = 1;
142 
143     frag = &txdata.FragmentTable[0];
144     frag->FragmentLength = len;
145     frag->FragmentBuffer = (void *)data;
146 
147     iotoken.Packet.TxData = &txdata;
148 
149     status = efi_setup_event(&iotoken.CompletionToken.Event,
150 			     (EFI_EVENT_NOTIFY)tcp_cb, &iotoken.CompletionToken);
151     if (status != EFI_SUCCESS)
152 	return -1;
153 
154     status = uefi_call_wrapper(tcp->Transmit, 2, tcp, &iotoken);
155     if (status != EFI_SUCCESS) {
156 	Print(L"tcp transmit failed, %d\n", status);
157 	goto out;
158     }
159 
160     while (cb_status == -1)
161 	uefi_call_wrapper(tcp->Poll, 1, tcp);
162 
163     if (cb_status == 0)
164 	rv = 0;
165 
166     /* Reset */
167     cb_status = -1;
168 
169 out:
170     uefi_call_wrapper(BS->CloseEvent, 1, iotoken.CompletionToken.Event);
171     return rv;
172 }
173 
core_tcp_close_file(struct inode * inode)174 void core_tcp_close_file(struct inode *inode)
175 {
176     struct pxe_pvt_inode *socket = PVT(inode);
177     struct efi_binding *b = socket->net.efi.binding;
178     EFI_TCP4_CLOSE_TOKEN token;
179     EFI_STATUS status;
180     EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
181 
182   if (!socket->tftp_goteof) {
183 	memset(&token, 0, sizeof(token));
184 
185 	status = efi_setup_event(&token.CompletionToken.Event,
186 				 (EFI_EVENT_NOTIFY)null_cb,
187 				 &token.CompletionToken);
188 	if (status != EFI_SUCCESS)
189 	    return;
190 
191 	status = uefi_call_wrapper(tcp->Close, 2, tcp, &token);
192 	if (status != EFI_SUCCESS)
193 	    Print(L"tcp close failed: %d\n", status);
194     }
195 
196     efi_destroy_binding(b, &Tcp4ServiceBindingProtocol);
197     socket->net.efi.binding = NULL;
198 }
199 
200 static char databuf[8192];
201 
core_tcp_fill_buffer(struct inode * inode)202 void core_tcp_fill_buffer(struct inode *inode)
203 {
204     struct pxe_pvt_inode *socket = PVT(inode);
205     struct efi_binding *b = socket->net.efi.binding;
206     EFI_TCP4_IO_TOKEN iotoken;
207     EFI_TCP4_RECEIVE_DATA rxdata;
208     EFI_TCP4_FRAGMENT_DATA *frag;
209     EFI_STATUS status;
210     EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
211     void *data;
212     size_t len;
213 
214     memset(&iotoken, 0, sizeof(iotoken));
215     memset(&rxdata, 0, sizeof(rxdata));
216 
217     status = efi_setup_event(&iotoken.CompletionToken.Event,
218 		      (EFI_EVENT_NOTIFY)tcp_cb, &iotoken.CompletionToken);
219     if (status != EFI_SUCCESS)
220 	return;
221 
222     iotoken.Packet.RxData = &rxdata;
223     rxdata.FragmentCount = 1;
224     rxdata.DataLength = sizeof(databuf);
225     frag = &rxdata.FragmentTable[0];
226     frag->FragmentBuffer = databuf;
227     frag->FragmentLength = sizeof(databuf);
228 
229     status = uefi_call_wrapper(tcp->Receive, 2, tcp, &iotoken);
230     if (status == EFI_CONNECTION_FIN) {
231 	socket->tftp_goteof = 1;
232 	if (inode->size == (uint64_t)-1)
233 	    inode->size = socket->tftp_filepos;
234 	socket->ops->close(inode);
235 	goto out;
236     }
237 
238     while (cb_status == -1)
239 	uefi_call_wrapper(tcp->Poll, 1, tcp);
240 
241     /* Reset */
242     cb_status = -1;
243 
244     len = frag->FragmentLength;
245     memcpy(databuf, frag->FragmentBuffer, len);
246     data = databuf;
247 
248     socket->tftp_dataptr = data;
249     socket->tftp_filepos += len;
250     socket->tftp_bytesleft = len;
251 
252 out:
253     uefi_call_wrapper(BS->CloseEvent, 1, iotoken.CompletionToken.Event);
254 }
255