Home | History | Annotate | Download | only in pxe
      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 (at) fensystems.co.uk>.
     19  * Portions (C) 2010 Shao Miller <shao.miller (at) 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 
    307