Example #1
0
/**
 * 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 );
}
Example #2
0
/**
 * Locate ACPI root system description table within a memory range
 *
 * @v start		Start address to search
 * @v len		Length to search
 * @ret rsdt		ACPI root system description table, or UNULL
 */
static userptr_t acpi_find_rsdt_range ( userptr_t start, size_t len ) {
	static const char signature[8] = RSDP_SIGNATURE;
	struct acpi_rsdp rsdp;
	userptr_t rsdt;
	size_t offset;
	uint8_t sum;
	unsigned int i;

	/* Search for RSDP */
	for ( offset = 0 ; ( ( offset + sizeof ( rsdp ) ) < len ) ;
	      offset += RSDP_STRIDE ) {

		/* Check signature and checksum */
		copy_from_user ( &rsdp, start, offset, sizeof ( rsdp ) );
		if ( memcmp ( rsdp.signature, signature,
			      sizeof ( signature ) ) != 0 )
			continue;
		for ( sum = 0, i = 0 ; i < sizeof ( rsdp ) ; i++ )
			sum += *( ( ( uint8_t * ) &rsdp ) + i );
		if ( sum != 0 )
			continue;

		/* Extract RSDT */
		rsdt = phys_to_user ( le32_to_cpu ( rsdp.rsdt ) );
		DBGC ( rsdt, "RSDT %#08lx found via RSDP %#08lx\n",
		       user_to_phys ( rsdt, 0 ),
		       user_to_phys ( start, offset ) );
		return rsdt;
	}

	return UNULL;
}
Example #3
0
/**
 * Extract \_Sx value from DSDT/SSDT
 *
 * @v rsdt		ACPI root system description table
 * @v signature		Signature (e.g. "_S5_")
 * @ret sx		\_Sx value, or negative error
 */
int acpi_sx ( userptr_t rsdt, uint32_t signature ) {
	struct acpi_fadt fadtab;
	userptr_t fadt;
	userptr_t dsdt;
	userptr_t ssdt;
	unsigned int i;
	int sx;

	/* Try DSDT first */
	fadt = acpi_find ( rsdt, FADT_SIGNATURE, 0 );
	if ( fadt ) {
		copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
		dsdt = phys_to_user ( fadtab.dsdt );
		if ( ( sx = acpi_sx_zsdt ( dsdt, signature ) ) >= 0 )
			return sx;
	}

	/* Try all SSDTs */
	for ( i = 0 ; ; i++ ) {
		ssdt = acpi_find ( rsdt, SSDT_SIGNATURE, i );
		if ( ! ssdt )
			break;
		if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 )
			return sx;
	}

	DBGC ( rsdt, "RSDT %#08lx could not find \\_Sx \"%s\"\n",
	       user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
	return -ENOENT;
}
Example #4
0
/**
 * Reallocate external memory
 *
 * @v old_ptr		Memory previously allocated by umalloc(), or UNULL
 * @v new_size		Requested size
 * @ret new_ptr		Allocated memory, or UNULL
 *
 * Calling realloc() with a new size of zero is a valid way to free a
 * memory block.
 */
static userptr_t efi_urealloc ( userptr_t old_ptr, size_t new_size ) {
	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
	EFI_PHYSICAL_ADDRESS phys_addr;
	unsigned int new_pages, old_pages;
	userptr_t new_ptr = UNOWHERE;
	size_t old_size;
	EFI_STATUS efirc;
	int rc;

	/* Allocate new memory if necessary.  If allocation fails,
	 * return without touching the old block.
	 */
	if ( new_size ) {
		new_pages = ( EFI_SIZE_TO_PAGES ( new_size ) + 1 );
		if ( ( efirc = bs->AllocatePages ( AllocateAnyPages,
						   EfiBootServicesData,
						   new_pages,
						   &phys_addr ) ) != 0 ) {
			rc = -EEFI ( efirc );
			DBG ( "EFI could not allocate %d pages: %s\n",
			      new_pages, strerror ( rc ) );
			return UNULL;
		}
		assert ( phys_addr != 0 );
		new_ptr = phys_to_user ( phys_addr + EFI_PAGE_SIZE );
		copy_to_user ( new_ptr, -EFI_PAGE_SIZE,
			       &new_size, sizeof ( new_size ) );
		DBG ( "EFI allocated %d pages at %llx\n",
		      new_pages, phys_addr );
	}

	/* Copy across relevant part of the old data region (if any),
	 * then free it.  Note that at this point either (a) new_ptr
	 * is valid, or (b) new_size is 0; either way, the memcpy() is
	 * valid.
	 */
	if ( old_ptr && ( old_ptr != UNOWHERE ) ) {
		copy_from_user ( &old_size, old_ptr, -EFI_PAGE_SIZE,
				 sizeof ( old_size ) );
		memcpy_user ( new_ptr, 0, old_ptr, 0,
			      ( (old_size < new_size) ? old_size : new_size ));
		old_pages = ( EFI_SIZE_TO_PAGES ( old_size ) + 1 );
		phys_addr = user_to_phys ( old_ptr, -EFI_PAGE_SIZE );
		if ( ( efirc = bs->FreePages ( phys_addr, old_pages ) ) != 0 ){
			rc = -EEFI ( efirc );
			DBG ( "EFI could not free %d pages at %llx: %s\n",
			      old_pages, phys_addr, strerror ( rc ) );
			/* Not fatal; we have leaked memory but successfully
			 * allocated (if asked to do so).
			 */
		}
		DBG ( "EFI freed %d pages at %llx\n", old_pages, phys_addr );
	}

	return new_ptr;
}
Example #5
0
/**
 * Scan for SMBIOS entry point structure
 *
 * @v start		Start address of region to scan
 * @v len		Length of region to scan
 * @v entry		SMBIOS entry point structure to fill in
 * @ret rc		Return status code
 */
