/**
 * Perform a series of memory copies from a list in low memory
 */
static void shuffle ( unsigned int list_segment, unsigned int list_offset, unsigned int count )
{
	comboot_shuffle_descriptor shuf[COMBOOT_MAX_SHUFFLE_DESCRIPTORS];
	unsigned int i;

	/* Copy shuffle descriptor list so it doesn't get overwritten */
	copy_from_user ( shuf, real_to_user ( list_segment, list_offset ), 0,
	                 count * sizeof( comboot_shuffle_descriptor ) );

	/* Do the copies */
	for ( i = 0; i < count; i++ ) {
		userptr_t src_u = phys_to_user ( shuf[ i ].src );
		userptr_t dest_u = phys_to_user ( shuf[ i ].dest );

		if ( shuf[ i ].src == 0xFFFFFFFF ) {
			/* Fill with 0 instead of copying */
			memset_user ( dest_u, 0, 0, shuf[ i ].len );
		} else if ( shuf[ i ].dest == 0xFFFFFFFF ) {
			/* Copy new list of descriptors */
			count = shuf[ i ].len / sizeof( comboot_shuffle_descriptor );
			assert ( count <= COMBOOT_MAX_SHUFFLE_DESCRIPTORS );
			copy_from_user ( shuf, src_u, 0, shuf[ i ].len );
			i = -1;
		} else {
			/* Regular copy */
			memmove_user ( dest_u, 0, src_u, 0, shuf[ i ].len );
		}
	}
}
Beispiel #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;
}
Beispiel #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;
}
Beispiel #4
0
/**
 * Initialise initrd
 *
 * @ret rc		Return status code
 */
static int initrd_init ( void ) {
	struct image *image;
	int rc;

	/* Do nothing if no initrd was specified */
	if ( ! initrd_phys ) {
		DBGC ( colour, "RUNTIME found no initrd\n" );
		return 0;
	}
	if ( ! initrd_len ) {
		DBGC ( colour, "RUNTIME found empty initrd\n" );
		return 0;
	}
	DBGC ( colour, "RUNTIME found initrd at [%x,%x)\n",
	       initrd_phys, ( initrd_phys + initrd_len ) );

	/* Allocate image */
	image = alloc_image();
	if ( ! image ) {
		DBGC ( colour, "RUNTIME could not allocate image for "
		       "initrd\n" );
		rc = -ENOMEM;
		goto err_alloc_image;
	}
	image_set_name ( image, "<INITRD>" );

	/* Allocate and copy initrd content */
	image->data = umalloc ( initrd_len );
	if ( ! image->data ) {
		DBGC ( colour, "RUNTIME could not allocate %zd bytes for "
		       "initrd\n", initrd_len );
		rc = -ENOMEM;
		goto err_umalloc;
	}
	image->len = initrd_len;
	memcpy_user ( image->data, 0, phys_to_user ( initrd_phys ), 0,
		      initrd_len );

	/* Mark initrd as consumed */
	initrd_phys = 0;

	/* Register image */
	if ( ( rc = register_image ( image ) ) != 0 ) {
		DBGC ( colour, "RUNTIME could not register initrd: %s\n",
		       strerror ( rc ) );
		goto err_register_image;
	}

	/* Drop our reference to the image */
	image_put ( image );

	return 0;

 err_register_image:
 err_umalloc:
	image_put ( image );
 err_alloc_image:
	return rc;
}
Beispiel #5
0
/**
 * Locate ACPI root system description table
 *
 * @ret rsdt		ACPI root system description table, or UNULL
 */
static userptr_t efi_find_rsdt ( void ) {

	/* Locate RSDT via ACPI configuration table, if available */
	if ( rsdp )
		return phys_to_user ( rsdp->RsdtAddress );

	return UNULL;
}
Beispiel #6
0
/**
 * Initialise command line
 *
 * @ret rc		Return status code
 */
