iPXE - Open Source Boot Firmware

[pxe] Introduce PXE exit hook for NBP chaining
[ipxe.git] / 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 <gpxe/uaccess.h>
12 #include <gpxe/posix_io.h>
13 #include <gpxe/features.h>
14 #include <pxe.h>
15 #include <realmode.h>
16
17 /*
18  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
19  * Portions (C) 2010 Shao Miller <shao.miller@yrdsb.edu.on.ca>.
20  *              [PXE exit hook logic]
21  *
22  * This program is free software; you can redistribute it and/or
23  * modify it under the terms of the GNU General Public License as
24  * published by the Free Software Foundation; either version 2 of the
25  * License, or any later version.
26  *
27  * This program is distributed in the hope that it will be useful, but
28  * WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
30  * General Public License for more details.
31  *
32  * You should have received a copy of the GNU General Public License
33  * along with this program; if not, write to the Free Software
34  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35  */
36
37 FILE_LICENCE ( GPL2_OR_LATER );
38
39 FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 );
40
41 /**
42  * FILE OPEN
43  *
44  * @v file_open                         Pointer to a struct s_PXENV_FILE_OPEN
45  * @v s_PXENV_FILE_OPEN::FileName       URL of file to open
46  * @ret #PXENV_EXIT_SUCCESS             File was opened
47  * @ret #PXENV_EXIT_FAILURE             File was not opened
48  * @ret s_PXENV_FILE_OPEN::Status       PXE status code
49  * @ret s_PXENV_FILE_OPEN::FileHandle   Handle of opened file
50  *
51  */
52 PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open ) {
53         userptr_t filename;
54         size_t filename_len;
55         int fd;
56
57         DBG ( "PXENV_FILE_OPEN" );
58
59         /* Copy name from external program, and open it */
60         filename = real_to_user ( file_open->FileName.segment,
61                               file_open->FileName.offset );
62         filename_len = strlen_user ( filename, 0 );
63         {
64                 char uri_string[ filename_len + 1 ];
65
66                 copy_from_user ( uri_string, filename, 0,
67                                  sizeof ( uri_string ) );
68                 DBG ( " %s", uri_string );
69                 fd = open ( uri_string );
70         }
71
72         if ( fd < 0 ) {
73                 file_open->Status = PXENV_STATUS ( fd );
74                 return PXENV_EXIT_FAILURE;
75         }
76
77         DBG ( " as file %d", fd );
78
79         file_open->FileHandle = fd;
80         file_open->Status = PXENV_STATUS_SUCCESS;
81         return PXENV_EXIT_SUCCESS;
82 }
83
84 /**
85  * FILE CLOSE
86  *
87  * @v file_close                        Pointer to a struct s_PXENV_FILE_CLOSE
88  * @v s_PXENV_FILE_CLOSE::FileHandle    File handle
89  * @ret #PXENV_EXIT_SUCCESS             File was closed
90  * @ret #PXENV_EXIT_FAILURE             File was not closed
91  * @ret s_PXENV_FILE_CLOSE::Status      PXE status code
92  *
93  */
94 PXENV_EXIT_t pxenv_file_close ( struct s_PXENV_FILE_CLOSE *file_close ) {
95
96         DBG ( "PXENV_FILE_CLOSE %d", file_close->FileHandle );
97
98         close ( file_close->FileHandle );
99         file_close->Status = PXENV_STATUS_SUCCESS;
100         return PXENV_EXIT_SUCCESS;
101 }
102
103 /**
104  * FILE SELECT
105  *
106  * @v file_select                       Pointer to a struct s_PXENV_FILE_SELECT
107  * @v s_PXENV_FILE_SELECT::FileHandle   File handle
108  * @ret #PXENV_EXIT_SUCCESS             File has been checked for readiness
109  * @ret #PXENV_EXIT_FAILURE             File has not been checked for readiness
110  * @ret s_PXENV_FILE_SELECT::Status     PXE status code
111  * @ret s_PXENV_FILE_SELECT::Ready      Indication of readiness
112  *
113  */
114 PXENV_EXIT_t 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 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 PXENV_EXIT_t pxenv_get_file_size ( struct s_PXENV_GET_FILE_SIZE
180                                    *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 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 segoff_t __data16 ( pxe_exit_hook ) = { 0, 0 };
237 #define pxe_exit_hook __use_data16 ( pxe_exit_hook )
238
239 /**
240  * FILE API CHECK
241  *
242  * @v file_exec                         Pointer to a struct s_PXENV_FILE_API_CHECK
243  * @v s_PXENV_FILE_API_CHECK::Magic     Inbound magic number (0x91d447b2)
244  * @ret #PXENV_EXIT_SUCCESS             Command was executed successfully
245  * @ret #PXENV_EXIT_FAILURE             Command was not executed successfully
246  * @ret s_PXENV_FILE_API_CHECK::Status  PXE status code
247  * @ret s_PXENV_FILE_API_CHECK::Magic   Outbound magic number (0xe9c17b20)
248  * @ret s_PXENV_FILE_API_CHECK::Provider "gPXE" (0x45585067)
249  * @ret s_PXENV_FILE_API_CHECK::APIMask API function bitmask
250  * @ret s_PXENV_FILE_API_CHECK::Flags   Reserved
251  *
252  */
253 PXENV_EXIT_t pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_api_check ) {
254         DBG ( "PXENV_FILE_API_CHECK" );
255
256         if ( file_api_check->Magic != 0x91d447b2 ) {
257                 file_api_check->Status = PXENV_STATUS_BAD_FUNC;
258                 return PXENV_EXIT_FAILURE;
259         } else if ( file_api_check->Size <
260                     sizeof(struct s_PXENV_FILE_API_CHECK) ) {
261                 file_api_check->Status = PXENV_STATUS_OUT_OF_RESOURCES;
262                 return PXENV_EXIT_FAILURE;
263         } else {
264                 file_api_check->Status   = PXENV_STATUS_SUCCESS;
265                 file_api_check->Size     = sizeof(struct s_PXENV_FILE_API_CHECK);
266                 file_api_check->Magic    = 0xe9c17b20;
267                 file_api_check->Provider = 0x45585067; /* "gPXE" */
268                 file_api_check->APIMask  = 0x0000007f; /* Functions e0-e6 */
269                 /* Check to see if we have a PXE exit hook */
270                 if ( pxe_exit_hook.segment | pxe_exit_hook.offset )
271                         /* Function e7, also */
272                         file_api_check->APIMask |= 0x00000080;
273                 file_api_check->Flags    = 0;          /* None defined */
274                 return PXENV_EXIT_SUCCESS;
275         }
276 }
277
278 /**
279  * FILE EXIT HOOK
280  *
281  * @v file_exit_hook                    Pointer to a struct
282  *                                      s_PXENV_FILE_EXIT_HOOK
283  * @v s_PXENV_FILE_EXIT_HOOK::Hook      SEG16:OFF16 to jump to
284  * @ret #PXENV_EXIT_SUCCESS             Successfully set hook
285  * @ret #PXENV_EXIT_FAILURE             We're not an NBP build
286  * @ret s_PXENV_FILE_EXIT_HOOK::Status  PXE status code
287  *
288  */
289 PXENV_EXIT_t pxenv_file_exit_hook ( struct s_PXENV_FILE_EXIT_HOOK
290                                         *file_exit_hook ) {
291         DBG ( "PXENV_FILE_EXIT_HOOK" );
292
293         /* Check to see if we have a PXE exit hook */
294         if ( pxe_exit_hook.segment | pxe_exit_hook.offset ) {
295                 /* We'll jump to the specified SEG16:OFF16 during exit */
296                 pxe_exit_hook.segment = file_exit_hook->Hook.segment;
297                 pxe_exit_hook.offset = file_exit_hook->Hook.offset;
298                 file_exit_hook->Status = PXENV_STATUS_SUCCESS;
299                 return PXENV_EXIT_SUCCESS;
300         }
301
302         DBG ( " not NBP" );
303         file_exit_hook->Status = PXENV_STATUS_UNSUPPORTED;
304         return PXENV_EXIT_FAILURE;
305 }
306