int find_smbios_entry ( userptr_t start, size_t len,
			struct smbios_entry *entry ) {
	uint8_t buf[256]; /* 256 is maximum length possible */
	static size_t offset = 0; /* Avoid repeated attempts to locate SMBIOS */
	size_t entry_len;
	unsigned int i;
	uint8_t sum;

	/* Try to find SMBIOS */
	for ( ; offset < len ; offset += 0x10 ) {

		/* Read start of header and verify signature */
		copy_from_user ( entry, start, offset, sizeof ( *entry ) );
		if ( entry->signature != SMBIOS_SIGNATURE )
			continue;

		/* Read whole header and verify checksum */
		entry_len = entry->len;
		assert ( entry_len <= sizeof ( buf ) );
		copy_from_user ( buf, start, offset, entry_len );
		for ( i = 0, sum = 0 ; i < entry_len ; i++ ) {
			sum += buf[i];
		}
		if ( sum != 0 ) {
			DBG ( "SMBIOS at %08lx has bad checksum %02x\n",
			      user_to_phys ( start, offset ), sum );
			continue;
		}

		/* Fill result structure */
		DBG ( "Found SMBIOS v%d.%d entry point at %08lx\n",
		      entry->major, entry->minor,
		      user_to_phys ( start, offset ) );
		return 0;
	}

	DBG ( "No SMBIOS found\n" );
	return -ENODEV;
}
Example #6
0
File: segment.c Project: 42wim/ipxe
/**
 * Prepare segment for loading
 *
 * @v segment		Segment start
 * @v filesz		Size of the "allocated bytes" portion of the segment
 * @v memsz		Size of the segment
 * @ret rc		Return status code
 */
int prep_segment ( userptr_t segment, size_t filesz, size_t memsz ) {
	struct memory_map memmap;
	physaddr_t start = user_to_phys ( segment, 0 );
	physaddr_t mid = user_to_phys ( segment, filesz );
	physaddr_t end = user_to_phys ( segment, memsz );
	unsigned int i;

	DBG ( "Preparing segment [%lx,%lx,%lx)\n", start, mid, end );

	/* Sanity check */
	if ( filesz > memsz ) {
		DBG ( "Insane segment [%lx,%lx,%lx)\n", start, mid, end );
		return -EINVAL;
	}

	/* Get a fresh memory map.  This allows us to automatically
	 * avoid treading on any regions that Etherboot is currently
	 * editing out of the memory map.
	 */
	get_memmap ( &memmap );

	/* Look for a suitable memory region */
	for ( i = 0 ; i < memmap.count ; i++ ) {
		if ( ( start >= memmap.regions[i].start ) &&
		     ( end <= memmap.regions[i].end ) ) {
			/* Found valid region: zero bss and return */
			memset_user ( segment, filesz, 0, ( memsz - filesz ) );
			return 0;
		}
	}

	/* No suitable memory region found */
	DBG ( "Segment [%lx,%lx,%lx) does not fit into available memory\n",
	      start, mid, end );
	return -ERANGE_SEGMENT;
}
Example #7
0
/**
 * Extract \_Sx value from DSDT/SSDT
 *
 * @v zsdt		DSDT or SSDT
 * @v signature		Signature (e.g. "_S5_")
 * @ret sx		\_Sx value, or negative error
 *
 * In theory, extracting the \_Sx value from the DSDT/SSDT requires a
 * full ACPI parser plus some heuristics to work around the various
 * broken encodings encountered in real ACPI implementations.
 *
 * In practice, we can get the same result by scanning through the
 * DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first
 * four bytes, removing any bytes with bit 3 set, and treating
 * whatever is left as a little-endian value.  This is one of the
 * uglier hacks I have ever implemented, but it's still prettier than
 * the ACPI specification itself.
 */