static int cmdline_init ( void ) {
	userptr_t cmdline_user;
	char *cmdline;
	size_t len;
	int rc;

	/* Do nothing if no command line was specified */
	if ( ! cmdline_phys ) {
		DBGC ( colour, "RUNTIME found no command line\n" );
		return 0;
	}
	cmdline_user = phys_to_user ( cmdline_phys );
	len = ( strlen_user ( cmdline_user, 0 ) + 1 /* NUL */ );

	/* Allocate and copy command line */
	cmdline_copy = malloc ( len );
	if ( ! cmdline_copy ) {
		DBGC ( colour, "RUNTIME could not allocate %zd bytes for "
		       "command line\n", len );
		rc = -ENOMEM;
		goto err_alloc_cmdline_copy;
	}
	cmdline = cmdline_copy;
	copy_from_user ( cmdline, cmdline_user, 0, len );
	DBGC ( colour, "RUNTIME found command line \"%s\" at %08x\n",
	       cmdline, cmdline_phys );

	/* Mark command line as consumed */
	cmdline_phys = 0;

	/* Strip unwanted cruft from the command line */
	cmdline_strip ( cmdline, "BOOT_IMAGE=" );
	cmdline_strip ( cmdline, "initrd=" );
	while ( isspace ( *cmdline ) )
		cmdline++;
	DBGC ( colour, "RUNTIME using command line \"%s\"\n", cmdline );

	/* Prepare and register image */
	cmdline_image.data = virt_to_user ( cmdline );
	cmdline_image.len = strlen ( cmdline );
	if ( cmdline_image.len ) {
		if ( ( rc = register_image ( &cmdline_image ) ) != 0 ) {
			DBGC ( colour, "RUNTIME could not register command "
			       "line: %s\n", strerror ( rc ) );
			goto err_register_image;
		}
	}

	/* Drop our reference to the image */
	image_put ( &cmdline_image );

	return 0;

 err_register_image:
	image_put ( &cmdline_image );
 err_alloc_cmdline_copy:
	return rc;
}
Beispiel #7
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;
}
Beispiel #8
0
/**
 * Cached DHCPACK startup function
 *
 */
static void cachedhcp_init ( void ) {
	struct dhcp_packet *dhcppkt;
	struct dhcp_packet *tmp;
	struct dhcphdr *dhcphdr;
	size_t len;

	/* Do nothing if no cached DHCPACK is present */
	if ( ! cached_dhcpack_phys ) {
		DBGC ( colour, "CACHEDHCP found no cached DHCPACK\n" );
		return;
	}

	/* No reliable way to determine length before parsing packet;
	 * start by assuming maximum length permitted by PXE.
	 */
	len = sizeof ( BOOTPLAYER_t );

	/* Allocate and populate DHCP packet */
	dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len );
	if ( ! dhcppkt ) {
		DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
		return;
	}
	dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
	copy_from_user ( dhcphdr, phys_to_user ( cached_dhcpack_phys ), 0,
			 len );
	dhcppkt_init ( dhcppkt, dhcphdr, len );

	/* Resize packet to required length.  If reallocation fails,
	 * just continue to use the original packet.
	 */
	len = dhcppkt_len ( dhcppkt );
	tmp = realloc ( dhcppkt, ( sizeof ( *dhcppkt ) + len ) );
	if ( tmp )
		dhcppkt = tmp;

	/* Reinitialise packet at new address */
	dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
	dhcppkt_init ( dhcppkt, dhcphdr, len );

	/* Store as cached DHCPACK, and mark original copy as consumed */
	DBGC ( colour, "CACHEDHCP found cached DHCPACK at %08x+%zx\n",
	       cached_dhcpack_phys, len );
	cached_dhcpack = dhcppkt;
	cached_dhcpack_phys = 0;
}
Beispiel #9
0
/**
 * Load ELF segment into memory
 *
 * @v image		ELF file
 * @v phdr		ELF program header
 * @ret rc		Return status code
 */
static int elf_load_segment ( struct image *image, Elf_Phdr *phdr ) {
	physaddr_t dest;
	userptr_t buffer;
	int rc;

	/* Do nothing for non-PT_LOAD segments */
	if ( phdr->p_type != PT_LOAD )
		return 0;

	/* Check segment lies within image */
	if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
		DBG ( "ELF segment outside ELF file\n" );
		return -ENOEXEC;
	}

	/* Find start address: use physical address for preference,
	 * fall back to virtual address if no physical address
	 * supplied.
	 */
	dest = phdr->p_paddr;
	if ( ! dest )
		dest = phdr->p_vaddr;
	if ( ! dest ) {
		DBG ( "ELF segment loads to physical address 0\n" );
		return -ENOEXEC;
	}
	buffer = phys_to_user ( dest );

	DBG ( "ELF loading segment [%x,%x) to [%x,%x,%x)\n",
	      phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
	      phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ),
	      ( phdr->p_paddr + phdr->p_memsz ) );

	/* Verify and prepare segment */
	if ( ( rc = prep_segment ( buffer, phdr->p_filesz,
				   phdr->p_memsz ) ) != 0 ) {
		DBG ( "ELF could not prepare segment: %s\n", strerror ( rc ) );
		return rc;
	}

	/* Copy image to segment */
	memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );

	return 0;
}
Beispiel #10
0
/**
 * Locate ACPI root system description table
 *
 * @v ebda		Extended BIOS data area, or UNULL
 * @ret rsdt		ACPI root system description table, or UNULL
 */
