Update the embedded copy of iPXE.

This commit is contained in:
David Anderson 2017-12-24 17:12:15 -08:00
parent 7eee7d4832
commit 18af042ed8
255 changed files with 17717 additions and 5239 deletions

54
third_party/ipxe/.travis.yml vendored Normal file
View File

@ -0,0 +1,54 @@
dist: trusty
sudo: false
language: c
cache: ccache
compiler:
- gcc
addons:
apt:
packages:
- binutils-dev
- liblzma-dev
- syslinux
- genisoimage
coverity_scan:
project:
name: "ipxe/ipxe"
version: $TRAVIS_COMMIT
build_command_prepend: "make -C src bin/deps"
build_command: "make -C src bin/blib.a"
branch_pattern: coverity_scan
env:
global:
- MAKEFLAGS="-j 4"
script:
- make -C src bin/blib.a
- make -C src bin/ipxe.pxe
- make -C src bin/ipxe.usb
- make -C src bin/ipxe.iso
- make -C src bin/8086100e.mrom
- make -C src bin-x86_64-pcbios/blib.a
- make -C src bin-x86_64-pcbios/ipxe.pxe
- make -C src bin-x86_64-pcbios/ipxe.usb
- make -C src bin-x86_64-pcbios/ipxe.iso
- make -C src bin-x86_64-pcbios/8086100e.mrom
- make -C src bin-x86_64-efi/blib.a
- make -C src bin-x86_64-efi/ipxe.efi
- make -C src bin-x86_64-efi/intel.efidrv
- make -C src bin-x86_64-efi/intel.efirom
- make -C src bin-i386-efi/blib.a
- make -C src bin-i386-efi/ipxe.efi
- make -C src bin-i386-efi/intel.efidrv
- make -C src bin-i386-efi/intel.efirom
- make -C src bin-x86_64-linux/blib.a
- make -C src bin-x86_64-linux/tap.linux
- make -C src bin-x86_64-linux/af_packet.linux
- make -C src bin-x86_64-linux/tests.linux
- ./src/bin-x86_64-linux/tests.linux

View File

@ -1 +1 @@
30f96c9f41f2596493c6ca18060bebaaaf44415b
aeffcce44fdf3ebe704b98ef3da8a74d4ec8a521

View File

@ -0,0 +1,21 @@
/*
* Coverity modelling file
*
*/
typedef long off_t;
typedef void * userptr_t;
typedef long long time_t;
struct tm;
/* Inhibit use of built-in models for functions where Coverity's
* assumptions about the modelled function are incorrect for iPXE.
*/
char * strerror ( int errno ) {
}
void copy_from_user ( void *dest, userptr_t src, off_t src_off, size_t len ) {
}
time_t mktime ( struct tm *tm ) {
}
int getchar ( void ) {
}

File diff suppressed because one or more lines are too long

View File

@ -62,7 +62,7 @@ QEMUIMG := qemu-img
SRCDIRS :=
SRCDIRS += libgcc
SRCDIRS += core
SRCDIRS += net net/oncrpc net/tcp net/udp net/infiniband net/80211
SRCDIRS += net net/tcp net/udp net/infiniband
SRCDIRS += image
SRCDIRS += drivers/bus
SRCDIRS += drivers/net
@ -71,13 +71,10 @@ SRCDIRS += drivers/net/e1000e
SRCDIRS += drivers/net/igb
SRCDIRS += drivers/net/igbvf
SRCDIRS += drivers/net/phantom
SRCDIRS += drivers/net/rtl818x
SRCDIRS += drivers/net/ath
SRCDIRS += drivers/net/ath/ath5k
SRCDIRS += drivers/net/ath/ath9k
SRCDIRS += drivers/net/vxge
SRCDIRS += drivers/net/efi
SRCDIRS += drivers/net/tg3
SRCDIRS += drivers/net/sfc
SRCDIRS += drivers/block
SRCDIRS += drivers/nvs
SRCDIRS += drivers/bitbash
@ -89,6 +86,7 @@ SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig
SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac
SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds
SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed
SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu
SRCDIRS += drivers/infiniband/mlx_nodnic/src
SRCDIRS += drivers/usb
SRCDIRS += interface/pxe interface/efi interface/smbios
@ -103,6 +101,16 @@ SRCDIRS += hci/keymap
SRCDIRS += usr
SRCDIRS += config
# These directories contain code that is not eligible for UEFI Secure
# Boot signing.
#
SRCDIRS_INSEC += net/oncrpc
SRCDIRS_INSEC += net/80211
SRCDIRS_INSEC += drivers/net/rtl818x
SRCDIRS_INSEC += drivers/net/ath
SRCDIRS_INSEC += drivers/net/ath/ath5k
SRCDIRS_INSEC += drivers/net/ath/ath9k
# NON_AUTO_SRCS lists files that are excluded from the normal
# automatic build system.
#

View File

@ -299,7 +299,7 @@ endif
#
# Select build architecture and platform based on $(BIN)
#
# BIN has the form bin[-[arch-]platform]
# BIN has the form bin[-[<arch>-]<platform>[-sb]]
ARCHS := $(patsubst arch/%,%,$(wildcard arch/*))
PLATFORMS := $(patsubst config/defaults/%.h,%,\
@ -312,17 +312,18 @@ platforms :
ifdef BIN
# Determine architecture portion of $(BIN), if present
BIN_ARCH := $(strip $(foreach A,$(ARCHS),\
$(patsubst bin-$(A)-%,$(A),\
$(filter bin-$(A)-%,$(BIN)))))
# Determine platform portion of $(BIN), if present
ifeq ($(BIN_ARCH),)
BIN_PLATFORM := $(patsubst bin-%,%,$(filter bin-%,$(BIN)))
# Split $(BIN) into architecture, platform, and security flag (where present)
BIN_ELEMENTS := $(subst -,$(SPACE),$(BIN))
BIN_APS := $(wordlist 2,4,$(BIN_ELEMENTS))
ifeq ($(lastword $(BIN_APS)),sb)
BIN_AP := $(wordlist 2,$(words $(BIN_APS)),discard $(BIN_APS))
BIN_SECUREBOOT := 1
else
BIN_PLATFORM := $(patsubst bin-$(BIN_ARCH)-%,%,$(BIN))
BIN_AP := $(BIN_APS)
BIN_SECUREBOOT := 0
endif
BIN_PLATFORM := $(lastword $(BIN_AP))
BIN_ARCH := $(wordlist 2,$(words $(BIN_AP)),discard $(BIN_AP))
# Determine build architecture
DEFAULT_ARCH := i386
@ -339,6 +340,13 @@ CFLAGS += -DPLATFORM=$(PLATFORM)
platform :
@$(ECHO) $(PLATFORM)
# Determine security flag
DEFAULT_SECUREBOOT := 0
SECUREBOOT := $(firstword $(BIN_SECUREBOOT) $(DEFAULT_SECUREBOOT))
CFLAGS += -DSECUREBOOT=$(SECUREBOOT)
secureboot :
@$(ECHO) $(SECUREBOOT)
endif # defined(BIN)
# Include architecture-specific Makefile
@ -357,6 +365,11 @@ endif
#
# Source file handling
# Exclude known-insecure files from Secure Boot builds
ifeq ($(SECUREBOOT),0)
SRCDIRS += $(SRCDIRS_INSEC)
endif
# SRCDIRS lists all directories containing source files.
srcdirs :
@$(ECHO) $(SRCDIRS)

View File

@ -0,0 +1,12 @@
#ifndef _BITS_ACPI_H
#define _BITS_ACPI_H
/** @file
*
* ARM-specific ACPI API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_ACPI_H */

View File

@ -1,5 +1,9 @@
# -*- makefile -*- : Force emacs to use Makefile mode
# UEFI requires that enums are always 32 bits
#
CFLAGS += -fno-short-enums
# Specify EFI image builder
#
ELF2EFI = $(ELF2EFI32)

View File

@ -4,10 +4,21 @@ SRCDIRS += arch/arm64/core
# ARM64-specific flags
#
CFLAGS += -mabi=lp64 -mlittle-endian -mcmodel=small
CFLAGS += -mlittle-endian -mcmodel=small
CFLAGS += -fomit-frame-pointer
ASFLAGS += -mabi=lp64 -EL
# We want to specify the LP64 model. There is an explicit -mabi=lp64
# on GCC 4.9 and later, and no guarantee as to which is the default
# model. In earlier versions of GCC, there is no -mabi option and the
# default appears to be LP64 anyway.
#
ifeq ($(CCTYPE),gcc)
LP64_TEST = $(CC) -mabi=lp64 -x c -c /dev/null -o /dev/null >/dev/null 2>&1
LP64_FLAGS := $(shell $(LP64_TEST) && $(ECHO) '-mabi=lp64')
WORKAROUND_CFLAGS += $(LP64_FLAGS)
endif
# EFI requires -fshort-wchar, and nothing else currently uses wchar_t
#
CFLAGS += -fshort-wchar

View File

@ -74,10 +74,15 @@ CFLAGS += -Ui386
# recognise an option that starts with "no", so we have to test for
# output on stderr instead of checking the exit status.
#
# Current versions of gcc require -no-pie; older versions require
# -nopie. We therefore test for both.
#
ifeq ($(CCTYPE),gcc)
PIE_TEST = [ -z "`$(CC) -fno-PIE -nopie -x c -c /dev/null -o /dev/null 2>&1`" ]
PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -nopie')
WORKAROUND_CFLAGS += $(PIE_FLAGS)
PIE_TEST = [ -z "`$(CC) -fno-PIE -no-pie -x c -c /dev/null -o /dev/null 2>&1`" ]
PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -no-pie')
PIE_TEST2 = [ -z "`$(CC) -fno-PIE -nopie -x c -c /dev/null -o /dev/null 2>&1`" ]
PIE_FLAGS2 := $(shell $(PIE_TEST2) && $(ECHO) '-fno-PIE -nopie')
WORKAROUND_CFLAGS += $(PIE_FLAGS) $(PIE_FLAGS2)
endif
# i386-specific directories containing source files

View File

@ -13,7 +13,6 @@ SRCDIRS += arch/x86/core
SRCDIRS += arch/x86/image
SRCDIRS += arch/x86/interface/pcbios
SRCDIRS += arch/x86/interface/pxe
SRCDIRS += arch/x86/interface/pxeparent
SRCDIRS += arch/x86/interface/efi
SRCDIRS += arch/x86/interface/vmware
SRCDIRS += arch/x86/interface/syslinux

View File

@ -84,7 +84,7 @@ int cpuid_supported ( uint32_t function ) {
return rc;
/* Find highest supported function number within this family */
cpuid ( ( function & CPUID_EXTENDED ), &max_function, &discard_b,
cpuid ( ( function & CPUID_EXTENDED ), 0, &max_function, &discard_b,
&discard_c, &discard_d );
/* Fail if maximum function number is meaningless (e.g. if we
@ -125,7 +125,7 @@ static void x86_intel_features ( struct x86_features *features ) {
}
/* Get features */
cpuid ( CPUID_FEATURES, &discard_a, &discard_b,
cpuid ( CPUID_FEATURES, 0, &discard_a, &discard_b,
&features->intel.ecx, &features->intel.edx );
DBGC ( features, "CPUID Intel features: %%ecx=%08x, %%edx=%08x\n",
features->intel.ecx, features->intel.edx );
@ -149,7 +149,7 @@ static void x86_amd_features ( struct x86_features *features ) {
}
/* Get features */
cpuid ( CPUID_AMD_FEATURES, &discard_a, &discard_b,
cpuid ( CPUID_AMD_FEATURES, 0, &discard_a, &discard_b,
&features->amd.ecx, &features->amd.edx );
DBGC ( features, "CPUID AMD features: %%ecx=%08x, %%edx=%08x\n",
features->amd.ecx, features->amd.edx );

View File

@ -37,10 +37,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* CPUID settings are numerically encoded as:
*
* Bit 31 Extended function
* Bits 30-28 Unused
* Bits 27-24 Number of consecutive functions to call, minus one
* Bits 30-24 (bit 22 = 1) Subfunction number
* (bit 22 = 0) Number of consecutive functions to call, minus one
* Bit 23 Return result as little-endian (used for strings)
* Bits 22-18 Unused
* Bit 22 Interpret bits 30-24 as a subfunction number
* Bits 21-18 Unused
* Bits 17-16 Number of registers in register array, minus one
* Bits 15-8 Array of register indices. First entry in array is in
* bits 9-8. Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
@ -50,6 +51,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* extracting a single register from a single function to be encoded
* using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
* retrieve the value of %ecx from calling CPUID with %eax=0x80000001.
*
* A subfunction (i.e. an input value for %ecx) may be specified using
* "cpuid/<subfunction>.0x40.<register>.<function>". This slightly
* cumbersome syntax is required in order to maintain backwards
* compatibility with older scripts.
*/
/** CPUID setting tag register indices */
@ -60,12 +66,18 @@ enum cpuid_registers {
CPUID_EDX = 3,
};
/** CPUID setting tag flags */
enum cpuid_flags {
CPUID_LITTLE_ENDIAN = 0x00800000UL,
CPUID_USE_SUBFUNCTION = 0x00400000UL,
};
/**
* Construct CPUID setting tag
*
* @v function Starting function number
* @v num_functions Number of consecutive functions
* @v little_endian Return result as little-endian
* @v subfunction Subfunction, or number of consecutive functions minus 1
* @v flags Flags
* @v num_registers Number of registers in register array
* @v register1 First register in register array (or zero, if empty)
* @v register2 Second register in register array (or zero, if empty)
@ -73,21 +85,13 @@ enum cpuid_registers {
* @v register4 Fourth register in register array (or zero, if empty)
* @ret tag Setting tag
*/
#define CPUID_TAG( function, num_functions, little_endian, num_registers, \
register1, register2, register3, register4 ) \
( (function) | ( ( (num_functions) - 1 ) << 24 ) | \
( (little_endian) << 23 ) | ( ( (num_registers) - 1) << 16 ) | \
( (register1) << 8 ) | ( (register2) << 10 ) | \
#define CPUID_TAG( function, subfunction, flags, num_registers, \
register1, register2, register3, register4 ) \
( (function) | ( (subfunction) << 24 ) | (flags) | \
( ( (num_registers) - 1 ) << 16 ) | \
( (register1) << 8 ) | ( (register2) << 10 ) | \
( (register3) << 12 ) | ( (register4) << 14 ) )
/**
* Extract endianness from CPUID setting tag
*
* @v tag Setting tag
* @ret little_endian Result should be returned as little-endian
*/
#define CPUID_LITTLE_ENDIAN( tag ) ( (tag) & 0x00800000UL )
/**
* Extract starting function number from CPUID setting tag
*
@ -97,12 +101,12 @@ enum cpuid_registers {
#define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
/**
* Extract number of consecutive functions from CPUID setting tag
* Extract subfunction number from CPUID setting tag
*
* @v tag Setting tag
* @ret num_functions Number of consecutive functions
* @ret subfunction Subfunction number
*/
#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0xf ) + 1 )
#define CPUID_SUBFUNCTION( tag ) ( ( (tag) >> 24 ) & 0x7f )
/**
* Extract register array from CPUID setting tag
@ -149,6 +153,7 @@ static int cpuid_settings_fetch ( struct settings *settings,
struct setting *setting,
void *data, size_t len ) {
uint32_t function;
uint32_t subfunction;
uint32_t num_functions;
uint32_t registers;
uint32_t num_registers;
@ -160,7 +165,13 @@ static int cpuid_settings_fetch ( struct settings *settings,
/* Call each function in turn */
function = CPUID_FUNCTION ( setting->tag );
num_functions = CPUID_NUM_FUNCTIONS ( setting->tag );
subfunction = CPUID_SUBFUNCTION ( setting->tag );
if ( setting->tag & CPUID_USE_SUBFUNCTION ) {
num_functions = 1;
} else {
num_functions = ( subfunction + 1 );
subfunction = 0;
}
for ( ; num_functions-- ; function++ ) {
/* Fail if this function is not supported */
@ -171,17 +182,17 @@ static int cpuid_settings_fetch ( struct settings *settings,
}
/* Issue CPUID */
cpuid ( function, &buf[CPUID_EAX], &buf[CPUID_EBX],
&buf[CPUID_ECX], &buf[CPUID_EDX] );
DBGC ( settings, "CPUID %#08x => %#08x:%#08x:%#08x:%#08x\n",
function, buf[0], buf[1], buf[2], buf[3] );
cpuid ( function, subfunction, &buf[CPUID_EAX],
&buf[CPUID_EBX], &buf[CPUID_ECX], &buf[CPUID_EDX] );
DBGC ( settings, "CPUID %#08x:%x => %#08x:%#08x:%#08x:%#08x\n",
function, subfunction, buf[0], buf[1], buf[2], buf[3] );
/* Copy results to buffer */
registers = CPUID_REGISTERS ( setting->tag );
num_registers = CPUID_NUM_REGISTERS ( setting->tag );
for ( ; num_registers-- ; registers >>= 2 ) {
output = buf[ registers & 0x3 ];
if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) )
if ( ! ( setting->tag & CPUID_LITTLE_ENDIAN ) )
output = cpu_to_be32 ( output );
frag_len = sizeof ( output );
if ( frag_len > len )
@ -237,7 +248,7 @@ const struct setting cpuvendor_setting __setting ( SETTING_HOST_EXTRA,
cpuvendor ) = {
.name = "cpuvendor",
.description = "CPU vendor",
.tag = CPUID_TAG ( CPUID_VENDOR_ID, 1, 1, 3,
.tag = CPUID_TAG ( CPUID_VENDOR_ID, 0, CPUID_LITTLE_ENDIAN, 3,
CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
.type = &setting_type_string,
.scope = &cpuid_settings_scope,
@ -248,7 +259,7 @@ const struct setting cpumodel_setting __setting ( SETTING_HOST_EXTRA,
cpumodel ) = {
.name = "cpumodel",
.description = "CPU model",
.tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4,
.tag = CPUID_TAG ( CPUID_MODEL, 2, CPUID_LITTLE_ENDIAN, 4,
CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
.type = &setting_type_string,
.scope = &cpuid_settings_scope,

View File

@ -132,7 +132,7 @@ static int rdtsc_probe ( void ) {
strerror ( rc ) );
return rc;
}
cpuid ( CPUID_APM, &discard_a, &discard_b, &discard_c, &apm );
cpuid ( CPUID_APM, 0, &discard_a, &discard_b, &discard_c, &apm );
if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) {
DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n",
apm );

View File

@ -57,7 +57,7 @@ static void video_scroll(void)
{
int i;
memcpy(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2);
memmove(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2);
for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2)
vidmem[i] = ' ';
}

View File

@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/malloc.h>
#include <ipxe/device.h>
#include <ipxe/timer.h>
#include <ipxe/quiesce.h>
#include <ipxe/cpuid.h>
#include <ipxe/msr.h>
#include <ipxe/hyperv.h>
@ -169,7 +170,7 @@ static int hv_check_hv ( void ) {
}
/* Check that hypervisor is Hyper-V */
cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx,
cpuid ( HV_CPUID_INTERFACE_ID, 0, &interface_id, &discard_ebx,
&discard_ecx, &discard_edx );
if ( interface_id != HV_INTERFACE_ID ) {
DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface "
@ -193,7 +194,7 @@ static int hv_check_features ( struct hv_hypervisor *hv ) {
uint32_t discard_edx;
/* Check that required features and privileges are available */
cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx,
cpuid ( HV_CPUID_FEATURES, 0, &available, &permissions, &discard_ecx,
&discard_edx );
if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) {
DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n",
@ -220,12 +221,34 @@ static int hv_check_features ( struct hv_hypervisor *hv ) {
}
/**
* Map hypercall page
* Check that Gen 2 UEFI firmware is not running
*
* @v hv Hyper-V hypervisor
* @ret rc Return status code
*
* We must not steal ownership from the Gen 2 UEFI firmware, since
* doing so will cause an immediate crash. Avoid this by checking for
* the guest OS identity known to be used by the Gen 2 UEFI firmware.
*/
static int hv_map_hypercall ( struct hv_hypervisor *hv ) {
static int hv_check_uefi ( struct hv_hypervisor *hv ) {
uint64_t guest_os_id;
/* Check for UEFI firmware's guest OS identity */
guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
if ( guest_os_id == HV_GUEST_OS_ID_UEFI ) {
DBGC ( hv, "HV %p is owned by UEFI firmware\n", hv );
return -ENOTSUP;
}
return 0;
}
/**
* Map hypercall page
*
* @v hv Hyper-V hypervisor
*/
static void hv_map_hypercall ( struct hv_hypervisor *hv ) {
union {
struct {
uint32_t ebx;
@ -245,19 +268,18 @@ static int hv_map_hypercall ( struct hv_hypervisor *hv ) {
/* Report guest OS identity */
guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
if ( guest_os_id != 0 ) {
DBGC ( hv, "HV %p guest OS ID MSR already set to %#08llx\n",
DBGC ( hv, "HV %p guest OS ID MSR was %#08llx\n",
hv, guest_os_id );
return -EBUSY;
}
guest_os_id = HV_GUEST_OS_ID_IPXE;
DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
/* Get hypervisor system identity (for debugging) */
cpuid ( HV_CPUID_VENDOR_ID, &discard_eax, &vendor_id.ebx,
cpuid ( HV_CPUID_VENDOR_ID, 0, &discard_eax, &vendor_id.ebx,
&vendor_id.ecx, &vendor_id.edx );
vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0';
cpuid ( HV_CPUID_HYPERVISOR_ID, &build, &version, &discard_ecx,
cpuid ( HV_CPUID_HYPERVISOR_ID, 0, &build, &version, &discard_ecx,
&discard_edx );
DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv,
vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build );
@ -268,8 +290,6 @@ static int hv_map_hypercall ( struct hv_hypervisor *hv ) {
hypercall |= ( virt_to_phys ( hv->hypercall ) | HV_HYPERCALL_ENABLE );
DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );
return 0;
}
/**
@ -297,13 +317,16 @@ static void hv_unmap_hypercall ( struct hv_hypervisor *hv ) {
* Map synthetic interrupt controller
*
* @v hv Hyper-V hypervisor
* @ret rc Return status code
*/
static int hv_map_synic ( struct hv_hypervisor *hv ) {
static void hv_map_synic ( struct hv_hypervisor *hv ) {
uint64_t simp;
uint64_t siefp;
uint64_t scontrol;
/* Zero SynIC message and event pages */
memset ( hv->synic.message, 0, PAGE_SIZE );
memset ( hv->synic.event, 0, PAGE_SIZE );
/* Map SynIC message page */
simp = rdmsr ( HV_X64_MSR_SIMP );
simp &= ( PAGE_SIZE - 1 );
@ -323,26 +346,17 @@ static int hv_map_synic ( struct hv_hypervisor *hv ) {
scontrol |= HV_SCONTROL_ENABLE;
DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
return 0;
}
/**
* Unmap synthetic interrupt controller
* Unmap synthetic interrupt controller, leaving SCONTROL untouched
*
* @v hv Hyper-V hypervisor
*/
static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
uint64_t scontrol;
static void hv_unmap_synic_no_scontrol ( struct hv_hypervisor *hv ) {
uint64_t siefp;
uint64_t simp;
/* Disable SynIC */
scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
scontrol &= ~HV_SCONTROL_ENABLE;
DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
/* Unmap SynIC event page */
siefp = rdmsr ( HV_X64_MSR_SIEFP );
siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE );
@ -356,6 +370,24 @@ static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
wrmsr ( HV_X64_MSR_SIMP, simp );
}
/**
* Unmap synthetic interrupt controller
*
* @v hv Hyper-V hypervisor
*/
static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
uint64_t scontrol;
/* Disable SynIC */
scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
scontrol &= ~HV_SCONTROL_ENABLE;
DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
/* Unmap SynIC event and message pages */
hv_unmap_synic_no_scontrol ( hv );
}
/**
* Enable synthetic interrupt
*
@ -392,8 +424,12 @@ void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) {
unsigned long msr = HV_X64_MSR_SINT ( sintx );
uint64_t sint;
/* Disable synthetic interrupt */
/* Do nothing if interrupt is already disabled */
sint = rdmsr ( msr );
if ( sint & HV_SINT_MASKED )
return;
/* Disable synthetic interrupt */
sint &= ~HV_SINT_AUTO_EOI;
sint |= HV_SINT_MASKED;
DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
@ -543,6 +579,10 @@ static int hv_probe ( struct root_device *rootdev ) {
if ( ( rc = hv_check_features ( hv ) ) != 0 )
goto err_check_features;
/* Check that Gen 2 UEFI firmware is not running */
if ( ( rc = hv_check_uefi ( hv ) ) != 0 )
goto err_check_uefi;
/* Allocate pages */
if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message,
&hv->synic.event, NULL ) ) != 0 )
@ -553,12 +593,10 @@ static int hv_probe ( struct root_device *rootdev ) {
goto err_alloc_message;
/* Map hypercall page */
if ( ( rc = hv_map_hypercall ( hv ) ) != 0 )
goto err_map_hypercall;
hv_map_hypercall ( hv );
/* Map synthetic interrupt controller */
if ( ( rc = hv_map_synic ( hv ) ) != 0 )
goto err_map_synic;
hv_map_synic ( hv );
/* Probe Hyper-V devices */
if ( ( rc = vmbus_probe ( hv, &rootdev->dev ) ) != 0 )
@ -570,14 +608,13 @@ static int hv_probe ( struct root_device *rootdev ) {
vmbus_remove ( hv, &rootdev->dev );
err_vmbus_probe:
hv_unmap_synic ( hv );
err_map_synic:
hv_unmap_hypercall ( hv );
err_map_hypercall:
hv_free_message ( hv );
err_alloc_message:
hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
NULL );
err_alloc_pages:
err_check_uefi:
err_check_features:
free ( hv );
err_alloc:
@ -600,6 +637,7 @@ static void hv_remove ( struct root_device *rootdev ) {
hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
NULL );
free ( hv );
rootdev_set_drvdata ( rootdev, NULL );
}
/** Hyper-V root device driver */
@ -614,6 +652,100 @@ struct root_device hv_root_device __root_device = {
.driver = &hv_root_driver,
};
/**
* Quiesce system
*
*/
static void hv_quiesce ( void ) {
struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
unsigned int i;
/* Do nothing if we are not running in Hyper-V */
if ( ! hv )
return;
/* The "enlightened" portions of the Windows Server 2016 boot
* process will not cleanly take ownership of an active
* Hyper-V connection. Experimentation shows that the minimum
* requirement is that we disable the SynIC message page
* (i.e. zero the SIMP MSR).
*
* We cannot perform a full shutdown of the Hyper-V
* connection. Experimentation shows that if we disable the
* SynIC (i.e. zero the SCONTROL MSR) then Windows Server 2016
* will enter an indefinite wait loop.
*
* Attempt to create a safe handover environment by resetting
* all MSRs except for SCONTROL.
*
* Note that we do not shut down our VMBus devices, since we
* may need to unquiesce the system and continue operation.
*/
/* Disable all synthetic interrupts */
for ( i = 0 ; i <= HV_SINT_MAX ; i++ )
hv_disable_sint ( hv, i );
/* Unmap synthetic interrupt controller, leaving SCONTROL
* enabled (see above).
*/
hv_unmap_synic_no_scontrol ( hv );
/* Unmap hypercall page */
hv_unmap_hypercall ( hv );
DBGC ( hv, "HV %p quiesced\n", hv );
}
/**
* Unquiesce system
*
*/
static void hv_unquiesce ( void ) {
struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
uint64_t simp;
int rc;
/* Do nothing if we are not running in Hyper-V */
if ( ! hv )
return;
/* Experimentation shows that the "enlightened" portions of
* Windows Server 2016 will break our Hyper-V connection at
* some point during a SAN boot. Surprisingly it does not
* change the guest OS ID MSR, but it does leave the SynIC
* message page disabled.
*
* Our own explicit quiescing procedure will also disable the
* SynIC message page. We can therefore use the SynIC message
* page enable bit as a heuristic to determine when we need to
* reestablish our Hyper-V connection.
*/
simp = rdmsr ( HV_X64_MSR_SIMP );
if ( simp & HV_SIMP_ENABLE )
return;
/* Remap hypercall page */
hv_map_hypercall ( hv );
/* Remap synthetic interrupt controller */
hv_map_synic ( hv );
/* Reset Hyper-V devices */
if ( ( rc = vmbus_reset ( hv, &hv_root_device.dev ) ) != 0 ) {
DBGC ( hv, "HV %p could not unquiesce: %s\n",
hv, strerror ( rc ) );
/* Nothing we can do */
return;
}
}
/** Hyper-V quiescer */
struct quiescer hv_quiescer __quiescer = {
.quiesce = hv_quiesce,
.unquiesce = hv_unquiesce,
};
/**
* Probe timer
*
@ -631,7 +763,7 @@ static int hv_timer_probe ( void ) {
return rc;
/* Check for available reference counter */
cpuid ( HV_CPUID_FEATURES, &available, &discard_ebx, &discard_ecx,
cpuid ( HV_CPUID_FEATURES, 0, &available, &discard_ebx, &discard_ecx,
&discard_edx );
if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) {
DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" );

View File

@ -31,7 +31,7 @@ undiisr:
movw %ax, %ds
/* Check that we have an UNDI entry point */
cmpw $0, pxeparent_entry_point
cmpw $0, undinet_entry_point
je chain
/* Issue UNDI API call */
@ -42,7 +42,7 @@ undiisr:
pushw %es
pushw %di
pushw %bx
lcall *pxeparent_entry_point
lcall *undinet_entry_point
cli /* Just in case */
addw $6, %sp
cmpw $PXENV_UNDI_ISR_OUT_OURS, funcflag

View File

@ -36,7 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/profile.h>
#include <undi.h>
#include <undinet.h>
#include <pxeparent.h>
/** @file
*
@ -56,6 +55,12 @@ struct undi_nic {
int hacks;
};
/* Disambiguate the various error causes */
#define EINFO_EPXECALL \
__einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
"External PXE API error" )
#define EPXECALL( status ) EPLATFORM ( EINFO_EPXECALL, status )
/**
* @defgroup undi_hacks UNDI workarounds
* @{
@ -80,29 +85,268 @@ struct undi_nic {
static void undinet_close ( struct net_device *netdev );
/** Address of UNDI entry point */
static SEGOFF16_t undinet_entry;
/**
* UNDI parameter block
*
* Used as the parameter block for all UNDI API calls. Resides in
* base memory.
*/
static union u_PXENV_ANY __bss16 ( undinet_params );
#define undinet_params __use_data16 ( undinet_params )
/** Transmit profiler */
static struct profiler undinet_tx_profiler __profiler =
{ .name = "undinet.tx" };
/** Transmit call profiler */
static struct profiler undinet_tx_call_profiler __profiler =
{ .name = "undinet.tx_call" };
/**
* UNDI entry point
*
* Used as the indirection vector for all UNDI API calls. Resides in
* base memory.
*/
SEGOFF16_t __bss16 ( undinet_entry_point );
#define undinet_entry_point __use_data16 ( undinet_entry_point )
/** IRQ profiler */
static struct profiler undinet_irq_profiler __profiler =
{ .name = "undinet.irq" };
/** ISR call profiler */
static struct profiler undinet_isr_call_profiler __profiler =
{ .name = "undinet.isr_call" };
/** Receive profiler */
static struct profiler undinet_rx_profiler __profiler =
{ .name = "undinet.rx" };
/** A PXE API call breakdown profiler */
struct undinet_profiler {
/** Total time spent performing REAL_CALL() */
struct profiler total;
/** Time spent transitioning to real mode */
struct profiler p2r;
/** Time spent in external code */
struct profiler ext;
/** Time spent transitioning back to protected mode */
struct profiler r2p;
};
/** PXENV_UNDI_TRANSMIT profiler */
static struct undinet_profiler undinet_tx_profiler __profiler = {
{ .name = "undinet.tx" },
{ .name = "undinet.tx_p2r" },
{ .name = "undinet.tx_ext" },
{ .name = "undinet.tx_r2p" },
};
/** PXENV_UNDI_ISR profiler
*
* Note that this profiler will not see calls to
* PXENV_UNDI_ISR_IN_START, which are handled by the UNDI ISR and do
* not go via undinet_call().
*/
static struct undinet_profiler undinet_isr_profiler __profiler = {
{ .name = "undinet.isr" },
{ .name = "undinet.isr_p2r" },
{ .name = "undinet.isr_ext" },
{ .name = "undinet.isr_r2p" },
};
/** PXE unknown API call profiler
*
* This profiler can be used to measure the overhead of a dummy PXE
* API call.
*/
static struct undinet_profiler undinet_unknown_profiler __profiler = {
{ .name = "undinet.unknown" },
{ .name = "undinet.unknown_p2r" },
{ .name = "undinet.unknown_ext" },
{ .name = "undinet.unknown_r2p" },
};
/** Miscellaneous PXE API call profiler */
static struct undinet_profiler undinet_misc_profiler __profiler = {
{ .name = "undinet.misc" },
{ .name = "undinet.misc_p2r" },
{ .name = "undinet.misc_ext" },
{ .name = "undinet.misc_r2p" },
};
/*****************************************************************************
*
* UNDI API call
*
*****************************************************************************
*/
/**
* Name PXE API call
*
* @v function API call number
* @ret name API call name
*/
static inline __attribute__ (( always_inline )) const char *
undinet_function_name ( unsigned int function ) {
switch ( function ) {
case PXENV_START_UNDI:
return "PXENV_START_UNDI";
case PXENV_STOP_UNDI:
return "PXENV_STOP_UNDI";
case PXENV_UNDI_STARTUP:
return "PXENV_UNDI_STARTUP";
case PXENV_UNDI_CLEANUP:
return "PXENV_UNDI_CLEANUP";
case PXENV_UNDI_INITIALIZE:
return "PXENV_UNDI_INITIALIZE";
case PXENV_UNDI_RESET_ADAPTER:
return "PXENV_UNDI_RESET_ADAPTER";
case PXENV_UNDI_SHUTDOWN:
return "PXENV_UNDI_SHUTDOWN";
case PXENV_UNDI_OPEN:
return "PXENV_UNDI_OPEN";
case PXENV_UNDI_CLOSE:
return "PXENV_UNDI_CLOSE";
case PXENV_UNDI_TRANSMIT:
return "PXENV_UNDI_TRANSMIT";
case PXENV_UNDI_SET_MCAST_ADDRESS:
return "PXENV_UNDI_SET_MCAST_ADDRESS";
case PXENV_UNDI_SET_STATION_ADDRESS:
return "PXENV_UNDI_SET_STATION_ADDRESS";
case PXENV_UNDI_SET_PACKET_FILTER:
return "PXENV_UNDI_SET_PACKET_FILTER";
case PXENV_UNDI_GET_INFORMATION:
return "PXENV_UNDI_GET_INFORMATION";
case PXENV_UNDI_GET_STATISTICS:
return "PXENV_UNDI_GET_STATISTICS";
case PXENV_UNDI_CLEAR_STATISTICS:
return "PXENV_UNDI_CLEAR_STATISTICS";
case PXENV_UNDI_INITIATE_DIAGS:
return "PXENV_UNDI_INITIATE_DIAGS";
case PXENV_UNDI_FORCE_INTERRUPT:
return "PXENV_UNDI_FORCE_INTERRUPT";
case PXENV_UNDI_GET_MCAST_ADDRESS:
return "PXENV_UNDI_GET_MCAST_ADDRESS";
case PXENV_UNDI_GET_NIC_TYPE:
return "PXENV_UNDI_GET_NIC_TYPE";
case PXENV_UNDI_GET_IFACE_INFO:
return "PXENV_UNDI_GET_IFACE_INFO";
/*
* Duplicate case value; this is a bug in the PXE specification.
*
* case PXENV_UNDI_GET_STATE:
* return "PXENV_UNDI_GET_STATE";
*/
case PXENV_UNDI_ISR:
return "PXENV_UNDI_ISR";
case PXENV_GET_CACHED_INFO:
return "PXENV_GET_CACHED_INFO";
default:
return "UNKNOWN API CALL";
}
}
/**
* Determine applicable profiler pair (for debugging)
*
* @v function API call number
* @ret profiler Profiler
*/
static struct undinet_profiler * undinet_profiler ( unsigned int function ) {
/* Determine applicable profiler */
switch ( function ) {
case PXENV_UNDI_TRANSMIT:
return &undinet_tx_profiler;
case PXENV_UNDI_ISR:
return &undinet_isr_profiler;
case PXENV_UNKNOWN:
return &undinet_unknown_profiler;
default:
return &undinet_misc_profiler;
}
}
/**
* Issue UNDI API call
*
* @v undinic UNDI NIC
* @v function API call number
* @v params PXE parameter block
* @v params_len Length of PXE parameter block
* @ret rc Return status code
*/
static int undinet_call ( struct undi_nic *undinic, unsigned int function,
void *params, size_t params_len ) {
struct undinet_profiler *profiler = undinet_profiler ( function );
PXENV_EXIT_t exit;
uint32_t before;
uint32_t started;
uint32_t stopped;
uint32_t after;
int discard_D;
int rc;
/* Copy parameter block and entry point */
assert ( params_len <= sizeof ( undinet_params ) );
memcpy ( &undinet_params, params, params_len );
/* Call real-mode entry point. This calling convention will
* work with both the !PXE and the PXENV+ entry points.
*/
profile_start ( &profiler->total );
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"rdtsc\n\t"
"pushl %%eax\n\t"
"pushw %%es\n\t"
"pushw %%di\n\t"
"pushw %%bx\n\t"
"lcall *undinet_entry_point\n\t"
"movw %%ax, %%bx\n\t"
"rdtsc\n\t"
"addw $6, %%sp\n\t"
"popl %%edx\n\t"
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( stopped ), "=d" ( started ),
"=b" ( exit ), "=D" ( discard_D )
: "b" ( function ),
"D" ( __from_data16 ( &undinet_params ) )
: "ecx", "esi" );
profile_stop ( &profiler->total );
before = profile_started ( &profiler->total );
after = profile_stopped ( &profiler->total );
profile_start_at ( &profiler->p2r, before );
profile_stop_at ( &profiler->p2r, started );
profile_start_at ( &profiler->ext, started );
profile_stop_at ( &profiler->ext, stopped );
profile_start_at ( &profiler->r2p, stopped );
profile_stop_at ( &profiler->r2p, after );
/* Determine return status code based on PXENV_EXIT and
* PXENV_STATUS
*/
rc = ( ( exit == PXENV_EXIT_SUCCESS ) ?
0 : -EPXECALL ( undinet_params.Status ) );
/* If anything goes wrong, print as much debug information as
* it's possible to give.
*/
if ( rc != 0 ) {
SEGOFF16_t rm_params = {
.segment = rm_ds,
.offset = __from_data16 ( &undinet_params ),
};
DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic,
undinet_function_name ( function ), strerror ( rc ) );
DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length "
"%#02zx, entry point at %04x:%04x\n", undinic,
rm_params.segment, rm_params.offset, params_len,
undinet_entry_point.segment,
undinet_entry_point.offset );
DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic );
DBGC_HDA ( undinic, rm_params, params, params_len );
DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic );
DBGC_HDA ( undinic, rm_params, &undinet_params, params_len );
}
/* Copy parameter block back */
memcpy ( params, &undinet_params, params_len );
return rc;
}
/*****************************************************************************
*
* UNDI interrupt service routine
@ -216,9 +460,6 @@ static int undinet_transmit ( struct net_device *netdev,
size_t len;
int rc;
/* Start profiling */
profile_start ( &undinet_tx_profiler );
/* Technically, we ought to make sure that the previous
* transmission has completed before we re-use the buffer.
* However, many PXE stacks (including at least some Intel PXE
@ -281,16 +522,12 @@ static int undinet_transmit ( struct net_device *netdev,
undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
/* Issue PXE API call */
profile_start ( &undinet_tx_call_profiler );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_TRANSMIT,
&undi_transmit,
sizeof ( undi_transmit ) ) ) != 0 )
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT, &undi_transmit,
sizeof ( undi_transmit ) ) ) != 0 )
goto done;
profile_stop ( &undinet_tx_call_profiler );
/* Free I/O buffer */
netdev_tx_complete ( netdev, iobuf );
profile_stop ( &undinet_tx_profiler );
done:
return rc;
}
@ -369,14 +606,11 @@ static void undinet_poll ( struct net_device *netdev ) {
/* Run through the ISR loop */
while ( quota ) {
profile_start ( &undinet_isr_call_profiler );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
&undi_isr,
sizeof ( undi_isr ) ) ) != 0 ) {
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
sizeof ( undi_isr ) ) ) != 0 ) {
netdev_rx_err ( netdev, NULL, rc );
break;
}
profile_stop ( &undinet_isr_call_profiler );
switch ( undi_isr.FuncFlag ) {
case PXENV_UNDI_ISR_OUT_TRANSMIT:
/* We don't care about transmit completions */
@ -480,8 +714,8 @@ static int undinet_open ( struct net_device *netdev ) {
*/
memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
sizeof ( undi_set_address.StationAddress ) );
pxeparent_call ( undinet_entry, PXENV_UNDI_SET_STATION_ADDRESS,
&undi_set_address, sizeof ( undi_set_address ) );
undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
&undi_set_address, sizeof ( undi_set_address ) );
/* Open NIC. We ask for promiscuous operation, since it's the
* only way to ask for all multicast addresses. On any
@ -490,8 +724,8 @@ static int undinet_open ( struct net_device *netdev ) {
*/
memset ( &undi_open, 0, sizeof ( undi_open ) );
undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_OPEN,
&undi_open, sizeof ( undi_open ) ) ) != 0 )
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
sizeof ( undi_open ) ) ) != 0 )
goto err;
DBGC ( undinic, "UNDINIC %p opened\n", undinic );
@ -516,9 +750,8 @@ static void undinet_close ( struct net_device *netdev ) {
/* Ensure ISR has exited cleanly */
while ( undinic->isr_processing ) {
undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
&undi_isr,
sizeof ( undi_isr ) ) ) != 0 )
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
sizeof ( undi_isr ) ) ) != 0 )
break;
switch ( undi_isr.FuncFlag ) {
case PXENV_UNDI_ISR_OUT_TRANSMIT:
@ -533,8 +766,8 @@ static void undinet_close ( struct net_device *netdev ) {
}
/* Close NIC */
pxeparent_call ( undinet_entry, PXENV_UNDI_CLOSE,
&undi_close, sizeof ( undi_close ) );
undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
sizeof ( undi_close ) );
/* Disable interrupt and unhook ISR if applicable */
if ( undinic->irq ) {
@ -647,7 +880,7 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) {
undi_set_drvdata ( undi, netdev );
netdev->dev = dev;
memset ( undinic, 0, sizeof ( *undinic ) );
undinet_entry = undi->entry;
undinet_entry_point = undi->entry;
DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
/* Hook in UNDI stack */
@ -658,9 +891,9 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) {
start_undi.DX = undi->isapnp_read_port;
start_undi.ES = BIOS_SEG;
start_undi.DI = find_pnp_bios();
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_START_UNDI,
&start_undi,
sizeof ( start_undi ) ) ) != 0 )
if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI,
&start_undi,
sizeof ( start_undi ) ) ) != 0 )
goto err_start_undi;
}
undi->flags |= UNDI_FL_STARTED;
@ -668,9 +901,9 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) {
/* Bring up UNDI stack */
if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
memset ( &undi_startup, 0, sizeof ( undi_startup ) );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_STARTUP,
&undi_startup,
sizeof ( undi_startup ) ) ) != 0 )
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP,
&undi_startup,
sizeof ( undi_startup ) ) ) != 0 )
goto err_undi_startup;
/* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail
* due to a transient condition (e.g. media test
@ -680,10 +913,10 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) {
*/
for ( retry = 0 ; ; ) {
memset ( &undi_init, 0, sizeof ( undi_init ) );
if ( ( rc = pxeparent_call ( undinet_entry,
PXENV_UNDI_INITIALIZE,
&undi_init,
sizeof ( undi_init ))) ==0)
if ( ( rc = undinet_call ( undinic,
PXENV_UNDI_INITIALIZE,
&undi_init,
sizeof ( undi_init ) ) ) ==0)
break;
if ( ++retry > UNDI_INITIALIZE_RETRY_MAX )
goto err_undi_initialize;
@ -698,8 +931,8 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) {
/* Get device information */
memset ( &undi_info, 0, sizeof ( undi_info ) );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_INFORMATION,
&undi_info, sizeof ( undi_info ) ) ) != 0 )
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
&undi_info, sizeof ( undi_info ) ) ) != 0 )
goto err_undi_get_information;
memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN );
@ -715,9 +948,8 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) {
/* Get interface information */
memset ( &undi_iface, 0, sizeof ( undi_iface ) );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_IFACE_INFO,
&undi_iface,
sizeof ( undi_iface ) ) ) != 0 )
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
&undi_iface, sizeof ( undi_iface ) ) ) != 0 )
goto err_undi_get_iface_info;
DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
@ -757,17 +989,17 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) {
err_undi_initialize:
/* Shut down UNDI stack */
memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
sizeof ( undi_shutdown ) );
undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
sizeof ( undi_shutdown ) );
memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP, &undi_cleanup,
sizeof ( undi_cleanup ) );
undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
sizeof ( undi_cleanup ) );
undi->flags &= ~UNDI_FL_INITIALIZED;
err_undi_startup:
/* Unhook UNDI stack */
memset ( &stop_undi, 0, sizeof ( stop_undi ) );
pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
sizeof ( stop_undi ) );
undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
sizeof ( stop_undi ) );
undi->flags &= ~UNDI_FL_STARTED;
err_start_undi:
netdev_nullify ( netdev );
@ -798,22 +1030,22 @@ void undinet_remove ( struct undi_device *undi ) {
/* Shut down UNDI stack */
memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN,
&undi_shutdown, sizeof ( undi_shutdown ) );
undinet_call ( undinic, PXENV_UNDI_SHUTDOWN,
&undi_shutdown, sizeof ( undi_shutdown ) );
memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP,
&undi_cleanup, sizeof ( undi_cleanup ) );
undinet_call ( undinic, PXENV_UNDI_CLEANUP,
&undi_cleanup, sizeof ( undi_cleanup ) );
undi->flags &= ~UNDI_FL_INITIALIZED;
/* Unhook UNDI stack */
memset ( &stop_undi, 0, sizeof ( stop_undi ) );
pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
sizeof ( stop_undi ) );
undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
sizeof ( stop_undi ) );
undi->flags &= ~UNDI_FL_STARTED;
}
/* Clear entry point */
memset ( &undinet_entry, 0, sizeof ( undinet_entry ) );
memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) );
/* Free network device */
netdev_nullify ( netdev );