static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
	struct acpi_header acpi;
	union {
		uint32_t dword;
		uint8_t byte[4];
	} buf;
	size_t offset;
	size_t len;
	unsigned int sx;
	uint8_t *byte;

	/* Read table header */
	copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) );
	len = le32_to_cpu ( acpi.length );

	/* Locate signature */
	for ( offset = sizeof ( acpi ) ;
	      ( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */
		  + sizeof ( buf ) /* value */ ) < len ) ;
	      offset++ ) {

		/* Check signature */
		copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) );
		if ( buf.dword != cpu_to_le32 ( signature ) )
			continue;
		DBGC ( zsdt, "DSDT/SSDT %#08lx found %s at offset %#zx\n",
		       user_to_phys ( zsdt, 0 ), acpi_name ( signature ),
		       offset );
		offset += sizeof ( buf );

		/* Read first four bytes of value */
		copy_from_user ( &buf, zsdt, ( offset + 3 /* pkg header */ ),
				 sizeof ( buf ) );
		DBGC ( zsdt, "DSDT/SSDT %#08lx found %s containing "
		       "%02x:%02x:%02x:%02x\n", user_to_phys ( zsdt, 0 ),
		       acpi_name ( signature ), buf.byte[0], buf.byte[1],
		       buf.byte[2], buf.byte[3] );

		/* Extract \Sx value.  There are three potential
		 * encodings that we might encounter:
		 *
		 * - SLP_TYPa, SLP_TYPb, rsvd, rsvd
		 *
		 * - <byteprefix>, SLP_TYPa, <byteprefix>, SLP_TYPb, ...
		 *
		 * - <dwordprefix>, SLP_TYPa, SLP_TYPb, 0, 0
		 *
		 * Since <byteprefix> and <dwordprefix> both have bit
		 * 3 set, and valid SLP_TYPx must have bit 3 clear
		 * (since SLP_TYPx is a 3-bit field), we can just skip
		 * any bytes with bit 3 set.
		 */
		byte = &buf.byte[0];
		if ( *byte & 0x08 )
			byte++;
		sx = *(byte++);
		if ( *byte & 0x08 )
			byte++;
		sx |= ( *byte << 8 );
		return sx;
	}

	return -ENOENT;
}
Example #8
0
/**
 * Locate ACPI table
 *
 * @v rsdt		ACPI root system description table
 * @v signature		Requested table signature
 * @v index		Requested index of table with this signature
 * @ret table		Table, or UNULL if not found
 */
userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ) {
	struct acpi_header acpi;
	struct acpi_rsdt *rsdtab;
	typeof ( rsdtab->entry[0] ) entry;
	userptr_t table;
	size_t len;
	unsigned int count;
	unsigned int i;

	/* Read RSDT header */
	copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) );
	if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) {
		DBGC ( rsdt, "RSDT %#08lx has invalid signature:\n",
		       user_to_phys ( rsdt, 0 ) );
		DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
			   sizeof ( acpi ) );
		return UNULL;
	}
	len = le32_to_cpu ( acpi.length );
	if ( len < sizeof ( rsdtab->acpi ) ) {
		DBGC ( rsdt, "RSDT %#08lx has invalid length:\n",
		       user_to_phys ( rsdt, 0 ) );
		DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
			   sizeof ( acpi ) );
		return UNULL;
	}

	/* Calculate number of entries */
	count = ( ( len - sizeof ( rsdtab->acpi ) ) / sizeof ( entry ) );

	/* Search through entries */
	for ( i = 0 ; i < count ; i++ ) {

		/* Get table address */
		copy_from_user ( &entry, rsdt,
				 offsetof ( typeof ( *rsdtab ), entry[i] ),
				 sizeof ( entry ) );

		/* Read table header */
		table = phys_to_user ( entry );
		copy_from_user ( &acpi.signature, table, 0,
				 sizeof ( acpi.signature ) );

		/* Check table signature */
		if ( acpi.signature != cpu_to_le32 ( signature ) )
			continue;

		/* Check index */
		if ( index-- )
			continue;

		DBGC ( rsdt, "RSDT %#08lx found %s at %08lx\n",
		       user_to_phys ( rsdt, 0 ), acpi_name ( signature ),
		       user_to_phys ( table, 0 ) );
		return table;
	}

	DBGC ( rsdt, "RSDT %#08lx could not find %s\n",
	       user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
	return UNULL;
}