userptr_t acpi_find_rsdt ( userptr_t ebda ) {
	userptr_t rsdt;

	/* Search EBDA, if applicable */
	if ( ebda ) {
		rsdt = acpi_find_rsdt_range ( ebda, RSDP_EBDA_LEN );
		if ( rsdt )
			return rsdt;
	}

	/* Search fixed BIOS area */
	rsdt = acpi_find_rsdt_range ( phys_to_user ( RSDP_BIOS_START ),
				      RSDP_BIOS_LEN );
	if ( rsdt )
		return rsdt;

	return UNULL;
}
Beispiel #11
0
/**
 * Find SMBIOS
 *
 * @v smbios		SMBIOS entry point descriptor structure to fill in
 * @ret rc		Return status code
 */
static int efi_find_smbios ( struct smbios *smbios ) {

	if ( ! smbios_entry ) {
		DBG ( "No SMBIOS table provided\n" );
		return -ENODEV;
	}

	if ( smbios_entry->signature != SMBIOS_SIGNATURE ) {
		DBG ( "Invalid SMBIOS signature\n" );
		return -ENODEV;
	}

	smbios->address = phys_to_user ( smbios_entry->smbios_address );
	smbios->len = smbios_entry->smbios_len;
	smbios->count = smbios_entry->smbios_count;
	DBG ( "Found SMBIOS v%d.%d entry point at %p (%x+%zx)\n",
	      smbios_entry->major, smbios_entry->minor, smbios_entry,
	      smbios_entry->smbios_address, smbios->len );

	return 0;
}
Beispiel #12
0
/**
 * Load ELF segment into memory
 *
 * @v image		ELF file
 * @v phdr		ELF program header
 * @v ehdr		ELF executable header
 * @ret entry		Entry point, if found
 * @ret max		Maximum used address
 * @ret rc		Return status code
 */
static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
			      Elf_Ehdr *ehdr, physaddr_t *entry,
			      physaddr_t *max ) {
	physaddr_t dest;
	physaddr_t end;
	userptr_t buffer;
	unsigned long e_offset;
	int rc;

	/* Do nothing for non-PT_LOAD segments */
	if ( phdr->p_type != PT_LOAD )
		return 0;

	/* Check segment lies within image */
	if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
		DBGC ( image, "ELF %p segment outside image\n", image );
		return -ENOEXEC;
	}

	/* Find start address: use physical address for preference,
	 * fall back to virtual address if no physical address
	 * supplied.
	 */
	dest = phdr->p_paddr;
	if ( ! dest )
		dest = phdr->p_vaddr;
	if ( ! dest ) {
		DBGC ( image, "ELF %p segment loads to physical address 0\n",
		       image );
		return -ENOEXEC;
	}
	buffer = phys_to_user ( dest );
	end = ( dest + phdr->p_memsz );

	DBGC ( image, "ELF %p loading segment [%x,%x) to [%x,%x,%x)\n", image,
	       phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
	       phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ),
	       ( phdr->p_paddr + phdr->p_memsz ) );

	/* Verify and prepare segment */
	if ( ( rc = prep_segment ( buffer, phdr->p_filesz,
				   phdr->p_memsz ) ) != 0 ) {
		DBGC ( image, "ELF %p could not prepare segment: %s\n",
		       image, strerror ( rc ) );
		return rc;
	}

	/* Update maximum used address, if applicable */
	if ( end > *max )
		*max = end;

	/* Copy image to segment */
	memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );

	/* Set execution address, if it lies within this segment */
	if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) {
		*entry = ehdr->e_entry;
		DBGC ( image, "ELF %p found physical entry point at %lx\n",
		       image, *entry );
	} else if ( ( e_offset = ( ehdr->e_entry - phdr->p_vaddr ) )
		    < phdr->p_filesz ) {
		if ( ! *entry ) {
			*entry = ( dest + e_offset );
			DBGC ( image, "ELF %p found virtual entry point at %lx"
			       " (virt %lx)\n", image, *entry,
			       ( ( unsigned long ) ehdr->e_entry ) );
		}
	}

	return 0;
}
Beispiel #13
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;
}