View File

@ -66,12 +66,12 @@ static int hvm_cpuid_base ( struct hvm_device *hvm ) {
/* Scan for magic signature */
for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ;
base += HVM_CPUID_STEP ) {
cpuid ( base, &discard_eax, &signature.ebx, &signature.ecx,
cpuid ( base, 0, &discard_eax, &signature.ebx, &signature.ecx,
&signature.edx );
if ( memcmp ( &signature, HVM_CPUID_MAGIC,
sizeof ( signature ) ) == 0 ) {
hvm->cpuid_base = base;
cpuid ( ( base + HVM_CPUID_VERSION ), &version,
cpuid ( ( base + HVM_CPUID_VERSION ), 0, &version,
&discard_ebx, &discard_ecx, &discard_edx );
DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n",
base, ( version >> 16 ), ( version & 0xffff ) );
@ -101,7 +101,7 @@ static int hvm_map_hypercall ( struct hvm_device *hvm ) {
int rc;
/* Get number of hypercall pages and MSR to use */
cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), &pages, &msr,
cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), 0, &pages, &msr,
&discard_ecx, &discard_edx );
/* Allocate pages */

View File

@ -282,9 +282,11 @@ static int bzimage_parse_cmdline ( struct image *image,
case 'G':
case 'g':
bzimg->mem_limit <<= 10;
/* Fall through */
case 'M':
case 'm':
bzimg->mem_limit <<= 10;
/* Fall through */
case 'K':
case 'k':
bzimg->mem_limit <<= 10;

View File

@ -0,0 +1,14 @@
#ifndef _BITS_ACPI_H
#define _BITS_ACPI_H
/** @file
*
* x86-specific ACPI API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/rsdp.h>
#endif /* _BITS_ACPI_H */

View File

@ -66,19 +66,20 @@ struct x86_features {
/**
* Issue CPUID instruction
*
* @v function CPUID function
* @v function CPUID function (input via %eax)
* @v subfunction CPUID subfunction (input via %ecx)
* @v eax Output via %eax
* @v ebx Output via %ebx
* @v ecx Output via %ecx
* @v edx Output via %edx
*/
static inline __attribute__ (( always_inline )) void
cpuid ( uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
uint32_t *edx ) {
cpuid ( uint32_t function, uint32_t subfunction, uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx ) {
__asm__ ( "cpuid"
: "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx )
: "0" ( function ) );
: "0" ( function ), "2" ( subfunction ) );
}
extern int cpuid_supported ( uint32_t function );

View File

@ -0,0 +1,18 @@
#ifndef _IPXE_RSDP_H
#define _IPXE_RSDP_H
/** @file
*
* Standard PC-BIOS ACPI RSDP interface
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef ACPI_RSDP
#define ACPI_PREFIX_rsdp
#else
#define ACPI_PREFIX_rsdp __rsdp_
#endif
#endif /* _IPXE_RSDP_H */

View File

@ -1,11 +0,0 @@
#ifndef PXEPARENT_H
#define PXEPARENT_H
FILE_LICENCE ( GPL2_OR_LATER );
#include <pxe_types.h>
extern int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
void *params, size_t params_len );
#endif

View File

@ -26,8 +26,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <unistd.h>
#include <errno.h>
#include <byteswap.h>
#include <realmode.h>
#include <bios.h>
#include <ipxe/io.h>
#include <ipxe/acpi.h>
#include <ipxe/acpipwr.h>
@ -51,8 +49,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
int acpi_poweroff ( void ) {
struct acpi_fadt fadtab;
uint16_t ebda;
userptr_t rsdt;
userptr_t fadt;
unsigned int pm1a_cnt_blk;
unsigned int pm1b_cnt_blk;
@ -63,18 +59,8 @@ int acpi_poweroff ( void ) {
int s5;
int rc;
/* Locate EBDA */
get_real ( ebda, BDA_SEG, BDA_EBDA );
/* Locate RSDT */
rsdt = acpi_find_rsdt ( real_to_user ( ebda, 0 ) );
if ( ! rsdt ) {
DBGC ( colour, "ACPI could not find RSDT (EBDA %04x)\n", ebda );
return -ENOENT;
}
/* Locate FADT */
fadt = acpi_find ( rsdt, FADT_SIGNATURE, 0 );
fadt = acpi_find ( FADT_SIGNATURE, 0 );
if ( ! fadt ) {
DBGC ( colour, "ACPI could not find FADT\n" );
return -ENOENT;
@ -88,7 +74,7 @@ int acpi_poweroff ( void ) {
pm1b_cnt = ( pm1b_cnt_blk + ACPI_PM1_CNT );
/* Extract \_S5 from DSDT or any SSDT */
s5 = acpi_sx ( rsdt, S5_SIGNATURE );
s5 = acpi_sx ( S5_SIGNATURE );
if ( s5 < 0 ) {
rc = s5;
DBGC ( colour, "ACPI could not extract \\_S5: %s\n",

File diff suppressed because it is too large Load Diff

View File

@ -62,6 +62,10 @@ struct int13con_header {
/** Log partition magic signature */
#define INT13CON_MAGIC "iPXE LOG\n\n"
/** Original INT13 vector */
static struct segoff __bss16 ( int13con_vector );
#define int13con_vector __use_data16 ( int13con_vector )
/** Sector buffer */
static uint8_t __bss16_array ( int13con_buffer, [INT13_BLKSIZE] );
#define int13con_buffer __use_data16 ( int13con_buffer )
@ -101,8 +105,13 @@ static int int13con_rw ( unsigned int op, uint64_t lba ) {
int13con_address.buffer.offset = __from_data16 ( int13con_buffer );
int13con_address.lba = lba;
/* Issue INT13 */
__asm__ ( REAL_CODE ( "int $0x13\n\t" )
/* Emulate INT13 via original vector. We do this since iPXE
* (or another subsequent bootloader) may hook INT13 and remap
* drive numbers.
*/
__asm__ ( REAL_CODE ( "pushfw\n\t"
"cli\n\t"
"lcall *int13con_vector\n\t" )
: "=a" ( error )
: "0" ( op << 8 ), "d" ( INT13CON_DRIVE ),
"S" ( __from_data16 ( &int13con_address ) ) );
@ -261,6 +270,12 @@ static void int13con_init ( void ) {
return;
}
/* Store original INT13 vector */
copy_from_real ( &int13con_vector, 0, ( 0x13 * 4 ),
sizeof ( int13con_vector ) );
DBG ( "INT13CON using original INT13 vector %04x:%04x\n",
int13con_vector.segment, int13con_vector.offset );
/* Locate log partition */
if ( ( rc = int13con_find() ) != 0)
return;

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* ACPI Root System Description Pointer
*
*/
#include <stdint.h>
#include <realmode.h>
#include <bios.h>
#include <ipxe/acpi.h>
#include <ipxe/rsdp.h>
/** EBDA RSDP maximum segment */
#define RSDP_EBDA_END_SEG 0xa000
/** Fixed BIOS area RSDP start address */
#define RSDP_BIOS_START 0xe0000
/** Fixed BIOS area RSDP length */
#define RSDP_BIOS_LEN 0x20000
/** Stride at which to search for RSDP */
#define RSDP_STRIDE 16
/**
* 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 rsdp_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;
}
/**
* Locate ACPI root system description table
*
* @ret rsdt ACPI root system description table, or UNULL
*/
static userptr_t rsdp_find_rsdt ( void ) {
static userptr_t rsdt;
uint16_t ebda_seg;
userptr_t ebda;
size_t ebda_len;
/* Return existing RSDT if already found */
if ( rsdt )
return rsdt;
/* Search EBDA */
get_real ( ebda_seg, BDA_SEG, BDA_EBDA );
if ( ebda_seg < RSDP_EBDA_END_SEG ) {
ebda = real_to_user ( ebda_seg, 0 );
ebda_len = ( ( RSDP_EBDA_END_SEG - ebda_seg ) * 16 );
rsdt = rsdp_find_rsdt_range ( ebda, ebda_len );
if ( rsdt )
return rsdt;
}
/* Search fixed BIOS area */
rsdt = rsdp_find_rsdt_range ( phys_to_user ( RSDP_BIOS_START ),
RSDP_BIOS_LEN );
if ( rsdt )
return rsdt;
return UNULL;
}
PROVIDE_ACPI ( rsdp, acpi_find_rsdt, rsdp_find_rsdt );

View File

@ -1,287 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/dhcp.h>
#include <ipxe/profile.h>
#include <pxeparent.h>
#include <pxe_api.h>
#include <pxe_types.h>
#include <pxe.h>
/** @file
*
* Call interface to parent PXE stack
*
*/
/* Disambiguate the various error causes */
#define EINFO_EPXECALL \
__einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
"External PXE API error" )
#define EPXECALL( status ) EPLATFORM ( EINFO_EPXECALL, status )
/** A parent PXE API call profiler */
struct pxeparent_profiler {
/** Total time spent performing REAL_CALL() */
struct profiler total;
/** Time spent transitioning to real mode */
struct profiler p2r;
/** Time spent in external code */
struct profiler ext;
/** Time spent transitioning back to protected mode */
struct profiler r2p;
};
/** PXENV_UNDI_TRANSMIT profiler */
static struct pxeparent_profiler pxeparent_tx_profiler __profiler = {
{ .name = "pxeparent.tx" },
{ .name = "pxeparent.tx_p2r" },
{ .name = "pxeparent.tx_ext" },
{ .name = "pxeparent.tx_r2p" },
};
/** PXENV_UNDI_ISR profiler
*
* Note that this profiler will not see calls to
* PXENV_UNDI_ISR_IN_START, which are handled by the UNDI ISR and do
* not go via pxeparent_call().
*/
static struct pxeparent_profiler pxeparent_isr_profiler __profiler = {
{ .name = "pxeparent.isr" },
{ .name = "pxeparent.isr_p2r" },
{ .name = "pxeparent.isr_ext" },
{ .name = "pxeparent.isr_r2p" },
};
/** PXE unknown API call profiler
*
* This profiler can be used to measure the overhead of a dummy PXE
* API call.
*/
static struct pxeparent_profiler pxeparent_unknown_profiler __profiler = {
{ .name = "pxeparent.unknown" },
{ .name = "pxeparent.unknown_p2r" },
{ .name = "pxeparent.unknown_ext" },
{ .name = "pxeparent.unknown_r2p" },
};
/** Miscellaneous PXE API call profiler */
static struct pxeparent_profiler pxeparent_misc_profiler __profiler = {
{ .name = "pxeparent.misc" },
{ .name = "pxeparent.misc_p2r" },
{ .name = "pxeparent.misc_ext" },
{ .name = "pxeparent.misc_r2p" },
};
/**
* Name PXE API call
*
* @v function API call number
* @ret name API call name
*/
static inline __attribute__ (( always_inline )) const char *
pxeparent_function_name ( unsigned int function ) {
switch ( function ) {
case PXENV_START_UNDI:
return "PXENV_START_UNDI";
case PXENV_STOP_UNDI:
return "PXENV_STOP_UNDI";
case PXENV_UNDI_STARTUP:
return "PXENV_UNDI_STARTUP";
case PXENV_UNDI_CLEANUP:
return "PXENV_UNDI_CLEANUP";
case PXENV_UNDI_INITIALIZE:
return "PXENV_UNDI_INITIALIZE";
case PXENV_UNDI_RESET_ADAPTER:
return "PXENV_UNDI_RESET_ADAPTER";
case PXENV_UNDI_SHUTDOWN:
return "PXENV_UNDI_SHUTDOWN";
case PXENV_UNDI_OPEN:
return "PXENV_UNDI_OPEN";
case PXENV_UNDI_CLOSE:
return "PXENV_UNDI_CLOSE";
case PXENV_UNDI_TRANSMIT:
return "PXENV_UNDI_TRANSMIT";
case PXENV_UNDI_SET_MCAST_ADDRESS:
return "PXENV_UNDI_SET_MCAST_ADDRESS";
case PXENV_UNDI_SET_STATION_ADDRESS:
return "PXENV_UNDI_SET_STATION_ADDRESS";
case PXENV_UNDI_SET_PACKET_FILTER:
return "PXENV_UNDI_SET_PACKET_FILTER";
case PXENV_UNDI_GET_INFORMATION:
return "PXENV_UNDI_GET_INFORMATION";
case PXENV_UNDI_GET_STATISTICS:
return "PXENV_UNDI_GET_STATISTICS";
case PXENV_UNDI_CLEAR_STATISTICS:
return "PXENV_UNDI_CLEAR_STATISTICS";
case PXENV_UNDI_INITIATE_DIAGS:
return "PXENV_UNDI_INITIATE_DIAGS";
case PXENV_UNDI_FORCE_INTERRUPT:
return "PXENV_UNDI_FORCE_INTERRUPT";
case PXENV_UNDI_GET_MCAST_ADDRESS:
return "PXENV_UNDI_GET_MCAST_ADDRESS";
case PXENV_UNDI_GET_NIC_TYPE:
return "PXENV_UNDI_GET_NIC_TYPE";
case PXENV_UNDI_GET_IFACE_INFO:
return "PXENV_UNDI_GET_IFACE_INFO";
/*
* Duplicate case value; this is a bug in the PXE specification.
*
* case PXENV_UNDI_GET_STATE:
* return "PXENV_UNDI_GET_STATE";
*/
case PXENV_UNDI_ISR:
return "PXENV_UNDI_ISR";
case PXENV_GET_CACHED_INFO:
return "PXENV_GET_CACHED_INFO";
default:
return "UNKNOWN API CALL";
}
}
/**
* Determine applicable profiler pair (for debugging)
*
* @v function API call number
* @ret profiler Profiler
*/
static struct pxeparent_profiler * pxeparent_profiler ( unsigned int function ){
/* Determine applicable profiler */
switch ( function ) {
case PXENV_UNDI_TRANSMIT:
return &pxeparent_tx_profiler;
case PXENV_UNDI_ISR:
return &pxeparent_isr_profiler;
case PXENV_UNKNOWN:
return &pxeparent_unknown_profiler;
default:
return &pxeparent_misc_profiler;
}
}
/**
* PXE parent parameter block
*
* Used as the parameter block for all parent PXE API calls. Resides
* in base memory.
*/
static union u_PXENV_ANY __bss16 ( pxeparent_params );
#define pxeparent_params __use_data16 ( pxeparent_params )
/** PXE parent entry point
*
* Used as the indirection vector for all parent PXE API calls. Resides in
* base memory.
*/
SEGOFF16_t __bss16 ( pxeparent_entry_point );
#define pxeparent_entry_point __use_data16 ( pxeparent_entry_point )
/**
* Issue parent PXE API call
*
* @v entry Parent PXE stack entry point
* @v function API call number
* @v params PXE parameter block
* @v params_len Length of PXE parameter block
* @ret rc Return status code
*/
int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
void *params, size_t params_len ) {
struct pxeparent_profiler *profiler = pxeparent_profiler ( function );
PXENV_EXIT_t exit;
uint32_t before;
uint32_t started;
uint32_t stopped;
uint32_t after;
int discard_D;
int rc;
/* Copy parameter block and entry point */
assert ( params_len <= sizeof ( pxeparent_params ) );
memcpy ( &pxeparent_params, params, params_len );
memcpy ( &pxeparent_entry_point, &entry, sizeof ( entry ) );
/* Call real-mode entry point. This calling convention will
* work with both the !PXE and the PXENV+ entry points.
*/
profile_start ( &profiler->total );
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"rdtsc\n\t"
"pushl %%eax\n\t"
"pushw %%es\n\t"
"pushw %%di\n\t"
"pushw %%bx\n\t"
"lcall *pxeparent_entry_point\n\t"
"movw %%ax, %%bx\n\t"
"rdtsc\n\t"
"addw $6, %%sp\n\t"
"popl %%edx\n\t"
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( stopped ), "=d" ( started ),
"=b" ( exit ), "=D" ( discard_D )
: "b" ( function ),
"D" ( __from_data16 ( &pxeparent_params ) )
: "ecx", "esi" );
profile_stop ( &profiler->total );
before = profile_started ( &profiler->total );
after = profile_stopped ( &profiler->total );
profile_start_at ( &profiler->p2r, before );
profile_stop_at ( &profiler->p2r, started );
profile_start_at ( &profiler->ext, started );
profile_stop_at ( &profiler->ext, stopped );
profile_start_at ( &profiler->r2p, stopped );
profile_stop_at ( &profiler->r2p, after );
/* Determine return status code based on PXENV_EXIT and
* PXENV_STATUS
*/
rc = ( ( exit == PXENV_EXIT_SUCCESS ) ?
0 : -EPXECALL ( pxeparent_params.Status ) );
/* If anything goes wrong, print as much debug information as
* it's possible to give.
*/
if ( rc != 0 ) {
SEGOFF16_t rm_params = {
.segment = rm_ds,
.offset = __from_data16 ( &pxeparent_params ),
};
DBG ( "PXEPARENT %s failed: %s\n",
pxeparent_function_name ( function ), strerror ( rc ) );
DBG ( "PXEPARENT parameters at %04x:%04x length "
"%#02zx, entry point at %04x:%04x\n",
rm_params.segment, rm_params.offset, params_len,
pxeparent_entry_point.segment,
pxeparent_entry_point.offset );
DBG ( "PXEPARENT parameters provided:\n" );
DBG_HDA ( rm_params, params, params_len );
DBG ( "PXEPARENT parameters returned:\n" );
DBG_HDA ( rm_params, &pxeparent_params, params_len );
}
/* Copy parameter block back */
memcpy ( params, &pxeparent_params, params_len );
return rc;
}

View File

@ -27,14 +27,18 @@ load_image:
popw %es
popal
1: /* Read to end of current track */
1: /* Read to end of current track (or end of image) */
movb %cl, %al
negb %al
addb max_sector, %al
incb %al
andb $0x3f, %al
movzbl %al, %eax
call *read_sectors
movl load_length, %ebx
cmpl %eax, %ebx
ja 2f
movl %ebx, %eax
2: call *read_sectors
jc load_failed
/* Update %es */
@ -53,12 +57,12 @@ load_image:
orb $0x01, %cl
incb %dh
cmpb max_head, %dh
jbe 2f
jbe 3f
xorb %dh, %dh
incb %ch
jnc 2f
jnc 3f
addb $0xc0, %cl
2:
3:
/* Loop until whole image is read */
subl %eax, load_length
ja 1b

View File

@ -492,6 +492,7 @@ mromheader:
.word 0
.size mromheader, . - mromheader
.align 4
mpciheader:
.ascii "PCIR" /* Signature */
.word pci_vendor_id /* Vendor identification */

View File

@ -88,6 +88,7 @@ checksum:
.previous
.ifeqs BUSTYPE, "PCIR"
.align 4
pciheader:
.ascii "PCIR" /* Signature */
.word pci_vendor_id /* Vendor identification */
@ -183,6 +184,7 @@ prodstr_pci_id:
.globl undiheader
.weak undiloader
.align 4
undiheader:
.ascii "UNDI" /* Signature */
.byte undiheader_len /* Length of structure */
@ -197,6 +199,7 @@ undiheader:
.equ undiheader_len, . - undiheader
.size undiheader, . - undiheader
.align 4
ipxeheader:
.ascii "iPXE" /* Signature */
.byte ipxeheader_len /* Length of structure */

View File

@ -197,7 +197,8 @@ static void * ioremap_pages ( unsigned long bus_addr, size_t len ) {
DBGC ( &io_pages, "IO mapping %08lx+%zx\n", bus_addr, len );
/* Sanity check */
assert ( len != 0 );
if ( ! len )
return NULL;
/* Round down start address to a page boundary */
start = ( bus_addr & ~( IO_PAGE_SIZE - 1 ) );

View File

@ -248,6 +248,9 @@ REQUIRE_OBJECT ( cpuid_cmd );
#ifdef SYNC_CMD
REQUIRE_OBJECT ( sync_cmd );
#endif
#ifdef SHELL_CMD
REQUIRE_OBJECT ( shell );
#endif
#ifdef NSLOOKUP_CMD
REQUIRE_OBJECT ( nslookup_cmd );
#endif
@ -337,6 +340,9 @@ REQUIRE_OBJECT ( memmap_settings );
#ifdef VRAM_SETTINGS
REQUIRE_OBJECT ( vram_settings );
#endif
#ifdef ACPI_SETTINGS
REQUIRE_OBJECT ( acpi_settings );
#endif
/*
* Drag in selected keyboard map

View File

@ -40,6 +40,9 @@ REQUIRE_OBJECT ( httpbasic );
#ifdef HTTP_AUTH_DIGEST
REQUIRE_OBJECT ( httpdigest );
#endif
#ifdef HTTP_AUTH_NTLM
REQUIRE_OBJECT ( httpntlm );
#endif
#ifdef HTTP_ENC_PEERDIST
REQUIRE_OBJECT ( peerdist );
#endif

View File

@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ENTROPY_EFI
#define TIME_EFI
#define REBOOT_EFI
#define ACPI_EFI
#define DOWNLOAD_PROTO_FILE /* Local filesystem access */
@ -31,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define SANBOOT_PROTO_AOE /* AoE protocol */
#define SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */
#define SANBOOT_PROTO_FCP /* Fibre Channel protocol */
#define SANBOOT_PROTO_HTTP /* HTTP SAN protocol */
#define USB_HCD_XHCI /* xHCI USB host controller */
#define USB_HCD_EHCI /* EHCI USB host controller */

View File

@ -15,7 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define UMALLOC_LINUX
#define NAP_LINUX
#define SMBIOS_LINUX
#define SANBOOT_NULL
#define SANBOOT_DUMMY
#define ENTROPY_LINUX
#define TIME_LINUX
#define REBOOT_NULL
@ -25,4 +25,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define IMAGE_SCRIPT
#define SANBOOT_PROTO_ISCSI
#define SANBOOT_PROTO_AOE
#define SANBOOT_PROTO_IB_SRP
#define SANBOOT_PROTO_FCP
#define SANBOOT_PROTO_HTTP
#endif /* CONFIG_DEFAULTS_LINUX_H */

View File

@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ENTROPY_RTC
#define TIME_RTC
#define REBOOT_PCBIOS
#define ACPI_RSDP
#ifdef __x86_64__
#define IOMAP_PAGES

View File

@ -77,6 +77,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#define HTTP_AUTH_BASIC /* Basic authentication */
#define HTTP_AUTH_DIGEST /* Digest authentication */
//#define HTTP_AUTH_NTLM /* NTLM authentication */
//#define HTTP_ENC_PEERDIST /* PeerDist content encoding */
//#define HTTP_HACK_GCE /* Google Compute Engine hacks */
@ -134,6 +135,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define MENU_CMD /* Menu commands */
#define LOGIN_CMD /* Login command */
#define SYNC_CMD /* Sync command */
#define SHELL_CMD /* Shell command */
//#define NSLOOKUP_CMD /* DNS resolving command */
//#define TIME_CMD /* Time commands */
//#define DIGEST_CMD /* Image crypto digest commands */

View File

@ -14,6 +14,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
//#define MEMMAP_SETTINGS /* Memory map settings */
//#define VMWARE_SETTINGS /* VMware GuestInfo settings */
//#define VRAM_SETTINGS /* Video RAM dump settings */
//#define ACPI_SETTINGS /* ACPI settings */
#include <config/named.h>
#include NAMED_CONFIG(settings.h)

View File

@ -43,19 +43,29 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
/**
* Transcribe ACPI table signature (for debugging)
* Compute ACPI table checksum
*
* @v signature ACPI table signature
* @ret name ACPI table signature name
* @v table Any ACPI table
* @ret checksum 0 if checksum is good
*/
static const char * acpi_name ( uint32_t signature ) {
static union {
uint32_t signature;
char name[5];
} u;
static uint8_t acpi_checksum ( userptr_t table ) {
struct acpi_header acpi;
uint8_t sum = 0;
uint8_t data = 0;
unsigned int i;
u.signature = cpu_to_le32 ( signature );
return u.name;
/* Read table length */
copy_from_user ( &acpi.length, table,
offsetof ( typeof ( acpi ), length ),
sizeof ( acpi.length ) );
/* Compute checksum */
for ( i = 0 ; i < le32_to_cpu ( acpi.length ) ; i++ ) {
copy_from_user ( &data, table, i, sizeof ( data ) );
sum += data;
}
return sum;
}
/**
@ -63,98 +73,36 @@ static const char * acpi_name ( uint32_t signature ) {
*
* @v acpi ACPI table header
*/
void acpi_fix_checksum ( struct acpi_description_header *acpi ) {
unsigned int i = 0;
uint8_t sum = 0;
void acpi_fix_checksum ( struct acpi_header *acpi ) {
for ( i = 0 ; i < acpi->length ; i++ ) {
sum += *( ( ( uint8_t * ) acpi ) + i );
}
acpi->checksum -= sum;
}
/**
* 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;
}
/**
* 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;
/* Update checksum */
acpi->checksum -= acpi_checksum ( virt_to_user ( acpi ) );
}
/**
* 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_description_header acpi;
userptr_t acpi_find ( uint32_t signature, unsigned int index ) {
struct acpi_header acpi;
struct acpi_rsdt *rsdtab;
typeof ( rsdtab->entry[0] ) entry;
userptr_t rsdt;
userptr_t table;
size_t len;
unsigned int count;
unsigned int i;
/* Locate RSDT */
rsdt = acpi_find_rsdt();
if ( ! rsdt ) {
DBG ( "RSDT not found\n" );
return UNULL;
}
/* Read RSDT header */
copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) );
if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) {
@ -197,6 +145,15 @@ userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ) {
if ( index-- )
continue;
/* Check table integrity */
if ( acpi_checksum ( table ) != 0 ) {
DBGC ( rsdt, "RSDT %#08lx found %s with bad checksum "
"at %08lx\n", user_to_phys ( rsdt, 0 ),
acpi_name ( signature ),
user_to_phys ( table, 0 ) );
break;
}
DBGC ( rsdt, "RSDT %#08lx found %s at %08lx\n",
user_to_phys ( rsdt, 0 ), acpi_name ( signature ),
user_to_phys ( table, 0 ) );
@ -227,7 +184,7 @@ userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ) {
* the ACPI specification itself.
*/
static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
struct acpi_description_header acpi;
struct acpi_header acpi;
union {
uint32_t dword;
uint8_t byte[4];
@ -294,20 +251,27 @@ static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
/**
* 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 ) {
int acpi_sx ( uint32_t signature ) {
struct acpi_fadt fadtab;
userptr_t rsdt;
userptr_t fadt;
userptr_t dsdt;
userptr_t ssdt;
unsigned int i;
int sx;
/* Locate RSDT */
rsdt = acpi_find_rsdt();
if ( ! rsdt ) {
DBG ( "RSDT not found\n" );
return -ENOENT;
}
/* Try DSDT first */
fadt = acpi_find ( rsdt, FADT_SIGNATURE, 0 );
fadt = acpi_find ( FADT_SIGNATURE, 0 );
if ( fadt ) {
copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
dsdt = phys_to_user ( fadtab.dsdt );
@ -317,7 +281,7 @@ int acpi_sx ( userptr_t rsdt, uint32_t signature ) {
/* Try all SSDTs */
for ( i = 0 ; ; i++ ) {
ssdt = acpi_find ( rsdt, SSDT_SIGNATURE, i );
ssdt = acpi_find ( SSDT_SIGNATURE, i );
if ( ! ssdt )
break;
if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 )
@ -331,34 +295,73 @@ int acpi_sx ( userptr_t rsdt, uint32_t signature ) {
/******************************************************************************
*
* Interface methods
* Descriptors
*
******************************************************************************
*/
/**
* Describe object in an ACPI table
* Add ACPI descriptor
*
* @v desc ACPI descriptor
*/
void acpi_add ( struct acpi_descriptor *desc ) {
/* Add to list of descriptors */
ref_get ( desc->refcnt );
list_add_tail ( &desc->list, &desc->model->descs );
}
/**
* Remove ACPI descriptor
*
* @v desc ACPI descriptor
*/
void acpi_del ( struct acpi_descriptor *desc ) {
/* Remove from list of descriptors */
list_check_contains_entry ( desc, &desc->model->descs, list );
list_del ( &desc->list );
ref_put ( desc->refcnt );
}
/**
* Get object's ACPI descriptor
*
* @v intf Interface
* @v acpi ACPI table
* @v len Length of ACPI table
* @ret rc Return status code
* @ret desc ACPI descriptor, or NULL
*/
int acpi_describe ( struct interface *intf,
struct acpi_description_header *acpi, size_t len ) {
struct acpi_descriptor * acpi_describe ( struct interface *intf ) {
struct interface *dest;
acpi_describe_TYPE ( void * ) *op =
intf_get_dest_op ( intf, acpi_describe, &dest );
void *object = intf_object ( dest );
int rc;
struct acpi_descriptor *desc;
if ( op ) {
rc = op ( object, acpi, len );
desc = op ( object );
} else {
/* Default is to fail to describe */
rc = -EOPNOTSUPP;
desc = NULL;
}
intf_put ( dest );
return rc;
return desc;
}
/**
* Install ACPI tables
*
* @v install Table installation method
* @ret rc Return status code
*/
int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) ){
struct acpi_model *model;
int rc;
for_each_table_entry ( model, ACPI_MODELS ) {
if ( ( rc = model->install ( install ) ) != 0 )
return rc;
}
return 0;
}

View File

@ -0,0 +1,161 @@
/*
* Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* ACPI settings
*
*/
#include <string.h>
#include <errno.h>
#include <ipxe/init.h>
#include <ipxe/settings.h>
#include <ipxe/acpi.h>
/** ACPI settings scope */
static const struct settings_scope acpi_settings_scope;
/**
* Check applicability of ACPI setting
*
* @v settings Settings block
* @v setting Setting
* @ret applies Setting applies within this settings block
*/
static int acpi_settings_applies ( struct settings *settings __unused,
const struct setting *setting ) {
return ( setting->scope == &acpi_settings_scope );
}
/**
* Fetch value of ACPI setting
*
* @v settings Settings block
* @v setting Setting to fetch
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int acpi_settings_fetch ( struct settings *settings,
struct setting *setting,
void *data, size_t len ) {
struct acpi_header acpi;
uint32_t tag_high;
uint32_t tag_low;
uint32_t tag_signature;
unsigned int tag_index;
size_t tag_offset;
size_t tag_len;
userptr_t table;
size_t offset;
size_t max_len;
int delta;
unsigned int i;
/* Parse settings tag */
tag_high = ( setting->tag >> 32 );
tag_low = setting->tag;
tag_signature = bswap_32 ( tag_high );
tag_index = ( ( tag_low >> 24 ) & 0xff );
tag_offset = ( ( tag_low >> 8 ) & 0xffff );
tag_len = ( ( tag_low >> 0 ) & 0xff );
DBGC ( settings, "ACPI %s.%d offset %#zx length %#zx\n",
acpi_name ( tag_signature ), tag_index, tag_offset, tag_len );
/* Locate ACPI table */
table = acpi_find ( tag_signature, tag_index );
if ( ! table )
return -ENOENT;
/* Read table header */
copy_from_user ( &acpi, table, 0, sizeof ( acpi ) );
/* Calculate starting offset and maximum available length */
max_len = le32_to_cpu ( acpi.length );
if ( tag_offset > max_len )
return -ENOENT;
offset = tag_offset;
max_len -= offset;
/* Restrict to requested length, if specified */
if ( tag_len && ( tag_len < max_len ) )
max_len = tag_len;
/* Invert endianness for numeric settings */
if ( setting->type && setting->type->numerate ) {
offset += ( max_len - 1 );
delta = -1;
} else {
delta = +1;
}
/* Read data */
for ( i = 0 ; ( ( i < max_len ) && ( i < len ) ) ; i++ ) {
copy_from_user ( data, table, offset, 1 );
data++;
offset += delta;
}
/* Set type if not already specified */
if ( ! setting->type )
setting->type = &setting_type_hexraw;
return max_len;
}
/** ACPI settings operations */
static struct settings_operations acpi_settings_operations = {
.applies = acpi_settings_applies,
.fetch = acpi_settings_fetch,
};
/** ACPI settings */
static struct settings acpi_settings = {
.refcnt = NULL,
.siblings = LIST_HEAD_INIT ( acpi_settings.siblings ),
.children = LIST_HEAD_INIT ( acpi_settings.children ),
.op = &acpi_settings_operations,
.default_scope = &acpi_settings_scope,
};
/** Initialise ACPI settings */
static void acpi_settings_init ( void ) {
int rc;
if ( ( rc = register_settings ( &acpi_settings, NULL,
"acpi" ) ) != 0 ) {
DBG ( "ACPI could not register settings: %s\n",
strerror ( rc ) );
return;
}
}
/** ACPI settings initialiser */
struct init_fn acpi_settings_init_fn __init_fn ( INIT_NORMAL ) = {
.initialise = acpi_settings_init,
};

View File

@ -111,13 +111,20 @@ static void downloader_finished ( struct downloader *downloader, int rc ) {
*/
static int downloader_progress ( struct downloader *downloader,
struct job_progress *progress ) {
int rc;
/* Allow data transfer to provide an accurate description */
if ( ( rc = job_progress ( &downloader->xfer, progress ) ) != 0 )
return rc;
/* This is not entirely accurate, since downloaded data may
* arrive out of order (e.g. with multicast protocols), but
* it's a reasonable first approximation.
*/
progress->completed = downloader->buffer.pos;
progress->total = downloader->buffer.len;
if ( ! progress->total ) {
progress->completed = downloader->buffer.pos;
progress->total = downloader->buffer.len;
}
return 0;
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* Dummy SAN device
*
*/
#include <errno.h>
#include <ipxe/sanboot.h>
/**
* Hook dummy SAN device
*
* @v drive Drive number
* @v uris List of URIs
* @v count Number of URIs
* @v flags Flags
* @ret drive Drive number, or negative error
*/
static int dummy_san_hook ( unsigned int drive, struct uri **uris,
unsigned int count, unsigned int flags ) {
struct san_device *sandev;
int rc;
/* Allocate SAN device */
sandev = alloc_sandev ( uris, count, 0 );
if ( ! sandev ) {
rc = -ENOMEM;
goto err_alloc;
}
/* Register SAN device */
if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x could not register: %s\n",
sandev->drive, strerror ( rc ) );
goto err_register;
}
return drive;
unregister_sandev ( sandev );
err_register:
sandev_put ( sandev );
err_alloc:
return rc;
}
/**
* Unhook dummy SAN device
*
* @v drive Drive number
*/
static void dummy_san_unhook ( unsigned int drive ) {
struct san_device *sandev;
/* Find drive */
sandev = sandev_find ( drive );
if ( ! sandev ) {
DBG ( "SAN %#02x does not exist\n", drive );
return;
}
/* Unregister SAN device */
unregister_sandev ( sandev );
/* Drop reference to drive */
sandev_put ( sandev );
}
/**
* Boot from dummy SAN device
*
* @v drive Drive number
* @v filename Filename (or NULL to use default)
* @ret rc Return status code
*/
static int dummy_san_boot ( unsigned int drive __unused,
const char *filename __unused ) {
return -EOPNOTSUPP;
}
/**
* Install ACPI table
*
* @v acpi ACPI description header
* @ret rc Return status code
*/
static int dummy_install ( struct acpi_header *acpi ) {
DBGC ( acpi, "ACPI table %s:\n", acpi_name ( acpi->signature ) );
DBGC_HDA ( acpi, 0, acpi, le32_to_cpu ( acpi->length ) );
return 0;
}
/**
* Describe dummy SAN device
*
* @ret rc Return status code
*/
static int dummy_san_describe ( void ) {
return acpi_install ( dummy_install );
}
PROVIDE_SANBOOT ( dummy, san_hook, dummy_san_hook );
PROVIDE_SANBOOT ( dummy, san_unhook, dummy_san_unhook );
PROVIDE_SANBOOT ( dummy, san_boot, dummy_san_boot );
PROVIDE_SANBOOT ( dummy, san_describe, dummy_san_describe );

View File

@ -47,6 +47,13 @@ size_t line_putchar ( struct line_console *line, int character ) {
if ( character < 0 )
return 0;
/* Handle backspace characters */
if ( character == '\b' ) {
if ( line->index )
line->index--;
return 0;
}
/* Ignore carriage return */
if ( character == '\r' )
return 0;

View File

@ -93,6 +93,12 @@ static LIST_HEAD ( free_blocks );
/** Total amount of free memory */
size_t freemem;
/** Total amount of used memory */
size_t usedmem;
/** Maximum amount of used memory */
size_t maxusedmem;
/**
* Heap size
*
@ -278,6 +284,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
size_t post_size;
struct memory_block *pre;
struct memory_block *post;
unsigned int discarded;
void *ptr;
/* Sanity checks */
@ -351,8 +358,11 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
VALGRIND_MAKE_MEM_NOACCESS ( pre,
sizeof ( *pre ) );
}
/* Update total free memory */
/* Update memory usage statistics */
freemem -= actual_size;
usedmem += actual_size;
if ( usedmem > maxusedmem )
maxusedmem = usedmem;
/* Return allocated block */
DBGC2 ( &heap, "Allocated [%p,%p)\n", block,
( ( ( void * ) block ) + size ) );
@ -362,7 +372,13 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
}
/* Try discarding some cached data to free up memory */
if ( ! discard_cache() ) {
DBGC ( &heap, "Attempting discard for %#zx (aligned %#zx+%zx), "
"used %zdkB\n", size, align, offset, ( usedmem >> 10 ) );
valgrind_make_blocks_noaccess();
discarded = discard_cache();
valgrind_make_blocks_defined();
check_blocks();
if ( ! discarded ) {
/* Nothing available to discard */
DBGC ( &heap, "Failed to allocate %#zx (aligned "
"%#zx)\n", size, align );
@ -474,8 +490,9 @@ void free_memblock ( void *ptr, size_t size ) {
VALGRIND_MAKE_MEM_NOACCESS ( block, sizeof ( *block ) );
}
/* Update free memory counter */
/* Update memory usage statistics */
freemem += actual_size;
usedmem -= actual_size;
check_blocks();
valgrind_make_blocks_noaccess();
@ -629,10 +646,17 @@ void * zalloc ( size_t size ) {
* @c start must be aligned to at least a multiple of sizeof(void*).
*/
void mpopulate ( void *start, size_t len ) {
/* Prevent free_memblock() from rounding up len beyond the end
* of what we were actually given...
*/
free_memblock ( start, ( len & ~( MIN_MEMBLOCK_SIZE - 1 ) ) );
len &= ~( MIN_MEMBLOCK_SIZE - 1 );
/* Add to allocation pool */
free_memblock ( start, len );
/* Fix up memory usage statistics */
usedmem += len;
}
/**
@ -656,6 +680,7 @@ struct init_fn heap_init_fn __init_fn ( INIT_EARLY ) = {
*/
static void shutdown_cache ( int booting __unused ) {
discard_all_cache();
DBGC ( &heap, "Maximum heap usage %zdkB\n", ( maxusedmem >> 10 ) );
}
/** Memory allocator shutdown function */

View File

@ -142,28 +142,36 @@ static int memmap_settings_fetch ( struct settings *settings,
struct memory_map memmap;
struct memory_region *region;
uint64_t result = 0;
unsigned int i;
unsigned int start;
unsigned int count;
unsigned int scale;
int include_start;
int include_length;
int ignore_nonexistent;
unsigned int i;
DBGC ( settings, "MEMMAP start %ld count %ld %s%s%s%s scale %ld\n",
MEMMAP_START ( setting->tag ), MEMMAP_COUNT ( setting->tag ),
( MEMMAP_INCLUDE_START ( setting->tag ) ? "start" : "" ),
( ( MEMMAP_INCLUDE_START ( setting->tag ) &&
MEMMAP_INCLUDE_LENGTH ( setting->tag ) ) ? "+" : "" ),
( MEMMAP_INCLUDE_LENGTH ( setting->tag ) ? "length" : "" ),
( MEMMAP_IGNORE_NONEXISTENT ( setting->tag ) ? " ignore" : "" ),
MEMMAP_SCALE ( setting->tag ) );
/* Parse settings tag */
start = MEMMAP_START ( setting->tag );
count = MEMMAP_COUNT ( setting->tag );
scale = MEMMAP_SCALE ( setting->tag );
include_start = MEMMAP_INCLUDE_START ( setting->tag );
include_length = MEMMAP_INCLUDE_LENGTH ( setting->tag );
ignore_nonexistent = MEMMAP_IGNORE_NONEXISTENT ( setting->tag );
DBGC ( settings, "MEMMAP start %d count %d %s%s%s%s scale %d\n",
start, count, ( include_start ? "start" : "" ),
( ( include_start && include_length ) ? "+" : "" ),
( include_length ? "length" : "" ),
( ignore_nonexistent ? " ignore" : "" ), scale );
/* Fetch memory map */
get_memmap ( &memmap );
/* Extract results from memory map */
count = MEMMAP_COUNT ( setting->tag );
for ( i = MEMMAP_START ( setting->tag ) ; count-- ; i++ ) {
for ( i = start ; count-- ; i++ ) {
/* Check that region exists */
if ( i >= memmap.count ) {
if ( MEMMAP_IGNORE_NONEXISTENT ( setting->tag ) ) {
if ( ignore_nonexistent ) {
continue;
} else {
DBGC ( settings, "MEMMAP region %d does not "
@ -174,12 +182,12 @@ static int memmap_settings_fetch ( struct settings *settings,
/* Extract results from this region */
region = &memmap.regions[i];
if ( MEMMAP_INCLUDE_START ( setting->tag ) ) {
if ( include_start ) {
result += region->start;
DBGC ( settings, "MEMMAP %d start %08llx\n",
i, region->start );
}
if ( MEMMAP_INCLUDE_LENGTH ( setting->tag ) ) {
if ( include_length ) {
result += ( region->end - region->start );
DBGC ( settings, "MEMMAP %d length %08llx\n",
i, ( region->end - region->start ) );
@ -187,7 +195,7 @@ static int memmap_settings_fetch ( struct settings *settings,
}
/* Scale result */
result >>= MEMMAP_SCALE ( setting->tag );
result >>= scale;
/* Return result */
result = cpu_to_be64 ( result );

View File

@ -55,6 +55,22 @@ static struct interface_descriptor monojob_intf_desc =
struct interface monojob = INTF_INIT ( monojob_intf_desc );
/**
* Clear previously displayed message
*
* @v len Length of previously displayed message
*/
static void monojob_clear ( size_t len ) {
unsigned int i;
for ( i = 0 ; i < len ; i++ )
putchar ( '\b' );
for ( i = 0 ; i < len ; i++ )
putchar ( ' ' );
for ( i = 0 ; i < len ; i++ )
putchar ( '\b' );
}
/**
* Wait for single foreground job to complete
*
@ -64,7 +80,7 @@ struct interface monojob = INTF_INIT ( monojob_intf_desc );
*/
int monojob_wait ( const char *string, unsigned long timeout ) {
struct job_progress progress;
unsigned long last_keycheck;
unsigned long last_check;
unsigned long last_progress;
unsigned long last_display;
unsigned long now;
@ -73,7 +89,7 @@ int monojob_wait ( const char *string, unsigned long timeout ) {
unsigned long scaled_completed;
unsigned long scaled_total;
unsigned int percentage;
int shown_percentage = 0;
size_t clear_len = 0;
int ongoing_rc;
int key;
int rc;
@ -81,26 +97,28 @@ int monojob_wait ( const char *string, unsigned long timeout ) {
if ( string )
printf ( "%s...", string );
monojob_rc = -EINPROGRESS;
last_keycheck = last_progress = last_display = currticks();
last_check = last_progress = last_display = currticks();
while ( monojob_rc == -EINPROGRESS ) {
/* Allow job to progress */
step();
now = currticks();
/* Check for keypresses. This can be time-consuming,
* so check only once per clock tick.
/* Continue until a timer tick occurs (to minimise
* time wasted checking for progress and keypresses).
*/
elapsed = ( now - last_keycheck );
if ( elapsed ) {
if ( iskey() ) {
key = getchar();
if ( key == CTRL_C ) {
monojob_rc = -ECANCELED;
break;
}
elapsed = ( now - last_check );
if ( ! elapsed )
continue;
last_check = now;
/* Check for keypresses */
if ( iskey() ) {
key = getchar();
if ( key == CTRL_C ) {
monojob_rc = -ECANCELED;
break;
}
last_keycheck = now;
}
/* Monitor progress */
@ -121,19 +139,21 @@ int monojob_wait ( const char *string, unsigned long timeout ) {
/* Display progress, if applicable */
elapsed = ( now - last_display );
if ( string && ( elapsed >= TICKS_PER_SEC ) ) {
if ( shown_percentage )
printf ( "\b\b\b\b \b\b\b\b" );
monojob_clear ( clear_len );
/* Normalise progress figures to avoid overflow */
scaled_completed = ( progress.completed / 128 );
scaled_total = ( progress.total / 128 );
if ( scaled_total ) {
percentage = ( ( 100 * scaled_completed ) /
scaled_total );
printf ( "%3d%%", percentage );
shown_percentage = 1;
clear_len = printf ( "%3d%%", percentage );
} else {
printf ( "." );
shown_percentage = 0;
clear_len = 0;
}
if ( progress.message[0] ) {
clear_len += printf ( " [%s]",
progress.message );
}
last_display = now;
}
@ -141,9 +161,7 @@ int monojob_wait ( const char *string, unsigned long timeout ) {
rc = monojob_rc;
monojob_close ( &monojob, rc );
if ( shown_percentage )
printf ( "\b\b\b\b \b\b\b\b" );
monojob_clear ( clear_len );
if ( string ) {
if ( rc ) {
printf ( " %s\n", strerror ( rc ) );

3
third_party/ipxe/src/core/null_acpi.c vendored Normal file
View File

@ -0,0 +1,3 @@
#include <ipxe/acpi.h>
PROVIDE_ACPI_INLINE ( null, acpi_find_rsdt );

View File

@ -26,8 +26,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <ipxe/sanboot.h>
static int null_san_hook ( struct uri *uri __unused,
unsigned int drive __unused ) {
static int null_san_hook ( unsigned int drive __unused,
struct uri **uris __unused,
unsigned int count __unused,
unsigned int flags __unused ) {
return -EOPNOTSUPP;
}
@ -35,15 +37,15 @@ static void null_san_unhook ( unsigned int drive __unused ) {
/* Do nothing */
}
static int null_san_boot ( unsigned int drive __unused ) {
static int null_san_boot ( unsigned int drive __unused,
const char *filename __unused ) {
return -EOPNOTSUPP;
}
static int null_san_describe ( unsigned int drive __unused ) {
static int null_san_describe ( void ) {
return -EOPNOTSUPP;
}
PROVIDE_SANBOOT_INLINE ( null, san_default_drive );
PROVIDE_SANBOOT ( null, san_hook, null_san_hook );
PROVIDE_SANBOOT ( null, san_unhook, null_san_unhook );
PROVIDE_SANBOOT ( null, san_boot, null_san_boot );

View File

@ -68,8 +68,10 @@ struct pixel_buffer * alloc_pixbuf ( unsigned int width, unsigned int height ) {
pixbuf->len = ( width * height * sizeof ( uint32_t ) );
/* Check for multiplication overflow */
if ( ( ( pixbuf->len / sizeof ( uint32_t ) ) / width ) != height )
if ( ( width != 0 ) &&
( ( pixbuf->len / sizeof ( uint32_t ) ) / width ) != height ) {
goto err_overflow;
}
/* Allocate pixel data buffer */
pixbuf->data = umalloc ( pixbuf->len );

53
third_party/ipxe/src/core/quiesce.c vendored Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* Quiesce system
*
*/
#include <ipxe/quiesce.h>
/** Quiesce system */
void quiesce ( void ) {
struct quiescer *quiescer;
/* Call all quiescers */
for_each_table_entry ( quiescer, QUIESCERS ) {
quiescer->quiesce();
}
}
/** Unquiesce system */
void unquiesce ( void ) {
struct quiescer *quiescer;
/* Call all quiescers */
for_each_table_entry ( quiescer, QUIESCERS ) {
quiescer->unquiesce();
}
}

View File

@ -180,19 +180,16 @@ static int resmux_try ( struct resolv_mux *mux ) {
}
/**
* Child resolved name
* Close name resolution multiplexer
*
* @v mux Name resolution multiplexer
* @v sa Completed socket address
* @v rc Reason for close
*/
static void resmux_child_resolv_done ( struct resolv_mux *mux,
struct sockaddr *sa ) {
static void resmux_close ( struct resolv_mux *mux, int rc ) {
DBGC ( mux, "RESOLV %p resolved \"%s\" to %s using method %s\n",
mux, mux->name, sock_ntoa ( sa ), mux->resolver->name );
/* Pass resolution to parent */
resolv_done ( &mux->parent, sa );
/* Shut down all interfaces */
intf_shutdown ( &mux->child, rc );
intf_shutdown ( &mux->parent, rc );
}
/**
@ -226,18 +223,28 @@ static void resmux_child_close ( struct resolv_mux *mux, int rc ) {
return;
finished:
intf_shutdown ( &mux->parent, rc );
resmux_close ( mux, rc );
}
/** Name resolution multiplexer child interface operations */
static struct interface_operation resmux_child_op[] = {
INTF_OP ( resolv_done, struct resolv_mux *, resmux_child_resolv_done ),
INTF_OP ( intf_close, struct resolv_mux *, resmux_child_close ),
};
/** Name resolution multiplexer child interface descriptor */
static struct interface_descriptor resmux_child_desc =
INTF_DESC ( struct resolv_mux, child, resmux_child_op );
INTF_DESC_PASSTHRU ( struct resolv_mux, child, resmux_child_op,
parent );
/** Name resolution multiplexer parent interface operations */
static struct interface_operation resmux_parent_op[] = {
INTF_OP ( intf_close, struct resolv_mux *, resmux_close ),
};
/** Name resolution multiplexer parent interface descriptor */
static struct interface_descriptor resmux_parent_desc =
INTF_DESC_PASSTHRU ( struct resolv_mux, parent, resmux_parent_op,
child );
/**
* Start name resolution
@ -258,7 +265,7 @@ int resolv ( struct interface *resolv, const char *name,
if ( ! mux )
return -ENOMEM;
ref_init ( &mux->refcnt, NULL );
intf_init ( &mux->parent, &null_intf_desc, &mux->refcnt );
intf_init ( &mux->parent, &resmux_parent_desc, &mux->refcnt );
intf_init ( &mux->child, &resmux_child_desc, &mux->refcnt );
mux->resolver = table_start ( RESOLVERS );
if ( sa )
@ -338,7 +345,8 @@ static struct interface_operation named_xfer_ops[] = {
/** Named socket opener data transfer interface descriptor */
static struct interface_descriptor named_xfer_desc =
INTF_DESC ( struct named_socket, xfer, named_xfer_ops );
INTF_DESC_PASSTHRU ( struct named_socket, xfer, named_xfer_ops,
resolv );
/**
* Name resolved
@ -379,7 +387,8 @@ static struct interface_operation named_resolv_op[] = {
/** Named socket opener resolver interface descriptor */
static struct interface_descriptor named_resolv_desc =
INTF_DESC ( struct named_socket, resolv, named_resolv_op );
INTF_DESC_PASSTHRU ( struct named_socket, resolv, named_resolv_op,
xfer );
/**
* Open named socket

995
third_party/ipxe/src/core/sanboot.c vendored Normal file
View File

@ -0,0 +1,995 @@
/*
* Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* SAN booting
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
#include <ipxe/timer.h>
#include <ipxe/process.h>
#include <ipxe/iso9660.h>
#include <ipxe/dhcp.h>
#include <ipxe/settings.h>
#include <ipxe/quiesce.h>
#include <ipxe/sanboot.h>
/**
* Default SAN drive number
*
* The drive number is a meaningful concept only in a BIOS
* environment, where it represents the INT13 drive number (0x80 for
* the first hard disk). We retain it in other environments to allow
* for a simple way for iPXE commands to refer to SAN drives.
*/
#define SAN_DEFAULT_DRIVE 0x80
/**
* Timeout for block device commands (in ticks)
*
* Underlying devices should ideally never become totally stuck.
* However, if they do, then the blocking SAN APIs provide no means
* for the caller to cancel the operation, and the machine appears to
* hang. Use an overall timeout for all commands to avoid this
* problem and bounce timeout failures to the caller.
*/
#define SAN_COMMAND_TIMEOUT ( 15 * TICKS_PER_SEC )
/**
* Default number of times to retry commands
*
* We may need to retry commands. For example, the underlying
* connection may be closed by the SAN target due to an inactivity
* timeout, or the SAN target may return pointless "error" messages
* such as "SCSI power-on occurred".
*/
#define SAN_DEFAULT_RETRIES 10
/**
* Delay between reopening attempts
*
* Some SAN targets will always accept connections instantly and
* report a temporary unavailability by e.g. failing the TEST UNIT
* READY command. Avoid bombarding such targets by introducing a
* small delay between attempts.
*/
#define SAN_REOPEN_DELAY_SECS 5
/** List of SAN devices */
LIST_HEAD ( san_devices );
/** Number of times to retry commands */
static unsigned long san_retries = SAN_DEFAULT_RETRIES;
/**
* Find SAN device by drive number
*
* @v drive Drive number
* @ret sandev SAN device, or NULL
*/
struct san_device * sandev_find ( unsigned int drive ) {
struct san_device *sandev;
list_for_each_entry ( sandev, &san_devices, list ) {
if ( sandev->drive == drive )
return sandev;
}
return NULL;
}
/**
* Free SAN device
*
* @v refcnt Reference count
*/
static void sandev_free ( struct refcnt *refcnt ) {
struct san_device *sandev =
container_of ( refcnt, struct san_device, refcnt );
unsigned int i;
assert ( ! timer_running ( &sandev->timer ) );
assert ( ! sandev->active );
assert ( list_empty ( &sandev->opened ) );
for ( i = 0 ; i < sandev->paths ; i++ ) {
uri_put ( sandev->path[i].uri );
assert ( sandev->path[i].desc == NULL );
}
free ( sandev );
}
/**
* Close SAN device command
*
* @v sandev SAN device
* @v rc Reason for close
*/
static void sandev_command_close ( struct san_device *sandev, int rc ) {
/* Stop timer */
stop_timer ( &sandev->timer );
/* Restart interface */
intf_restart ( &sandev->command, rc );
/* Record command status */
sandev->command_rc = rc;
}
/**
* Record SAN device capacity
*
* @v sandev SAN device
* @v capacity SAN device capacity
*/
static void sandev_command_capacity ( struct san_device *sandev,
struct block_device_capacity *capacity ) {
/* Record raw capacity information */
memcpy ( &sandev->capacity, capacity, sizeof ( sandev->capacity ) );
}
/** SAN device command interface operations */
static struct interface_operation sandev_command_op[] = {
INTF_OP ( intf_close, struct san_device *, sandev_command_close ),
INTF_OP ( block_capacity, struct san_device *,
sandev_command_capacity ),
};
/** SAN device command interface descriptor */
static struct interface_descriptor sandev_command_desc =
INTF_DESC ( struct san_device, command, sandev_command_op );
/**
* Handle SAN device command timeout
*
* @v retry Retry timer
*/
static void sandev_command_expired ( struct retry_timer *timer,
int over __unused ) {
struct san_device *sandev =
container_of ( timer, struct san_device, timer );
sandev_command_close ( sandev, -ETIMEDOUT );
}
/**
* Open SAN path
*
* @v sanpath SAN path
* @ret rc Return status code
*/
static int sanpath_open ( struct san_path *sanpath ) {
struct san_device *sandev = sanpath->sandev;
int rc;
/* Sanity check */
list_check_contains_entry ( sanpath, &sandev->closed, list );
/* Open interface */
if ( ( rc = xfer_open_uri ( &sanpath->block, sanpath->uri ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x.%d could not (re)open URI: "
"%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
return rc;
}
/* Update ACPI descriptor, if applicable */
if ( ! ( sandev->flags & SAN_NO_DESCRIBE ) ) {
if ( sanpath->desc )
acpi_del ( sanpath->desc );
sanpath->desc = acpi_describe ( &sanpath->block );
if ( sanpath->desc )
acpi_add ( sanpath->desc );
}
/* Start process */
process_add ( &sanpath->process );
/* Mark as opened */
list_del ( &sanpath->list );
list_add_tail ( &sanpath->list, &sandev->opened );
/* Record as in progress */
sanpath->path_rc = -EINPROGRESS;
return 0;
}
/**
* Close SAN path
*
* @v sanpath SAN path
* @v rc Reason for close
*/
static void sanpath_close ( struct san_path *sanpath, int rc ) {
struct san_device *sandev = sanpath->sandev;
/* Record status */
sanpath->path_rc = rc;
/* Mark as closed */
list_del ( &sanpath->list );
list_add_tail ( &sanpath->list, &sandev->closed );
/* Stop process */
process_del ( &sanpath->process );
/* Restart interfaces, avoiding potential loops */
if ( sanpath == sandev->active ) {
intfs_restart ( rc, &sandev->command, &sanpath->block, NULL );
sandev->active = NULL;
sandev_command_close ( sandev, rc );
} else {
intf_restart ( &sanpath->block, rc );
}
}
/**
* Handle closure of underlying block device interface
*
* @v sanpath SAN path
* @v rc Reason for close
*/
static void sanpath_block_close ( struct san_path *sanpath, int rc ) {
struct san_device *sandev = sanpath->sandev;
/* Any closure is an error from our point of view */
if ( rc == 0 )
rc = -ENOTCONN;
DBGC ( sandev, "SAN %#02x.%d closed: %s\n",
sandev->drive, sanpath->index, strerror ( rc ) );
/* Close path */
sanpath_close ( sanpath, rc );
}
/**
* Check flow control window
*
* @v sanpath SAN path
*/
static size_t sanpath_block_window ( struct san_path *sanpath __unused ) {
/* We are never ready to receive data via this interface.
* This prevents objects that support both block and stream
* interfaces from attempting to send us stream data.
*/
return 0;
}
/**
* SAN path process
*
* @v sanpath SAN path
*/
static void sanpath_step ( struct san_path *sanpath ) {
struct san_device *sandev = sanpath->sandev;
/* Ignore if we are already the active device */
if ( sanpath == sandev->active )
return;
/* Wait until path has become available */
if ( ! xfer_window ( &sanpath->block ) )
return;
/* Record status */
sanpath->path_rc = 0;
/* Mark as active path or close as applicable */
if ( ! sandev->active ) {
DBGC ( sandev, "SAN %#02x.%d is active\n",
sandev->drive, sanpath->index );
sandev->active = sanpath;
} else {
DBGC ( sandev, "SAN %#02x.%d is available\n",
sandev->drive, sanpath->index );
sanpath_close ( sanpath, 0 );
}
}
/** SAN path block interface operations */
static struct interface_operation sanpath_block_op[] = {
INTF_OP ( intf_close, struct san_path *, sanpath_block_close ),
INTF_OP ( xfer_window, struct san_path *, sanpath_block_window ),
INTF_OP ( xfer_window_changed, struct san_path *, sanpath_step ),
};
/** SAN path block interface descriptor */
static struct interface_descriptor sanpath_block_desc =
INTF_DESC ( struct san_path, block, sanpath_block_op );
/** SAN path process descriptor */
static struct process_descriptor sanpath_process_desc =
PROC_DESC_ONCE ( struct san_path, process, sanpath_step );
/**
* Restart SAN device interface
*
* @v sandev SAN device
* @v rc Reason for restart
*/
static void sandev_restart ( struct san_device *sandev, int rc ) {
struct san_path *sanpath;
/* Restart all block device interfaces */
while ( ( sanpath = list_first_entry ( &sandev->opened,
struct san_path, list ) ) ) {
sanpath_close ( sanpath, rc );
}
/* Clear active path */
sandev->active = NULL;
/* Close any outstanding command */
sandev_command_close ( sandev, rc );
}
/**
* (Re)open SAN device
*
* @v sandev SAN device
* @ret rc Return status code
*
* This function will block until the device is available.
*/
int sandev_reopen ( struct san_device *sandev ) {
struct san_path *sanpath;
int rc;
/* Unquiesce system */
unquiesce();
/* Close any outstanding command and restart interfaces */
sandev_restart ( sandev, -ECONNRESET );
assert ( sandev->active == NULL );
assert ( list_empty ( &sandev->opened ) );
/* Open all paths */
while ( ( sanpath = list_first_entry ( &sandev->closed,
struct san_path, list ) ) ) {
if ( ( rc = sanpath_open ( sanpath ) ) != 0 )
goto err_open;
}
/* Wait for any device to become available, or for all devices
* to fail.
*/
while ( sandev->active == NULL ) {
step();
if ( list_empty ( &sandev->opened ) ) {
/* Get status of the first device to be
* closed. Do this on the basis that earlier
* errors (e.g. "invalid IQN") are probably
* more interesting than later errors
* (e.g. "TCP timeout").
*/
rc = -ENODEV;
list_for_each_entry ( sanpath, &sandev->closed, list ) {
rc = sanpath->path_rc;
break;
}
DBGC ( sandev, "SAN %#02x never became available: %s\n",
sandev->drive, strerror ( rc ) );
goto err_none;
}
}
assert ( ! list_empty ( &sandev->opened ) );
return 0;
err_none:
err_open:
sandev_restart ( sandev, rc );
return rc;
}
/** SAN device read/write command parameters */
struct san_command_rw_params {
/** SAN device read/write operation */
int ( * block_rw ) ( struct interface *control, struct interface *data,
uint64_t lba, unsigned int count,
userptr_t buffer, size_t len );
/** Data buffer */
userptr_t buffer;
/** Starting LBA */
uint64_t lba;
/** Block count */
unsigned int count;
};
/** SAN device command parameters */
union san_command_params {
/** Read/write command parameters */
struct san_command_rw_params rw;
};
/**
* Initiate SAN device read/write command
*
* @v sandev SAN device
* @v params Command parameters
* @ret rc Return status code
*/
static int sandev_command_rw ( struct san_device *sandev,
const union san_command_params *params ) {
struct san_path *sanpath = sandev->active;
size_t len = ( params->rw.count * sandev->capacity.blksize );
int rc;
/* Sanity check */
assert ( sanpath != NULL );
/* Initiate read/write command */
if ( ( rc = params->rw.block_rw ( &sanpath->block, &sandev->command,
params->rw.lba, params->rw.count,
params->rw.buffer, len ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x.%d could not initiate read/write: "
"%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Initiate SAN device read capacity command
*
* @v sandev SAN device
* @v params Command parameters
* @ret rc Return status code
*/
static int
sandev_command_read_capacity ( struct san_device *sandev,
const union san_command_params *params __unused){
struct san_path *sanpath = sandev->active;
int rc;
/* Sanity check */
assert ( sanpath != NULL );
/* Initiate read capacity command */
if ( ( rc = block_read_capacity ( &sanpath->block,
&sandev->command ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x.%d could not initiate read capacity: "
"%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Execute a single SAN device command and wait for completion
*
* @v sandev SAN device
* @v command Command
* @v params Command parameters (if required)
* @ret rc Return status code
*/
static int
sandev_command ( struct san_device *sandev,
int ( * command ) ( struct san_device *sandev,
const union san_command_params *params ),
const union san_command_params *params ) {
unsigned int retries = 0;
int rc;
/* Sanity check */
assert ( ! timer_running ( &sandev->timer ) );
/* Unquiesce system */
unquiesce();
/* (Re)try command */
do {
/* Reopen block device if applicable */
if ( sandev_needs_reopen ( sandev ) &&
( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) {
/* Delay reopening attempts */
sleep_fixed ( SAN_REOPEN_DELAY_SECS );
/* Retry opening indefinitely for multipath devices */
if ( sandev->paths <= 1 )
retries++;
continue;
}
/* Initiate command */
if ( ( rc = command ( sandev, params ) ) != 0 ) {
retries++;
continue;
}
/* Start expiry timer */
start_timer_fixed ( &sandev->timer, SAN_COMMAND_TIMEOUT );
/* Wait for command to complete */
while ( timer_running ( &sandev->timer ) )
step();
/* Check command status */
if ( ( rc = sandev->command_rc ) != 0 ) {
retries++;
continue;
}
return 0;
} while ( retries <= san_retries );
/* Sanity check */
assert ( ! timer_running ( &sandev->timer ) );
return rc;
}
/**
* Reset SAN device
*
* @v sandev SAN device
* @ret rc Return status code
*/
int sandev_reset ( struct san_device *sandev ) {
int rc;
DBGC ( sandev, "SAN %#02x reset\n", sandev->drive );
/* Close and reopen underlying block device */
if ( ( rc = sandev_reopen ( sandev ) ) != 0 )
return rc;
return 0;
}
/**
* Read from or write to SAN device
*
* @v sandev SAN device
* @v lba Starting logical block address
* @v count Number of logical blocks
* @v buffer Data buffer
* @v block_rw Block read/write method
* @ret rc Return status code
*/
static int sandev_rw ( struct san_device *sandev, uint64_t lba,
unsigned int count, userptr_t buffer,
int ( * block_rw ) ( struct interface *control,
struct interface *data,
uint64_t lba, unsigned int count,
userptr_t buffer, size_t len ) ) {
union san_command_params params;
unsigned int remaining;
size_t frag_len;
int rc;
/* Initialise command parameters */
params.rw.block_rw = block_rw;
params.rw.buffer = buffer;
params.rw.lba = ( lba << sandev->blksize_shift );
params.rw.count = sandev->capacity.max_count;
remaining = ( count << sandev->blksize_shift );
/* Read/write fragments */
while ( remaining ) {
/* Determine fragment length */
if ( params.rw.count > remaining )
params.rw.count = remaining;
/* Execute command */
if ( ( rc = sandev_command ( sandev, sandev_command_rw,
&params ) ) != 0 )
return rc;
/* Move to next fragment */
frag_len = ( sandev->capacity.blksize * params.rw.count );
params.rw.buffer = userptr_add ( params.rw.buffer, frag_len );
params.rw.lba += params.rw.count;
remaining -= params.rw.count;
}
return 0;
}
/**
* Read from SAN device
*
* @v sandev SAN device
* @v lba Starting logical block address
* @v count Number of logical blocks
* @v buffer Data buffer
* @ret rc Return status code
*/
int sandev_read ( struct san_device *sandev, uint64_t lba,
unsigned int count, userptr_t buffer ) {
int rc;
/* Read from device */
if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_read ) ) != 0 )
return rc;
return 0;
}
/**
* Write to SAN device
*
* @v sandev SAN device
* @v lba Starting logical block address
* @v count Number of logical blocks
* @v buffer Data buffer
* @ret rc Return status code
*/
int sandev_write ( struct san_device *sandev, uint64_t lba,
unsigned int count, userptr_t buffer ) {
int rc;
/* Write to device */
if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_write ) ) != 0 )
return rc;
/* Quiesce system. This is a heuristic designed to ensure
* that the system is quiesced before Windows starts up, since
* a Windows SAN boot will typically write a status flag to
* the disk as its last action before transferring control to
* the native drivers.
*/
quiesce();
return 0;
}
/**
* Describe SAN device
*
* @v sandev SAN device
* @ret rc Return status code
*
* Allow connections to progress until all existent path descriptors
* are complete.
*/
static int sandev_describe ( struct san_device *sandev ) {
struct san_path *sanpath;
struct acpi_descriptor *desc;
int rc;
/* Wait for all paths to be either described or closed */
while ( 1 ) {
/* Allow connections to progress */
step();
/* Fail if any closed path has an incomplete descriptor */
list_for_each_entry ( sanpath, &sandev->closed, list ) {
desc = sanpath->desc;
if ( ! desc )
continue;
if ( ( rc = desc->model->complete ( desc ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x.%d could not be "
"described: %s\n", sandev->drive,
sanpath->index, strerror ( rc ) );
return rc;
}
}
/* Succeed if no paths have an incomplete descriptor */
rc = 0;
list_for_each_entry ( sanpath, &sandev->opened, list ) {
desc = sanpath->desc;
if ( ! desc )
continue;
if ( ( rc = desc->model->complete ( desc ) ) != 0 )
break;
}
if ( rc == 0 )
return 0;
}
}
/**
* Remove SAN device descriptors
*
* @v sandev SAN device
*/
static void sandev_undescribe ( struct san_device *sandev ) {
struct san_path *sanpath;
unsigned int i;
/* Remove all ACPI descriptors */
for ( i = 0 ; i < sandev->paths ; i++ ) {
sanpath = &sandev->path[i];
if ( sanpath->desc ) {
acpi_del ( sanpath->desc );
sanpath->desc = NULL;
}
}
}
/**
* Configure SAN device as a CD-ROM, if applicable
*
* @v sandev SAN device
* @ret rc Return status code
*
* Both BIOS and UEFI require SAN devices to be accessed with a block
* size of 2048. While we could require the user to configure the
* block size appropriately, this is non-trivial and would impose a
* substantial learning effort on the user. Instead, we check for the
* presence of the ISO9660 primary volume descriptor and, if found,
* then we force a block size of 2048 and map read/write requests
* appropriately.
*/
static int sandev_parse_iso9660 ( struct san_device *sandev ) {
static const struct iso9660_primary_descriptor_fixed primary_check = {
.type = ISO9660_TYPE_PRIMARY,
.id = ISO9660_ID,
};
union {
struct iso9660_primary_descriptor primary;
char bytes[ISO9660_BLKSIZE];
} *scratch;
unsigned int blksize;
unsigned int blksize_shift;
unsigned int lba;
unsigned int count;
int rc;
/* Calculate required blocksize shift for potential CD-ROM access */
blksize = sandev->capacity.blksize;
blksize_shift = 0;
while ( blksize < ISO9660_BLKSIZE ) {
blksize <<= 1;
blksize_shift++;
}
if ( blksize > ISO9660_BLKSIZE ) {
/* Cannot be a CD-ROM. This is not an error. */
rc = 0;
goto invalid_blksize;
}
lba = ( ISO9660_PRIMARY_LBA << blksize_shift );
count = ( 1 << blksize_shift );
/* Allocate scratch area */
scratch = malloc ( ISO9660_BLKSIZE );
if ( ! scratch ) {
rc = -ENOMEM;
goto err_alloc;
}
/* Read primary volume descriptor */
if ( ( rc = sandev_read ( sandev, lba, count,
virt_to_user ( scratch ) ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x could not read ISO9660 primary"
"volume descriptor: %s\n",
sandev->drive, strerror ( rc ) );
goto err_rw;
}
/* Configure as CD-ROM if applicable */
if ( memcmp ( &scratch->primary.fixed, &primary_check,
sizeof ( primary_check ) ) == 0 ) {
DBGC ( sandev, "SAN %#02x contains an ISO9660 filesystem; "
"treating as CD-ROM\n", sandev->drive );
sandev->blksize_shift = blksize_shift;
sandev->is_cdrom = 1;
}
err_rw:
free ( scratch );
err_alloc:
invalid_blksize:
return rc;
}
/**
* Allocate SAN device
*
* @v uris List of URIs
* @v count Number of URIs
* @v priv_size Size of private data
* @ret sandev SAN device, or NULL
*/
struct san_device * alloc_sandev ( struct uri **uris, unsigned int count,
size_t priv_size ) {
struct san_device *sandev;
struct san_path *sanpath;
size_t size;
unsigned int i;
/* Allocate and initialise structure */
size = ( sizeof ( *sandev ) + ( count * sizeof ( sandev->path[0] ) ) );
sandev = zalloc ( size + priv_size );
if ( ! sandev )
return NULL;
ref_init ( &sandev->refcnt, sandev_free );
intf_init ( &sandev->command, &sandev_command_desc, &sandev->refcnt );
timer_init ( &sandev->timer, sandev_command_expired, &sandev->refcnt );
sandev->priv = ( ( ( void * ) sandev ) + size );
sandev->paths = count;
INIT_LIST_HEAD ( &sandev->opened );
INIT_LIST_HEAD ( &sandev->closed );
for ( i = 0 ; i < count ; i++ ) {
sanpath = &sandev->path[i];
sanpath->sandev = sandev;
sanpath->index = i;
sanpath->uri = uri_get ( uris[i] );
list_add_tail ( &sanpath->list, &sandev->closed );
intf_init ( &sanpath->block, &sanpath_block_desc,
&sandev->refcnt );
process_init_stopped ( &sanpath->process, &sanpath_process_desc,
&sandev->refcnt );
sanpath->path_rc = -EINPROGRESS;
}
return sandev;
}
/**
* Register SAN device
*
* @v sandev SAN device
* @v drive Drive number
* @v flags Flags
* @ret rc Return status code
*/
int register_sandev ( struct san_device *sandev, unsigned int drive,
unsigned int flags ) {
int rc;
/* Check that drive number is not in use */
if ( sandev_find ( drive ) != NULL ) {
DBGC ( sandev, "SAN %#02x is already in use\n", drive );
rc = -EADDRINUSE;
goto err_in_use;
}
/* Record drive number and flags */
sandev->drive = drive;
sandev->flags = flags;
/* Check that device is capable of being opened (i.e. that all
* URIs are well-formed and that at least one path is
* working).
*/
if ( ( rc = sandev_reopen ( sandev ) ) != 0 )
goto err_reopen;
/* Describe device */
if ( ( rc = sandev_describe ( sandev ) ) != 0 )
goto err_describe;
/* Read device capacity */
if ( ( rc = sandev_command ( sandev, sandev_command_read_capacity,
NULL ) ) != 0 )
goto err_capacity;
/* Configure as a CD-ROM, if applicable */
if ( ( rc = sandev_parse_iso9660 ( sandev ) ) != 0 )
goto err_iso9660;
/* Add to list of SAN devices */
list_add_tail ( &sandev->list, &san_devices );
DBGC ( sandev, "SAN %#02x registered\n", sandev->drive );
return 0;
list_del ( &sandev->list );
err_iso9660:
err_capacity:
err_describe:
err_reopen:
sandev_restart ( sandev, rc );
sandev_undescribe ( sandev );
err_in_use:
return rc;
}
/**
* Unregister SAN device
*
* @v sandev SAN device
*/
void unregister_sandev ( struct san_device *sandev ) {
/* Sanity check */
assert ( ! timer_running ( &sandev->timer ) );
/* Remove from list of SAN devices */
list_del ( &sandev->list );
/* Shut down interfaces */
sandev_restart ( sandev, 0 );
/* Remove ACPI descriptors */
sandev_undescribe ( sandev );
DBGC ( sandev, "SAN %#02x unregistered\n", sandev->drive );
}
/** The "san-drive" setting */
const struct setting san_drive_setting __setting ( SETTING_SANBOOT_EXTRA,
san-drive ) = {
.name = "san-drive",
.description = "SAN drive number",
.tag = DHCP_EB_SAN_DRIVE,
.type = &setting_type_uint8,
};
/**
* Get default SAN drive number
*
* @ret drive Default drive number
*/
unsigned int san_default_drive ( void ) {
unsigned long drive;
/* Use "san-drive" setting, if specified */
if ( fetch_uint_setting ( NULL, &san_drive_setting, &drive ) >= 0 )
return drive;
/* Otherwise, default to booting from first hard disk */
return SAN_DEFAULT_DRIVE;
}
/** The "san-retries" setting */
const struct setting san_retries_setting __setting ( SETTING_SANBOOT_EXTRA,
san-retries ) = {
.name = "san-retries",
.description = "SAN retry count",
.tag = DHCP_EB_SAN_RETRY,
.type = &setting_type_int8,
};
/**
* Apply SAN boot settings
*
* @ret rc Return status code
*/
static int sandev_apply ( void ) {
/* Apply "san-retries" setting */
if ( fetch_uint_setting ( NULL, &san_retries_setting,
&san_retries ) < 0 ) {
san_retries = SAN_DEFAULT_RETRIES;
}
return 0;
}
/** Settings applicator */
struct settings_applicator sandev_applicator __settings_applicator = {
.apply = sandev_apply,
};

View File

@ -1478,9 +1478,9 @@ struct setting * find_setting ( const char *name ) {
* @v name Name
* @ret tag Tag number, or 0 if not a valid number
*/
static unsigned long parse_setting_tag ( const char *name ) {
static uint64_t parse_setting_tag ( const char *name ) {
char *tmp = ( ( char * ) name );
unsigned long tag = 0;
uint64_t tag = 0;
while ( 1 ) {
tag = ( ( tag << 8 ) | strtoul ( tmp, &tmp, 0 ) );
@ -2396,6 +2396,15 @@ const struct setting root_path_setting __setting ( SETTING_SANBOOT, root-path)={
.type = &setting_type_string,
};
/** SAN filename setting */
const struct setting san_filename_setting __setting ( SETTING_SANBOOT,
san-filename ) = {
.name = "san-filename",
.description = "SAN filename",
.tag = DHCP_EB_SAN_FILENAME,
.type = &setting_type_string,
};
/** Username setting */
const struct setting username_setting __setting ( SETTING_AUTH, username ) = {
.name = "username",
@ -2429,6 +2438,15 @@ const struct setting user_class_setting __setting ( SETTING_HOST_EXTRA,
.type = &setting_type_string,
};
/** DHCP vendor class setting */
const struct setting vendor_class_setting __setting ( SETTING_HOST_EXTRA,
vendor-class ) = {
.name = "vendor-class",
.description = "DHCP vendor class",
.tag = DHCP_VENDOR_CLASS_ID,
.type = &setting_type_string,
};
/******************************************************************************
*
* Built-in settings block

View File

@ -90,19 +90,21 @@ void mdelay ( unsigned long msecs ) {
}
/**
* Sleep (interruptibly) for a fixed number of seconds
* Sleep (possibly interruptibly) for a fixed number of seconds
*
* @v secs Number of seconds for which to delay
* @v interrupted Interrupt checking method, or NULL
* @ret secs Number of seconds remaining, if interrupted
*/
unsigned int sleep ( unsigned int secs ) {
static unsigned int sleep_interruptible ( unsigned int secs,
int ( * interrupted ) ( void ) ) {
unsigned long start = currticks();
unsigned long now;
for ( ; secs ; secs-- ) {
while ( ( ( now = currticks() ) - start ) < TICKS_PER_SEC ) {
step();
if ( iskey() && ( getchar() == CTRL_C ) )
if ( interrupted && interrupted() )
return secs;
cpu_nap();
}
@ -112,6 +114,37 @@ unsigned int sleep ( unsigned int secs ) {
return 0;
}
/**
* Check if sleep has been interrupted by keypress
*
* @ret interrupted Sleep has been interrupted
*/
static int keypress_interrupted ( void ) {
return ( iskey() && ( getchar() == CTRL_C ) );
}
/**
* Sleep (interruptibly) for a fixed number of seconds
*
* @v secs Number of seconds for which to delay
* @ret secs Number of seconds remaining, if interrupted
*/
unsigned int sleep ( unsigned int secs ) {
return sleep_interruptible ( secs, keypress_interrupted );
}
/**
* Sleep (uninterruptibly) for a fixed number of seconds
*
* @v secs Number of seconds for which to delay
*/
void sleep_fixed ( unsigned int secs ) {
sleep_interruptible ( secs, NULL );
}
/**
* Find a working timer
*

View File

@ -306,11 +306,11 @@ int xfer_vprintf ( struct interface *intf, const char *format,
/* Create temporary string */
va_copy ( args_tmp, args );
len = vasprintf ( &buf, format, args );
va_end ( args_tmp );
if ( len < 0 ) {
rc = len;
goto err_asprintf;
}
va_end ( args_tmp );
/* Transmit string */
if ( ( rc = xfer_deliver_raw ( intf, buf, len ) ) != 0 )

View File

@ -739,7 +739,7 @@ static size_t asn1_header ( struct asn1_builder_header *header,
* @v extra Extra space to prepend
* @ret rc Return status code
*/
static int asn1_grow ( struct asn1_builder *builder, size_t extra ) {
int asn1_grow ( struct asn1_builder *builder, size_t extra ) {
size_t new_len;
void *new;

280
third_party/ipxe/src/crypto/md4.c vendored Normal file
View File

@ -0,0 +1,280 @@
/*
* Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* MD4 algorithm
*
*/
#include <stdint.h>
#include <string.h>
#include <byteswap.h>
#include <assert.h>
#include <ipxe/rotate.h>
#include <ipxe/crypto.h>
#include <ipxe/asn1.h>
#include <ipxe/md4.h>
/** MD4 variables */
struct md4_variables {
/* This layout matches that of struct md4_digest_data,
* allowing for efficient endianness-conversion,
*/
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
uint32_t w[16];
} __attribute__ (( packed ));
/** MD4 shift amounts */
static const uint8_t r[3][4] = {
{ 3, 7, 11, 19 },
{ 3, 5, 9, 13 },
{ 3, 9, 11, 15 },
};
/**
* f(b,c,d,w) for steps 0 to 15
*
* @v v MD4 variables
* @v i Index within round
* @ret f f(b,c,d,w)
*/
static uint32_t md4_f_0_15 ( struct md4_variables *v, unsigned int i ) {
return ( ( ( v->b & v->c ) | ( ~v->b & v->d ) ) + v->w[i] );
}
/**
* f(b,c,d,w) for steps 16 to 31
*
* @v v MD4 variables
* @v i Index within round
* @ret f f(b,c,d,w)
*/
static uint32_t md4_f_16_31 ( struct md4_variables *v, unsigned int i ) {
return ( ( ( v->b & v->c ) | ( v->b & v->d ) | ( v->c & v->d ) ) +
v->w[ ( ( i << 2 ) | ( i >> 2 ) ) % 16 ] );
}
/**
* f(b,c,d,w) for steps 32 to 47
*
* @v v MD4 variables
* @v i Index within round
* @ret f f(b,c,d,w)
*/
static uint32_t md4_f_32_47 ( struct md4_variables *v, unsigned int i ) {
static const uint8_t reverse[16] = {
0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
};
return ( ( v->b ^ v->c ^ v->d ) + v->w[reverse[i]] );
}
/** An MD4 step function */
struct md4_step {
/**
* Calculate f(b,c,d,w)
*
* @v v MD4 variables
* @v i Index within round
* @ret f f(b,c,d,w)
*/
uint32_t ( * f ) ( struct md4_variables *v, unsigned int i );
/** Constant */
uint32_t constant;
};
/** MD4 steps */
static struct md4_step md4_steps[4] = {
/** 0 to 15 */
{ .f = md4_f_0_15, .constant = 0x00000000UL },
/** 16 to 31 */
{ .f = md4_f_16_31, .constant = 0x5a827999UL },
/** 32 to 47 */
{ .f = md4_f_32_47, .constant = 0x6ed9eba1UL },
};
/**
* Initialise MD4 algorithm
*
* @v ctx MD4 context
*/
static void md4_init ( void *ctx ) {
struct md4_context *context = ctx;
context->ddd.dd.digest.h[0] = cpu_to_le32 ( 0x67452301 );
context->ddd.dd.digest.h[1] = cpu_to_le32 ( 0xefcdab89 );
context->ddd.dd.digest.h[2] = cpu_to_le32 ( 0x98badcfe );
context->ddd.dd.digest.h[3] = cpu_to_le32 ( 0x10325476 );
context->len = 0;
}
/**
* Calculate MD4 digest of accumulated data
*
* @v context MD4 context
*/
static void md4_digest ( struct md4_context *context ) {
union {
union md4_digest_data_dwords ddd;
struct md4_variables v;
} u;
uint32_t *a = &u.v.a;
uint32_t *b = &u.v.b;
uint32_t *c = &u.v.c;
uint32_t *d = &u.v.d;
uint32_t *w = u.v.w;
uint32_t f;
uint32_t temp;
struct md4_step *step;
unsigned int round;
unsigned int i;
/* Sanity checks */
assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 );
linker_assert ( &u.ddd.dd.digest.h[0] == a, md4_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[1] == b, md4_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[2] == c, md4_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[3] == d, md4_bad_layout );
linker_assert ( &u.ddd.dd.data.dword[0] == w, md4_bad_layout );
DBGC ( context, "MD4 digesting:\n" );
DBGC_HDA ( context, 0, &context->ddd.dd.digest,
sizeof ( context->ddd.dd.digest ) );
DBGC_HDA ( context, context->len, &context->ddd.dd.data,
sizeof ( context->ddd.dd.data ) );
/* Convert h[0..3] to host-endian, and initialise a, b, c, d,
* and x[0..15]
*/
for ( i = 0 ; i < ( sizeof ( u.ddd.dword ) /
sizeof ( u.ddd.dword[0] ) ) ; i++ ) {
le32_to_cpus ( &context->ddd.dword[i] );
u.ddd.dword[i] = context->ddd.dword[i];
}
/* Main loop */
for ( i = 0 ; i < 48 ; i++ ) {
round = ( i / 16 );
step = &md4_steps[round];
f = step->f ( &u.v, ( i % 16 ) );
temp = *d;
*d = *c;
*c = *b;
*b = rol32 ( ( *a + f + step->constant ), r[round][ i % 4 ] );
*a = temp;
DBGC2 ( context, "%2d : %08x %08x %08x %08x\n",
i, *a, *b, *c, *d );
}
/* Add chunk to hash and convert back to little-endian */
for ( i = 0 ; i < 4 ; i++ ) {
context->ddd.dd.digest.h[i] =
cpu_to_le32 ( context->ddd.dd.digest.h[i] +
u.ddd.dd.digest.h[i] );
}
DBGC ( context, "MD4 digested:\n" );
DBGC_HDA ( context, 0, &context->ddd.dd.digest,
sizeof ( context->ddd.dd.digest ) );
}
/**
* Accumulate data with MD4 algorithm
*
* @v ctx MD4 context
* @v data Data
* @v len Length of data
*/
static void md4_update ( void *ctx, const void *data, size_t len ) {
struct md4_context *context = ctx;
const uint8_t *byte = data;
size_t offset;
/* Accumulate data a byte at a time, performing the digest
* whenever we fill the data buffer
*/
while ( len-- ) {
offset = ( context->len % sizeof ( context->ddd.dd.data ) );
context->ddd.dd.data.byte[offset] = *(byte++);
context->len++;
if ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 )
md4_digest ( context );
}
}
/**
* Generate MD4 digest
*
* @v ctx MD4 context
* @v out Output buffer
*/
static void md4_final ( void *ctx, void *out ) {
struct md4_context *context = ctx;
uint64_t len_bits;
uint8_t pad;
/* Record length before pre-processing */
len_bits = cpu_to_le64 ( ( ( uint64_t ) context->len ) * 8 );
/* Pad with a single "1" bit followed by as many "0" bits as required */
pad = 0x80;
do {
md4_update ( ctx, &pad, sizeof ( pad ) );
pad = 0x00;
} while ( ( context->len % sizeof ( context->ddd.dd.data ) ) !=
offsetof ( typeof ( context->ddd.dd.data ), final.len ) );
/* Append length (in bits) */
md4_update ( ctx, &len_bits, sizeof ( len_bits ) );
assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 );
/* Copy out final digest */
memcpy ( out, &context->ddd.dd.digest,
sizeof ( context->ddd.dd.digest ) );
}
/** MD4 algorithm */
struct digest_algorithm md4_algorithm = {
.name = "md4",
.ctxsize = sizeof ( struct md4_context ),
.blocksize = sizeof ( union md4_block ),
.digestsize = sizeof ( struct md4_digest ),
.init = md4_init,
.update = md4_update,
.final = md4_final,
};
/** "md4" object identifier */
static uint8_t oid_md4[] = { ASN1_OID_MD4 };
/** "md4" OID-identified algorithm */
struct asn1_algorithm oid_md4_algorithm __asn1_algorithm = {
.name = "md4",
.digest = &md4_algorithm,
.oid = ASN1_OID_CURSOR ( oid_md4 ),
};

View File

@ -66,11 +66,11 @@ static const uint32_t k[64] = {
};
/** MD5 shift amounts */
static const uint8_t r[64] = {
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
static const uint8_t r[4][4] = {
{ 7, 12, 17, 22 },
{ 5, 9, 14, 20 },
{ 4, 11, 16, 23 },
{ 6, 10, 15, 21 },
};
/**
@ -174,6 +174,7 @@ static void md5_digest ( struct md5_context *context ) {
uint32_t g;
uint32_t temp;
struct md5_step *step;
unsigned int round;
unsigned int i;
/* Sanity checks */
@ -201,19 +202,21 @@ static void md5_digest ( struct md5_context *context ) {
/* Main loop */
for ( i = 0 ; i < 64 ; i++ ) {
step = &md5_steps[ i / 16 ];
round = ( i / 16 );
step = &md5_steps[round];
f = step->f ( &u.v );
g = ( ( ( step->coefficient * i ) + step->constant ) % 16 );
temp = *d;
*d = *c;
*c = *b;
*b = ( *b + rol32 ( ( *a + f + k[i] + w[g] ), r[i] ) );
*b = ( *b + rol32 ( ( *a + f + k[i] + w[g] ),
r[round][ i % 4 ] ) );
*a = temp;
DBGC2 ( context, "%2d : %08x %08x %08x %08x\n",
i, *a, *b, *c, *d );
}
/* Add chunk to hash and convert back to big-endian */
/* Add chunk to hash and convert back to little-endian */
for ( i = 0 ; i < 4 ; i++ ) {
context->ddd.dd.digest.h[i] =
cpu_to_le32 ( context->ddd.dd.digest.h[i] +

334
third_party/ipxe/src/crypto/ntlm.c vendored Normal file
View File

@ -0,0 +1,334 @@
/*
* Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* NT LAN Manager (NTLM) authentication
*
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/md4.h>
#include <ipxe/md5.h>
#include <ipxe/hmac.h>
#include <ipxe/ntlm.h>
/** Negotiate message
*
* This message content is fixed since there is no need to specify the
* calling workstation name or domain name, and the set of flags is
* mandated by the MS-NLMP specification.
*/
const struct ntlm_negotiate ntlm_negotiate = {
.header = {
.magic = NTLM_MAGIC,
.type = cpu_to_le32 ( NTLM_NEGOTIATE ),
},
.flags = cpu_to_le32 ( NTLM_NEGOTIATE_EXTENDED_SESSIONSECURITY |
NTLM_NEGOTIATE_ALWAYS_SIGN |
NTLM_NEGOTIATE_NTLM |
NTLM_REQUEST_TARGET |
NTLM_NEGOTIATE_UNICODE ),
};
/**
* Parse NTLM Challenge
*
* @v challenge Challenge message
* @v len Length of Challenge message
* @v info Challenge information to fill in
* @ret rc Return status code
*/
int ntlm_challenge ( struct ntlm_challenge *challenge, size_t len,
struct ntlm_challenge_info *info ) {
size_t offset;
DBGC ( challenge, "NTLM challenge message:\n" );
DBGC_HDA ( challenge, 0, challenge, len );
/* Sanity checks */
if ( len < sizeof ( *challenge ) ) {
DBGC ( challenge, "NTLM underlength challenge (%zd bytes)\n",
len );
return -EINVAL;
}
/* Extract nonce */
info->nonce = &challenge->nonce;
DBGC ( challenge, "NTLM challenge nonce:\n" );
DBGC_HDA ( challenge, 0, info->nonce, sizeof ( *info->nonce ) );
/* Extract target information */
info->len = le16_to_cpu ( challenge->info.len );
offset = le32_to_cpu ( challenge->info.offset );
if ( ( offset > len ) ||
( info->len > ( len - offset ) ) ) {
DBGC ( challenge, "NTLM target information outside "
"challenge\n" );
DBGC_HDA ( challenge, 0, challenge, len );
return -EINVAL;
}
info->target = ( ( ( void * ) challenge ) + offset );
DBGC ( challenge, "NTLM challenge target information:\n" );
DBGC_HDA ( challenge, 0, info->target, info->len );
return 0;
}
/**
* Calculate NTLM verification key
*
* @v domain Domain name (or NULL)
* @v username User name (or NULL)
* @v password Password (or NULL)
* @v key Key to fill in
*
* This is the NTOWFv2() function as defined in MS-NLMP.
*/
void ntlm_key ( const char *domain, const char *username,
const char *password, struct ntlm_key *key ) {
struct digest_algorithm *md4 = &md4_algorithm;
struct digest_algorithm *md5 = &md5_algorithm;
union {
uint8_t md4[MD4_CTX_SIZE];
uint8_t md5[MD5_CTX_SIZE];
} ctx;
uint8_t digest[MD4_DIGEST_SIZE];
size_t digest_len;
uint8_t c;
uint16_t wc;
/* Use empty usernames/passwords if not specified */
if ( ! domain )
domain = "";
if ( ! username )
username = "";
if ( ! password )
password = "";
/* Construct MD4 digest of (Unicode) password */
digest_init ( md4, ctx.md4 );
while ( ( c = *(password++) ) ) {
wc = cpu_to_le16 ( c );
digest_update ( md4, ctx.md4, &wc, sizeof ( wc ) );
}
digest_final ( md4, ctx.md4, digest );
/* Construct HMAC-MD5 of (Unicode) upper-case username */
digest_len = sizeof ( digest );
hmac_init ( md5, ctx.md5, digest, &digest_len );
while ( ( c = *(username++) ) ) {
wc = cpu_to_le16 ( toupper ( c ) );
hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) );
}
while ( ( c = *(domain++) ) ) {
wc = cpu_to_le16 ( c );
hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) );
}
hmac_final ( md5, ctx.md5, digest, &digest_len, key->raw );
DBGC ( key, "NTLM key:\n" );
DBGC_HDA ( key, 0, key, sizeof ( *key ) );
}
/**
* Construct NTLM responses
*
* @v info Challenge information
* @v key Verification key
* @v nonce Nonce, or NULL to use a random nonce
* @v lm LAN Manager response to fill in
* @v nt NT response to fill in
*/
void ntlm_response ( struct ntlm_challenge_info *info, struct ntlm_key *key,
struct ntlm_nonce *nonce, struct ntlm_lm_response *lm,
struct ntlm_nt_response *nt ) {
struct digest_algorithm *md5 = &md5_algorithm;
struct ntlm_nonce tmp_nonce;
uint8_t ctx[MD5_CTX_SIZE];
size_t key_len = sizeof ( *key );
unsigned int i;
/* Generate random nonce, if needed */
if ( ! nonce ) {
for ( i = 0 ; i < sizeof ( tmp_nonce ) ; i++ )
tmp_nonce.raw[i] = random();
nonce = &tmp_nonce;
}
/* Construct LAN Manager response */
memcpy ( &lm->nonce, nonce, sizeof ( lm->nonce ) );
hmac_init ( md5, ctx, key->raw, &key_len );
hmac_update ( md5, ctx, info->nonce, sizeof ( *info->nonce ) );
hmac_update ( md5, ctx, &lm->nonce, sizeof ( lm->nonce ) );
hmac_final ( md5, ctx, key->raw, &key_len, lm->digest );
DBGC ( key, "NTLM LAN Manager response:\n" );
DBGC_HDA ( key, 0, lm, sizeof ( *lm ) );
/* Construct NT response */
memset ( nt, 0, sizeof ( *nt ) );
nt->version = NTLM_VERSION_NTLMV2;
nt->high = NTLM_VERSION_NTLMV2;
memcpy ( &nt->nonce, nonce, sizeof ( nt->nonce ) );
hmac_init ( md5, ctx, key->raw, &key_len );
hmac_update ( md5, ctx, info->nonce, sizeof ( *info->nonce ) );
hmac_update ( md5, ctx, &nt->version,
( sizeof ( *nt ) -
offsetof ( typeof ( *nt ), version ) ) );
hmac_update ( md5, ctx, info->target, info->len );
hmac_update ( md5, ctx, &nt->zero, sizeof ( nt->zero ) );
hmac_final ( md5, ctx, key->raw, &key_len, nt->digest );
DBGC ( key, "NTLM NT response prefix:\n" );
DBGC_HDA ( key, 0, nt, sizeof ( *nt ) );
}
/**
* Append data to NTLM message
*
* @v header Message header, or NULL to only calculate next payload
* @v data Data descriptor
* @v payload Data payload
* @v len Length of data
* @ret payload Next data payload
*/
static void * ntlm_append ( struct ntlm_header *header, struct ntlm_data *data,
void *payload, size_t len ) {
/* Populate data descriptor */
if ( header ) {
data->offset = cpu_to_le32 ( payload - ( ( void * ) header ) );
data->len = data->max_len = cpu_to_le16 ( len );
}
return ( payload + len );
}
/**
* Append Unicode string data to NTLM message
*
* @v header Message header, or NULL to only calculate next payload
* @v data Data descriptor
* @v payload Data payload
* @v string String to append, or NULL
* @ret payload Next data payload
*/
static void * ntlm_append_string ( struct ntlm_header *header,
struct ntlm_data *data, void *payload,
const char *string ) {
uint16_t *tmp = payload;
uint8_t c;
/* Convert string to Unicode */
for ( tmp = payload ; ( string && ( c = *(string++) ) ) ; tmp++ ) {
if ( header )
*tmp = cpu_to_le16 ( c );
}
/* Append string data */
return ntlm_append ( header, data, payload,
( ( ( void * ) tmp ) - payload ) );
}
/**
* Construct NTLM Authenticate message
*
* @v info Challenge information
* @v domain Domain name, or NULL
* @v username User name, or NULL
* @v workstation Workstation name, or NULL
* @v lm LAN Manager response
* @v nt NT response
* @v auth Message to fill in, or NULL to only calculate length
* @ret len Length of message
*/
size_t ntlm_authenticate ( struct ntlm_challenge_info *info, const char *domain,
const char *username, const char *workstation,
struct ntlm_lm_response *lm,
struct ntlm_nt_response *nt,
struct ntlm_authenticate *auth ) {
void *tmp;
size_t nt_len;
size_t len;
/* Construct response header */
if ( auth ) {
memset ( auth, 0, sizeof ( *auth ) );
memcpy ( auth->header.magic, ntlm_negotiate.header.magic,
sizeof ( auth->header.magic ) );
auth->header.type = cpu_to_le32 ( NTLM_AUTHENTICATE );
auth->flags = ntlm_negotiate.flags;
}
tmp = ( ( ( void * ) auth ) + sizeof ( *auth ) );
/* Construct LAN Manager response */
if ( auth )
memcpy ( tmp, lm, sizeof ( *lm ) );
tmp = ntlm_append ( &auth->header, &auth->lm, tmp, sizeof ( *lm ) );
/* Construct NT response */
nt_len = ( sizeof ( *nt ) + info->len + sizeof ( nt->zero ) );
if ( auth ) {
memcpy ( tmp, nt, sizeof ( *nt ) );
memcpy ( ( tmp + sizeof ( *nt ) ), info->target, info->len );
memset ( ( tmp + sizeof ( *nt ) + info->len ), 0,
sizeof ( nt->zero ) );
}
tmp = ntlm_append ( &auth->header, &auth->nt, tmp, nt_len );
/* Populate domain, user, and workstation names */
tmp = ntlm_append_string ( &auth->header, &auth->domain, tmp, domain );
tmp = ntlm_append_string ( &auth->header, &auth->user, tmp, username );
tmp = ntlm_append_string ( &auth->header, &auth->workstation, tmp,
workstation );
/* Calculate length */
len = ( tmp - ( ( void * ) auth ) );
if ( auth ) {
DBGC ( auth, "NTLM authenticate message:\n" );
DBGC_HDA ( auth, 0, auth, len );
}
return len;
}
/**
* Calculate NTLM Authenticate message length
*
* @v info Challenge information
* @v domain Domain name, or NULL
* @v username User name, or NULL
* @v workstation Workstation name, or NULL
* @ret len Length of Authenticate message
*/
size_t ntlm_authenticate_len ( struct ntlm_challenge_info *info,
const char *domain, const char *username,
const char *workstation ) {
return ntlm_authenticate ( info, domain, username, workstation,
NULL, NULL, NULL );
}

View File

@ -625,7 +625,7 @@ static int rsa_match ( const void *private_key, size_t private_key_len,
/** RSA public-key algorithm */
struct pubkey_algorithm rsa_algorithm = {
.name = "rsa",
.ctxsize = sizeof ( struct rsa_context ),
.ctxsize = RSA_CTX_SIZE,
.init = rsa_init,
.max_len = rsa_max_len,
.encrypt = rsa_encrypt,

View File

@ -28,6 +28,7 @@
FILE_LICENCE ( BSD2 );
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
@ -38,6 +39,7 @@ FILE_LICENCE ( BSD2 );
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/vlan.h>
#include <ipxe/tcpip.h>
#include <ipxe/dhcp.h>
#include <ipxe/iscsi.h>
#include <ipxe/ibft.h>
@ -54,37 +56,31 @@ FILE_LICENCE ( BSD2 );
*/
/**
* An iBFT created by iPXE
*
*/
struct ipxe_ibft {
/** The fixed section */
struct ibft_table table;
/** The Initiator section */
struct ibft_initiator initiator __attribute__ (( aligned ( 16 ) ));
/** The NIC section */
struct ibft_nic nic __attribute__ (( aligned ( 16 ) ));
/** The Target section */
struct ibft_target target __attribute__ (( aligned ( 16 ) ));
/** Strings block */
char strings[0];
} __attribute__ (( packed, aligned ( 16 ) ));
/**
* iSCSI string block descriptor
* iSCSI string buffer
*
* This is an internal structure that we use to keep track of the
* allocation of string data.
*/
struct ibft_strings {
/** The iBFT containing these strings */
struct ibft_table *table;
/** Offset of first free byte within iBFT */
size_t offset;
/** Total length of the iBFT */
/** Strings data */
char *data;
/** Starting offset of strings */
size_t start;
/** Total length */
size_t len;
};
/**
* Align structure within iBFT
*
* @v len Unaligned length (or offset)
* @ret len Aligned length (or offset)
*/
static inline size_t ibft_align ( size_t len ) {
return ( ( len + IBFT_ALIGN - 1 ) & ~( IBFT_ALIGN - 1 ) );
}
/**
* Fill in an IP address field within iBFT
*
@ -141,15 +137,29 @@ static const char * ibft_ipaddr ( struct ibft_ipaddr *ipaddr ) {
*/
static char * ibft_alloc_string ( struct ibft_strings *strings,
struct ibft_string *string, size_t len ) {
size_t new_len;
char *new_data;
char *dest;
if ( ( strings->offset + len ) >= strings->len )
/* Extend string data buffer */
new_len = ( strings->len + len + 1 /* NUL */ );
new_data = realloc ( strings->data, new_len );
if ( ! new_data )
return NULL;
strings->data = new_data;
string->offset = cpu_to_le16 ( strings->offset );
/* Fill in string field */
string->offset = cpu_to_le16 ( strings->start + strings->len );
string->len = cpu_to_le16 ( len );
strings->offset += ( len + 1 );
return ( ( ( char * ) strings->table ) + string->offset );
/* Zero string */
dest = ( strings->data + strings->len );
memset ( dest, 0, ( len + 1 /* NUL */ ) );
/* Update allocated length */
strings->len = new_len;
return dest;
}
/**
@ -217,8 +227,28 @@ static int ibft_set_string_setting ( struct settings *settings,
*/
static const char * ibft_string ( struct ibft_strings *strings,
struct ibft_string *string ) {
return ( string->offset ?
( ( ( char * ) strings->table ) + string->offset ) : NULL );
size_t offset = le16_to_cpu ( string->offset );
return ( offset ? ( strings->data + offset - strings->start ) : NULL );
}
/**
* Check if network device is required for the iBFT
*
* @v netdev Network device
* @ret is_required Network device is required
*/
static int ibft_netdev_is_required ( struct net_device *netdev ) {
struct iscsi_session *iscsi;
struct sockaddr_tcpip *st_target;
list_for_each_entry ( iscsi, &ibft_model.descs, desc.list ) {
st_target = ( struct sockaddr_tcpip * ) &iscsi->target_sockaddr;
if ( tcpip_netdev ( st_target ) == netdev )
return 1;
}
return 0;
}
/**
@ -245,29 +275,33 @@ static int ibft_fill_nic ( struct ibft_nic *nic,
nic->header.length = cpu_to_le16 ( sizeof ( *nic ) );
nic->header.flags = ( IBFT_FL_NIC_BLOCK_VALID |
IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED );
DBG ( "iBFT NIC %d is %s\n", nic->header.index, netdev->name );
/* Determine origin of IP address */
fetch_setting ( parent, &ip_setting, &origin, NULL, NULL, 0 );
nic->origin = ( ( origin == parent ) ?
IBFT_NIC_ORIGIN_MANUAL : IBFT_NIC_ORIGIN_DHCP );
DBG ( "iBFT NIC origin = %d\n", nic->origin );
DBG ( "iBFT NIC %d origin = %d\n", nic->header.index, nic->origin );
/* Extract values from configuration settings */
ibft_set_ipaddr_setting ( parent, &nic->ip_address, &ip_setting, 1 );
DBG ( "iBFT NIC IP = %s\n", ibft_ipaddr ( &nic->ip_address ) );
DBG ( "iBFT NIC %d IP = %s\n",
nic->header.index, ibft_ipaddr ( &nic->ip_address ) );
ibft_set_ipaddr_setting ( parent, &nic->gateway, &gateway_setting, 1 );
DBG ( "iBFT NIC gateway = %s\n", ibft_ipaddr ( &nic->gateway ) );
DBG ( "iBFT NIC %d gateway = %s\n",
nic->header.index, ibft_ipaddr ( &nic->gateway ) );
ibft_set_ipaddr_setting ( NULL, &nic->dns[0], &dns_setting,
( sizeof ( nic->dns ) /
sizeof ( nic->dns[0] ) ) );
ibft_set_ipaddr_setting ( parent, &nic->dhcp, &dhcp_server_setting, 1 );
DBG ( "iBFT NIC DNS = %s", ibft_ipaddr ( &nic->dns[0] ) );
DBG ( "iBFT NIC %d DNS = %s",
nic->header.index, ibft_ipaddr ( &nic->dns[0] ) );
DBG ( ", %s\n", ibft_ipaddr ( &nic->dns[1] ) );
if ( ( rc = ibft_set_string_setting ( NULL, strings, &nic->hostname,
&hostname_setting ) ) != 0 )
return rc;
DBG ( "iBFT NIC hostname = %s\n",
ibft_string ( strings, &nic->hostname ) );
DBG ( "iBFT NIC %d hostname = %s\n",
nic->header.index, ibft_string ( strings, &nic->hostname ) );
/* Derive subnet mask prefix from subnet mask */
fetch_ipv4_setting ( parent, &netmask_setting, &netmask_addr );
@ -277,19 +311,24 @@ static int ibft_fill_nic ( struct ibft_nic *nic,
netmask_addr.s_addr >>= 1;
}
nic->subnet_mask_prefix = netmask_count;
DBG ( "iBFT NIC subnet = /%d\n", nic->subnet_mask_prefix );
DBG ( "iBFT NIC %d subnet = /%d\n",
nic->header.index, nic->subnet_mask_prefix );
/* Extract values from net-device configuration */
nic->vlan = cpu_to_le16 ( vlan_tag ( netdev ) );
DBG ( "iBFT NIC VLAN = %02x\n", le16_to_cpu ( nic->vlan ) );
DBG ( "iBFT NIC %d VLAN = %02x\n",
nic->header.index, le16_to_cpu ( nic->vlan ) );
if ( ( rc = ll_protocol->eth_addr ( netdev->ll_addr,
nic->mac_address ) ) != 0 ) {
DBG ( "Could not determine iBFT MAC: %s\n", strerror ( rc ) );
DBG ( "Could not determine %s MAC: %s\n",
netdev->name, strerror ( rc ) );
return rc;
}
DBG ( "iBFT NIC MAC = %s\n", eth_ntoa ( nic->mac_address ) );
DBG ( "iBFT NIC %d MAC = %s\n",
nic->header.index, eth_ntoa ( nic->mac_address ) );
nic->pci_bus_dev_func = cpu_to_le16 ( netdev->dev->desc.location );
DBG ( "iBFT NIC PCI = %04x\n", le16_to_cpu ( nic->pci_bus_dev_func ) );
DBG ( "iBFT NIC %d PCI = %04x\n",
nic->header.index, le16_to_cpu ( nic->pci_bus_dev_func ) );
return 0;
}
@ -299,12 +338,12 @@ static int ibft_fill_nic ( struct ibft_nic *nic,
*
* @v initiator Initiator portion of iBFT
* @v strings iBFT string block descriptor
* @v iscsi iSCSI session
* @v initiator_iqn Initiator IQN
* @ret rc Return status code
*/
static int ibft_fill_initiator ( struct ibft_initiator *initiator,
struct ibft_strings *strings,
struct iscsi_session *iscsi ) {
const char *initiator_iqn ) {
int rc;
/* Fill in common header */
@ -314,16 +353,56 @@ static int ibft_fill_initiator ( struct ibft_initiator *initiator,
initiator->header.flags = ( IBFT_FL_INITIATOR_BLOCK_VALID |
IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED );
/* Fill in hostname */
/* Fill in initiator name */
if ( ( rc = ibft_set_string ( strings, &initiator->initiator_name,
iscsi->initiator_iqn ) ) != 0 )
initiator_iqn ) ) != 0 )
return rc;
DBG ( "iBFT initiator hostname = %s\n",
DBG ( "iBFT initiator name = %s\n",
ibft_string ( strings, &initiator->initiator_name ) );
return 0;
}
/**
* Fill in Target NIC association
*
* @v target Target portion of iBFT
* @v iscsi iSCSI session
* @ret rc Return status code
*/
static int ibft_fill_target_nic_association ( struct ibft_target *target,
struct iscsi_session *iscsi ) {
struct sockaddr_tcpip *st_target =
( struct sockaddr_tcpip * ) &iscsi->target_sockaddr;
struct net_device *associated;
struct net_device *netdev;
/* Find network device used to reach target */
associated = tcpip_netdev ( st_target );
if ( ! associated ) {
DBG ( "iBFT target %d has no net device\n",
target->header.index );
return -EHOSTUNREACH;
}
/* Calculate association */
for_each_netdev ( netdev ) {
if ( netdev == associated ) {
DBG ( "iBFT target %d uses NIC %d (%s)\n",
target->header.index, target->nic_association,
netdev->name );
return 0;
}
if ( ! ibft_netdev_is_required ( netdev ) )
continue;
target->nic_association++;
}
DBG ( "iBFT target %d has impossible NIC %s\n",
target->header.index, netdev->name );
return -EINVAL;
}
/**
* Fill in Target CHAP portion of iBFT
*
@ -347,12 +426,12 @@ static int ibft_fill_target_chap ( struct ibft_target *target,
if ( ( rc = ibft_set_string ( strings, &target->chap_name,
iscsi->initiator_username ) ) != 0 )
return rc;
DBG ( "iBFT target username = %s\n",
DBG ( "iBFT target %d username = %s\n", target->header.index,
ibft_string ( strings, &target->chap_name ) );
if ( ( rc = ibft_set_string ( strings, &target->chap_secret,
iscsi->initiator_password ) ) != 0 )
return rc;
DBG ( "iBFT target password = <redacted>\n" );
DBG ( "iBFT target %d password = <redacted>\n", target->header.index );
return 0;
}
@ -382,12 +461,13 @@ static int ibft_fill_target_reverse_chap ( struct ibft_target *target,
if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_name,
iscsi->target_username ) ) != 0 )
return rc;
DBG ( "iBFT target reverse username = %s\n",
DBG ( "iBFT target %d reverse username = %s\n", target->header.index,
ibft_string ( strings, &target->chap_name ) );
if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_secret,
iscsi->target_password ) ) != 0 )
return rc;
DBG ( "iBFT target reverse password = <redacted>\n" );
DBG ( "iBFT target %d reverse password = <redacted>\n",
target->header.index );
return 0;
}
@ -403,6 +483,8 @@ static int ibft_fill_target_reverse_chap ( struct ibft_target *target,
static int ibft_fill_target ( struct ibft_target *target,
struct ibft_strings *strings,
struct iscsi_session *iscsi ) {
struct sockaddr_tcpip *st_target =
( struct sockaddr_tcpip * ) &iscsi->target_sockaddr;
struct sockaddr_in *sin_target =
( struct sockaddr_in * ) &iscsi->target_sockaddr;
int rc;
@ -416,17 +498,21 @@ static int ibft_fill_target ( struct ibft_target *target,
/* Fill in Target values */
ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr );
DBG ( "iBFT target IP = %s\n", ibft_ipaddr ( &target->ip_address ) );
target->socket = cpu_to_le16 ( ntohs ( sin_target->sin_port ) );
DBG ( "iBFT target port = %d\n", target->socket );
DBG ( "iBFT target %d IP = %s\n",
target->header.index, ibft_ipaddr ( &target->ip_address ) );
target->socket = cpu_to_le16 ( ntohs ( st_target->st_port ) );
DBG ( "iBFT target %d port = %d\n",
target->header.index, target->socket );
memcpy ( &target->boot_lun, &iscsi->lun, sizeof ( target->boot_lun ) );
DBG ( "iBFT target boot LUN = " SCSI_LUN_FORMAT "\n",
SCSI_LUN_DATA ( target->boot_lun ) );
DBG ( "iBFT target %d boot LUN = " SCSI_LUN_FORMAT "\n",
target->header.index, SCSI_LUN_DATA ( target->boot_lun ) );
if ( ( rc = ibft_set_string ( strings, &target->target_name,
iscsi->target_iqn ) ) != 0 )
return rc;
DBG ( "iBFT target name = %s\n",
DBG ( "iBFT target %d name = %s\n", target->header.index,
ibft_string ( strings, &target->target_name ) );
if ( ( rc = ibft_fill_target_nic_association ( target, iscsi ) ) != 0 )
return rc;
if ( ( rc = ibft_fill_target_chap ( target, strings, iscsi ) ) != 0 )
return rc;
if ( ( rc = ibft_fill_target_reverse_chap ( target, strings,
@ -437,62 +523,169 @@ static int ibft_fill_target ( struct ibft_target *target,
}
/**
* Fill in iBFT
* Check if iBFT descriptor is complete
*
* @v iscsi iSCSI session
* @v acpi ACPI table
* @v len Length of ACPI table
* @v desc ACPI descriptor
* @ret rc Return status code
*/
int ibft_describe ( struct iscsi_session *iscsi,
struct acpi_description_header *acpi,
size_t len ) {
struct ipxe_ibft *ibft =
container_of ( acpi, struct ipxe_ibft, table.acpi );
struct ibft_strings strings = {
.table = &ibft->table,
.offset = offsetof ( typeof ( *ibft ), strings ),
.len = len,
};
struct net_device *netdev;
int rc;
static int ibft_complete ( struct acpi_descriptor *desc ) {
struct iscsi_session *iscsi =
container_of ( desc, struct iscsi_session, desc );
/* Ugly hack. Now that we have a generic interface mechanism
* that can support ioctls, we can potentially eliminate this.
*/
netdev = last_opened_netdev();
if ( ! netdev ) {
DBGC ( iscsi, "iSCSI %p cannot guess network device\n",
iscsi );
return -ENODEV;
}
/* Fill in ACPI header */
ibft->table.acpi.signature = cpu_to_le32 ( IBFT_SIG );
ibft->table.acpi.length = cpu_to_le32 ( len );
ibft->table.acpi.revision = 1;
/* Fill in Control block */
ibft->table.control.header.structure_id = IBFT_STRUCTURE_ID_CONTROL;
ibft->table.control.header.version = 1;
ibft->table.control.header.length =
cpu_to_le16 ( sizeof ( ibft->table.control ) );
ibft->table.control.initiator =
cpu_to_le16 ( offsetof ( typeof ( *ibft ), initiator ) );
ibft->table.control.nic_0 =
cpu_to_le16 ( offsetof ( typeof ( *ibft ), nic ) );
ibft->table.control.target_0 =
cpu_to_le16 ( offsetof ( typeof ( *ibft ), target ) );
/* Fill in NIC, Initiator and Target blocks */
if ( ( rc = ibft_fill_nic ( &ibft->nic, &strings, netdev ) ) != 0 )
return rc;
if ( ( rc = ibft_fill_initiator ( &ibft->initiator, &strings,
iscsi ) ) != 0 )
return rc;
if ( ( rc = ibft_fill_target ( &ibft->target, &strings,
iscsi ) ) != 0 )
return rc;
/* Fail if we do not yet have the target address */
if ( ! iscsi->target_sockaddr.sa_family )
return -EAGAIN;
return 0;
}
/**
* Install iBFT
*
* @v install Installation method
* @ret rc Return status code
*/
static int ibft_install ( int ( * install ) ( struct acpi_header *acpi ) ) {
struct net_device *netdev;
struct iscsi_session *iscsi;
struct ibft_table *table;
struct ibft_initiator *initiator;
struct ibft_nic *nic;
struct ibft_target *target;
struct ibft_strings strings;
struct acpi_header *acpi;
void *data;
unsigned int targets = 0;
unsigned int pairs = 0;
size_t offset = 0;
size_t table_len;
size_t control_len;
size_t initiator_offset;
size_t nic_offset;
size_t target_offset;
size_t strings_offset;
size_t len;
unsigned int i;
int rc;
/* Calculate table sizes and offsets */
list_for_each_entry ( iscsi, &ibft_model.descs, desc.list )
targets++;
pairs = ( sizeof ( table->control.pair ) /
sizeof ( table->control.pair[0] ) );
if ( pairs < targets )
pairs = targets;
offset = offsetof ( typeof ( *table ), control.pair );
offset += ( pairs * sizeof ( table->control.pair[0] ) );
table_len = offset;
control_len = ( table_len - offsetof ( typeof ( *table ), control ) );
offset = ibft_align ( offset );
initiator_offset = offset;
offset += ibft_align ( sizeof ( *initiator ) );
nic_offset = offset;
offset += ( pairs * ibft_align ( sizeof ( *nic ) ) );
target_offset = offset;
offset += ( pairs * ibft_align ( sizeof ( *target ) ) );
strings_offset = offset;
strings.data = NULL;
strings.start = strings_offset;
strings.len = 0;
len = offset;
/* Do nothing if no targets exist */
if ( ! targets ) {
rc = 0;
goto no_targets;
}
/* Allocate table */
data = zalloc ( len );
if ( ! data ) {
rc = -ENOMEM;
goto err_alloc;
}
/* Fill in Control block */
table = data;
table->control.header.structure_id = IBFT_STRUCTURE_ID_CONTROL;
table->control.header.version = 1;
table->control.header.length = cpu_to_le16 ( control_len );
/* Fill in Initiator block */
initiator = ( data + initiator_offset );
table->control.initiator = cpu_to_le16 ( initiator_offset );
iscsi = list_first_entry ( &ibft_model.descs, struct iscsi_session,
desc.list );
if ( ( rc = ibft_fill_initiator ( initiator, &strings,
iscsi->initiator_iqn ) ) != 0 )
goto err_initiator;
/* Fill in NIC blocks */
i = 0;
for_each_netdev ( netdev ) {
if ( ! ibft_netdev_is_required ( netdev ) )
continue;
assert ( i < pairs );
table->control.pair[i].nic = nic_offset;
nic = ( data + nic_offset );
nic->header.index = i;
if ( ( rc = ibft_fill_nic ( nic, &strings, netdev ) ) != 0 )
goto err_nic;
i++;
nic_offset += ibft_align ( sizeof ( *nic ) );
}
/* Fill in Target blocks */
i = 0;
list_for_each_entry ( iscsi, &ibft_model.descs, desc.list ) {
assert ( i < pairs );
table->control.pair[i].target = target_offset;
target = ( data + target_offset );
target->header.index = i;
if ( ( rc = ibft_fill_target ( target, &strings, iscsi ) ) != 0)
goto err_target;
i++;
target_offset += ibft_align ( sizeof ( *target ) );
}
/* Reallocate table to include space for strings */
len += strings.len;
acpi = realloc ( data, len );
if ( ! acpi )
goto err_realloc;
data = NULL;
/* Fill in ACPI header */
acpi->signature = cpu_to_le32 ( IBFT_SIG );
acpi->length = cpu_to_le32 ( len );
acpi->revision = 1;
/* Append strings */
memcpy ( ( ( ( void * ) acpi ) + strings_offset ), strings.data,
strings.len );
/* Install ACPI table */
if ( ( rc = install ( acpi ) ) != 0 ) {
DBG ( "iBFT could not install: %s\n", strerror ( rc ) );
goto err_install;
}
err_install:
free ( acpi );
err_realloc:
err_target:
err_nic:
err_initiator:
free ( data );
err_alloc:
no_targets:
free ( strings.data );
return rc;
}
/** iBFT model */
struct acpi_model ibft_model __acpi_model = {
.descs = LIST_HEAD_INIT ( ibft_model.descs ),
.complete = ibft_complete,
.install = ibft_install,
};

View File

@ -40,8 +40,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
/** Maximum number of command retries */
#define SCSICMD_MAX_RETRIES 10
/** Maximum number of TEST UNIT READY retries */
#define SCSI_READY_MAX_RETRIES 10
/* Error numbers generated by SCSI sense data */
#define EIO_NO_SENSE __einfo_error ( EINFO_EIO_NO_SENSE )
@ -243,6 +243,8 @@ struct scsi_device {
struct interface ready;
/** TEST UNIT READY process */
struct process process;
/** TEST UNIT READY retry count */
unsigned int retries;
/** List of commands */
struct list_head cmds;
@ -283,9 +285,6 @@ struct scsi_command {
/** Command tag */
uint32_t tag;
/** Retry count */
unsigned int retries;
/** Private data */
uint8_t priv[0];
};
@ -377,8 +376,7 @@ static void scsicmd_free ( struct refcnt *refcnt ) {
struct scsi_command *scsicmd =
container_of ( refcnt, struct scsi_command, refcnt );
/* Remove from list of commands */
list_del ( &scsicmd->list );
/* Drop reference to SCSI device */
scsidev_put ( scsicmd->scsidev );
/* Free command */
@ -399,9 +397,14 @@ static void scsicmd_close ( struct scsi_command *scsicmd, int rc ) {
scsidev, scsicmd->tag, strerror ( rc ) );
}
/* Remove from list of commands */
list_del ( &scsicmd->list );
/* Shut down interfaces */
intf_shutdown ( &scsicmd->scsi, rc );
intf_shutdown ( &scsicmd->block, rc );
intfs_shutdown ( rc, &scsicmd->scsi, &scsicmd->block, NULL );
/* Drop list's reference */
scsicmd_put ( scsicmd );
}
/**
@ -449,28 +452,11 @@ static int scsicmd_command ( struct scsi_command *scsicmd ) {
* @v rc Reason for close
*/
static void scsicmd_done ( struct scsi_command *scsicmd, int rc ) {
struct scsi_device *scsidev = scsicmd->scsidev;
/* Restart SCSI interface */
intf_restart ( &scsicmd->scsi, rc );
/* SCSI targets have an annoying habit of returning occasional
* pointless "error" messages such as "power-on occurred", so
* we have to be prepared to retry commands.
*/
if ( ( rc != 0 ) && ( scsicmd->retries++ < SCSICMD_MAX_RETRIES ) ) {
/* Retry command */
DBGC ( scsidev, "SCSI %p tag %08x failed: %s\n",
scsidev, scsicmd->tag, strerror ( rc ) );
DBGC ( scsidev, "SCSI %p tag %08x retrying (retry %d)\n",
scsidev, scsicmd->tag, scsicmd->retries );
if ( ( rc = scsicmd_command ( scsicmd ) ) == 0 )
return;
}
/* If we didn't (successfully) reissue the command, hand over
* to the command completion handler.
*/
/* Hand over to the command completion handler */
scsicmd->type->done ( scsicmd, rc );
}
@ -757,9 +743,8 @@ static int scsidev_command ( struct scsi_device *scsidev,
if ( ( rc = scsicmd_command ( scsicmd ) ) != 0 )
goto err_command;
/* Attach to parent interface, mortalise self, and return */
/* Attach to parent interface, transfer reference to list, and return */
intf_plug_plug ( &scsicmd->block, block );
ref_put ( &scsicmd->refcnt );
return 0;
err_command:
@ -863,16 +848,12 @@ static void scsidev_close ( struct scsi_device *scsidev, int rc ) {
process_del ( &scsidev->process );
/* Shut down interfaces */
intf_shutdown ( &scsidev->block, rc );
intf_shutdown ( &scsidev->scsi, rc );
intf_shutdown ( &scsidev->ready, rc );
intfs_shutdown ( rc, &scsidev->block, &scsidev->scsi, &scsidev->ready,
NULL );
/* Shut down any remaining commands */
list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list ) {
scsicmd_get ( scsicmd );
list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list )
scsicmd_close ( scsicmd, rc );
scsicmd_put ( scsicmd );
}
}
/** SCSI device block interface operations */
@ -899,20 +880,40 @@ static struct interface_descriptor scsidev_block_desc =
static void scsidev_ready ( struct scsi_device *scsidev, int rc ) {
/* Shut down interface */
intf_shutdown ( &scsidev->ready, rc );
intf_restart ( &scsidev->ready, rc );
/* Close device on failure */
if ( rc != 0 ) {
DBGC ( scsidev, "SCSI %p not ready: %s\n",
scsidev, strerror ( rc ) );
scsidev_close ( scsidev, rc );
/* Mark device as ready, if applicable */
if ( rc == 0 ) {
DBGC ( scsidev, "SCSI %p unit is ready\n", scsidev );
scsidev->flags |= SCSIDEV_UNIT_READY;
xfer_window_changed ( &scsidev->block );
return;
}
DBGC ( scsidev, "SCSI %p not ready: %s\n", scsidev, strerror ( rc ) );
/* SCSI targets have an annoying habit of returning occasional
* pointless "error" messages such as "power-on occurred", so
* we have to be prepared to retry commands.
*
* For most commands, we rely on the caller (e.g. the generic
* SAN device layer) to retry commands as needed. However, a
* TEST UNIT READY failure is used as an indication that the
* whole SCSI device is unavailable and should be closed. We
* must therefore perform this retry loop within the SCSI
* layer.
*/
if ( scsidev->retries++ < SCSI_READY_MAX_RETRIES ) {
DBGC ( scsidev, "SCSI %p retrying (retry %d)\n",
scsidev, scsidev->retries );
scsidev->flags &= ~SCSIDEV_UNIT_TESTED;
process_add ( &scsidev->process );
return;
}
/* Mark device as ready */
scsidev->flags |= SCSIDEV_UNIT_READY;
xfer_window_changed ( &scsidev->block );
DBGC ( scsidev, "SCSI %p unit is ready\n", scsidev );
/* Close device */
DBGC ( scsidev, "SCSI %p never became ready: %s\n",
scsidev, strerror ( rc ) );
scsidev_close ( scsidev, rc );
}
/** SCSI device TEST UNIT READY interface operations */

View File

@ -113,13 +113,6 @@ struct srp_device {
/** Login completed successfully */
int logged_in;
/** Initiator port ID (for boot firmware table) */
union srp_port_id initiator;
/** Target port ID (for boot firmware table) */
union srp_port_id target;
/** SCSI LUN (for boot firmware table) */
struct scsi_lun lun;
/** List of active commands */
struct list_head commands;
};
@ -684,61 +677,6 @@ static size_t srpdev_window ( struct srp_device *srpdev ) {
return ( srpdev->logged_in ? ~( ( size_t ) 0 ) : 0 );
}
/**
* A (transport-independent) sBFT created by iPXE
*/
struct ipxe_sbft {
/** The table header */
struct sbft_table table;
/** The SCSI subtable */
struct sbft_scsi_subtable scsi;
/** The SRP subtable */
struct sbft_srp_subtable srp;
} __attribute__ (( packed, aligned ( 16 ) ));
/**
* Describe SRP device in an ACPI table
*
* @v srpdev SRP device
* @v acpi ACPI table
* @v len Length of ACPI table
* @ret rc Return status code
*/
static int srpdev_describe ( struct srp_device *srpdev,
struct acpi_description_header *acpi,
size_t len ) {
struct ipxe_sbft *sbft =
container_of ( acpi, struct ipxe_sbft, table.acpi );
int rc;
/* Sanity check */
if ( len < sizeof ( *sbft ) )
return -ENOBUFS;
/* Populate table */
sbft->table.acpi.signature = cpu_to_le32 ( SBFT_SIG );
sbft->table.acpi.length = cpu_to_le32 ( sizeof ( *sbft ) );
sbft->table.acpi.revision = 1;
sbft->table.scsi_offset =
cpu_to_le16 ( offsetof ( typeof ( *sbft ), scsi ) );
memcpy ( &sbft->scsi.lun, &srpdev->lun, sizeof ( sbft->scsi.lun ) );
sbft->table.srp_offset =
cpu_to_le16 ( offsetof ( typeof ( *sbft ), srp ) );
memcpy ( &sbft->srp.initiator, &srpdev->initiator,
sizeof ( sbft->srp.initiator ) );
memcpy ( &sbft->srp.target, &srpdev->target,
sizeof ( sbft->srp.target ) );
/* Ask transport layer to describe transport-specific portions */
if ( ( rc = acpi_describe ( &srpdev->socket, acpi, len ) ) != 0 ) {
DBGC ( srpdev, "SRP %p cannot describe transport layer: %s\n",
srpdev, strerror ( rc ) );
return rc;
}
return 0;
}
/** SRP device socket interface operations */
static struct interface_operation srpdev_socket_op[] = {
INTF_OP ( xfer_deliver, struct srp_device *, srpdev_deliver ),
@ -755,7 +693,6 @@ static struct interface_operation srpdev_scsi_op[] = {
INTF_OP ( scsi_command, struct srp_device *, srpdev_scsi_command ),
INTF_OP ( xfer_window, struct srp_device *, srpdev_window ),
INTF_OP ( intf_close, struct srp_device *, srpdev_close ),
INTF_OP ( acpi_describe, struct srp_device *, srpdev_describe ),
};
/** SRP device SCSI interface descriptor */
@ -797,11 +734,6 @@ int srp_open ( struct interface *block, struct interface *socket,
ntohl ( target->dwords[0] ), ntohl ( target->dwords[1] ),
ntohl ( target->dwords[2] ), ntohl ( target->dwords[3] ) );
/* Preserve parameters required for boot firmware table */
memcpy ( &srpdev->initiator, initiator, sizeof ( srpdev->initiator ) );
memcpy ( &srpdev->target, target, sizeof ( srpdev->target ) );
memcpy ( &srpdev->lun, lun, sizeof ( srpdev->lun ) );
/* Attach to socket interface and initiate login */
intf_plug_plug ( &srpdev->socket, socket );
tag = srp_new_tag ( srpdev );

View File

@ -843,12 +843,40 @@ int usb_control ( struct usb_device *usb, unsigned int request,
return rc;
}
/**
* Get default language ID
*
* @v usb USB device
* @ret language Language ID
*/
static unsigned int usb_get_default_language ( struct usb_device *usb ) {
struct {
struct usb_descriptor_header header;
uint16_t language[1];
} __attribute__ (( packed )) desc;
unsigned int language;
int rc;
/* Get descriptor */
if ( ( rc = usb_get_descriptor ( usb, 0, USB_STRING_DESCRIPTOR, 0, 0,
&desc.header, sizeof ( desc ) ) ) !=0){
DBGC ( usb, "USB %s has no default language: %s\n",
usb->name, strerror ( rc ) );
return USB_LANG_ENGLISH;
}
/* Use first language ID */
language = le16_to_cpu ( desc.language[0] );
DBGC2 ( usb, "USB %s default language %#04x\n", usb->name, language );
return language;
}
/**
* Get USB string descriptor
*
* @v usb USB device
* @v index String index
* @v language Language ID
* @v language Language ID, or 0 to use default
* @v buf Data buffer
* @v len Length of buffer
* @ret len String length (excluding NUL), or negative error
@ -864,6 +892,13 @@ int usb_get_string_descriptor ( struct usb_device *usb, unsigned int index,
unsigned int i;
int rc;
/* Use default language ID, if applicable */
if ( ( language == 0 ) && ( index != 0 ) ) {
if ( ! usb->language )
usb->language = usb_get_default_language ( usb );
language = usb->language;
}
/* Allocate buffer for string */
desc = malloc ( sizeof ( *desc ) );
if ( ! desc ) {
@ -1005,8 +1040,8 @@ static int usb_describe ( struct usb_device *usb,
}
/* Describe function */
memcpy ( &desc->class, &association->class,
sizeof ( desc->class ) );
memcpy ( &desc->class.class, &association->class,
sizeof ( desc->class.class ) );
desc->count = association->count;
for ( i = 0 ; i < association->count ; i++ )
interfaces[i] = ( first + i );
@ -1022,7 +1057,8 @@ static int usb_describe ( struct usb_device *usb,
}
/* Describe function */
memcpy ( &desc->class, &interface->class, sizeof ( desc->class ) );
memcpy ( &desc->class.class, &interface->class,
sizeof ( desc->class.class ) );
desc->count = 1;
interfaces[0] = first;

View File

@ -1972,6 +1972,7 @@ static int arbel_map_vpm ( struct arbel *arbel,
assert ( ( va & ( ARBEL_PAGE_SIZE - 1 ) ) == 0 );
assert ( ( pa & ( ARBEL_PAGE_SIZE - 1 ) ) == 0 );
assert ( ( len & ( ARBEL_PAGE_SIZE - 1 ) ) == 0 );
assert ( len != 0 );
/* Calculate starting points */
start = pa;
@ -1994,7 +1995,7 @@ static int arbel_map_vpm ( struct arbel *arbel,
if ( ( low - size ) >= start ) {
low -= size;
pa = low;
} else if ( ( high + size ) <= end ) {
} else if ( high <= ( end - size ) ) {
pa = high;
high += size;
} else {

View File

@ -44,6 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h"
#include "mlx_utils/include/public/mlx_pci_gw.h"
#include "mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h"
#include "mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h"
/***************************************************************************
*
@ -823,6 +824,7 @@ static void flexboot_nodnic_eth_complete_recv ( struct ib_device *ibdev __unused
netdev_rx_err ( netdev, iobuf, -ENOTTY );
return;
}
netdev_rx ( netdev, iobuf );
}
@ -860,6 +862,7 @@ static int flexboot_nodnic_eth_open ( struct net_device *netdev ) {
mlx_uint64 cq_size = 0;
mlx_uint32 qpn = 0;
nodnic_port_state state = nodnic_port_state_down;
int rc;
if ( port->port_priv.port_state & NODNIC_PORT_OPENED ) {
DBGC ( flexboot_nodnic, "%s: port %d is already opened\n",
@ -877,11 +880,11 @@ static int flexboot_nodnic_eth_open ( struct net_device *netdev ) {
}
INIT_LIST_HEAD ( &dummy_cq->work_queues );
port->eth_qp = ib_create_qp ( ibdev, IB_QPT_ETH,
FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES, dummy_cq,
FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES, dummy_cq,
&flexboot_nodnic_eth_qp_op, netdev->name );
if ( !port->eth_qp ) {
if ( ( rc = ib_create_qp ( ibdev, IB_QPT_ETH,
FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES, dummy_cq,
FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES, dummy_cq,
&flexboot_nodnic_eth_qp_op, netdev->name,
&port->eth_qp ) ) != 0 ) {
DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not create queue pair\n",
flexboot_nodnic, ibdev->port );
status = MLX_OUT_OF_RESOURCES;
@ -894,9 +897,8 @@ static int flexboot_nodnic_eth_open ( struct net_device *netdev ) {
MLX_FATAL_CHECK_STATUS(status, get_cq_size_err,
"nodnic_port_get_cq_size failed");
port->eth_cq = ib_create_cq ( ibdev, cq_size,
&flexboot_nodnic_eth_cq_op );
if ( !port->eth_cq ) {
if ( ( rc = ib_create_cq ( ibdev, cq_size, &flexboot_nodnic_eth_cq_op,
&port->eth_cq ) ) != 0 ) {
DBGC ( flexboot_nodnic,
"flexboot_nodnic %p port %d could not create completion queue\n",
flexboot_nodnic, ibdev->port );
@ -907,6 +909,7 @@ static int flexboot_nodnic_eth_open ( struct net_device *netdev ) {
list_del(&port->eth_qp->send.list);
list_add ( &port->eth_qp->send.list, &port->eth_cq->work_queues );
port->eth_qp->recv.cq = port->eth_cq;
port->cmdsn = 0;
list_del(&port->eth_qp->recv.list);
list_add ( &port->eth_qp->recv.list, &port->eth_cq->work_queues );
@ -1445,12 +1448,6 @@ static int flexboot_nodnic_alloc_uar ( struct flexboot_nodnic *flexboot_nodnic )
struct pci_device *pci = flexboot_nodnic->pci;
nodnic_uar *uar = &flexboot_nodnic->port[0].port_priv.device->uar;
if ( ! flexboot_nodnic->device_priv.utils ) {
uar->virt = NULL;
DBGC ( flexboot_nodnic, "%s: mlx_utils is not initialized \n", __FUNCTION__ );
return -EINVAL;
}
if ( ! flexboot_nodnic->device_priv.device_cap.support_uar_tx_db ) {
DBGC ( flexboot_nodnic, "%s: tx db using uar is not supported \n", __FUNCTION__ );
return -ENOTSUP;
@ -1467,6 +1464,18 @@ static int flexboot_nodnic_alloc_uar ( struct flexboot_nodnic *flexboot_nodnic )
return status;
}
static int flexboot_nodnic_dealloc_uar ( struct flexboot_nodnic *flexboot_nodnic ) {
nodnic_uar *uar = &flexboot_nodnic->port[0].port_priv.device->uar;
if ( uar->virt ) {
iounmap( uar->virt );
uar->virt = NULL;
}
return MLX_SUCCESS;
}
int flexboot_nodnic_probe ( struct pci_device *pci,
struct flexboot_nodnic_callbacks *callbacks,
void *drv_priv __unused ) {
@ -1508,6 +1517,10 @@ int flexboot_nodnic_probe ( struct pci_device *pci,
MLX_FATAL_CHECK_STATUS(status, get_cap_err,
"nodnic_device_get_cap failed");
if ( mlx_set_admin_mtu ( device_priv->utils, 1, EN_DEFAULT_ADMIN_MTU ) ) {
MLX_DEBUG_ERROR( device_priv->utils, "Failed to set admin mtu\n" );
}
status = flexboot_nodnic_set_port_masking ( flexboot_nodnic_priv );
MLX_FATAL_CHECK_STATUS(status, err_set_masking,
"flexboot_nodnic_set_port_masking failed");
@ -1522,7 +1535,7 @@ int flexboot_nodnic_probe ( struct pci_device *pci,
"flexboot_nodnic_thin_init_ports failed");
if ( ( status = flexboot_nodnic_alloc_uar ( flexboot_nodnic_priv ) ) ) {
DBGC(flexboot_nodnic_priv, "%s: flexboot_nodnic_pci_init failed"
DBGC(flexboot_nodnic_priv, "%s: flexboot_nodnic_alloc_uar failed"
" ( status = %d )\n",__FUNCTION__, status );
}
@ -1550,6 +1563,7 @@ int flexboot_nodnic_probe ( struct pci_device *pci,
flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv );
reg_err:
err_set_ports_types:
flexboot_nodnic_dealloc_uar ( flexboot_nodnic_priv );
err_thin_init_ports:
err_alloc_ibdev:
err_set_masking:
@ -1568,6 +1582,7 @@ void flexboot_nodnic_remove ( struct pci_device *pci )
struct flexboot_nodnic *flexboot_nodnic_priv = pci_get_drvdata ( pci );
nodnic_device_priv *device_priv = & ( flexboot_nodnic_priv->device_priv );
flexboot_nodnic_dealloc_uar ( flexboot_nodnic_priv );
flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv );
nodnic_device_teardown( device_priv );
free_mlx_utils ( & device_priv->utils );

View File

@ -42,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define FLEXBOOT_NODNIC_PAGE_SHIFT 12
#define FLEXBOOT_NODNIC_PAGE_SIZE (1 << FLEXBOOT_NODNIC_PAGE_SHIFT)
#define FLEXBOOT_NODNIC_PAGE_MASK (FLEXBOOT_NODNIC_PAGE_SIZE - 1)
#define EN_DEFAULT_ADMIN_MTU 1522
/* Port protocol */
enum flexboot_nodnic_protocol {

View File

@ -42,80 +42,47 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include "mlx_utils/include/public/mlx_bail.h"
#include "mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h"
#define DEVICE_IS_CIB( device ) ( device == 0x1011 )
/******************************************************************************/
/************* Very simple memory management for umalloced pages **************/
/******* Temporary solution until full memory management is implemented *******/
/******************************************************************************/
struct golan_page {
struct list_head list;
userptr_t addr;
};
static void golan_free_pages ( struct list_head *head ) {
struct golan_page *page, *tmp;
list_for_each_entry_safe ( page, tmp, head, list ) {
list_del ( &page->list );
ufree ( page->addr );
free ( page );
}
}
static void golan_free_fw_areas ( struct golan *golan ) {
int i;
static int golan_init_pages ( struct list_head *head ) {
int rc = 0;
if ( !head ) {
rc = -EINVAL;
goto err_golan_init_pages_bad_param;
}
INIT_LIST_HEAD ( head );
return rc;
err_golan_init_pages_bad_param:
return rc;
}
static userptr_t golan_get_page ( struct list_head *head ) {
struct golan_page *page;
userptr_t addr;
if ( list_empty ( head ) ) {
addr = umalloc ( GOLAN_PAGE_SIZE );
if ( addr == UNULL ) {
goto err_golan_iget_page_alloc_page;
for (i = 0; i < GOLAN_FW_AREAS_NUM; i++) {
if ( golan->fw_areas[i].area ) {
ufree ( golan->fw_areas[i].area );
golan->fw_areas[i].area = UNULL;
}
} else {
page = list_first_entry ( head, struct golan_page, list );
list_del ( &page->list );
addr = page->addr;
free ( page );
}
err_golan_iget_page_alloc_page:
return addr;
}
static int golan_return_page ( struct list_head *head,
userptr_t addr ) {
struct golan_page *new_entry;
int rc = 0;
static int golan_init_fw_areas ( struct golan *golan ) {
int rc = 0, i = 0;
if ( ! head ) {
if ( ! golan ) {
rc = -EINVAL;
goto err_golan_return_page_bad_param;
goto err_golan_init_fw_areas_bad_param;
}
new_entry = zalloc ( sizeof ( *new_entry ) );
if ( new_entry == NULL ) {
rc = -ENOMEM;
goto err_golan_return_page_alloc_page;
}
new_entry->addr = addr;
list_add_tail( &new_entry->list, head );
err_golan_return_page_alloc_page:
err_golan_return_page_bad_param:
for (i = 0; i < GOLAN_FW_AREAS_NUM; i++)
golan->fw_areas[i].area = UNULL;
return rc;
err_golan_init_fw_areas_bad_param:
return rc;
}
/******************************************************************************/
const char *golan_qp_state_as_string[] = {
@ -177,16 +144,6 @@ static inline u8 xor8_buf(void *buf, int len)
return sum;
}
static inline int verify_block_sig(struct golan_cmd_prot_block *block)
{
if (xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 1) != 0xff)
return -EINVAL;
if (xor8_buf(block, sizeof(*block)) != 0xff)
return -EINVAL;
return 0;
}
static inline const char *cmd_status_str(u8 status)
{
switch (status) {
@ -258,24 +215,6 @@ static inline void golan_calc_sig(struct golan *golan, uint32_t cmd_idx,
cmd->sig = ~xor8_buf(cmd, sizeof(*cmd));
}
/**
* Get Golan FW
*/
static int fw_ver_and_cmdif ( struct golan *golan ) {
DBGC (golan ,"\n[%x:%x]rev maj.min.submin = %x.%x.%x cmdif = %x\n",
golan->iseg->fw_rev,
golan->iseg->cmdif_rev_fw_sub,
fw_rev_maj ( golan ), fw_rev_min ( golan ),
fw_rev_sub ( golan ), cmdif_rev ( golan));
if (cmdif_rev ( golan) != PXE_CMDIF_REF) {
DBGC (golan ,"CMDIF %d not supported current is %d\n",
cmdif_rev ( golan ), PXE_CMDIF_REF);
return 1;
}
return 0;
}
static inline void show_out_status(uint32_t *out)
{
DBG("%x\n", be32_to_cpu(out[0]));
@ -466,10 +405,8 @@ static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16
while ( pages > 0 ) {
uint32_t pas_num = min(pages, MAX_PASE_MBOX);
unsigned i;
struct golan_cmd_layout *cmd;
struct golan_manage_pages_inbox *in;
struct golan_manage_pages_outbox_data *out;
size_ibox = sizeof(struct golan_manage_pages_inbox) + (pas_num * GOLAN_PAS_SIZE);
size_obox = sizeof(struct golan_manage_pages_outbox) + (pas_num * GOLAN_PAS_SIZE);
@ -485,11 +422,7 @@ static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16
in->num_entries = cpu_to_be32(pas_num);
if ( ( rc = send_command_and_wait(golan, MEM_CMD_IDX, MEM_MBOX, MEM_MBOX, __FUNCTION__) ) == 0 ) {
out = (struct golan_manage_pages_outbox_data *)GET_OUTBOX(golan, MEM_MBOX);
out_num_entries = be32_to_cpu(((struct golan_manage_pages_outbox *)(cmd->out))->num_entries);
for (i = 0; i < out_num_entries; ++i) {
golan_return_page ( &golan->pages, ( BE64_BUS_2_USR( out->pas[i] ) ) );
}
} else {
if ( rc == -EBUSY ) {
DBGC (golan ,"HCA is busy (rc = -EBUSY)\n" );
@ -506,17 +439,29 @@ static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16
pages -= out_num_entries;
}
DBGC( golan , "%s Pages handled\n", __FUNCTION__);
return 0;
return rc;
}
static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __be16 func_id ) {
static inline int golan_provide_pages ( struct golan *golan , uint32_t pages
, __be16 func_id,struct golan_firmware_area *fw_area) {
struct mbox *mailbox;
int size_ibox = 0;
int size_obox = 0;
int rc = 0;
userptr_t next_page_addr = UNULL;
DBGC(golan, "%s\n", __FUNCTION__);
if ( ! fw_area->area ) {
fw_area->area = umalloc ( GOLAN_PAGE_SIZE * pages );
if ( fw_area->area == UNULL ) {
rc = -ENOMEM;
DBGC (golan ,"Failed to allocated %d pages \n",pages);
goto err_golan_alloc_fw_area;
}
fw_area->npages = pages;
}
assert ( fw_area->npages == pages );
next_page_addr = fw_area->area;
while ( pages > 0 ) {
uint32_t pas_num = min(pages, MAX_PASE_MBOX);
unsigned i, j;
@ -538,12 +483,9 @@ static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __
in->func_id = func_id; /* Already BE */
in->num_entries = cpu_to_be32(pas_num);
for ( i = 0 , j = MANAGE_PAGES_PSA_OFFSET; i < pas_num; ++i ,++j ) {
if ( ! ( addr = golan_get_page ( & golan->pages ) ) ) {
rc = -ENOMEM;
DBGC (golan ,"Couldnt allocated page \n");
goto malloc_dma_failed;
}
for ( i = 0 , j = MANAGE_PAGES_PSA_OFFSET; i < pas_num; ++i ,++j,
next_page_addr += GOLAN_PAGE_SIZE ) {
addr = next_page_addr;
if (GOLAN_PAGE_MASK & user_to_phys(addr, 0)) {
DBGC (golan ,"Addr not Page alligned [%lx %lx]\n", user_to_phys(addr, 0), addr);
}
@ -563,7 +505,6 @@ static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __
get_cmd( golan , MEM_CMD_IDX )->status_own,
be32_to_cpu(CMD_SYND(golan, MEM_CMD_IDX)), pas_num);
}
golan_return_page ( &golan->pages ,addr );
goto err_send_command;
}
}
@ -571,7 +512,7 @@ static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __
return 0;
err_send_command:
malloc_dma_failed:
err_golan_alloc_fw_area:
/* Go over In box and free pages */
/* Send Error to FW */
/* What is next - Disable HCA? */
@ -609,7 +550,7 @@ static inline int golan_handle_pages(struct golan *golan,
total_pages = (( pages >= 0 ) ? pages : ( pages * ( -1 ) ));
if ( mode == GOLAN_PAGES_GIVE ) {
rc = golan_provide_pages(golan, total_pages, func_id);
rc = golan_provide_pages(golan, total_pages, func_id, & ( golan->fw_areas[qry-1] ));
} else {
rc = golan_take_pages(golan, golan->total_dma_pages, func_id);
golan->total_dma_pages = 0;
@ -799,16 +740,14 @@ static int golan_create_eq(struct golan *golan)
struct golan_cmd_layout *cmd;
struct golan_create_eq_mbox_out *out;
int rc, i;
userptr_t addr;
eq->cons_index = 0;
eq->size = GOLAN_NUM_EQES * sizeof(eq->eqes[0]);
addr = golan_get_page ( &golan->pages );
if (!addr) {
eq->eqes = malloc_dma ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE );
if (!eq->eqes) {
rc = -ENOMEM;
goto err_create_eq_eqe_alloc;
}
eq->eqes = (struct golan_eqe *)user_to_virt(addr, 0);
/* Set EQEs ownership bit to HW ownership */
for (i = 0; i < GOLAN_NUM_EQES; ++i) {
@ -823,7 +762,7 @@ static int golan_create_eq(struct golan *golan)
in = (struct golan_create_eq_mbox_in_data *)GET_INBOX(golan, GEN_MBOX);
/* Fill the physical address of the page */
in->pas[0] = USR_2_BE64_BUS(addr);
in->pas[0] = VIRT_2_BE64_BUS( eq->eqes );
in->ctx.log_sz_usr_page = cpu_to_be32((ilog2(GOLAN_NUM_EQES)) << 24 | golan->uar.index);
DBGC( golan , "UAR idx %x (BE %x)\n", golan->uar.index, in->ctx.log_sz_usr_page);
in->events_mask = cpu_to_be64(1 << GOLAN_EVENT_TYPE_PORT_CHANGE);
@ -842,7 +781,7 @@ static int golan_create_eq(struct golan *golan)
return 0;
err_create_eq_cmd:
golan_return_page ( & golan->pages, virt_to_user ( eq->eqes ) );
free_dma ( eq->eqes , GOLAN_PAGE_SIZE );
err_create_eq_eqe_alloc:
DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc);
return rc;
@ -867,7 +806,7 @@ static void golan_destory_eq(struct golan *golan)
rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
GOLAN_PRINT_RC_AND_CMD_STATUS;
golan_return_page ( &golan->pages, virt_to_user ( golan->eq.eqes ) );
free_dma ( golan->eq.eqes , GOLAN_PAGE_SIZE );
golan->eq.eqn = 0;
DBGC( golan, "%s Event queue (0x%x) was destroyed\n", __FUNCTION__, eqn);
@ -1016,7 +955,6 @@ static int golan_create_cq(struct ib_device *ibdev,
struct golan_create_cq_mbox_out *out;
int rc;
unsigned int i;
userptr_t addr;
golan_cq = zalloc(sizeof(*golan_cq));
if (!golan_cq) {
@ -1031,12 +969,11 @@ static int golan_create_cq(struct ib_device *ibdev,
goto err_create_cq_db_alloc;
}
addr = golan_get_page ( &golan->pages );
if (!addr) {
golan_cq->cqes = malloc_dma ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE );
if (!golan_cq->cqes) {
rc = -ENOMEM;
goto err_create_cq_cqe_alloc;
}
golan_cq->cqes = (struct golan_cqe64 *)user_to_virt(addr, 0);
/* Set CQEs ownership bit to HW ownership */
for (i = 0; i < cq->num_cqes; ++i) {
@ -1053,7 +990,7 @@ static int golan_create_cq(struct ib_device *ibdev,
in = (struct golan_create_cq_mbox_in_data *)GET_INBOX(golan, GEN_MBOX);
/* Fill the physical address of the page */
in->pas[0] = USR_2_BE64_BUS(addr);
in->pas[0] = VIRT_2_BE64_BUS( golan_cq->cqes );
in->ctx.cqe_sz_flags = GOLAN_CQE_SIZE_64 << 5;
in->ctx.log_sz_usr_page = cpu_to_be32(((ilog2(cq->num_cqes)) << 24) | golan->uar.index);
in->ctx.c_eqn = cpu_to_be16(golan->eq.eqn);
@ -1071,7 +1008,7 @@ static int golan_create_cq(struct ib_device *ibdev,
return 0;
err_create_cq_cmd:
golan_return_page ( & golan->pages, virt_to_user ( golan_cq->cqes ) );
free_dma( golan_cq->cqes , GOLAN_PAGE_SIZE );
err_create_cq_cqe_alloc:
free_dma(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE);
err_create_cq_db_alloc:
@ -1108,7 +1045,7 @@ static void golan_destroy_cq(struct ib_device *ibdev,
cq->cqn = 0;
ib_cq_set_drvdata(cq, NULL);
golan_return_page ( & golan->pages, virt_to_user ( golan_cq->cqes ) );
free_dma ( golan_cq->cqes , GOLAN_PAGE_SIZE );
free_dma(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE);
free(golan_cq);
@ -1154,7 +1091,6 @@ static int golan_create_qp_aux(struct ib_device *ibdev,
struct golan_cmd_layout *cmd;
struct golan_wqe_data_seg *data;
struct golan_create_qp_mbox_out *out;
userptr_t addr;
uint32_t wqe_size_in_bytes;
uint32_t max_qp_size_in_wqes;
unsigned int i;
@ -1202,12 +1138,11 @@ static int golan_create_qp_aux(struct ib_device *ibdev,
golan_qp->size = golan_qp->sq.size + golan_qp->rq.size;
/* allocate dma memory for WQEs (1 page is enough) - should change it */
addr = golan_get_page ( &golan->pages );
if (!addr) {
golan_qp->wqes = malloc_dma ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE );
if (!golan_qp->wqes) {
rc = -ENOMEM;
goto err_create_qp_wqe_alloc;
}
golan_qp->wqes = user_to_virt(addr, 0);
golan_qp->rq.wqes = golan_qp->wqes;
golan_qp->sq.wqes = golan_qp->wqes + golan_qp->rq.size;//(union golan_send_wqe *)&
//(((struct golan_recv_wqe_ud *)(golan_qp->wqes))[qp->recv.num_wqes]);
@ -1241,7 +1176,7 @@ static int golan_create_qp_aux(struct ib_device *ibdev,
in = (struct golan_create_qp_mbox_in_data *)GET_INBOX(golan, GEN_MBOX);
/* Fill the physical address of the page */
in->pas[0] = USR_2_BE64_BUS(addr);
in->pas[0] = VIRT_2_BE64_BUS(golan_qp->wqes);
in->ctx.qp_counter_set_usr_page = cpu_to_be32(golan->uar.index);
in->ctx.flags_pd = cpu_to_be32(golan->pdn);
@ -1280,7 +1215,7 @@ static int golan_create_qp_aux(struct ib_device *ibdev,
err_create_qp_cmd:
free_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db));
err_create_qp_db_alloc:
golan_return_page ( & golan->pages, ( userptr_t ) golan_qp->wqes );
free_dma ( golan_qp->wqes, GOLAN_PAGE_SIZE );
err_create_qp_wqe_alloc:
err_create_qp_sq_size:
err_create_qp_sq_wqe_size:
@ -1488,7 +1423,7 @@ static void golan_destroy_qp(struct ib_device *ibdev,
ib_qp_set_drvdata(qp, NULL);
free_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db));
golan_return_page ( & golan->pages, ( userptr_t ) golan_qp->wqes );
free_dma ( golan_qp->wqes, GOLAN_PAGE_SIZE );
free(golan_qp);
DBGC( golan ,"%s QP 0x%lx was destroyed\n", __FUNCTION__, qpn);
@ -1526,7 +1461,6 @@ static int golan_post_send(struct ib_device *ibdev,
unsigned long wqe_idx;
struct golan_wqe_data_seg *data = NULL;
struct golan_wqe_ctrl_seg *ctrl = NULL;
// static uint8_t toggle = 0;
wqe_idx_mask = (qp->send.num_wqes - 1);
@ -1576,8 +1510,9 @@ static int golan_post_send(struct ib_device *ibdev,
golan_qp->sq.next_idx = (golan_qp->sq.next_idx + GOLAN_WQEBBS_PER_SEND_UD_WQE);
golan_qp->doorbell_record->send_db = cpu_to_be16(golan_qp->sq.next_idx);
wmb();
writeq(*((__be64 *)ctrl), golan->uar.virt + 0x800);// +
// ((toggle++ & 0x1) ? 0x100 : 0x0));
writeq(*((__be64 *)ctrl), golan->uar.virt
+ ( ( golan_qp->sq.next_idx & 0x1 ) ? DB_BUFFER0_EVEN_OFFSET
: DB_BUFFER0_ODD_OFFSET ) );
return 0;
}
@ -1702,7 +1637,6 @@ err_query_vport_gid_cmd:
static int golan_query_vport_pkey ( struct ib_device *ibdev ) {
struct golan *golan = ib_get_drvdata ( ibdev );
struct golan_cmd_layout *cmd;
//struct golan_query_hca_vport_pkey_data *pkey_table;
struct golan_query_hca_vport_pkey_inbox *in;
int pkey_table_size_in_entries = (1 << (7 + golan->caps.pkey_table_size));
int rc;
@ -1719,8 +1653,6 @@ static int golan_query_vport_pkey ( struct ib_device *ibdev ) {
rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ );
GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_pkey_cmd );
//pkey_table = (struct golan_query_hca_vport_pkey_data *)( GET_OUTBOX ( golan, GEN_MBOX ) );
return 0;
err_query_vport_pkey_cmd:
DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc);
@ -2024,6 +1956,7 @@ static inline void golan_handle_port_event(struct golan *golan, struct golan_eqe
case GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG:
case GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE:
golan_ib_update ( ibdev );
/* Fall through */
case GOLAN_PORT_CHANGE_SUBTYPE_DOWN:
case GOLAN_PORT_CHANGE_SUBTYPE_LID:
case GOLAN_PORT_CHANGE_SUBTYPE_PKEY:
@ -2100,10 +2033,15 @@ static void golan_poll_eq(struct ib_device *ibdev)
cqn, eqe->data.cq_err.syndrome);
// mlx5_cq_event(dev, cqn, eqe->type);
break;
/*
* currently the driver do not support dynamic memory request
* during FW run, a follow up change will allocate FW pages once and
* never release them till driver shutdown, this change will not support
* this request as currently this request is not issued anyway.
case GOLAN_EVENT_TYPE_PAGE_REQUEST:
{
/* we should check if we get this event while we
* waiting for a command */
// we should check if we get this event while we
// waiting for a command
u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id);
s16 npages = be16_to_cpu(eqe->data.req_pages.num_pages);
@ -2112,6 +2050,7 @@ static void golan_poll_eq(struct ib_device *ibdev)
golan_provide_pages(golan, npages, func_id);
}
break;
*/
default:
DBGC (golan ,"%s Unhandled event 0x%x on EQ 0x%x\n", __FUNCTION__,
eqe->type, eq->eqn);
@ -2231,7 +2170,6 @@ static int golan_register_ibdev(struct golan_port *port)
static inline void golan_bring_down(struct golan *golan)
{
DBGC(golan, "%s: start\n", __FUNCTION__);
if (~golan->flags & GOLAN_OPEN) {
@ -2413,7 +2351,8 @@ static int golan_probe_normal ( struct pci_device *pci ) {
goto err_golan_alloc;
}
if ( golan_init_pages( &golan->pages ) ) {
/* at POST stage some BIOSes have limited available dynamic memory */
if ( golan_init_fw_areas ( golan ) ) {
rc = -ENOMEM;
goto err_golan_golan_init_pages;
}
@ -2423,11 +2362,6 @@ static int golan_probe_normal ( struct pci_device *pci ) {
golan->pci = pci;
golan_pci_init( golan );
/* config command queues */
if ( fw_ver_and_cmdif( golan ) ) {
rc = -1;
goto err_fw_ver_cmdif;
}
if ( golan_bring_up( golan ) ) {
DBGC (golan ,"golan bringup failed\n");
rc = -1;
@ -2482,9 +2416,8 @@ err_golan_probe_alloc_ibdev:
err_utils_init:
golan_bring_down ( golan );
err_golan_bringup:
err_fw_ver_cmdif:
iounmap( golan->iseg );
golan_free_pages( &golan->pages );
golan_free_fw_areas ( golan );
err_golan_golan_init_pages:
free ( golan );
err_golan_alloc:
@ -2513,7 +2446,7 @@ static void golan_remove_normal ( struct pci_device *pci ) {
free_mlx_utils ( & golan->utils );
}
iounmap( golan->iseg );
golan_free_pages( &golan->pages );
golan_free_fw_areas ( golan );
free(golan);
}
@ -2528,14 +2461,16 @@ static mlx_status shomron_tx_uar_send_db ( struct ib_device *ibdev,
( struct shomron_nodnic_eth_send_wqe * )wqbb;
struct shomronprm_wqe_segment_ctrl_send *ctrl;
if ( ! ibdev || ! eth_wqe || ! flexboot_nodnic->device_priv.uar.virt ) {
if ( ! eth_wqe || ! flexboot_nodnic->device_priv.uar.virt ) {
DBG("%s: Invalid parameters\n",__FUNCTION__);
status = MLX_FAILED;
goto err;
}
wmb();
ctrl = & eth_wqe->ctrl;
writeq(*((__be64 *)ctrl), flexboot_nodnic->device_priv.uar.virt + 0x800);
writeq(*((__be64 *)ctrl), flexboot_nodnic->device_priv.uar.virt +
( ( MLX_GET ( ctrl, wqe_index ) & 0x1 ) ? DB_BUFFER0_ODD_OFFSET
: DB_BUFFER0_EVEN_OFFSET ) );
err:
return status;
}

View File

@ -111,6 +111,18 @@ struct golan_uar {
unsigned long phys;
};
struct golan_firmware_area {
/* length of area in pages */
uint32_t npages;
/** Firmware area in external memory
*
* This is allocated when first needed, and freed only on
* final teardown, in order to avoid memory map changes at
* runtime.
*/
userptr_t area;
};
/* Queue Pair */
#define GOLAN_SEND_WQE_BB_SIZE 64
#define GOLAN_SEND_UD_WQE_SIZE sizeof(struct golan_send_wqe_ud)
@ -204,6 +216,8 @@ struct golan_completion_queue {
#define GOLAN_EQE_SIZE sizeof(struct golan_eqe)
#define GOLAN_NUM_EQES 8
#define GOLAN_EQ_DOORBELL_OFFSET 0x40
#define DB_BUFFER0_EVEN_OFFSET 0x800
#define DB_BUFFER0_ODD_OFFSET 0x900
#define GOLAN_EQ_MAP_ALL_EVENTS \
((1 << GOLAN_EVENT_TYPE_PATH_MIG )| \
@ -323,6 +337,8 @@ struct golan {
mlx_utils *utils;
struct golan_port ports[GOLAN_MAX_PORTS];
#define GOLAN_FW_AREAS_NUM 2
struct golan_firmware_area fw_areas[GOLAN_FW_AREAS_NUM];
};
#endif /* _GOLAN_H_*/

View File

@ -2113,6 +2113,7 @@ static int hermon_map_vpm ( struct hermon *hermon,
assert ( ( va & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
assert ( ( pa & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
assert ( ( len & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
assert ( len != 0 );
/* Calculate starting points */
start = pa;
@ -2135,7 +2136,7 @@ static int hermon_map_vpm ( struct hermon *hermon,
if ( ( low - size ) >= start ) {
low -= size;
pa = low;
} else if ( ( high + size ) <= end ) {
} else if ( high <= ( end - size ) ) {
pa = high;
high += size;
} else {
@ -3261,24 +3262,20 @@ static int hermon_eth_open ( struct net_device *netdev ) {
goto err_open;
/* Allocate completion queue */
port->eth_cq = ib_create_cq ( ibdev, HERMON_ETH_NUM_CQES,
&hermon_eth_cq_op );
if ( ! port->eth_cq ) {
if ( ( rc = ib_create_cq ( ibdev, HERMON_ETH_NUM_CQES,
&hermon_eth_cq_op, &port->eth_cq ) ) != 0 ) {
DBGC ( hermon, "Hermon %p port %d could not create completion "
"queue\n", hermon, ibdev->port );
rc = -ENOMEM;
"queue: %s\n", hermon, ibdev->port, strerror ( rc ) );
goto err_create_cq;
}
/* Allocate queue pair */
port->eth_qp = ib_create_qp ( ibdev, IB_QPT_ETH,
HERMON_ETH_NUM_SEND_WQES, port->eth_cq,
HERMON_ETH_NUM_RECV_WQES, port->eth_cq,
&hermon_eth_qp_op, netdev->name );
if ( ! port->eth_qp ) {
if ( ( rc = ib_create_qp ( ibdev, IB_QPT_ETH, HERMON_ETH_NUM_SEND_WQES,
port->eth_cq, HERMON_ETH_NUM_RECV_WQES,
port->eth_cq, &hermon_eth_qp_op,
netdev->name, &port->eth_qp ) ) != 0 ) {
DBGC ( hermon, "Hermon %p port %d could not create queue "
"pair\n", hermon, ibdev->port );
rc = -ENOMEM;
"pair: %s\n", hermon, ibdev->port, strerror ( rc ) );
goto err_create_qp;
}
ib_qp_set_ownerdata ( port->eth_qp, netdev );

View File

@ -537,7 +537,7 @@ static int linda_init_send ( struct linda *linda ) {
rc = -ENOMEM;
goto err_alloc_sendbufavail;
}
memset ( linda->sendbufavail, 0, sizeof ( linda->sendbufavail ) );
memset ( linda->sendbufavail, 0, sizeof ( *linda->sendbufavail ) );
/* Program SendBufAvailAddr into the hardware */
memset ( &sendbufavailaddr, 0, sizeof ( sendbufavailaddr ) );

View File

@ -169,13 +169,7 @@ nodnic_device_clear_int (
mlx_status status = MLX_SUCCESS;
mlx_uint32 disable = 1;
#ifndef DEVICE_CX3
#define NODNIC_CLEAR_INT_BAR_OFFSET 0x100C
if ( device_priv->device_cap.support_bar_cq_ctrl ) {
status = mlx_pci_mem_write ( device_priv->utils, MlxPciWidthUint32, 0,
( mlx_uint64 ) ( NODNIC_CLEAR_INT_BAR_OFFSET ), 1, &disable );
} else {
status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable);
}
status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable);
MLX_CHECK_STATUS(device_priv, status, clear_int_done, "failed writing to disable_bit");
#else
mlx_utils *utils = device_priv->utils;

View File

@ -20,8 +20,8 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include "mlx_mtu.h"
#include "mlx_memory.h"
#include "mlx_bail.h"
#include "../../include/public/mlx_memory.h"
#include "../../include/public/mlx_bail.h"
mlx_status
mlx_get_max_mtu(
@ -58,3 +58,37 @@ reg_err:
bad_param:
return status;
}
mlx_status
mlx_set_admin_mtu(
IN mlx_utils *utils,
IN mlx_uint8 port_num,
IN mlx_uint32 admin_mtu
)
{
mlx_status status = MLX_SUCCESS;
struct mlx_mtu mtu;
mlx_uint32 reg_status;
if (utils == NULL) {
status = MLX_INVALID_PARAMETER;
goto bad_param;
}
mlx_memory_set(utils, &mtu, 0, sizeof(mtu));
mtu.local_port = port_num;
mtu.admin_mtu = admin_mtu;
status = mlx_reg_access(utils, REG_ID_PMTU, REG_ACCESS_WRITE, &mtu,
sizeof(mtu), &reg_status);
MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed ");
if (reg_status != 0) {
MLX_DEBUG_ERROR(utils,"mlx_reg_access failed with status = %d\n", reg_status);
status = MLX_FAILED;
goto reg_err;
}
reg_err:
bad_param:
return status;
}

View File

@ -22,8 +22,8 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include "mlx_reg_access.h"
#include "mlx_utils.h"
#include "../../include/public/mlx_utils.h"
#include "../../mlx_lib/mlx_reg_access/mlx_reg_access.h"
#define BYTE_TO_BIT 0x8
@ -49,4 +49,10 @@ mlx_get_max_mtu(
OUT mlx_uint32 *max_mtu
);
mlx_status
mlx_set_admin_mtu(
IN mlx_utils *utils,
IN mlx_uint8 port_num,
IN mlx_uint32 admin_mtu
);
#endif /* MLX_MTU_H_ */

View File

@ -39,7 +39,6 @@ struct nvconfig_tlv_mapping nvconfig_tlv_mapping[] = {
TlvMappingEntry(0x2001, 0x195, NVRAM_TLV_CLASS_HOST, FALSE),
TlvMappingEntry(0x2010, 0x210, NVRAM_TLV_CLASS_HOST, FALSE),
TlvMappingEntry(0x2011, 0x211, NVRAM_TLV_CLASS_GLOBAL, FALSE),
TlvMappingEntry(0x2020, 0x2020, NVRAM_TLV_CLASS_PHYSICAL_PORT, FALSE),
TlvMappingEntry(0x2021, 0x221, NVRAM_TLV_CLASS_HOST, FALSE),
TlvMappingEntry(0x2023, 0x223, NVRAM_TLV_CLASS_HOST, FALSE),
TlvMappingEntry(0x2006, 0x206, NVRAM_TLV_CLASS_HOST, FALSE),
@ -67,6 +66,7 @@ struct nvconfig_tlv_mapping nvconfig_tlv_mapping[] = {
TlvMappingEntry(0x110, 0x110, NVRAM_TLV_CLASS_HOST, FALSE),
TlvMappingEntry(0x192, 0x192, NVRAM_TLV_CLASS_GLOBAL, FALSE),
TlvMappingEntry(0x101, 0x101, NVRAM_TLV_CLASS_GLOBAL, TRUE),
TlvMappingEntry(0x194, 0x194, NVRAM_TLV_CLASS_GLOBAL, FALSE),
TlvMappingEntry(0, 0, 0, 0),
};
@ -239,6 +239,7 @@ nvconfig_nvdata_access(
IN REG_ACCESS_OPT opt,
IN mlx_size data_size,
IN NV_DEFAULT_OPT def_en,
IN NVDA_WRITER_ID writer_id,
IN OUT mlx_uint8 *version,
IN OUT mlx_void *data
)
@ -263,10 +264,9 @@ nvconfig_nvdata_access(
data_size_align_to_dword = ((data_size + 3) / sizeof(mlx_uint32)) * sizeof(mlx_uint32);
mlx_memory_set(utils, &nvda, 0, sizeof(nvda));
nvda.nv_header.length = data_size_align_to_dword;
nvda.nv_header.rd_en = 0;
nvda.nv_header.def_en = def_en;
nvda.nv_header.over_en = 1;
nvda.nv_header.access_mode = def_en;
nvda.nv_header.version = *version;
nvda.nv_header.writer_id = writer_id;
nvconfig_fill_tlv_type(port, class_code, real_tlv_type, &nvda.nv_header.tlv_type);

View File

@ -31,6 +31,17 @@ typedef enum {
NVRAM_TLV_CLASS_HOST = 3,
} NVRAM_CLASS_CODE;
typedef enum {
NVDA_NV_HEADER_WRITER_ID_UEFI_HII = 0x6,
NVDA_NV_HEADER_WRITER_ID_FLEXBOOT = 0x8,
} NVDA_WRITER_ID;
typedef enum {
TLV_ACCESS_DEFAULT_DIS = 0,
TLV_ACCESS_CURRENT = 1,
TLV_ACCESS_DEFAULT_EN = 2,
} NV_DEFAULT_OPT;
struct nvconfig_tlv_type_per_port {
mlx_uint32 param_idx :16;
mlx_uint32 port :8;
@ -78,26 +89,24 @@ struct nvconfig_header {
mlx_uint32 length :9; /*Size of configuration item data in bytes between 0..256 */
mlx_uint32 reserved0 :3;
mlx_uint32 version :4; /* Configuration item version */
mlx_uint32 reserved1 :7;
mlx_uint32 writer_id :5;
mlx_uint32 reserved1 :1;
mlx_uint32 def_en :1; /*Choose whether to access the default value or the user-defined value.
0x0 Read or write the user-defined value.
0x1 Read the default value (only valid for reads).*/
mlx_uint32 access_mode :2; /*Defines which value of the Configuration Item will be accessed.
0x0: NEXT - Next value to be applied
0x1: CURRENT - Currently set values (only valid for Query operation) Supported only if NVGC.nvda_read_current_settings==1.
0x2: FACTORY - Default factory values (only valid for Query operation). Supported only if NVGC.nvda_read_factory_settings==1.*/
mlx_uint32 rd_en :1; /*enables reading the TLV by lower priorities
0 - TLV can be read by the subsequent lifecycle priorities.
1 - TLV cannot be read by the subsequent lifecycle priorities. */
mlx_uint32 over_en :1; /*enables overwriting the TLV by lower priorities
0 - Can only be overwritten by the current lifecycle priority
1 - Allowed to be overwritten by subsequent lifecycle priorities */
mlx_uint32 reserved2 :2;
mlx_uint32 header_type :2;
mlx_uint32 priority :2;
mlx_uint32 reserved3 :2;
mlx_uint32 valid :2;
/* -------------- */
union nvconfig_tlv_type tlv_type;;
/* -------------- */
mlx_uint32 crc :16;
mlx_uint32 reserved :16;
};
#define NVCONFIG_MAX_TLV_SIZE 256
@ -149,6 +158,7 @@ nvconfig_nvdata_access(
IN REG_ACCESS_OPT opt,
IN mlx_size data_size,
IN NV_DEFAULT_OPT def_en,
IN NVDA_WRITER_ID writer_id,
IN OUT mlx_uint8 *version,
IN OUT mlx_void *data
);

View File

@ -386,7 +386,8 @@ nvconfig_nvdata_default_access(
mlx_uint8 version = 0;
status = nvconfig_nvdata_access(utils, port, tlv_type, REG_ACCESS_READ,
data_size, TLV_ACCESS_DEFAULT_EN, &version, data);
data_size, TLV_ACCESS_DEFAULT_EN, 0,
&version, data);
MLX_CHECK_STATUS(NULL, status, nvdata_access_err,
"nvconfig_nvdata_access failed ");
for (index = 0; index * 4 < data_size; index++) {
@ -493,6 +494,8 @@ nvconfig_read_rom_ini_values(
)
{
mlx_status status = MLX_SUCCESS;
mlx_uint8 version = 0;
mlx_uint32 index;
if (utils == NULL || rom_ini == NULL) {
status = MLX_INVALID_PARAMETER;
@ -501,8 +504,16 @@ nvconfig_read_rom_ini_values(
}
mlx_memory_set(utils, rom_ini, 0, sizeof(*rom_ini));
status = nvconfig_nvdata_default_access(utils, 0, GLOBAL_ROM_INI_TYPE,
sizeof(*rom_ini), rom_ini);
status = nvconfig_nvdata_access(utils, 0, GLOBAL_ROM_INI_TYPE, REG_ACCESS_READ,
sizeof(*rom_ini), TLV_ACCESS_DEFAULT_DIS, 0,
&version, rom_ini);
MLX_CHECK_STATUS(NULL, status, bad_param,
"nvconfig_nvdata_access failed ");
for (index = 0; index * 4 < sizeof(*rom_ini); index++) {
mlx_memory_be32_to_cpu(utils, (((mlx_uint32 *) rom_ini)[index]),
((mlx_uint32 *) rom_ini) + index);
}
bad_param:
return status;
}

View File

@ -1,145 +0,0 @@
/*
* Copyright (C) 2015 Mellanox Technologies Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include "mlx_ocbb.h"
#include "mlx_icmd.h"
#include "mlx_bail.h"
mlx_status
mlx_ocbb_init (
IN mlx_utils *utils,
IN mlx_uint64 address
)
{
mlx_status status = MLX_SUCCESS;
struct mlx_ocbb_init ocbb_init;
ocbb_init.address_hi = (mlx_uint32)(address >> 32);
ocbb_init.address_lo = (mlx_uint32)address;
if (utils == NULL) {
status = MLX_INVALID_PARAMETER;
goto bad_param;
}
status = mlx_icmd_send_command(
utils,
OCBB_INIT,
&ocbb_init,
sizeof(ocbb_init),
0
);
MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed");
icmd_err:
bad_param:
return status;
}
mlx_status
mlx_ocbb_query_header_status (
IN mlx_utils *utils,
OUT mlx_uint8 *ocbb_status
)
{
mlx_status status = MLX_SUCCESS;
struct mlx_ocbb_query_status ocbb_query_status;
if (utils == NULL) {
status = MLX_INVALID_PARAMETER;
goto bad_param;
}
status = mlx_icmd_send_command(
utils,
OCBB_QUERY_HEADER_STATUS,
&ocbb_query_status,
0,
sizeof(ocbb_query_status)
);
MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed");
*ocbb_status = ocbb_query_status.status;
icmd_err:
bad_param:
return status;
}
mlx_status
mlx_ocbb_query_etoc_status (
IN mlx_utils *utils,
OUT mlx_uint8 *ocbb_status
)
{
mlx_status status = MLX_SUCCESS;
struct mlx_ocbb_query_status ocbb_query_status;
if (utils == NULL) {
status = MLX_INVALID_PARAMETER;
goto bad_param;
}
status = mlx_icmd_send_command(
utils,
OCBB_QUERY_ETOC_STATUS,
&ocbb_query_status,
0,
sizeof(ocbb_query_status)
);
MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed");
*ocbb_status = ocbb_query_status.status;
icmd_err:
bad_param:
return status;
}
mlx_status
mlx_ocbb_set_event (
IN mlx_utils *utils,
IN mlx_uint64 event_data,
IN mlx_uint8 event_number,
IN mlx_uint8 event_length,
IN mlx_uint8 data_length,
IN mlx_uint8 data_start_offset
)
{
mlx_status status = MLX_SUCCESS;
struct mlx_ocbb_set_event ocbb_event;
if (utils == NULL) {
status = MLX_INVALID_PARAMETER;
goto bad_param;
}
ocbb_event.data_length = data_length;
ocbb_event.data_start_offset = data_start_offset;
ocbb_event.event_number = event_number;
ocbb_event.event_data = event_data;
ocbb_event.event_length = event_length;
status = mlx_icmd_send_command(
utils,
OCBB_QUERY_SET_EVENT,
&ocbb_event,
sizeof(ocbb_event),
0
);
MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed");
icmd_err:
bad_param:
return status;
}

View File

@ -1,73 +0,0 @@
#ifndef MLX_OCBB_H_
#define MLX_OCBB_H_
/*
* Copyright (C) 2015 Mellanox Technologies Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include "mlx_utils.h"
#define MLX_OCBB_EVENT_DATA_SIZE 2
struct mlx_ocbb_init {
mlx_uint32 address_hi;
mlx_uint32 address_lo;
};
struct mlx_ocbb_query_status {
mlx_uint32 reserved :24;
mlx_uint32 status :8;
};
struct mlx_ocbb_set_event {
mlx_uint64 event_data;
mlx_uint32 event_number :8;
mlx_uint32 event_length :8;
mlx_uint32 data_length :8;
mlx_uint32 data_start_offset :8;
};
mlx_status
mlx_ocbb_init (
IN mlx_utils *utils,
IN mlx_uint64 address
);
mlx_status
mlx_ocbb_query_header_status (
IN mlx_utils *utils,
OUT mlx_uint8 *ocbb_status
);
mlx_status
mlx_ocbb_query_etoc_status (
IN mlx_utils *utils,
OUT mlx_uint8 *ocbb_status
);
mlx_status
mlx_ocbb_set_event (
IN mlx_utils *utils,
IN mlx_uint64 EventData,
IN mlx_uint8 EventNumber,
IN mlx_uint8 EventLength,
IN mlx_uint8 DataLength,
IN mlx_uint8 DataStartOffset
);
#endif /* MLX_OCBB_H_ */

View File

@ -31,11 +31,6 @@ typedef enum {
REG_ACCESS_WRITE = 2,
} REG_ACCESS_OPT;
typedef enum {
TLV_ACCESS_DEFAULT_DIS = 0,
TLV_ACCESS_DEFAULT_EN = 1,
} NV_DEFAULT_OPT;
#define REG_ID_NVDA 0x9024
#define REG_ID_NVDI 0x9025
#define REG_ID_NVIA 0x9029

View File

@ -1,84 +0,0 @@
/*
* Copyright (C) 2015 Mellanox Technologies Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include "mlx_wol_rol.h"
#include "mlx_icmd.h"
#include "mlx_memory.h"
#include "mlx_bail.h"
mlx_status
mlx_set_wol (
IN mlx_utils *utils,
IN mlx_uint8 wol_mask
)
{
mlx_status status = MLX_SUCCESS;
struct mlx_wol_rol wol_rol;
if (utils == NULL) {
status = MLX_INVALID_PARAMETER;
goto bad_param;
}
mlx_memory_set(utils, &wol_rol, 0, sizeof(wol_rol));
wol_rol.wol_mode_valid = TRUE;
wol_rol.wol_mode = wol_mask;
status = mlx_icmd_send_command(
utils,
SET_WOL_ROL,
&wol_rol,
sizeof(wol_rol),
0
);
MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed");
icmd_err:
bad_param:
return status;
}
mlx_status
mlx_query_wol (
IN mlx_utils *utils,
OUT mlx_uint8 *wol_mask
)
{
mlx_status status = MLX_SUCCESS;
struct mlx_wol_rol wol_rol;
if (utils == NULL || wol_mask == NULL) {
status = MLX_INVALID_PARAMETER;
goto bad_param;
}
mlx_memory_set(utils, &wol_rol, 0, sizeof(wol_rol));
status = mlx_icmd_send_command(
utils,
QUERY_WOL_ROL,
&wol_rol,
0,
sizeof(wol_rol)
);
MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed");
*wol_mask = wol_rol.wol_mode;
icmd_err:
bad_param:
return status;
}

View File

@ -1,61 +0,0 @@
#ifndef MLX_WOL_ROL_H_
#define MLX_WOL_ROL_H_
/*
* Copyright (C) 2015 Mellanox Technologies Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include "mlx_utils.h"
typedef enum {
WOL_MODE_DISABLE = 0x0,
WOL_MODE_SECURE = 0x2,
WOL_MODE_MAGIC = 0x4,
WOL_MODE_ARP = 0x8,
WOL_MODE_BC = 0x10,
WOL_MODE_MC = 0x20,
WOL_MODE_UC = 0x40,
WOL_MODE_PHY = 0x80,
} WOL_MODE;
struct mlx_wol_rol {
mlx_uint32 reserved0 :32;
mlx_uint32 reserved1 :32;
mlx_uint32 wol_mode :8;
mlx_uint32 rol_mode :8;
mlx_uint32 reserved3 :14;
mlx_uint32 wol_mode_valid :1;
mlx_uint32 rol_mode_valid :1;
};
mlx_status
mlx_set_wol (
IN mlx_utils *utils,
IN mlx_uint8 wol_mask
);
mlx_status
mlx_query_wol (
IN mlx_utils *utils,
OUT mlx_uint8 *wol_mask
);
#endif /* MLX_WOL_ROL_H_ */

View File

@ -1,9 +0,0 @@
MlxDebugLogImpl()
{
DBGC((DEBUG),"");
}
MlxInfoLogImpl()
{
DBGC((INFO),"");
}
}

View File

@ -107,7 +107,7 @@ mlx_pci_mem_read(
status = MLX_INVALID_PARAMETER;
goto bail;
}
status = mlx_pci_mem_read_priv(utils, bar_index, width, offset, count, buffer);
status = mlx_pci_mem_read_priv(utils, width,bar_index, offset, count, buffer);
bail:
return status;
}

View File

@ -675,7 +675,7 @@ static int qib7322_init_send ( struct qib7322 *qib7322 ) {
rc = -ENOMEM;
goto err_alloc_sendbufavail;
}
memset ( qib7322->sendbufavail, 0, sizeof ( qib7322->sendbufavail ) );
memset ( qib7322->sendbufavail, 0, sizeof ( *qib7322->sendbufavail ) );
/* Program SendBufAvailAddr into the hardware */
memset ( &sendbufavailaddr, 0, sizeof ( sendbufavailaddr ) );

View File

@ -31,6 +31,7 @@
#include <ipxe/socket.h>
/* This hack prevents pre-2.6.32 headers from redefining struct sockaddr */
#define _SYS_SOCKET_H
#define __GLIBC__ 2
#include <linux/socket.h>
#include <linux/if.h>

View File

@ -31,6 +31,7 @@
#include <ipxe/socket.h>
/* This hack prevents pre-2.6.32 headers from redefining struct sockaddr */
#define _SYS_SOCKET_H
#define __GLIBC__ 2
#include <linux/socket.h>
#undef __GLIBC__
@ -39,6 +40,7 @@
#include <linux/if_tun.h>
#define RX_BUF_SIZE 1536
#define RX_QUOTA 4
/** @file
*
@ -126,6 +128,7 @@ static void tap_poll(struct net_device *netdev)
struct tap_nic * nic = netdev->priv;
struct pollfd pfd;
struct io_buffer * iobuf;
unsigned int quota = RX_QUOTA;
int r;
pfd.fd = nic->fd;
@ -143,7 +146,8 @@ static void tap_poll(struct net_device *netdev)
if (! iobuf)
goto allocfail;
while ((r = linux_read(nic->fd, iobuf->data, RX_BUF_SIZE)) > 0) {
while (quota-- &&
((r = linux_read(nic->fd, iobuf->data, RX_BUF_SIZE)) > 0)) {
DBGC2(nic, "tap %p read %d bytes\n", nic, r);
iob_put(iobuf, r);

View File

@ -26,7 +26,6 @@ FILE_LICENCE ( BSD2 );
#include <ipxe/net80211.h>
/* This block of functions are from kernel.h v3.0.1 */
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define BITS_PER_BYTE 8
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

View File

@ -34,8 +34,6 @@ FILE_LICENCE ( MIT );
#undef ERRFILE
#define ERRFILE ERRFILE_ath5k
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
/* RX/TX descriptor hw structs */
#include "desc.h"

View File

@ -104,10 +104,13 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
case AR5K_PKT_TYPE_BEACON:
case AR5K_PKT_TYPE_PROBE_RESP:
frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY;
break;
case AR5K_PKT_TYPE_PIFS:
frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS;
break;
default:
frame_type = type /*<< 2 ?*/;
break;
}
tx_ctl->tx_control_0 |=

View File

@ -640,12 +640,14 @@ static void ar5008_hw_init_chain_masks(struct ath_hw *ah)
case 0x5:
REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
AR_PHY_SWAP_ALT_CHAIN);
/* Fall through */
case 0x3:
if (ah->hw_version.macVersion == AR_SREV_REVISION_5416_10) {
REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7);
REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7);
break;
}
/* Fall through */
case 0x1:
case 0x2:
case 0x7:

View File

@ -122,6 +122,7 @@ static int ar9002_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
aModeRefSel = 2;
if (aModeRefSel)
break;
/* Fall through */
case 1:
default:
aModeRefSel = 0;

View File

@ -539,6 +539,7 @@ void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx)
case 0x5:
REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
AR_PHY_SWAP_ALT_CHAIN);
/* Fall through */
case 0x3:
case 0x1:
case 0x2:

View File

@ -101,13 +101,18 @@ int ecm_fetch_mac ( struct usb_device *usb,
}
/* Sanity check */
if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) )
if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) ) {
DBGC ( usb, "USB %s has invalid ECM MAC \"%s\"\n",
usb->name, buf );
return -EINVAL;
}
/* Decode MAC address */
len = base16_decode ( buf, hw_addr, ETH_ALEN );
if ( len < 0 ) {
rc = len;
DBGC ( usb, "USB %s could not decode ECM MAC \"%s\": %s\n",
usb->name, buf, strerror ( rc ) );
return rc;
}

View File

@ -202,7 +202,7 @@ static int nii_pci_open ( struct nii_nic *nii ) {
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *acpi;
void *resource;
} desc;
unsigned int bar;
int bar;
EFI_STATUS efirc;
int rc;
@ -230,7 +230,7 @@ static int nii_pci_open ( struct nii_nic *nii ) {
/* Identify memory and I/O BARs */
nii->mem_bar = PCI_MAX_BAR;
nii->io_bar = PCI_MAX_BAR;
for ( bar = 0 ; bar < PCI_MAX_BAR ; bar++ ) {
for ( bar = ( PCI_MAX_BAR - 1 ) ; bar >= 0 ; bar-- ) {
efirc = nii->pci_io->GetBarAttributes ( nii->pci_io, bar, NULL,
&desc.resource );
if ( efirc == EFI_UNSUPPORTED ) {
@ -402,7 +402,9 @@ static EFIAPI VOID nii_block ( UINT64 unique_id, UINT32 acquire ) {
*/
static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb,
size_t cpb_len, void *db, size_t db_len ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
PXE_CDB cdb;
UINTN tpl;
/* Prepare command descriptor block */
memset ( &cdb, 0, sizeof ( cdb ) );
@ -414,6 +416,9 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb,
cdb.DBsize = db_len;
cdb.IFnum = nii->nii->IfNum;
/* Raise task priority level */
tpl = bs->RaiseTPL ( TPL_CALLBACK );
/* Issue command */
DBGC2 ( nii, "NII %s issuing %02x:%04x ifnum %d%s%s\n",
nii->dev.name, cdb.OpCode, cdb.OpFlags, cdb.IFnum,
@ -424,6 +429,9 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb,
DBGC2_HD ( nii, db, db_len );
nii->issue ( ( intptr_t ) &cdb );
/* Restore task priority level */
bs->RestoreTPL ( tpl );
/* Check completion status */
if ( cdb.StatCode != PXE_STATCODE_SUCCESS )
return -cdb.StatCode;
@ -632,22 +640,6 @@ static int nii_initialise ( struct nii_nic *nii ) {
return nii_initialise_flags ( nii, flags );
}
/**
* Initialise UNDI and detect cable
*
* @v nii NII NIC
* @ret rc Return status code
*/
static int nii_initialise_and_detect ( struct nii_nic *nii ) {
unsigned int flags;
/* Initialise UNDI and detect cable. This is required to work
* around bugs in some Emulex NII drivers.
*/
flags = PXE_OPFLAGS_INITIALIZE_DETECT_CABLE;
return nii_initialise_flags ( nii, flags );
}
/**
* Shut down UNDI
*
@ -968,20 +960,32 @@ static void nii_poll ( struct net_device *netdev ) {
*/
static int nii_open ( struct net_device *netdev ) {
struct nii_nic *nii = netdev->priv;
unsigned int flags;
int rc;
/* Initialise NIC
*
* We don't care about link state here, and would prefer to
* have the NIC initialise even if no cable is present, to
* match the behaviour of all other iPXE drivers.
*
* Some Emulex NII drivers have a bug which prevents packets
* from being sent or received unless we specifically ask it
* to detect cable presence during initialisation. Work
* around these buggy drivers by requesting cable detection at
* this point, even though we don't care about link state here
* (and would prefer to have the NIC initialise even if no
* cable is present, to match the behaviour of all other iPXE
* drivers).
* to detect cable presence during initialisation.
*
* Unfortunately, some other NII drivers (e.g. Mellanox) may
* time out and report failure if asked to detect cable
* presence during initialisation on links that are physically
* slow to reach link-up.
*
* Attempt to work around both of these problems by requesting
* cable detection at this point if any only if the driver is
* not capable of reporting link status changes at runtime via
* PXE_OPCODE_GET_STATUS.
*/
if ( ( rc = nii_initialise_and_detect ( nii ) ) != 0 )
flags = ( nii->media ? PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE
: PXE_OPFLAGS_INITIALIZE_DETECT_CABLE );
if ( ( rc = nii_initialise_flags ( nii, flags ) ) != 0 )
goto err_initialise;
/* Attempt to set station address */

View File

@ -538,22 +538,19 @@ static int eoib_open ( struct net_device *netdev ) {
}
/* Allocate completion queue */
eoib->cq = ib_create_cq ( ibdev, EOIB_NUM_CQES, &eoib_cq_op );
if ( ! eoib->cq ) {
DBGC ( eoib, "EoIB %s could not allocate completion queue\n",
eoib->name );
rc = -ENOMEM;
if ( ( rc = ib_create_cq ( ibdev, EOIB_NUM_CQES, &eoib_cq_op,
&eoib->cq ) ) != 0 ) {
DBGC ( eoib, "EoIB %s could not create completion queue: %s\n",
eoib->name, strerror ( rc ) );
goto err_create_cq;
}
/* Allocate queue pair */
eoib->qp = ib_create_qp ( ibdev, IB_QPT_UD, EOIB_NUM_SEND_WQES,
if ( ( rc = ib_create_qp ( ibdev, IB_QPT_UD, EOIB_NUM_SEND_WQES,
eoib->cq, EOIB_NUM_RECV_WQES, eoib->cq,
&eoib_qp_op, netdev->name );
if ( ! eoib->qp ) {
DBGC ( eoib, "EoIB %s could not allocate queue pair\n",
eoib->name );
rc = -ENOMEM;
&eoib_qp_op, netdev->name, &eoib->qp ) )!=0){
DBGC ( eoib, "EoIB %s could not create queue pair: %s\n",
eoib->name, strerror ( rc ) );
goto err_create_qp;
}
ib_qp_set_ownerdata ( eoib->qp, eoib );
@ -870,8 +867,9 @@ static void eoib_duplicate ( struct eoib_device *eoib,
err_post_send:
err_path:
list_del ( &copy->list );
err_alloc:
netdev_tx_complete_err ( netdev, copy, rc );
netdev_tx_err ( netdev, copy, rc );
}
/**

View File

@ -0,0 +1,915 @@
/*
* Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/if_ether.h>
#include <ipxe/iobuf.h>
#include <ipxe/malloc.h>
#include <ipxe/umalloc.h>
#include <ipxe/pci.h>
#include "exanic.h"
/** @file
*
* Exablaze ExaNIC driver
*
*/
/* Disambiguate the various error causes */
#define EIO_ABORTED __einfo_error ( EINFO_EIO_ABORTED )
#define EINFO_EIO_ABORTED \
__einfo_uniqify ( EINFO_EIO, 0x01, "Frame aborted" )
#define EIO_CORRUPT __einfo_error ( EINFO_EIO_CORRUPT )
#define EINFO_EIO_CORRUPT \
__einfo_uniqify ( EINFO_EIO, 0x02, "CRC incorrect" )
#define EIO_HWOVFL __einfo_error ( EINFO_EIO_HWOVFL )
#define EINFO_EIO_HWOVFL \
__einfo_uniqify ( EINFO_EIO, 0x03, "Hardware overflow" )
#define EIO_STATUS( status ) \
EUNIQ ( EINFO_EIO, ( (status) & EXANIC_STATUS_ERROR_MASK ), \
EIO_ABORTED, EIO_CORRUPT, EIO_HWOVFL )
/**
* Write DMA base address register
*
* @v addr DMA base address
* @v reg Register
*/
static void exanic_write_base ( physaddr_t addr, void *reg ) {
uint32_t lo;
uint32_t hi;
/* Write high and low registers, setting flags as appropriate */
lo = addr;
if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) {
/* 64-bit build; may be a 32-bit or 64-bit address */
hi = ( ( ( uint64_t ) addr ) >> 32 );
if ( ! hi )
lo |= EXANIC_DMA_32_BIT;
} else {
/* 32-bit build; always a 32-bit address */
hi = 0;
lo |= EXANIC_DMA_32_BIT;
}
writel ( hi, ( reg + 0 ) );
writel ( lo, ( reg + 4 ) );
}
/**
* Clear DMA base address register
*
* @v reg Register
*/
static inline void exanic_clear_base ( void *reg ) {
/* Clear both high and low registers */
writel ( 0, ( reg + 0 ) );
writel ( 0, ( reg + 4 ) );
}
/******************************************************************************
*
* Device reset
*
******************************************************************************
*/
/**
* Reset hardware
*
* @v exanic ExaNIC device
*/
static void exanic_reset ( struct exanic *exanic ) {
void *port_regs;
unsigned int i;
/* Disable all possible ports */
for ( i = 0 ; i < EXANIC_MAX_PORTS ; i++ ) {
port_regs = ( exanic->regs + EXANIC_PORT_REGS ( i ) );
writel ( 0, ( port_regs + EXANIC_PORT_ENABLE ) );
writel ( 0, ( port_regs + EXANIC_PORT_IRQ ) );
exanic_clear_base ( port_regs + EXANIC_PORT_RX_BASE );
}
/* Disable transmit feedback */
exanic_clear_base ( exanic->regs + EXANIC_TXF_BASE );
}
/******************************************************************************
*
* MAC address
*
******************************************************************************
*/
/**
* Read I2C line status
*
* @v basher Bit-bashing interface
* @v bit_id Bit number
* @ret zero Input is a logic 0
* @ret non-zero Input is a logic 1
*/
static int exanic_i2c_read_bit ( struct bit_basher *basher,
unsigned int bit_id ) {
struct exanic *exanic =
container_of ( basher, struct exanic, basher.basher );
unsigned int shift;
uint32_t i2c;
/* Identify bit */
assert ( bit_id == I2C_BIT_SDA );
shift = exanic->i2cfg.getsda;
/* Read I2C register */
DBG_DISABLE ( DBGLVL_IO );
i2c = readl ( exanic->regs + EXANIC_I2C );
DBG_ENABLE ( DBGLVL_IO );
return ( ( i2c >> shift ) & 1 );
}
/**
* Write I2C line status
*
* @v basher Bit-bashing interface
* @v bit_id Bit number
* @v data Value to write
*/
static void exanic_i2c_write_bit ( struct bit_basher *basher,
unsigned int bit_id, unsigned long data ) {
struct exanic *exanic =
container_of ( basher, struct exanic, basher.basher );
unsigned int shift;
uint32_t mask;
uint32_t i2c;
/* Identify shift */
assert ( ( bit_id == I2C_BIT_SCL ) || ( bit_id == I2C_BIT_SDA ) );
shift = ( ( bit_id == I2C_BIT_SCL ) ?
exanic->i2cfg.setscl : exanic->i2cfg.setsda );
mask = ( 1UL << shift );
/* Modify I2C register */
DBG_DISABLE ( DBGLVL_IO );
i2c = readl ( exanic->regs + EXANIC_I2C );
i2c &= ~mask;
if ( ! data )
i2c |= mask;
writel ( i2c, ( exanic->regs + EXANIC_I2C ) );
DBG_ENABLE ( DBGLVL_IO );
}
/** I2C bit-bashing interface operations */
static struct bit_basher_operations exanic_i2c_basher_ops = {
.read = exanic_i2c_read_bit,
.write = exanic_i2c_write_bit,
};
/** Possible I2C bus configurations */
static struct exanic_i2c_config exanic_i2cfgs[] = {
/* X2/X10 */
{ .setscl = 7, .setsda = 4, .getsda = 12 },
/* X4 */
{ .setscl = 7, .setsda = 5, .getsda = 13 },
};
/**
* Initialise EEPROM
*
* @v exanic ExaNIC device
* @v i2cfg I2C bus configuration
* @ret rc Return status code
*/
static int exanic_try_init_eeprom ( struct exanic *exanic,
struct exanic_i2c_config *i2cfg ) {
int rc;
/* Configure I2C bus */
memcpy ( &exanic->i2cfg, i2cfg, sizeof ( exanic->i2cfg ) );
/* Initialise I2C bus */
if ( ( rc = init_i2c_bit_basher ( &exanic->basher,
&exanic_i2c_basher_ops ) ) != 0 ) {
DBGC2 ( exanic, "EXANIC %p found no I2C bus via %d/%d/%d\n",
exanic, exanic->i2cfg.setscl,
exanic->i2cfg.setsda, exanic->i2cfg.getsda );
return rc;
}
/* Check for EEPROM presence */
init_i2c_eeprom ( &exanic->eeprom, EXANIC_EEPROM_ADDRESS );
if ( ( rc = i2c_check_presence ( &exanic->basher.i2c,
&exanic->eeprom ) ) != 0 ) {
DBGC2 ( exanic, "EXANIC %p found no EEPROM via %d/%d/%d\n",
exanic, exanic->i2cfg.setscl,
exanic->i2cfg.setsda, exanic->i2cfg.getsda );
return rc;
}
DBGC ( exanic, "EXANIC %p found EEPROM via %d/%d/%d\n",
exanic, exanic->i2cfg.setscl,
exanic->i2cfg.setsda, exanic->i2cfg.getsda );
return 0;
}
/**
* Initialise EEPROM
*
* @v exanic ExaNIC device
* @ret rc Return status code
*/
static int exanic_init_eeprom ( struct exanic *exanic ) {
struct exanic_i2c_config *i2cfg;
unsigned int i;
int rc;
/* Try all possible bus configurations */
for ( i = 0 ; i < ( sizeof ( exanic_i2cfgs ) /
sizeof ( exanic_i2cfgs[0] ) ) ; i++ ) {
i2cfg = &exanic_i2cfgs[i];
if ( ( rc = exanic_try_init_eeprom ( exanic, i2cfg ) ) == 0 )
return 0;
}
DBGC ( exanic, "EXANIC %p found no EEPROM\n", exanic );
return -ENODEV;
}
/**
* Fetch base MAC address
*
* @v exanic ExaNIC device
* @ret rc Return status code
*/
static int exanic_fetch_mac ( struct exanic *exanic ) {
struct i2c_interface *i2c = &exanic->basher.i2c;
int rc;
/* Initialise EEPROM */
if ( ( rc = exanic_init_eeprom ( exanic ) ) != 0 )
return rc;
/* Fetch base MAC address */
if ( ( rc = i2c->read ( i2c, &exanic->eeprom, 0, exanic->mac,
sizeof ( exanic->mac ) ) ) != 0 ) {
DBGC ( exanic, "EXANIC %p could not read MAC address: %s\n",
exanic, strerror ( rc ) );
return rc;
}
return 0;
}
/******************************************************************************
*
* Link state
*
******************************************************************************
*/
/**
* Check link state
*
* @v netdev Network device
*/
static void exanic_check_link ( struct net_device *netdev ) {
struct exanic_port *port = netdev->priv;
uint32_t status;
uint32_t speed;
/* Report port status changes */
status = readl ( port->regs + EXANIC_PORT_STATUS );
speed = readl ( port->regs + EXANIC_PORT_SPEED );
if ( status != port->status ) {
DBGC ( port, "EXANIC %s port status %#08x speed %dMbps\n",
netdev->name, status, speed );
if ( status & EXANIC_PORT_STATUS_LINK ) {
netdev_link_up ( netdev );
} else {
netdev_link_down ( netdev );
}
port->status = status;
}
}
/**
* Check link state periodically
*
* @v retry Link state check timer
* @v over Failure indicator
*/
static void exanic_expired ( struct retry_timer *timer, int over __unused ) {
struct exanic_port *port =
container_of ( timer, struct exanic_port, timer );
struct net_device *netdev = port->netdev;
static const uint32_t speeds[] = {
100, 1000, 10000, 40000, 100000,
};
unsigned int index;
/* Restart timer */
start_timer_fixed ( timer, EXANIC_LINK_INTERVAL );
/* Check link state */
exanic_check_link ( netdev );
/* Do nothing further if link is already up */
if ( netdev_link_ok ( netdev ) )
return;
/* Do nothing further unless we have a valid list of supported speeds */
if ( ! port->speeds )
return;
/* Autonegotiation is not supported; try manually selecting
* the next supported link speed.
*/
do {
if ( ! port->speed )
port->speed = ( 8 * sizeof ( port->speeds ) );
port->speed--;
} while ( ! ( ( 1UL << port->speed ) & port->speeds ) );
index = ( port->speed - ( ffs ( EXANIC_CAPS_SPEED_MASK ) - 1 ) );
assert ( index < ( sizeof ( speeds ) / sizeof ( speeds[0] ) ) );
/* Attempt the selected speed */
DBGC ( netdev, "EXANIC %s attempting %dMbps\n",
netdev->name, speeds[index] );
writel ( speeds[index], ( port->regs + EXANIC_PORT_SPEED ) );
}
/******************************************************************************
*
* Network device interface
*
******************************************************************************
*/
/**
* Open network device
*
* @v netdev Network device
* @ret rc Return status code
*/
static int exanic_open ( struct net_device *netdev ) {
struct exanic_port *port = netdev->priv;
struct exanic_tx_chunk *tx;
unsigned int i;
/* Reset transmit region contents */
for ( i = 0 ; i < port->tx_count ; i++ ) {
tx = ( port->tx + ( i * sizeof ( *tx ) ) );
writew ( port->txf_slot, &tx->desc.txf_slot );
writeb ( EXANIC_TYPE_RAW, &tx->desc.type );
writeb ( 0, &tx->desc.flags );
writew ( 0, &tx->pad );
}
/* Reset receive region contents */
memset_user ( port->rx, 0, 0xff, EXANIC_RX_LEN );
/* Reset transmit feedback region */
*(port->txf) = 0;
/* Reset counters */
port->tx_prod = 0;
port->tx_cons = 0;
port->rx_cons = 0;
/* Map receive region */
exanic_write_base ( phys_to_bus ( user_to_phys ( port->rx, 0 ) ),
( port->regs + EXANIC_PORT_RX_BASE ) );
/* Enable promiscuous mode */
writel ( EXANIC_PORT_FLAGS_PROMISC,
( port->regs + EXANIC_PORT_FLAGS ) );
/* Reset to default speed and clear cached status */
writel ( port->default_speed, ( port->regs + EXANIC_PORT_SPEED ) );
port->speed = 0;
port->status = 0;
/* Enable port */
wmb();
writel ( EXANIC_PORT_ENABLE_ENABLED,
( port->regs + EXANIC_PORT_ENABLE ) );
/* Start link state timer */
start_timer_fixed ( &port->timer, EXANIC_LINK_INTERVAL );
return 0;
}
/**
* Close network device
*
* @v netdev Network device
*/
static void exanic_close ( struct net_device *netdev ) {
struct exanic_port *port = netdev->priv;
/* Stop link state timer */
stop_timer ( &port->timer );
/* Disable port */
writel ( 0, ( port->regs + EXANIC_PORT_ENABLE ) );
wmb();
/* Clear receive region */
exanic_clear_base ( port->regs + EXANIC_PORT_RX_BASE );
/* Discard any in-progress receive */
if ( port->rx_iobuf ) {
netdev_rx_err ( netdev, port->rx_iobuf, -ECANCELED );
port->rx_iobuf = NULL;
}
}
/**
* Transmit packet
*
* @v netdev Network device
* @v iobuf I/O buffer
* @ret rc Return status code
*/
static int exanic_transmit ( struct net_device *netdev,
struct io_buffer *iobuf ) {
struct exanic_port *port = netdev->priv;
struct exanic_tx_chunk *tx;
unsigned int tx_fill;
unsigned int tx_index;
size_t offset;
size_t len;
uint8_t *src;
uint8_t *dst;
/* Sanity check */
len = iob_len ( iobuf );
if ( len > sizeof ( tx->data ) ) {
DBGC ( port, "EXANIC %s transmit too large\n", netdev->name );
return -ENOTSUP;
}
/* Get next transmit descriptor */
tx_fill = ( port->tx_prod - port->tx_cons );
if ( tx_fill >= port->tx_count ) {
DBGC ( port, "EXANIC %s out of transmit descriptors\n",
netdev->name );
return -ENOBUFS;
}
tx_index = ( port->tx_prod & ( port->tx_count - 1 ) );
offset = ( tx_index * sizeof ( *tx ) );
tx = ( port->tx + offset );
DBGC2 ( port, "EXANIC %s TX %04x at [%05zx,%05zx)\n",
netdev->name, port->tx_prod, ( port->tx_offset + offset ),
( port->tx_offset + offset +
offsetof ( typeof ( *tx ), data ) + len ) );
port->tx_prod++;
/* Populate transmit descriptor */
writew ( port->tx_prod, &tx->desc.txf_id );
writew ( ( sizeof ( tx->pad ) + len ), &tx->desc.len );
/* Copy data to transmit region. There is no DMA on the
* transmit data path.
*/
src = iobuf->data;
dst = tx->data;
while ( len-- )
writeb ( *(src++), dst++ );
/* Send transmit command */
wmb();
writel ( ( port->tx_offset + offset ),
( port->regs + EXANIC_PORT_TX_COMMAND ) );
return 0;
}
/**
* Poll for completed packets
*
* @v netdev Network device
*/
static void exanic_poll_tx ( struct net_device *netdev ) {
struct exanic_port *port = netdev->priv;
/* Report any completed packets */
while ( port->tx_cons != *(port->txf) ) {
DBGC2 ( port, "EXANIC %s TX %04x complete\n",
netdev->name, port->tx_cons );
netdev_tx_complete_next ( netdev );
port->tx_cons++;
}
}
/**
* Poll for received packets
*
* @v netdev Network device
*/
static void exanic_poll_rx ( struct net_device *netdev ) {
struct exanic_port *port = netdev->priv;
struct exanic_rx_chunk *rx;
struct exanic_rx_descriptor desc;
uint8_t current;
uint8_t previous;
size_t offset;
size_t len;
for ( ; ; port->rx_cons++ ) {
/* Fetch descriptor */
offset = ( ( port->rx_cons * sizeof ( *rx ) ) % EXANIC_RX_LEN );
copy_from_user ( &desc, port->rx,
( offset + offsetof ( typeof ( *rx ), desc ) ),
sizeof ( desc ) );
/* Calculate generation */
current = ( port->rx_cons / ( EXANIC_RX_LEN / sizeof ( *rx ) ));
previous = ( current - 1 );
/* Do nothing if no chunk is ready */
if ( desc.generation == previous )
break;
/* Allocate I/O buffer if needed */
if ( ! port->rx_iobuf ) {
port->rx_iobuf = alloc_iob ( EXANIC_MAX_RX_LEN );
if ( ! port->rx_iobuf ) {
/* Wait for next poll */
break;
}
port->rx_rc = 0;
}
/* Calculate chunk length */
len = ( desc.len ? desc.len : sizeof ( rx->data ) );
/* Append data to I/O buffer */
if ( len <= iob_tailroom ( port->rx_iobuf ) ) {
copy_from_user ( iob_put ( port->rx_iobuf, len ),
port->rx,
( offset + offsetof ( typeof ( *rx ),
data ) ), len );
} else {
DBGC ( port, "EXANIC %s RX too large\n",
netdev->name );
port->rx_rc = -ERANGE;
}
/* Check for overrun */
rmb();
copy_from_user ( &desc.generation, port->rx,
( offset + offsetof ( typeof ( *rx ),
desc.generation ) ),
sizeof ( desc.generation ) );
if ( desc.generation != current ) {
DBGC ( port, "EXANIC %s RX overrun\n", netdev->name );
port->rx_rc = -ENOBUFS;
continue;
}
/* Wait for end of packet */
if ( ! desc.len )
continue;
/* Check for receive errors */
if ( desc.status & EXANIC_STATUS_ERROR_MASK ) {
port->rx_rc = -EIO_STATUS ( desc.status );
DBGC ( port, "EXANIC %s RX %04x error: %s\n",
netdev->name, port->rx_cons,
strerror ( port->rx_rc ) );
} else {
DBGC2 ( port, "EXANIC %s RX %04x\n",
netdev->name, port->rx_cons );
}
/* Hand off to network stack */
if ( port->rx_rc ) {
netdev_rx_err ( netdev, port->rx_iobuf, port->rx_rc );
} else {
iob_unput ( port->rx_iobuf, 4 /* strip CRC */ );
netdev_rx ( netdev, port->rx_iobuf );
}
port->rx_iobuf = NULL;
}
}
/**
* Poll for completed and received packets
*
* @v netdev Network device
*/
static void exanic_poll ( struct net_device *netdev ) {
/* Poll for completed packets */
exanic_poll_tx ( netdev );
/* Poll for received packets */
exanic_poll_rx ( netdev );
}
/** ExaNIC network device operations */
static struct net_device_operations exanic_operations = {
.open = exanic_open,
.close = exanic_close,
.transmit = exanic_transmit,
.poll = exanic_poll,
};
/******************************************************************************
*
* PCI interface
*
******************************************************************************
*/
/**
* Probe port
*
* @v exanic ExaNIC device
* @v dev Parent device
* @v index Port number
* @ret rc Return status code
*/
static int exanic_probe_port ( struct exanic *exanic, struct device *dev,
unsigned int index ) {
struct net_device *netdev;
struct exanic_port *port;
void *port_regs;
uint32_t status;
size_t tx_len;
int rc;
/* Do nothing if port is not physically present */
port_regs = ( exanic->regs + EXANIC_PORT_REGS ( index ) );
status = readl ( port_regs + EXANIC_PORT_STATUS );
tx_len = readl ( port_regs + EXANIC_PORT_TX_LEN );
if ( ( status & EXANIC_PORT_STATUS_ABSENT ) || ( tx_len == 0 ) ) {
rc = 0;
goto absent;
}
/* Allocate network device */
netdev = alloc_etherdev ( sizeof ( *port ) );
if ( ! netdev ) {
rc = -ENOMEM;
goto err_alloc_netdev;
}
netdev_init ( netdev, &exanic_operations );
netdev->dev = dev;
port = netdev->priv;
memset ( port, 0, sizeof ( *port ) );
exanic->port[index] = port;
port->netdev = netdev;
port->regs = port_regs;
timer_init ( &port->timer, exanic_expired, &netdev->refcnt );
/* Identify transmit region */
port->tx_offset = readl ( port->regs + EXANIC_PORT_TX_OFFSET );
if ( tx_len > EXANIC_MAX_TX_LEN )
tx_len = EXANIC_MAX_TX_LEN;
assert ( ! ( tx_len & ( tx_len - 1 ) ) );
port->tx = ( exanic->tx + port->tx_offset );
port->tx_count = ( tx_len / sizeof ( struct exanic_tx_chunk ) );
/* Identify transmit feedback region */
port->txf_slot = EXANIC_TXF_SLOT ( index );
port->txf = ( exanic->txf +
( port->txf_slot * sizeof ( *(port->txf) ) ) );
/* Allocate receive region (via umalloc()) */
port->rx = umalloc ( EXANIC_RX_LEN );
if ( ! port->rx ) {
rc = -ENOMEM;
goto err_alloc_rx;
}
/* Set MAC address */
memcpy ( netdev->hw_addr, exanic->mac, ETH_ALEN );
netdev->hw_addr[ ETH_ALEN - 1 ] += index;
/* Record default link speed and supported speeds */
port->default_speed = readl ( port->regs + EXANIC_PORT_SPEED );
port->speeds = ( exanic->caps & EXANIC_CAPS_SPEED_MASK );
/* Register network device */
if ( ( rc = register_netdev ( netdev ) ) != 0 )
goto err_register_netdev;
DBGC ( port, "EXANIC %s port %d TX [%#05zx,%#05zx) TXF %#02x RX "
"[%#lx,%#lx)\n", netdev->name, index, port->tx_offset,
( port->tx_offset + tx_len ), port->txf_slot,
user_to_phys ( port->rx, 0 ),
user_to_phys ( port->rx, EXANIC_RX_LEN ) );
/* Set initial link state */
exanic_check_link ( netdev );
return 0;
unregister_netdev ( netdev );
err_register_netdev:
ufree ( port->rx );
err_alloc_rx:
netdev_nullify ( netdev );
netdev_put ( netdev );
err_alloc_netdev:
absent:
return rc;
}
/**
* Probe port
*
* @v exanic ExaNIC device
* @v index Port number
*/
static void exanic_remove_port ( struct exanic *exanic, unsigned int index ) {
struct exanic_port *port;
/* Do nothing if port is not physically present */
port = exanic->port[index];
if ( ! port )
return;
/* Unregister network device */
unregister_netdev ( port->netdev );
/* Free receive region */
ufree ( port->rx );
/* Free network device */
netdev_nullify ( port->netdev );
netdev_put ( port->netdev );
}
/**
* Probe PCI device
*
* @v pci PCI device
* @ret rc Return status code
*/
static int exanic_probe ( struct pci_device *pci ) {
struct exanic *exanic;
unsigned long regs_bar_start;
unsigned long tx_bar_start;
size_t tx_bar_len;
int i;
int rc;
/* Allocate and initialise structure */
exanic = zalloc ( sizeof ( *exanic ) );
if ( ! exanic ) {
rc = -ENOMEM;
goto err_alloc;
}
pci_set_drvdata ( pci, exanic );
/* Fix up PCI device */
adjust_pci_device ( pci );
/* Map registers */
regs_bar_start = pci_bar_start ( pci, EXANIC_REGS_BAR );
exanic->regs = ioremap ( regs_bar_start, EXANIC_REGS_LEN );
if ( ! exanic->regs ) {
rc = -ENODEV;
goto err_ioremap_regs;
}
/* Reset device */
exanic_reset ( exanic );
/* Read capabilities */
exanic->caps = readl ( exanic->regs + EXANIC_CAPS );
/* Power up PHYs */
writel ( EXANIC_POWER_ON, ( exanic->regs + EXANIC_POWER ) );
/* Fetch base MAC address */
if ( ( rc = exanic_fetch_mac ( exanic ) ) != 0 )
goto err_fetch_mac;
DBGC ( exanic, "EXANIC %p capabilities %#08x base MAC %s\n",
exanic, exanic->caps, eth_ntoa ( exanic->mac ) );
/* Map transmit region */
tx_bar_start = pci_bar_start ( pci, EXANIC_TX_BAR );
tx_bar_len = pci_bar_size ( pci, EXANIC_TX_BAR );
exanic->tx = ioremap ( tx_bar_start, tx_bar_len );
if ( ! exanic->tx ) {
rc = -ENODEV;
goto err_ioremap_tx;
}
/* Allocate transmit feedback region (shared between all ports) */
exanic->txf = malloc_dma ( EXANIC_TXF_LEN, EXANIC_ALIGN );
if ( ! exanic->txf ) {
rc = -ENOMEM;
goto err_alloc_txf;
}
memset ( exanic->txf, 0, EXANIC_TXF_LEN );
exanic_write_base ( virt_to_bus ( exanic->txf ),
( exanic->regs + EXANIC_TXF_BASE ) );
/* Allocate and initialise per-port network devices */
for ( i = 0 ; i < EXANIC_MAX_PORTS ; i++ ) {
if ( ( rc = exanic_probe_port ( exanic, &pci->dev, i ) ) != 0 )
goto err_probe_port;
}
return 0;
i = EXANIC_MAX_PORTS;
err_probe_port:
for ( i-- ; i >= 0 ; i-- )
exanic_remove_port ( exanic, i );
exanic_reset ( exanic );
free_dma ( exanic->txf, EXANIC_TXF_LEN );
err_alloc_txf:
iounmap ( exanic->tx );
err_ioremap_tx:
iounmap ( exanic->regs );
err_fetch_mac:
err_ioremap_regs:
free ( exanic );
err_alloc:
return rc;
}
/**
* Remove PCI device
*
* @v pci PCI device
*/
static void exanic_remove ( struct pci_device *pci ) {
struct exanic *exanic = pci_get_drvdata ( pci );
unsigned int i;
/* Remove all ports */
for ( i = 0 ; i < EXANIC_MAX_PORTS ; i++ )
exanic_remove_port ( exanic, i );
/* Reset device */
exanic_reset ( exanic );
/* Free transmit feedback region */
free_dma ( exanic->txf, EXANIC_TXF_LEN );
/* Unmap transmit region */
iounmap ( exanic->tx );
/* Unmap registers */
iounmap ( exanic->regs );
/* Free device */
free ( exanic );
}
/** ExaNIC PCI device IDs */
static struct pci_device_id exanic_ids[] = {
PCI_ROM ( 0x10ee, 0x2b00, "exanic-old", "ExaNIC (old)", 0 ),
PCI_ROM ( 0x1ce4, 0x0001, "exanic-x4", "ExaNIC X4", 0 ),
PCI_ROM ( 0x1ce4, 0x0002, "exanic-x2", "ExaNIC X2", 0 ),
PCI_ROM ( 0x1ce4, 0x0003, "exanic-x10", "ExaNIC X10", 0 ),
PCI_ROM ( 0x1ce4, 0x0004, "exanic-x10gm", "ExaNIC X10 GM", 0 ),
PCI_ROM ( 0x1ce4, 0x0005, "exanic-x40", "ExaNIC X40", 0 ),
PCI_ROM ( 0x1ce4, 0x0006, "exanic-x10hpt", "ExaNIC X10 HPT", 0 ),
PCI_ROM ( 0x1ce4, 0x0007, "exanic-x40g", "ExaNIC X40", 0 ),
};
/** ExaNIC PCI driver */
struct pci_driver exanic_driver __pci_driver = {
.ids = exanic_ids,
.id_count = ( sizeof ( exanic_ids ) / sizeof ( exanic_ids[0] ) ),
.probe = exanic_probe,
.remove = exanic_remove,
};

View File

@ -0,0 +1,261 @@
#ifndef _EXANIC_H
#define _EXANIC_H
/** @file
*
* Exablaze ExaNIC driver
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/pci.h>
#include <ipxe/ethernet.h>
#include <ipxe/uaccess.h>
#include <ipxe/retry.h>
#include <ipxe/i2c.h>
#include <ipxe/bitbash.h>
/** Maximum number of ports */
#define EXANIC_MAX_PORTS 8
/** Register BAR */
#define EXANIC_REGS_BAR PCI_BASE_ADDRESS_0
/** Transmit region BAR */
#define EXANIC_TX_BAR PCI_BASE_ADDRESS_2
/** Alignment for DMA regions */
#define EXANIC_ALIGN 0x1000
/** Flag for 32-bit DMA addresses */
#define EXANIC_DMA_32_BIT 0x00000001UL
/** Register set length */
#define EXANIC_REGS_LEN 0x2000
/** Transmit feedback region length */
#define EXANIC_TXF_LEN 0x1000
/** Transmit feedback slot
*
* This is a policy decision.
*/
#define EXANIC_TXF_SLOT( index ) ( 0x40 * (index) )
/** Receive region length */
#define EXANIC_RX_LEN 0x200000
/** Transmit feedback base address register */
#define EXANIC_TXF_BASE 0x0014
/** Capabilities register */
#define EXANIC_CAPS 0x0038
#define EXANIC_CAPS_100M 0x01000000UL /**< 100Mbps supported */
#define EXANIC_CAPS_1G 0x02000000UL /**< 1Gbps supported */
#define EXANIC_CAPS_10G 0x04000000UL /**< 10Gbps supported */
#define EXANIC_CAPS_40G 0x08000000UL /**< 40Gbps supported */
#define EXANIC_CAPS_100G 0x10000000UL /**< 100Gbps supported */
#define EXANIC_CAPS_SPEED_MASK 0x1f000000UL /**< Supported speeds mask */
/** I2C GPIO register */
#define EXANIC_I2C 0x012c
/** Power control register */
#define EXANIC_POWER 0x0138
#define EXANIC_POWER_ON 0x000000f0UL /**< Power on PHYs */
/** Port register offset */
#define EXANIC_PORT_REGS( index ) ( 0x0200 + ( 0x40 * (index) ) )
/** Port enable register */
#define EXANIC_PORT_ENABLE 0x0000
#define EXANIC_PORT_ENABLE_ENABLED 0x00000001UL /**< Port is enabled */
/** Port speed register */
#define EXANIC_PORT_SPEED 0x0004
/** Port status register */
#define EXANIC_PORT_STATUS 0x0008
#define EXANIC_PORT_STATUS_LINK 0x00000008UL /**< Link is up */
#define EXANIC_PORT_STATUS_ABSENT 0x80000000UL /**< Port is not present */
/** Port MAC address (second half) register */
#define EXANIC_PORT_MAC 0x000c
/** Port flags register */
#define EXANIC_PORT_FLAGS 0x0010
#define EXANIC_PORT_FLAGS_PROMISC 0x00000001UL /**< Promiscuous mode */
/** Port receive chunk base address register */
#define EXANIC_PORT_RX_BASE 0x0014
/** Port transmit command register */
#define EXANIC_PORT_TX_COMMAND 0x0020
/** Port transmit region offset register */
#define EXANIC_PORT_TX_OFFSET 0x0024
/** Port transmit region length register */
#define EXANIC_PORT_TX_LEN 0x0028
/** Port MAC address (first half) register */
#define EXANIC_PORT_OUI 0x0030
/** Port interrupt configuration register */
#define EXANIC_PORT_IRQ 0x0034
/** An ExaNIC transmit chunk descriptor */
struct exanic_tx_descriptor {
/** Feedback ID */
uint16_t txf_id;
/** Feedback slot */
uint16_t txf_slot;
/** Payload length (including padding */
uint16_t len;
/** Payload type */
uint8_t type;
/** Flags */
uint8_t flags;
} __attribute__ (( packed ));
/** An ExaNIC transmit chunk */
struct exanic_tx_chunk {
/** Descriptor */
struct exanic_tx_descriptor desc;
/** Padding */
uint8_t pad[2];
/** Payload data */
uint8_t data[2038];
} __attribute__ (( packed ));
/** Raw Ethernet frame type */
#define EXANIC_TYPE_RAW 0x01
/** An ExaNIC receive chunk descriptor */
struct exanic_rx_descriptor {
/** Timestamp */
uint32_t timestamp;
/** Status (valid only on final chunk) */
uint8_t status;
/** Length (zero except on the final chunk) */
uint8_t len;
/** Filter number */
uint8_t filter;
/** Generation */
uint8_t generation;
} __attribute__ (( packed ));
/** An ExaNIC receive chunk */
struct exanic_rx_chunk {
/** Payload data */
uint8_t data[120];
/** Descriptor */
struct exanic_rx_descriptor desc;
} __attribute__ (( packed ));
/** Receive status error mask */
#define EXANIC_STATUS_ERROR_MASK 0x0f
/** An ExaNIC I2C bus configuration */
struct exanic_i2c_config {
/** GPIO bit for pulling SCL low */
uint8_t setscl;
/** GPIO bit for pulling SDA low */
uint8_t setsda;
/** GPIO bit for reading SDA */
uint8_t getsda;
};
/** EEPROM address */
#define EXANIC_EEPROM_ADDRESS 0x50
/** An ExaNIC port */
struct exanic_port {
/** Network device */
struct net_device *netdev;
/** Port registers */
void *regs;
/** Transmit region offset */
size_t tx_offset;
/** Transmit region */
void *tx;
/** Number of transmit descriptors */
uint16_t tx_count;
/** Transmit producer counter */
uint16_t tx_prod;
/** Transmit consumer counter */
uint16_t tx_cons;
/** Transmit feedback slot */
uint16_t txf_slot;
/** Transmit feedback region */
uint16_t *txf;
/** Receive region */
userptr_t rx;
/** Receive consumer counter */
unsigned int rx_cons;
/** Receive I/O buffer (if any) */
struct io_buffer *rx_iobuf;
/** Receive status */
int rx_rc;
/** Port status */
uint32_t status;
/** Default link speed (as raw register value) */
uint32_t default_speed;
/** Speed capability bitmask */
uint32_t speeds;
/** Current attempted link speed (as a capability bit index) */
unsigned int speed;
/** Port status check timer */
struct retry_timer timer;
};
/** An ExaNIC */
struct exanic {
/** Registers */
void *regs;
/** Transmit region */
void *tx;
/** Transmit feedback region */
void *txf;
/** I2C bus configuration */
struct exanic_i2c_config i2cfg;
/** I2C bit-bashing interface */
struct i2c_bit_basher basher;
/** I2C serial EEPROM */
struct i2c_device eeprom;
/** Capabilities */
uint32_t caps;
/** Base MAC address */
uint8_t mac[ETH_ALEN];
/** Ports */
struct exanic_port *port[EXANIC_MAX_PORTS];
};
/** Maximum used length of transmit region
*
* This is a policy decision to avoid overflowing the 16-bit transmit
* producer and consumer counters.
*/
#define EXANIC_MAX_TX_LEN ( 256 * sizeof ( struct exanic_tx_chunk ) )
/** Maximum length of received packet
*
* This is a policy decision.
*/
#define EXANIC_MAX_RX_LEN ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ )
/** Interval between link state checks
*
* This is a policy decision.
*/
#define EXANIC_LINK_INTERVAL ( 1 * TICKS_PER_SEC )
#endif /* _EXANIC_H */

Some files were not shown because too many files have changed in this diff Show More