Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / interface / pxe / pxe_file.c
1 /** @file
2  *
3  * PXE FILE API
4  *
5  */
6
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include <byteswap.h>
11 #include <ipxe/uaccess.h>
12 #include <ipxe/posix_io.h>
13 #include <ipxe/features.h>
14 #include <pxe.h>
15 #include <realmode.h>
16
17 /*
18  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
19  *
20  * This program is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU General Public License as
22  * published by the Free Software Foundation; either version 2 of the
23  * License, or any later version.
24  *
25  * This program is distributed in the hope that it will be useful, but
26  * WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
28  * General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public License
31  * along with this program; if not, write to the Free Software
32  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
33  * 02110-1301, USA.
34  */
35
36 FILE_LICENCE ( GPL2_OR_LATER );
37
38 FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 );
39
40 /**
41  * FILE OPEN
42  *
43  * @v file_open                         Pointer to a struct s_PXENV_FILE_OPEN
44  * @v s_PXENV_FILE_OPEN::FileName       URL of file to open
45  * @ret #PXENV_EXIT_SUCCESS             File was opened
46  * @ret #PXENV_EXIT_FAILURE             File was not opened
47  * @ret s_PXENV_FILE_OPEN::Status       PXE status code
48  * @ret s_PXENV_FILE_OPEN::FileHandle   Handle of opened file
49  *
50  */
51 static PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open ) {
52         userptr_t filename;
53         size_t filename_len;
54         int fd;
55
56         DBG ( "PXENV_FILE_OPEN" );
57
58         /* Copy name from external program, and open it */
59         filename = real_to_user ( file_open->FileName.segment,
60                               file_open->FileName.offset );
61         filename_len = strlen_user ( filename, 0 );
62         {
63                 char uri_string[ filename_len + 1 ];
64
65                 copy_from_user ( uri_string, filename, 0,
66                                  sizeof ( uri_string ) );
67                 DBG ( " %s", uri_string );
68                 fd = open ( uri_string );
69         }
70
71         if ( fd < 0 ) {
72                 file_open->Status = PXENV_STATUS ( fd );
73                 return PXENV_EXIT_FAILURE;
74         }
75
76         DBG ( " as file %d", fd );
77
78         file_open->FileHandle = fd;
79         file_open->Status = PXENV_STATUS_SUCCESS;
80         return PXENV_EXIT_SUCCESS;
81 }
82
83 /**
84  * FILE CLOSE
85  *
86  * @v file_close                        Pointer to a struct s_PXENV_FILE_CLOSE
87  * @v s_PXENV_FILE_CLOSE::FileHandle    File handle
88  * @ret #PXENV_EXIT_SUCCESS             File was closed
89  * @ret #PXENV_EXIT_FAILURE             File was not closed
90  * @ret s_PXENV_FILE_CLOSE::Status      PXE status code
91  *
92  */
93 static PXENV_EXIT_t pxenv_file_close ( struct s_PXENV_FILE_CLOSE *file_close ) {
94
95         DBG ( "PXENV_FILE_CLOSE %d", file_close->FileHandle );
96
97         close ( file_close->FileHandle );
98         file_close->Status = PXENV_STATUS_SUCCESS;
99         return PXENV_EXIT_SUCCESS;
100 }
101
102 /**
103  * FILE SELECT
104  *
105  * @v file_select                       Pointer to a struct s_PXENV_FILE_SELECT
106  * @v s_PXENV_FILE_SELECT::FileHandle   File handle
107  * @ret #PXENV_EXIT_SUCCESS             File has been checked for readiness
108  * @ret #PXENV_EXIT_FAILURE             File has not been checked for readiness
109  * @ret s_PXENV_FILE_SELECT::Status     PXE status code
110  * @ret s_PXENV_FILE_SELECT::Ready      Indication of readiness
111  *
112  */
113 static PXENV_EXIT_t
114 pxenv_file_select ( struct s_PXENV_FILE_SELECT *file_select ) {
115         fd_set fdset;
116         int ready;
117
118         DBG ( "PXENV_FILE_SELECT %d", file_select->FileHandle );
119
120         FD_ZERO ( &fdset );
121         FD_SET ( file_select->FileHandle, &fdset );
122         if ( ( ready = select ( &fdset, 0 ) ) < 0 ) {
123                 file_select->Status = PXENV_STATUS ( ready );
124                 return PXENV_EXIT_FAILURE;
125         }
126
127         file_select->Ready = ( ready ? RDY_READ : 0 );
128         file_select->Status = PXENV_STATUS_SUCCESS;
129         return PXENV_EXIT_SUCCESS;
130 }
131
132 /**
133  * FILE READ
134  *
135  * @v file_read                         Pointer to a struct s_PXENV_FILE_READ
136  * @v s_PXENV_FILE_READ::FileHandle     File handle
137  * @v s_PXENV_FILE_READ::BufferSize     Size of data buffer
138  * @v s_PXENV_FILE_READ::Buffer         Data buffer
139  * @ret #PXENV_EXIT_SUCCESS             Data has been read from file
140  * @ret #PXENV_EXIT_FAILURE             Data has not been read from file
141  * @ret s_PXENV_FILE_READ::Status       PXE status code
142  * @ret s_PXENV_FILE_READ::Ready        Indication of readiness
143  * @ret s_PXENV_FILE_READ::BufferSize   Length of data read
144  *
145  */
146 static PXENV_EXIT_t pxenv_file_read ( struct s_PXENV_FILE_READ *file_read ) {
147         userptr_t buffer;
148         ssize_t len;
149
150         DBG ( "PXENV_FILE_READ %d to %04x:%04x+%04x", file_read->FileHandle,
151               file_read->Buffer.segment, file_read->Buffer.offset,
152               file_read->BufferSize );
153
154         buffer = real_to_user ( file_read->Buffer.segment,
155                                 file_read->Buffer.offset );
156         if ( ( len = read_user ( file_read->FileHandle, buffer, 0,
157                                 file_read->BufferSize ) ) < 0 ) {
158                 file_read->Status = PXENV_STATUS ( len );
159                 return PXENV_EXIT_FAILURE;
160         }
161
162         DBG ( " read %04zx", ( ( size_t ) len ) );
163
164         file_read->BufferSize = len;
165         file_read->Status = PXENV_STATUS_SUCCESS;
166         return PXENV_EXIT_SUCCESS;
167 }
168
169 /**
170  * GET FILE SIZE
171  *
172  * @v get_file_size                     Pointer to a struct s_PXENV_GET_FILE_SIZE
173  * @v s_PXENV_GET_FILE_SIZE::FileHandle File handle
174  * @ret #PXENV_EXIT_SUCCESS             File size has been determined
175  * @ret #PXENV_EXIT_FAILURE             File size has not been determined
176  * @ret s_PXENV_GET_FILE_SIZE::Status   PXE status code
177  * @ret s_PXENV_GET_FILE_SIZE::FileSize Size of file
178  */
179 static PXENV_EXIT_t
180 pxenv_get_file_size ( struct s_PXENV_GET_FILE_SIZE *get_file_size ) {
181         ssize_t filesize;
182
183         DBG ( "PXENV_GET_FILE_SIZE %d", get_file_size->FileHandle );
184
185         filesize = fsize ( get_file_size->FileHandle );
186         if ( filesize < 0 ) {
187                 get_file_size->Status = PXENV_STATUS ( filesize );
188                 return PXENV_EXIT_FAILURE;
189         }
190
191         DBG ( " is %zd", ( ( size_t ) filesize ) );
192
193         get_file_size->FileSize = filesize;
194         get_file_size->Status = PXENV_STATUS_SUCCESS;
195         return PXENV_EXIT_SUCCESS;
196 }
197
198 /**
199  * FILE EXEC
200  *
201  * @v file_exec                         Pointer to a struct s_PXENV_FILE_EXEC
202  * @v s_PXENV_FILE_EXEC::Command        Command to execute
203  * @ret #PXENV_EXIT_SUCCESS             Command was executed successfully
204  * @ret #PXENV_EXIT_FAILURE             Command was not executed successfully
205  * @ret s_PXENV_FILE_EXEC::Status       PXE status code
206  *
207  */
208 static PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ) {
209         userptr_t command;
210         size_t command_len;
211         int rc;
212
213         DBG ( "PXENV_FILE_EXEC" );
214
215         /* Copy name from external program, and exec it */
216         command = real_to_user ( file_exec->Command.segment,
217                                  file_exec->Command.offset );
218         command_len = strlen_user ( command, 0 );
219         {
220                 char command_string[ command_len + 1 ];
221
222                 copy_from_user ( command_string, command, 0,
223                                  sizeof ( command_string ) );
224                 DBG ( " %s", command_string );
225
226                 if ( ( rc = system ( command_string ) ) != 0 ) {
227                         file_exec->Status = PXENV_STATUS ( rc );
228                         return PXENV_EXIT_FAILURE;
229                 }
230         }
231
232         file_exec->Status = PXENV_STATUS_SUCCESS;
233         return PXENV_EXIT_SUCCESS;
234 }
235
236 /**
237  * FILE CMDLINE
238  *
239  * @v file_cmdline                      Pointer to a struct s_PXENV_FILE_CMDLINE
240  * @v s_PXENV_FILE_CMDLINE::Buffer      Buffer to contain command line
241  * @v s_PXENV_FILE_CMDLINE::BufferSize  Size of buffer
242  * @ret #PXENV_EXIT_SUCCESS             Command was executed successfully
243  * @ret #PXENV_EXIT_FAILURE             Command was not executed successfully
244  * @ret s_PXENV_FILE_EXEC::Status       PXE status code
245  * @ret s_PXENV_FILE_EXEC::BufferSize   Length of command line (including NUL)
246  *
247  */
248 static PXENV_EXIT_t
249 pxenv_file_cmdline ( struct s_PXENV_FILE_CMDLINE *file_cmdline ) {
250         userptr_t buffer;
251         size_t max_len;
252         size_t len;
253
254         DBG ( "PXENV_FILE_CMDLINE to %04x:%04x+%04x \"%s\"\n",
255               file_cmdline->Buffer.segment, file_cmdline->Buffer.offset,
256               file_cmdline->BufferSize, pxe_cmdline );
257
258         buffer = real_to_user ( file_cmdline->Buffer.segment,
259                                 file_cmdline->Buffer.offset );
260         len = file_cmdline->BufferSize;
261         max_len = ( pxe_cmdline ?
262                     ( strlen ( pxe_cmdline ) + 1 /* NUL */ ) : 0 );
263         if ( len > max_len )
264                 len = max_len;
265         copy_to_user ( buffer, 0, pxe_cmdline, len );
266         file_cmdline->BufferSize = max_len;
267
268         file_cmdline->Status = PXENV_STATUS_SUCCESS;
269         return PXENV_EXIT_SUCCESS;
270 }
271
272 /**
273  * FILE API CHECK
274  *
275  * @v file_exec                         Pointer to a struct s_PXENV_FILE_API_CHECK
276  * @v s_PXENV_FILE_API_CHECK::Magic     Inbound magic number (0x91d447b2)
277  * @ret #PXENV_EXIT_SUCCESS             Command was executed successfully
278  * @ret #PXENV_EXIT_FAILURE             Command was not executed successfully
279  * @ret s_PXENV_FILE_API_CHECK::Status  PXE status code
280  * @ret s_PXENV_FILE_API_CHECK::Magic   Outbound magic number (0xe9c17b20)
281  * @ret s_PXENV_FILE_API_CHECK::Provider "iPXE" (0x45585067)
282  * @ret s_PXENV_FILE_API_CHECK::APIMask API function bitmask
283  * @ret s_PXENV_FILE_API_CHECK::Flags   Reserved
284  *
285  */
286 static PXENV_EXIT_t
287 pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_api_check ) {
288         struct pxe_api_call *call;
289         unsigned int mask = 0;
290         unsigned int offset;
291
292         DBG ( "PXENV_FILE_API_CHECK" );
293
294         /* Check for magic value */
295         if ( file_api_check->Magic != 0x91d447b2 ) {
296                 file_api_check->Status = PXENV_STATUS_BAD_FUNC;
297                 return PXENV_EXIT_FAILURE;
298         }
299
300         /* Check for required parameter size */
301         if ( file_api_check->Size < sizeof ( *file_api_check ) ) {
302                 file_api_check->Status = PXENV_STATUS_OUT_OF_RESOURCES;
303                 return PXENV_EXIT_FAILURE;
304         }
305
306         /* Determine supported calls */
307         for_each_table_entry ( call, PXE_API_CALLS ) {
308                 offset = ( call->opcode - PXENV_FILE_MIN );
309                 if ( offset <= ( PXENV_FILE_MAX - PXENV_FILE_MIN ) )
310                         mask |= ( 1 << offset );
311         }
312
313         /* Fill in parameters */
314         file_api_check->Size = sizeof ( *file_api_check );
315         file_api_check->Magic = 0xe9c17b20;
316         file_api_check->Provider = 0x45585067; /* "iPXE" */
317         file_api_check->APIMask = mask;
318         file_api_check->Flags = 0; /* None defined */
319
320         file_api_check->Status = PXENV_STATUS_SUCCESS;
321         return PXENV_EXIT_SUCCESS;
322 }
323
324 /** PXE file API */
325 struct pxe_api_call pxe_file_api[] __pxe_api_call = {
326         PXE_API_CALL ( PXENV_FILE_OPEN, pxenv_file_open,
327                        struct s_PXENV_FILE_OPEN ),
328         PXE_API_CALL ( PXENV_FILE_CLOSE, pxenv_file_close,
329                        struct s_PXENV_FILE_CLOSE ),
330         PXE_API_CALL ( PXENV_FILE_SELECT, pxenv_file_select,
331                        struct s_PXENV_FILE_SELECT ),
332         PXE_API_CALL ( PXENV_FILE_READ, pxenv_file_read,
333                        struct s_PXENV_FILE_READ ),
334         PXE_API_CALL ( PXENV_GET_FILE_SIZE, pxenv_get_file_size,
335                        struct s_PXENV_GET_FILE_SIZE ),
336         PXE_API_CALL ( PXENV_FILE_EXEC, pxenv_file_exec,
337                        struct s_PXENV_FILE_EXEC ),
338         PXE_API_CALL ( PXENV_FILE_CMDLINE, pxenv_file_cmdline,
339                        struct s_PXENV_FILE_CMDLINE ),
340         PXE_API_CALL ( PXENV_FILE_API_CHECK, pxenv_file_api_check,
341                        struct s_PXENV_FILE_API_CHECK ),
342 };