/** * Transmit SRP SCSI command * * @v srp SRP device */ static void srp_cmd ( struct srp_device *srp ) { struct io_buffer *iobuf; struct srp_cmd *cmd; struct srp_memory_descriptor *data_out; struct srp_memory_descriptor *data_in; int rc; assert ( srp->state & SRP_STATE_LOGGED_IN ); /* Allocate I/O buffer */ iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN ); if ( ! iobuf ) { rc = -ENOMEM; goto err; } /* Construct base portion */ cmd = iob_put ( iobuf, sizeof ( *cmd ) ); memset ( cmd, 0, sizeof ( *cmd ) ); cmd->type = SRP_CMD; cmd->tag.dwords[1] = htonl ( ++srp_tag ); cmd->lun = srp->lun; memcpy ( &cmd->cdb, &srp->command->cdb, sizeof ( cmd->cdb ) ); /* Construct data-out descriptor, if present */ if ( srp->command->data_out ) { cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT; data_out = iob_put ( iobuf, sizeof ( *data_out ) ); data_out->address = cpu_to_be64 ( user_to_phys ( srp->command->data_out, 0 ) ); data_out->handle = ntohl ( srp->memory_handle ); data_out->len = ntohl ( srp->command->data_out_len ); } /* Construct data-in descriptor, if present */ if ( srp->command->data_in ) { cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT; data_in = iob_put ( iobuf, sizeof ( *data_in ) ); data_in->address = cpu_to_be64 ( user_to_phys ( srp->command->data_in, 0 ) ); data_in->handle = ntohl ( srp->memory_handle ); data_in->len = ntohl ( srp->command->data_in_len ); } DBGC2 ( srp, "SRP %p TX SCSI command tag %08x%08x\n", srp, ntohl ( cmd->tag.dwords[0] ), ntohl ( cmd->tag.dwords[1] ) ); DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) ); /* Send IU */ if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) { DBGC ( srp, "SRP %p could not send command: %s\n", srp, strerror ( rc ) ); goto err; } return; err: srp_fail ( srp, rc ); }
/** * Deliver datagram as raw data * * @v intf Data transfer interface * @v data Data * @v len Length of data * @v meta Data transfer metadata * @ret rc Return status code */ int xfer_deliver_raw_meta ( struct interface *intf, const void *data, size_t len, struct xfer_metadata *meta ) { struct io_buffer *iobuf; iobuf = xfer_alloc_iob ( intf, len ); if ( ! iobuf ) return -ENOMEM; memcpy ( iob_put ( iobuf, len ), data, len ); return xfer_deliver ( intf, iobuf, meta ); }
/** * Initiate SRP login * * @v srp SRP device */ static void srp_login ( struct srp_device *srp ) { struct io_buffer *iobuf; struct srp_login_req *login_req; int rc; assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ); /* Open underlying socket */ if ( ( rc = srp->transport->connect ( srp ) ) != 0 ) { DBGC ( srp, "SRP %p could not open socket: %s\n", srp, strerror ( rc ) ); goto err; } srp->state |= SRP_STATE_SOCKET_OPEN; /* Allocate I/O buffer */ iobuf = xfer_alloc_iob ( &srp->socket, sizeof ( *login_req ) ); if ( ! iobuf ) { rc = -ENOMEM; goto err; } /* Construct login request IU */ login_req = iob_put ( iobuf, sizeof ( *login_req ) ); memset ( login_req, 0, sizeof ( *login_req ) ); login_req->type = SRP_LOGIN_REQ; login_req->tag.dwords[1] = htonl ( ++srp_tag ); login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN ); login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD; memcpy ( &login_req->port_ids, &srp->port_ids, sizeof ( login_req->port_ids ) ); DBGC2 ( srp, "SRP %p TX login request tag %08x%08x\n", srp, ntohl ( login_req->tag.dwords[0] ), ntohl ( login_req->tag.dwords[1] ) ); DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) ); /* Send login request IU */ if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) { DBGC ( srp, "SRP %p could not send login request: %s\n", srp, strerror ( rc ) ); goto err; } return; err: srp_fail ( srp, rc ); }
/** * Seek to position * * @v intf Data transfer interface * @v offset Offset to new position * @ret rc Return status code */ int xfer_seek ( struct interface *intf, off_t offset ) { struct io_buffer *iobuf; struct xfer_metadata meta = { .flags = XFER_FL_ABS_OFFSET, .offset = offset, }; DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " seek to %ld\n", INTF_DBG ( intf ), offset ); /* Allocate and send a zero-length data buffer */ iobuf = xfer_alloc_iob ( intf, 0 ); if ( ! iobuf ) return -ENOMEM; return xfer_deliver ( intf, iobuf, &meta ); }
/** * UDP WRITE * * @v pxenv_udp_write Pointer to a struct s_PXENV_UDP_WRITE * @v s_PXENV_UDP_WRITE::ip Destination IP address * @v s_PXENV_UDP_WRITE::gw Relay agent IP address, or 0.0.0.0 * @v s_PXENV_UDP_WRITE::src_port Source UDP port, or 0 * @v s_PXENV_UDP_WRITE::dst_port Destination UDP port * @v s_PXENV_UDP_WRITE::buffer_size Length of the UDP payload * @v s_PXENV_UDP_WRITE::buffer Address of the UDP payload * @ret #PXENV_EXIT_SUCCESS Packet was transmitted successfully * @ret #PXENV_EXIT_FAILURE Packet could not be transmitted * @ret s_PXENV_UDP_WRITE::Status PXE status code * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open * @err #PXENV_STATUS_UNDI_TRANSMIT_ERROR Could not transmit packet * * Transmits a single UDP packet. A valid IP and UDP header will be * prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer * should not contain precomputed IP and UDP headers, nor should it * contain space allocated for these headers. The first byte of the * buffer will be transmitted as the first byte following the UDP * header. * * If s_PXENV_UDP_WRITE::gw is 0.0.0.0, normal IP routing will take * place. See the relevant @ref pxe_routing "implementation note" for * more details. * * If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used. * * You must have opened a UDP connection with pxenv_udp_open() before * calling pxenv_udp_write(). * * On x86, you must set the s_PXE::StatusCallout field to a nonzero * value before calling this function in protected mode. You cannot * call this function with a 32-bit stack segment. (See the relevant * @ref pxe_x86_pmode16 "implementation note" for more details.) * * @note Etherboot currently ignores the s_PXENV_UDP_WRITE::gw * parameter. * */ PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) { struct sockaddr_in dest; struct xfer_metadata meta = { .src = ( struct sockaddr * ) &pxe_udp.local, .dest = ( struct sockaddr * ) &dest, .netdev = pxe_netdev, }; size_t len; struct io_buffer *iobuf; userptr_t buffer; int rc; DBG ( "PXENV_UDP_WRITE" ); /* Construct destination socket address */ memset ( &dest, 0, sizeof ( dest ) ); dest.sin_family = AF_INET; dest.sin_addr.s_addr = pxenv_udp_write->ip; dest.sin_port = pxenv_udp_write->dst_port; /* Set local (source) port. PXE spec says source port is 2069 * if not specified. Really, this ought to be set at UDP open * time but hey, we didn't design this API. */ pxe_udp.local.sin_port = pxenv_udp_write->src_port; if ( ! pxe_udp.local.sin_port ) pxe_udp.local.sin_port = htons ( 2069 ); /* FIXME: we ignore the gateway specified, since we're * confident of being able to do our own routing. We should * probably allow for multiple gateways. */ /* Allocate and fill data buffer */ len = pxenv_udp_write->buffer_size; iobuf = xfer_alloc_iob ( &pxe_udp.xfer, len ); if ( ! iobuf ) { DBG ( " out of memory\n" ); pxenv_udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES; return PXENV_EXIT_FAILURE; } buffer = real_to_user ( pxenv_udp_write->buffer.segment, pxenv_udp_write->buffer.offset ); copy_from_user ( iob_put ( iobuf, len ), buffer, 0, len ); DBG ( " %04x:%04x+%x %d->%s:%d\n", pxenv_udp_write->buffer.segment, pxenv_udp_write->buffer.offset, pxenv_udp_write->buffer_size, ntohs ( pxenv_udp_write->src_port ), inet_ntoa ( dest.sin_addr ), ntohs ( pxenv_udp_write->dst_port ) ); /* Transmit packet */ if ( ( rc = xfer_deliver ( &pxe_udp.xfer, iobuf, &meta ) ) != 0 ) { DBG ( "PXENV_UDP_WRITE could not transmit: %s\n", strerror ( rc ) ); pxenv_udp_write->Status = PXENV_STATUS ( rc ); return PXENV_EXIT_FAILURE; } pxenv_udp_write->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; } /** * UDP READ * * @v pxenv_udp_read Pointer to a struct s_PXENV_UDP_READ * @v s_PXENV_UDP_READ::dest_ip Destination IP address, or 0.0.0.0 * @v s_PXENV_UDP_READ::d_port Destination UDP port, or 0 * @v s_PXENV_UDP_READ::buffer_size Size of the UDP payload buffer * @v s_PXENV_UDP_READ::buffer Address of the UDP payload buffer * @ret #PXENV_EXIT_SUCCESS A packet has been received * @ret #PXENV_EXIT_FAILURE No packet has been received * @ret s_PXENV_UDP_READ::Status PXE status code * @ret s_PXENV_UDP_READ::src_ip Source IP address * @ret s_PXENV_UDP_READ::dest_ip Destination IP address * @ret s_PXENV_UDP_READ::s_port Source UDP port * @ret s_PXENV_UDP_READ::d_port Destination UDP port * @ret s_PXENV_UDP_READ::buffer_size Length of UDP payload * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open * @err #PXENV_STATUS_FAILURE No packet was ready to read * * Receive a single UDP packet. This is a non-blocking call; if no * packet is ready to read, the call will return instantly with * s_PXENV_UDP_READ::Status==PXENV_STATUS_FAILURE. * * If s_PXENV_UDP_READ::dest_ip is 0.0.0.0, UDP packets addressed to * any IP address will be accepted and may be returned to the caller. * * If s_PXENV_UDP_READ::d_port is 0, UDP packets addressed to any UDP * port will be accepted and may be returned to the caller. * * You must have opened a UDP connection with pxenv_udp_open() before * calling pxenv_udp_read(). * * On x86, you must set the s_PXE::StatusCallout field to a nonzero * value before calling this function in protected mode. You cannot * call this function with a 32-bit stack segment. (See the relevant * @ref pxe_x86_pmode16 "implementation note" for more details.) * * @note The PXE specification (version 2.1) does not state that we * should fill in s_PXENV_UDP_READ::dest_ip and * s_PXENV_UDP_READ::d_port, but Microsoft Windows' NTLDR program * expects us to do so, and will fail if we don't. * */ PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip }; struct in_addr dest_ip; uint16_t d_port_wanted = pxenv_udp_read->d_port; uint16_t d_port; /* Try receiving a packet */ pxe_udp.pxenv_udp_read = pxenv_udp_read; step(); if ( pxe_udp.pxenv_udp_read ) { /* No packet received */ DBG2 ( "PXENV_UDP_READ\n" ); pxe_udp.pxenv_udp_read = NULL; goto no_packet; } dest_ip.s_addr = pxenv_udp_read->dest_ip; d_port = pxenv_udp_read->d_port; DBG ( "PXENV_UDP_READ" ); /* Filter on destination address and/or port */ if ( dest_ip_wanted.s_addr && ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) { DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) ); DBG ( " (wanted %s)\n", inet_ntoa ( dest_ip_wanted ) ); goto no_packet; } if ( d_port_wanted && ( d_port_wanted != d_port ) ) { DBG ( " wrong port %d", htons ( d_port ) ); DBG ( " (wanted %d)\n", htons ( d_port_wanted ) ); goto no_packet; } DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment, pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size, inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) )); DBG ( "%d<-%s:%d\n", ntohs ( pxenv_udp_read->s_port ), inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ), ntohs ( pxenv_udp_read->d_port ) ); pxenv_udp_read->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; no_packet: pxenv_udp_read->Status = PXENV_STATUS_FAILURE; return PXENV_EXIT_FAILURE; }